T09 · Insecure Skill Coding Practices
Error
- Location
- src/extractors/BrowserManager.ts:22
- Finding
- Unrestricted URL Navigation Enables Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `src/extractors/BrowserManager.ts:22-31` **Related Input Location**: `src/cli.ts:57-59` **Vulnerability Type**: Server-Side Request Forgery (SSRF) **Risk Level**: High ### Vulnerable Code ```ts async scrapePage(url: string, options: ScrapeOptions): Promise<{ content: string; screenshot: Buffer; structure: PageStructure; }> { const page = await this.newPage(); try { await page.goto(url, { waitUntil: 'networkidle' }); ``` The CLI accepts the navigation target without validation: ```ts case '--url': case '-u': options.url = args[++i]; break; ``` ### Technical Analysis The user-controlled URL is passed directly to Playwright's `page.goto()` method. The implementation does not validate the URL scheme, hostname, resolved IP addresses, port, or redirect destinations. Because the request originates from the machine running the Skill, an attacker who can control the URL may direct Chromium to resources that are inaccessible from the attacker's own network position. Relevant targets include: - Loopback services such as `127.0.0.1` and `::1` - RFC 1918 private networks - Link-local services and cloud instance metadata endpoints - Internal administrative panels or development services - Public URLs that redirect to private destinations After navigation, the Skill extracts `document.body.innerText`, selected page text, or table content and returns it to the caller. This creates a response-reading SSRF condition rather than merely a blind request primitive. ### Attack Path 1. An attacker supplies an internal target, for example `http://127.0.0.1:8080/admin`, as the `--url` argument or through the exported `SmartScraper.scrape()` API. 2. `src/cli.ts` stores the value in `options.url` without validation. 3. `SmartScraper.scrape()` passes the URL to `BrowserManager.scrapePage()`. 4. Headless Chromium sends the request from the Skill host's network context. 5. The extractor reads the result ...[truncated 781 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse targets with the standard `URL` class and reject malformed URLs. 2. Allow only explicitly required schemes, normally `https:` and optionally `http:`. 3. Reject embedded usernames and passwords, nonstandard schemes, and unexpected ports. 4. Resolve the hostname before navigation and block all loopback, private, link-local, multicast, unspecified, and reserved IPv4 and IPv6 ranges. 5. Protect against DNS rebinding by validating the address actually used for each connection rather than relying exclusively on an initial DNS lookup. 6. Intercept Playwright requests with `page.route()` or browser-context routing and validate every outgoing destination, including subresources. 7. Validate every redirect destination before following it. 8. Prefer an explicit hostname allowlist when the set of legitimate scraping targets is known. 9. Apply network-level egress restrictions so the scraper process cannot reach cloud metadata endpoints, internal management networks, or sensitive local services. 10. Add tests covering loopback, private IPv4, private IPv6, link-local addresses, alternate address encodings, DNS rebinding, and public-to-private redirects. ]]>
