T09 · Insecure Skill Coding Practices
- Location
examples/example-map.js:24- Finding
Unescaped Bus Data Embedded into Executable JavaScript
- Content
View full analysis
Vulnerability Details
File Location:
examples/example-map.js, lines 24–35 and 90
Vulnerability Type: JavaScript injection in generated HTML
Risk Level: MediumVulnerable Code
javascript function generateMapHTML(busesWithCoords, routeFilter = null) { const busMarkers = busesWithCoords .filter(b => !routeFilter || b.RouteID === routeFilter) .map(b => `{ id: '${b.BusID}', lat: ${b.lat}, lng: ${b.lng}, route: '${b.RouteID}', status: '${b.CarOnStop === '1' ? '停站' : '行駛中'}', direction: '${b.GoBack === '1' ? '去程' : '返程'}', updated: '${b.DataTime}' }`) .join(',\n');The resulting text is inserted directly into an inline script:
javascript const busData = [${busMarkers}];Technical Analysis
generateMapHTML()constructs JavaScript source by interpolating properties frombusesWithCoordsdirectly into quoted string literals. Values such asBusID,RouteID, andDataTimeare not escaped or validated. Likewise,latandlngare inserted as executable JavaScript expressions without verification that they are finite numbers.If an attacker can influence a supplied bus record, a value containing a quote followed by JavaScript syntax can terminate the intended string literal and inject arbitrary script into the generated page. For example, a malicious identifier shaped like:
text '; attackerCode(); //would alter the syntax of the generated inline script. This flaw arises from treating data as JavaScript source rather than serializing it as data.
The current example does not export or write the generator by itself, which limits immediate exposure. However, it is explicitly presented as implementation guidance for generating an interactive map and is designed to consume records derived from external transit data. Applications that reuse this function and open or serve its output would expose the injection path.
Attack Path
- An attacker compromise ...[truncated 1443 chars]
- Remediation
View remediation
Remediation Suggestions
- Construct ordinary JavaScript objects and serialize the complete array with
JSON.stringify()instead of manually creating JavaScript source:
javascript const busData = busesWithCoords .filter(b => !routeFilter || b.RouteID === routeFilter) .map(b => ({ id: String(b.BusID), lat: Number(b.lat), lng: Number(b.lng), route: String(b.RouteID), status: b.CarOnStop === '1' ? 'stopped' : 'moving', direction: b.GoBack === '1' ? 'outbound' : 'inbound', updated: String(b.DataTime) })); const serialized = JSON.stringify(busData) .replace(/</g, '\\u003c') .replace(/\u2028/g, '\\u2028') .replace(/\u2029/g, '\\u2029');- Validate coordinates before serialization:
javascript if (!Number.isFinite(record.lat) || !Number.isFinite(record.lng)) { return false; }Also enforce valid geographic ranges for latitude and longitude.
- Prefer embedding serialized data in a non-executable block:
html <script id="bus-data" type="application/json">...</script>Then parse it with:
javascript const busData = JSON.parse( document.getElementById('bus-data').textContent );The serialized JSON must still escape
<to prevent an injected</script>sequence from ending the element.-
Avoid inline scripts and event handlers such as
onclick. Move page logic into a static script file and attach event listeners programmatically. -
Deploy a restrictive Content Security Policy that disallows arbitrary inline scripts and limits script sources to the application and the explicitly required mapping provider.
-
Add regression tests using values containing quotes, backslashes, newlines,
</script>, U+2028/U+2029, and JavaScript fragments. Verify that these values remain inert data in the generated document. -
Treat all remotely retrieved transit records as untrusted, even when they normally originate from a government endpoint.
- Construct ordinary JavaScript objects and serialize the complete array with
