T09 · Insecure Skill Coding Practices
- Location
lib/get-agent.js:13- Finding
Unrestricted Fetch of Attacker-Controlled On-Chain URI Enables SSRF
- Content
View full analysis
Vulnerability Details
File Location:
lib/get-agent.js:13-29
Vulnerability Type: Server-Side Request Forgery (SSRF)
Risk Level: MediumVulnerable 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
agentURIvalue 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 texthttp.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
- An attacker registers or updates an agent ...[truncated 1853 chars]
- Remediation
View remediation
Remediation Suggestions
-
Make remote metadata retrieval opt-in
- Separate on-chain reads from URI retrieval.
- Return
owner,agentURI, andagentWalletby default. - Require an explicit option such as
fetchMetadata: truebefore making a network request.
-
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.
-
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.
- Parse the value with
-
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.
-
Control redirects
- Disable automatic redirects with
redirect: "manual", or validate every redirect target before following it. - Set a small maximum redirect count.
- Disable automatic redirects with
-
Apply resource limits
- Use
AbortControllerto enforce a short timeout. - Validate
Content-Typebefore JSON parsing. - Stream the body and enforce a strict maximum byte size.
- Reject excessively deep or otherwise resource-intensive JSON structures where applicable.
- Use
-
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.
-
Treat metadata as untrusted
- Do not execute fetched content ...[truncated 120 chars]
-
