T09 · Insecure Skill Coding Practices
- Location
scripts/register.py:184- Finding
Unrestricted Remote Metadata and Image Fetching Enables SSRF
- Content
View full analysis
Vulnerability Details
File Location:
scripts/register.py:184-200,scripts/register.py:587-608
Vulnerability Type: Server-Side Request Forgery through attacker-controlled metadata URLs
Risk Level: HighVulnerable Code
python def decode_data_uri(uri: str) -> Optional[Dict[str, Any]]: """Decode base64 data URI to JSON.""" if uri.startswith("data:application/json;base64,"): b64 = uri.replace("data:application/json;base64,", "") try: json_str = base64.b64decode(b64).decode() return json.loads(json_str) except Exception as e: print(f"Warning: Could not decode data URI: {e}") return None elif uri.startswith("http://") or uri.startswith("https://"): # Fetch from URL try: req = urllib.request.Request(uri, headers={"User-Agent": "ERC8004-CLI/1.0"}) with urllib.request.urlopen(req, timeout=10) as resp: return json.loads(resp.read().decode()) except Exception as e: print(f"Warning: Could not fetch URI: {e}") return NoneThe image-validation path also performs an unrestricted request:
python image = data.get("image", "") if image: # Check for local paths if image.startswith("/") or image.startswith("./") or image.startswith("../"): issues.append(f"Image is a local path (not accessible): {image}") elif image.startswith("file://"): issues.append(f"Image is a file:// URL (not accessible): {image}") elif image.startswith("http://") or image.startswith("https://"): # Try to reach the image try: req = urllib.request.Request(image, method="HEAD", headers={"User-Agent": "ERC8004-Validator/1.0"}) with urllib.request.urlopen(req, timeout=5) as resp: if resp.status >= 400: ...[truncated 3944 chars]- Remediation
View remediation
Remediation Suggestions
- Prefer embedded
data:application/json;base64metadata and reject remote metadata by default. - Require an explicit user option before fetching remote metadata.
- Permit only HTTPS and, where practical, use an explicit allowlist of trusted metadata hosts.
- Resolve destination hostnames before connecting and reject all loopback, private, link-local, multicast, reserved, and unspecified IPv4 and IPv6 addresses.
- Protect against DNS rebinding by ensuring the validated address is the address actually used for the connection.
- Disable redirects or validate the hostname, resolved address, scheme, and port of every redirect destination.
- Explicitly deny common cloud metadata destinations, including
169.254.169.254, while retaining general IP-range validation. - Restrict destination ports to approved values, normally TCP 443.
- Enforce response-size limits and require an appropriate JSON content type before parsing metadata.
- Do not automatically republish remotely fetched content. Display the exact proposed metadata and require explicit confirmation before an on-chain update.
- Apply the same URL policy to image validation. Consider disabling active image reachability checks and performing only syntactic validation.
- Align implementation with the documentation by rejecting
http://image URLs rather than accepting both HTTP and HTTPS.
- Prefer embedded
