T09 · Insecure Skill Coding Practices
Error
- Location
- references/web-search-js.md:119
- Finding
- DOM-based cross-site scripting through unescaped search result fields<![CDATA[ ## Vulnerability Details **File Location**: `references/web-search-js.md`, lines 119–123, 181–190, and 347–357 **Vulnerability Type**: DOM-based cross-site scripting **Risk Level**: High ### Vulnerable Code ```javascript .setHTML( `<h3>${result.properties.name}</h3> <p>${result.properties.full_address || ''}</p>` ) ``` ```javascript resultsContainer.innerHTML = response.suggestions .map( (suggestion) => ` <div class="result-item" data-id="${suggestion.mapbox_id}"> <strong>${suggestion.name}</strong> <div>${suggestion.place_formatted}</div> </div> ` ) .join(''); ``` ```javascript resultsContainer.innerHTML = results .map( (result) => ` <div class="result" data-id="${result.mapbox_id}"> <strong>${result.name}</strong> <p>${result.place_formatted || ''}</p> </div> ` ) .join(''); ``` ### Technical Analysis The examples interpolate Mapbox response fields directly into HTML strings and pass the resulting strings to `setHTML()` or `innerHTML`. These APIs parse their input as active HTML rather than plain text. The affected values include result names, formatted addresses, and identifiers. They originate outside the application’s trust boundary. If a malicious or compromised upstream record contains HTML event handlers, dangerous elements, or malformed attribute content, the browser can interpret that content in the application’s origin. The use of an unquoted `data-id` attribute increases the attack surface because specially formed identifier content could break out of the attribute even when it does not contain a complete HTML element. ### Attack Path 1. An attacker causes malicious markup to appear in a place name, formatted address, or other search-result field available through the upstream search service. 2. A victim searches for a term that returns the attacker-controlled record. 3. The application interpolates the returned value into an HTML template without contextual escaping. 4. The app ...[truncated 856 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Do not construct result interfaces using `innerHTML` with external values. - Create DOM elements explicitly and assign external values through `textContent`: ```javascript const item = document.createElement('div'); item.className = 'result-item'; item.dataset.id = String(suggestion.mapbox_id); const name = document.createElement('strong'); name.textContent = suggestion.name || ''; const address = document.createElement('div'); address.textContent = suggestion.place_formatted || ''; item.append(name, address); resultsContainer.appendChild(item); ``` - For popups, use a DOM-node API such as `setDOMContent()` if supported: ```javascript const popupContent = document.createElement('div'); const heading = document.createElement('h3'); const address = document.createElement('p'); heading.textContent = result.properties.name || ''; address.textContent = result.properties.full_address || ''; popupContent.append(heading, address); new mapboxgl.Popup() .setLngLat(result.geometry.coordinates) .setDOMContent(popupContent) .addTo(map); ``` - If HTML rendering is unavoidable, sanitize every untrusted value with a maintained allowlist-based sanitizer before insertion. - Quote all generated attributes and validate identifiers against an expected character set. - Deploy a restrictive Content Security Policy as defense in depth. CSP must not replace output encoding or safe DOM construction. ]]>
