T09 · Insecure Skill Coding Practices
- Location
src/price-check.js:27- Finding
Arbitrary Product URL Fetching Enables Server-Side Request Forgery
- Content
View full analysis
Vulnerability Details
File Location:
src/price-check.js:27-31
Related Input Flow:src/cli.js:18-24,src/products.js:13-18
Vulnerability Type: Server-Side Request Forgery (SSRF)
Risk Level: HighVulnerable code in
src/price-check.js:js try { const html = await cb.call(async () => { const response = await fetch(url); if (!response.ok) throw new Error(`HTTP ${response.status}`); return await response.text(); });User-controlled URL entry in
src/cli.js:js watch.command('add') .description('Add product to watchlist') .argument('<name>', 'Product name') .requiredOption('--url <url>', 'Product URL') .option('--target <price>', 'Target price') .action((name, opts) => { const id = addProduct(db, { name, url: opts.url, target_price: opts.target ? parseFloat(opts.target) : null }); console.log(`Added product ${id}`); });URL storage in
src/products.js:js export function addProduct(db, opts) { if (!opts.name) throw new Error('Product name is required'); if (!opts.url) throw new Error('Product URL is required'); const id = uuidv4(); db.prepare(`INSERT INTO products (id, name, url, source, target_price, currency) VALUES (?, ?, ?, ?, ?, ?)`) .run(id, opts.name, opts.url, opts.source || 'generic', opts.target_price || null, opts.currency || 'USD'); return id; }Technical Analysis
The CLI accepts an arbitrary product URL and stores it without validating its scheme, hostname, resolved IP address, or destination network. The price-checking implementation later supplies this URL directly to the built-in
fetch()function.Consequently, a user who can add products and invoke or influence price checking can direct the process to send requests to destinations reachable from the Agent host, including:
- Loopback services such as
127.0.0.1or::1 - RFC1918 private networks
- Link-local addresses
- Cloud instance metadata endpoints
- Internal administ ...[truncated 2782 chars]
- Loopback services such as
- Remediation
View remediation
Remediation Suggestions
- Parse all submitted URLs with the standard
URLclass and reject malformed inputs. - Permit only explicitly required protocols, preferably
https:. Rejectfile:,data:,ftp:, and other schemes. - Resolve the hostname before connecting and reject every address in loopback, private, link-local, multicast, unspecified, carrier-grade NAT, documentation, and other reserved IPv4 and IPv6 ranges.
- Protect against DNS rebinding by binding the validated resolution to the actual connection or rechecking the connected peer address.
- Disable automatic redirects where possible. Otherwise, validate the scheme, hostname, and resolved address at every redirect hop and enforce a low redirect limit.
- Prefer an explicit allowlist of approved merchant domains if the operational model permits it.
- Add an abort-based request timeout and enforce a conservative maximum response size while streaming the body.
- Run the scraper in a network-restricted environment that cannot reach cloud metadata, loopback services, or internal management networks.
- Add tests covering loopback, RFC1918, link-local, IPv6, alternative numeric IP representations, DNS rebinding scenarios, and redirects to prohibited destinations.
- Repair the existing module errors without bypassing these controls, including the invalid in-function import, missing
price-history.js, incompatible database import, and undefinedproductreference.
- Parse all submitted URLs with the standard
