T09 · Insecure Skill Coding Practices
- Location
scripts/detect_brand.py:17- Finding
User-Controlled URL Construction Enables Server-Side Request Forgery
- Content
View full analysis
Vulnerability Details
File Location:
scripts/detect_brand.py:17-26; equivalent patterns also occur inscripts/detect_brand_real.py:46-58andscripts/detect_brand_v2.py:83-89
Vulnerability Type: Server-Side Request Forgery through insufficient URL validation
Risk Level: Highpython def check_website_exists(brand): """Check whether the brand website exists.""" domains = [ f"https://www.{brand}.com", f"https://{brand.lower()}.com", f"https://www.{brand}.net" ] for domain in domains: try: response = requests.get( domain, timeout=10, allow_redirects=True )The other affected implementations use the same unvalidated brand value when building URLs:
python domains = [ f"https://www.{brand}.com", f"https://{brand.lower()}.com", f"https://www.{brand}.net" ]python domains = [f"www.{self.brand}.com", f"www.{self.brand}.com/en"] for domain in domains: try: response = requests.get( f"https://{domain}", timeout=10, allow_redirects=True )Technical Analysis
The required
--brandargument is accepted as an unrestricted string and interpolated directly into a URL. Characters with URL semantics, including#,?,/, and@, are not rejected or encoded. Consequently, the hostname interpreted by the HTTP client may differ from the intendedbrand.comhostname.For example, a value containing a fragment delimiter can cause the appended
.comtext to be interpreted as a fragment rather than part of the hostname. The HTTP client may therefore connect to an attacker-selected host. The code also follows redirects automatically without validating the address of each redirect target.No DNS-resolution checks reject loopback, private, link-local, multicast, or o ...[truncated 1618 chars]
- Remediation
View remediation
Remediation Suggestions
- Restrict brand values to a conservative format, such as ASCII letters, digits, and hyphens:
python if not re.fullmatch(r"[A-Za-z0-9-]{1,63}", brand): raise ValueError("Invalid brand name") - Reject dots, URL delimiters, whitespace, control characters, path separators, and percent-encoded delimiters.
- Construct URLs with
urllib.parserather than string interpolation. - Resolve the hostname before connecting and reject every address in loopback, private, link-local, multicast, reserved, and unspecified ranges.
- Disable automatic redirects or validate the hostname and resolved IP address at every redirect hop.
- Apply an outbound network policy that permits only required public search and marketplace services.
- Add tests for malformed input, IPv4 and IPv6 literals, URL fragments, user-information syntax, alternate numeric IP formats, DNS rebinding, and redirect-to-private-address behavior.
- Restrict brand values to a conservative format, such as ASCII letters, digits, and hyphens:
