T09 · Insecure Skill Coding Practices
- Location
scripts/render-image.js:130- Finding
Unrestricted Network Access and Script Execution During HTML Rendering
- Content
View full analysis
Vulnerability Details
File Location:
scripts/render-image.js, lines 130–136
Vulnerability Type: Untrusted active HTML rendered without JavaScript or network isolation
Risk Level: MediumComplete 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
- An attacker supplies crafted content, or attacker-controlled text is inserted into an HTML card without correct contextual escaping.
- The resulting document contains executable JavaScript, active markup, or remote resource references.
- A user or agent invokes:
sh node scripts/render-image.js malicious.html output.png - The script opens the document through
page.goto()in a JavaScript-enabled ...[truncated 1277 chars]
- Remediation
View remediation
Remediation Suggestions
-
Disable JavaScript for static text and data cards before navigation:
js await page.setJavaScriptEnabled(false); -
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(); } }); -
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:, andws:.
-
Escape all user-derived values according to their HTML context. Do not insert untrusted text through raw HTML concatenation or
innerHTML. -
Validate generated documents and reject active content, including:
<script><iframe><object><embed>- Event-handler attributes such as
onloadandonclick - JavaScript URLs
- External stylesheets, fonts, images, and media
-
Consider rendering from a trusted fixed template with text passed through a structured data interface instead of accepting arbitrary HTML files.
-
Run Chromium in a network-restricted container or sandbox as defense in depth, while retaining the browser's operating-system sandbox.
-
Add automated tests proving that JavaScript execution and outbound, loopback, private-network, and metadata-service requests are blocked.
-
