T09 · Insecure Skill Coding Practices
Warning
- Location
- lib/get-agent.js:13
- Finding
- Unrestricted Fetch of Attacker-Controlled On-Chain URI Enables SSRF## Vulnerability Details **File Location**: `lib/get-agent.js:13-29` **Vulnerability Type**: Server-Side Request Forgery (SSRF) **Risk Level**: Medium ### Vulnerable Code ```js const [owner, agentURI, wallet] = await Promise.all([ registry.ownerOf(id), registry.tokenURI(id), registry.getAgentWallet(id), ]); const ZERO = "0x0000000000000000000000000000000000000000"; const result = { owner, agentURI, agentWallet: wallet === ZERO ? null : wallet, }; // Try to fetch and parse the agentURI if (agentURI && agentURI.startsWith("http")) { try { const res = await fetch(agentURI); if (res.ok) result.parsedJson = (await res.json()); } catch { // skip — optional } } ``` ### Technical Analysis The `agentURI` value is read directly from an on-chain token and may be controlled by an arbitrary registry participant. The implementation automatically dereferences that URI whenever it begins with the text `http`. This prefix check is not an adequate security boundary. The code does not: - Parse and validate the URI using a strict URL parser. - Restrict requests to approved metadata services. - Reject loopback, private, link-local, multicast, or cloud metadata addresses. - Resolve hostnames and validate their resulting IP addresses. - Validate redirect destinations. - Restrict destination ports. - Apply a request timeout. - Limit the response size. - Validate the response content type before parsing it as JSON. The request does not explicitly include the configured private key, environment variables, authentication headers, or other local secrets. Therefore, this is not evidence of direct private-key exfiltration. Nevertheless, it creates an SSRF primitive because an untrusted on-chain participant can choose a URL that the victim's runtime will request. ### Attack Path 1. An attacker registers or updates an agent ...[truncated 1853 chars]
- Remediation
- ## Remediation Suggestions 1. **Make remote metadata retrieval opt-in** - Separate on-chain reads from URI retrieval. - Return `owner`, `agentURI`, and `agentWallet` by default. - Require an explicit option such as `fetchMetadata: true` before making a network request. 2. **Use a strict destination allowlist** - Prefer fetching only from approved Agent URI hosts or trusted content gateways. - Compare normalized hostnames rather than using string-prefix checks. - If broad Internet retrieval is essential, apply all of the network restrictions below. 3. **Validate the URL and protocol** - Parse the value with `new URL(agentURI)`. - Allow only explicitly supported protocols, preferably HTTPS. - Reject embedded credentials, unusual ports, malformed hosts, and unsupported schemes. 4. **Block internal network destinations** - Resolve the hostname before connecting. - Reject IPv4 and IPv6 loopback, private, link-local, multicast, unspecified, and reserved address ranges. - Explicitly block cloud metadata destinations. - Repeat validation for every DNS resolution and redirect to mitigate DNS rebinding and redirect-based bypasses. 5. **Control redirects** - Disable automatic redirects with `redirect: "manual"`, or validate every redirect target before following it. - Set a small maximum redirect count. 6. **Apply resource limits** - Use `AbortController` to enforce a short timeout. - Validate `Content-Type` before JSON parsing. - Stream the body and enforce a strict maximum byte size. - Reject excessively deep or otherwise resource-intensive JSON structures where applicable. 7. **Isolate outbound retrieval** - Route metadata fetching through a hardened proxy with egress restrictions. - Run the retrieval component without access to sensitive internal networks whenever possible. 8. **Treat metadata as untrusted** - Do not execute fetched content ...[truncated 120 chars]
