T09 · Insecure Skill Coding Practices
Error
- Location
- SKILL.md:76
- Finding
- DOM XSS Through Unsanitized Choropleth Feature Properties<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:76-96` **Vulnerability Type**: DOM-based cross-site scripting through unsafe HTML generation **Risk Level**: High ### Vulnerable Code ```javascript // Add hover effect with reusable popup const popup = new mapboxgl.Popup({ closeButton: false, closeOnClick: false }); map.on('mousemove', 'states-layer', (e) => { if (e.features.length > 0) { map.getCanvas().style.cursor = 'pointer'; const feature = e.features[0]; popup .setLngLat(e.lngLat) .setHTML( ` <h3>${feature.properties.name}</h3> <p>Population: ${feature.properties.population.toLocaleString()}</p> ` ) .addTo(map); } }); ``` ### Technical Analysis The example inserts GeoJSON feature properties into an HTML template and passes the result to `mapboxgl.Popup#setHTML()`. The `name` property is not encoded, sanitized, or constrained before the browser parses it as markup. GeoJSON can originate from remote services or user-controlled datasets. If an attacker can influence `feature.properties.name`, they can supply HTML containing executable event handlers or other active content. For example, a malicious name containing an image element with an `onerror` handler would be interpreted as HTML when the popup is displayed. ### Attack Path 1. An attacker gains control over, contributes to, or compromises the GeoJSON source used by the map. 2. The attacker places an HTML payload in the `name` property of a state feature. 3. The application loads the malicious feature. 4. A user moves the pointer over the affected feature. 5. The event handler interpolates the property into a string and calls `setHTML()`. 6. The browser parses the attacker-controlled markup in the application's origin and may execute its script-capable content. ### Impact Assessment Successful exploitation permits arbitrary client-side script execution in the security context of an application implementing th ...[truncated 340 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Do not pass untrusted feature properties to `setHTML()`. - Construct popup content using DOM nodes and assign dynamic values through `textContent`. - Pass the resulting node to `Popup#setDOMContent()` where supported. - If formatted HTML is unavoidable, sanitize the completed markup with a maintained sanitizer such as DOMPurify under a restrictive configuration. - Validate expected property types before formatting them. - Deploy a restrictive Content Security Policy as defense in depth; do not treat it as a replacement for output encoding. Example hardened pattern: ```javascript const content = document.createElement('div'); const heading = document.createElement('h3'); const population = document.createElement('p'); heading.textContent = String(feature.properties.name ?? ''); population.textContent = `Population: ${Number(feature.properties.population).toLocaleString()}`; content.append(heading, population); popup.setLngLat(e.lngLat).setDOMContent(content).addTo(map); ``` ]]>
