T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/render-image.js:130
- Finding
- Unrestricted Network Access and Script Execution During HTML Rendering## Vulnerability Details **File Location**: `scripts/render-image.js`, lines 130–136 **Vulnerability Type**: Untrusted active HTML rendered without JavaScript or network isolation **Risk Level**: Medium **Complete Code Snippet**: ```js const page = await browser.newPage(); await page.setViewport({ width: opts.width, height: opts.height, deviceScaleFactor: opts.scale, }); await page.goto("file://" + path.resolve(inputPath), { waitUntil: "networkidle0" }); await page.screenshot({ path: outputPath, fullPage: opts.full }); process.stdout.write(`OK\t${path.resolve(outputPath)}\t${opts.width}x${opts.height}@${opts.scale}x\n`); ``` ### Technical Analysis The renderer accepts an arbitrary local HTML path and opens it in a JavaScript-enabled Chromium page. It does not disable JavaScript, intercept browser requests, restrict URL schemes, or block external and private-network destinations. Project documentation instructs authors to create self-contained HTML without external assets, but the implementation does not enforce that requirement. Therefore, crafted HTML can contain active elements such as `<script>`, `<iframe>`, or resource references that cause Chromium to initiate network requests. For example, a malicious input document could execute JavaScript that sends a request to an attacker-controlled endpoint or attempts blind interaction with services reachable from the rendering host. The browser sandbox limits direct host access, but it does not provide network isolation. ### Attack Path 1. An attacker supplies crafted content, or attacker-controlled text is inserted into an HTML card without correct contextual escaping. 2. The resulting document contains executable JavaScript, active markup, or remote resource references. 3. A user or agent invokes: ```sh node scripts/render-image.js malicious.html output.png ``` 4. The script opens the document through `page.goto()` in a JavaScript-enabled ...[truncated 1277 chars]
- Remediation
- ## Remediation Suggestions 1. Disable JavaScript for static text and data cards before navigation: ```js await page.setJavaScriptEnabled(false); ``` 2. Enable request interception and deny network access by default. Permit only the initial local document and explicitly approved local resources: ```js await page.setRequestInterception(true); page.on("request", (request) => { const url = new URL(request.url()); if (url.protocol === "file:") { request.continue(); } else { request.abort(); } }); ``` 3. If network access is ever required, use a strict allowlist. Explicitly block: - Loopback addresses. - Private IPv4 and IPv6 ranges. - Link-local and cloud metadata endpoints. - Redirects to non-allowlisted destinations. - Unnecessary schemes such as `http:`, `https:`, `ftp:`, and `ws:`. 4. Escape all user-derived values according to their HTML context. Do not insert untrusted text through raw HTML concatenation or `innerHTML`. 5. Validate generated documents and reject active content, including: - `<script>` - `<iframe>` - `<object>` - `<embed>` - Event-handler attributes such as `onload` and `onclick` - JavaScript URLs - External stylesheets, fonts, images, and media 6. Consider rendering from a trusted fixed template with text passed through a structured data interface instead of accepting arbitrary HTML files. 7. Run Chromium in a network-restricted container or sandbox as defense in depth, while retaining the browser's operating-system sandbox. 8. Add automated tests proving that JavaScript execution and outbound, loopback, private-network, and metadata-service requests are blocked.
