T09 · Insecure Skill Coding Practices
- Location
scripts/ai_product_radar.py:332- Finding
RSS-Controlled JavaScript Injection Enables Arbitrary Local Command Execution
- Content
View full analysis
bool: try: import subprocess script = f""" const {{ chromium }} = require('playwright'); (async () => {{ const browser = await chromium.launch(); const page = await browser.newPage({viewport: {{width: 1280, height: 800}}}); await page.goto('{url}', {{waitUntil: 'domcontentloaded', timeout: 20000}}); await page.waitForTimeout(2000); await page.screenshot({{path: '{path}', fullPage: false}}); await browser.close(); }})(); """ result = subprocess.run(["node", "-e", script], capture_output=True, text=True, timeout=45) return result.returncode == 0 and path.exists() except Exception: return False ``` ### Technical Analysis The `url` value originates from an RSS or Atom entry link and is directly interpolated into JavaScript source code enclosed by single quotes. The generated source is then executed using `node -e`. An attacker-controlled URL containing a single quote can terminate the `page.goto()` string and inject arbitrary JavaScript. Because Node.js exposes operating-system functionality through modules such as `child_process`, successful injection can be converted into arbitrary command execution under the account running the Skill. The screenshot path is also interpolated into JavaScript without serialization. Although it is primarily derived from the user-selected output directory and a sanitized filename, it remains an additional injection surface if the output path contains JavaScript metacharacters. Passing the generated source as an argument rather than through a shell does not prevent this vulnerability: the injec ...[truncated 1448 chars]- Remediation
View remediation
{ const url = process.argv[1]; const outputPath = process.argv[2]; const browser = await chromium.launch(); try { const page = await browser.newPage({ viewport: { width: 1280, height: 800 } }); await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 20000 }); await page.waitForTimeout(2000); await page.screenshot({ path: outputPath, fullPage: false }); } finally { await browser.close(); } })(); """ subprocess.run( ["node", "-e", node_script, url, str(path)], capture_output=True, text=True, timeout=45, check=False, ) ``` 4. Validate links before browser navigation. Permit only explicitly supported `http` and `https` URLs. 5. Combine URL validation with private-address and redirect restrictions to address the SSRF risk described separately. 6. Add regression tests containing quotes, backslashes, line separators, and JavaScript fragments in RSS entry links and output paths. ]]>
