T09 · Insecure Skill Coding Practices
- Location
scripts/fetch.py:22- Finding
Unrestricted Marketplace Host Enables Server-Side Request Forgery
- Content
View full analysis
Vulnerability Details
File Location:
scripts/fetch.py, lines 22 and 58–68; user-controlled assignment at lines 196–199
Vulnerability Type: Server-Side Request Forgery through an unrestricted network destination
Risk Level: MediumVulnerable Code
python MARKETPLACE = os.environ.get("AMAZON_MARKETPLACE", "www.amazon.com")python def build_url(asin: str) -> str: return f"https://{MARKETPLACE}/dp/{asin}"python def fetch_page(url: str) -> str: req = urllib.request.Request(url, headers=HEADERS) try: with urllib.request.urlopen(req, timeout=15) as resp: charset = resp.headers.get_content_charset() or "utf-8" return resp.read().decode(charset, errors="replace") except Exception as e: print(f"Error fetching page: {e}", file=sys.stderr) sys.exit(1)python if args.marketplace: global MARKETPLACE MARKETPLACE = args.marketplaceTechnical Analysis
The
--marketplaceargument andAMAZON_MARKETPLACEenvironment variable are accepted without validation and interpolated directly into the request URL. Although the scheme is fixed to HTTPS, an attacker who can control invocation arguments or the process environment can select an arbitrary hostname and port.urllib.request.urlopenalso follows HTTP redirects by default. Consequently, even a destination that initially appears acceptable could redirect the request to another host unless redirect targets are independently validated.This behavior exceeds the minimum privileges required by the declared functionality. Fetching Amazon product data only requires access to supported Amazon marketplace domains; arbitrary network destinations are unnecessary. No evidence was found that credentials, cookies, authorization headers, environment dumps, or local-file contents are transmitted. The pre-scan concern is therefore best characterized as destination-control risk rather than confirmed sensitive-dat ...[truncated 1884 chars]
- Remediation
View remediation
Remediation Suggestions
-
Replace arbitrary marketplace configuration with an explicit allowlist of supported Amazon domains:
python ALLOWED_MARKETPLACES = { "www.amazon.com", "www.amazon.co.uk", "www.amazon.de", "www.amazon.fr", "www.amazon.it", "www.amazon.es", "www.amazon.ca", "www.amazon.co.jp", } def validate_marketplace(value: str) -> str: host = value.strip().lower().rstrip(".") if host not in ALLOWED_MARKETPLACES: raise ValueError("Unsupported Amazon marketplace") return host -
Reject marketplace values containing credentials, paths, query strings, fragments, custom ports, or schemes. Parse configuration as a hostname rather than concatenating an arbitrary string into a URL.
-
Reject IP literals and hostnames resolving to loopback, private, link-local, multicast, reserved, or unspecified address ranges. Perform this check for every resolved address and account for both IPv4 and IPv6.
-
Disable automatic redirects or validate every redirect destination against the same marketplace allowlist and address-range policy.
-
Validate ASIN input before constructing the URL:
python if not re.fullmatch(r"[A-Z0-9]{10}", asin): raise ValueError("Invalid ASIN") -
Prefer a fixed mapping from marketplace identifiers such as
us,uk, ordeto trusted Amazon hostnames instead of accepting raw hostnames. -
Add tests confirming rejection of localhost, private addresses, IP literals, embedded credentials, custom ports, malformed hostnames, and redirects to non-allowlisted destinations.
-
