T09 · Insecure Skill Coding Practices
Error
- Location
- helpers/discover_chapter.py:239
- Finding
- Registry-Controlled Endpoint Discovery Enables SSRF<![CDATA[ ## Vulnerability Details **File Location**: `helpers/discover_chapter.py`, lines 239-253 **Vulnerability Type**: Server-Side Request Forgery through unvalidated registry endpoints **Risk Level**: High ### Complete Code Snippet ```python confirmed: list[dict[str, Any]] = [] for chapter in candidates: try: health = client.get( f"{chapter['endpoint']}/health", timeout=HTTP_TIMEOUT ).json() except (httpx.HTTPError, json.JSONDecodeError, ValueError): continue declared_slug = health.get("slug") if not declared_slug: continue # not a NANDA chapter — heuristic false positive chapter["slug"] = declared_slug ``` The endpoint originates from the remote NEST registry: ```python endpoint = agent.get("endpoint", "") if not endpoint: continue candidates.append( { "slug": derive_chapter_slug(agent_id), "agent_id": agent_id, "endpoint": endpoint, ``` ### Technical Analysis The discovery helper retrieves endpoint URLs from a remote registry and issues requests to each endpoint's `/health` path without validating: - The URL scheme - Whether the hostname resolves to loopback, link-local, private, or reserved addresses - Whether the URL contains credentials or an unexpected port - Whether DNS resolution changes between validation and connection - Whether the endpoint belongs to an authenticated chapter operator `follow_redirects=False` prevents redirect-based retargeting but does not prevent direct requests to internal addresses. An attacker able to publish or modify a NEST agent record can make the local OpenClaw host probe an attacker-selected network location. The `sanitize_chapter_record` function only sanitizes textual presentation. It does not enforce network destination policy and therefore does not mitigate SSRF. ### Attack Path 1. An attacker regis ...[truncated 1476 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse each endpoint with a strict URL parser before making a request. 2. Permit only `https` URLs with a non-empty hostname and no embedded credentials. 3. Resolve all hostname addresses and reject loopback, private, link-local, multicast, unspecified, reserved, and carrier-grade NAT ranges for both IPv4 and IPv6. 4. Revalidate the connected peer address to prevent DNS rebinding and time-of-check/time-of-use bypasses. 5. Restrict ports to an explicit allowlist, preferably TCP 443. 6. Apply the same validation to every registry endpoint before writing it to the signed cache. 7. Consider routing discovery probes through a controlled public proxy with no access to the user's private network. 8. Authenticate registry records or require a verifiable chapter identity attestation rather than treating a successful `/health` response as sufficient proof. ]]>
