T09 · Insecure Skill Coding Practices
Error
- Location
- bb-sites/gumtree/listing.js:65
- Finding
- Unrestricted Absolute URL Fetch Enables Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `bb-sites/gumtree/listing.js`, lines 65–76 **Vulnerability Type**: Server-Side Request Forgery (SSRF) through insufficient URL validation **Risk Level**: High ### Vulnerable Code ```javascript if (!args.url) return { error: 'Missing argument: url', hint: 'e.g. bb-browser site gumtree/listing "https://www.gumtree.com/p/.../ID"' }; let path = String(args.url).trim(); if (!path.startsWith('http')) { if (!path.startsWith('/')) path = '/' + path; path = 'https://www.gumtree.com' + path; } const resp = await fetch(path, { headers: { ``` ### Technical Analysis The listing adapter is intended to retrieve pages from `www.gumtree.com`, but it only prepends the Gumtree origin when the supplied value does not begin with `http`. Any attacker-controlled absolute HTTP or HTTPS URL is therefore passed directly to `fetch()`. The code does not validate: - The URL scheme. - The destination hostname. - The destination port. - Whether the address resolves to a loopback, link-local, or private-network address. - The destination of HTTP redirects. The metadata declaration naming `www.gumtree.com` is not a substitute for validation inside the adapter. Unless the bb-browser runtime independently and reliably restricts network access to that domain, this creates an SSRF primitive. After fetching the response, the adapter reads the complete response body and parses JSON-LD and Open Graph metadata. Data obtained from an internal endpoint could consequently be exposed through returned fields such as `title`, `description`, `firstImageUrl`, or `imageUrls`. ### Attack Path 1. An attacker supplies a crafted absolute URL as the listing argument, such as a loopback URL, private-network service, or cloud metadata endpoint. 2. Because the value begins with `http`, the adapter does not prepend `https://www.gumtree.com`. 3. The untrusted URL is passed directly to `fetch()`. 4. The runtime makes a request from its own network co ...[truncated 1056 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse the supplied value using `new URL()` and reject malformed URLs. 2. Require the `https:` scheme. 3. Allow only an explicit hostname, preferably exactly `www.gumtree.com`. 4. Reject embedded credentials, unexpected ports, protocol-relative URLs, and alternate textual representations of prohibited addresses. 5. Disable automatic redirects or validate the scheme, hostname, port, and resolved address after every redirect. 6. Resolve the hostname and reject loopback, link-local, private, reserved, and otherwise non-public IP ranges for both IPv4 and IPv6. 7. Consider accepting only Gumtree-relative listing paths rather than arbitrary absolute URLs. 8. Apply outbound network controls so the runtime cannot reach localhost, private networks, or cloud metadata services. A minimal hostname restriction should resemble: ```javascript const candidate = new URL(String(args.url), 'https://www.gumtree.com'); if ( candidate.protocol !== 'https:' || candidate.hostname !== 'www.gumtree.com' || candidate.username || candidate.password || candidate.port ) { return { error: 'Only HTTPS URLs on www.gumtree.com are allowed' }; } ``` This validation should be supplemented with redirect validation and network-level egress restrictions. ]]>
