T09 · Insecure Skill Coding Practices
- Location
scripts/download.py:87- Finding
JavaScript Injection Through Unescaped URL Interpolation
- Content
View full analysis
{{ const browser = await chromium.launch({{ headless: true }}); const context = await browser.newContext(); const page = await context.newPage(); // Navigate to URL await page.goto('{url}', {{ waitUntil: 'networkidle' }}); // Wait for content await page.waitForTimeout(2000); // Handle authentication if needed // (Would need credentials for institutional access) // Get PDF link or download directly const pdfLink = await page.$('a[href$=".pdf"]'); if (pdfLink) {{ const href = await pdfLink.getAttribute('href'); console.log(href); }} await browser.close(); }})(); ''' # Save and run script with tempfile.NamedTemporaryFile(mode='w', suffix='.js', delete=False) as f: f.write(script) temp_path = f.name try: result = subprocess.run(['node', temp_path], capture_output=True, text=True) return result.stdout.strip() ``` ### Technical Analysis The `url` parameter is inserted directly into a generated JavaScript program inside a single-quoted string. No JavaScript-safe encoding, validation, or separation between code and data is applied. An attacker-controlled URL containing a single quote can terminate the argument to `page.goto()`. The attacker can then append arbitrary JavaScript and comment out the remaining generated source. Because the resulting temporary file is executed using Node.js, injected JavaScript has access to Node APIs, including `child_process`, filesystem operations, environment variables, and n ...[truncated 1704 chars]- Remediation
View remediation
{ const url = process.argv[2]; const browser = await chromium.launch({ headless: true }); const context = await browser.newContext(); const page = await context.newPage(); await page.goto(url, { waitUntil: 'networkidle' }); await browser.close(); })(); ''' result = subprocess.run( ['node', temp_path, url], capture_output=True, text=True, check=True ) ``` ]]>
