SECURITY DESIGN · RUNTIME NETWORK
Runtime outbound URL and SSRF protection
Evidence snapshot: 2026-08-12, Runtime implementation through the Task 10 consolidated fix (Task 1–8 anchor 80c377fa).
This document is ordered as OpenProgram current state, other projects, then the OpenProgram future plan.
Implemented behavior and future work are labeled separately.
Runtime-owned HTTP requests use a registry-keyed URL policy and managed transport. The implementation validates all DNS answers, dials only approved peers, handles redirects explicitly, confines credentials to their declared origin, bounds response resources, and fails static verification for unclassified consumers or active unmanaged SDK transports.
01Implemented boundary
| Component | Implemented responsibility | Explicit non-responsibility |
|---|---|---|
openprogram/security/url_policy.py | URL and origin normalization; scheme, userinfo, host, and port validation; full A/AAAA classification; exact owner exceptions; stable sanitized reason codes | Does not send network requests or infer a caller's trust class |
openprogram/security/safe_http.py | Immutable 42-consumer registry; sync/async peer-constrained transports; Host/SNI preservation; manual redirects; credential, header, MIME, decoded-body, encoding, timeout, proxy, and atomic-download enforcement | Does not accept caller-created policy objects or expose verify=False |
| Consumer adapters | Select one immutable registry key and, for configured services or callbacks, supply the exact owner-configured origin | May not implement a second private-address classifier or retry through a raw client |
runtime_http_audit.py, doctor, and scripts/check_runtime_http.py | Bounded sanitized denial state, exact owner exception/proxy reporting, AST inventory, SDK disposition verification, and stale exclusion detection | Do not display userinfo, query, fragment, credentials, peer response bodies, or token-bearing paths |
02Runtime consumers and separate boundaries
| Boundary | Current treatment | Status in this feature |
|---|---|---|
| Arbitrary and derived Runtime URLs | web_fetch, attachments, generated assets, image result downloads, catalogs, marketplaces, and updater metadata use managed clients with public or exact-service policy | Included |
| Fixed/configured APIs | Web search, image APIs, channel REST, TTS, provider listing/probes/OAuth, Web model listing, MCP HTTP/SSE, and local probes use fixed or owner-configured exact origins | Included |
| Third-party SDKs | Provider and MCP clients receive injected managed transports; exact-origin REST adapters remain managed; uninjectable Bedrock, Slack/Discord gateway, and edge-tts SDK paths fail closed | Included and classified |
| Browser navigation, popup, Service Worker, and page subresources | Governed by the browser-control design and any owner egress isolation | Excluded |
| Sandboxed arbitrary code, plugin code, external provider CLIs, and package managers | Governed by sandbox, plugin trust, child-process, and installation boundaries | Excluded |
| Owner control-plane liveness probes | Narrow loopback-only exclusions are inventoried with exact call kind, owner, reason, and count | Classified, not claimed as Runtime URL fetch |
◐ for this bounded feature after the integrated repository gate passed. Browsers, arbitrary code,
plugins, external CLIs, and package-manager child processes remain separate boundaries.The comparison preserves all twelve external feature-matrix columns plus OWASP. Permission prompts, hostname rules, application URL validation, policy proxies, and operating-system network sandboxes remain distinct controls; upstream wrappers and protocol libraries are not counted again as independent implementations.
03Reference mechanisms
| Project | Application URL-fetch evidence | Network or permission boundary | Adopted, modified, or rejected |
|---|---|---|---|
| Hermes | Shared url_safety covers web, browser, vision, and gateway media. DNS failure is closed; loopback, RFC1918, link-local, CGNAT, metadata, reserved, multicast, and unspecified results are rejected; redirects are checked hop by hop; URLs are sanitized and media is streamed with limits. | A process-wide allow_private_urls switch relaxes multiple entries together. Application checks still do not replace OS network isolation. | Adopted shared fail-closed validation, the full address set, redirect checks, sanitization, and limits. Modified the global switch into exact consumer-scoped service-origin exceptions. |
| OpenClaw | Private, internal, and special-use targets are denied by default, with exact or wildcard hostname exceptions and checks before navigation, interaction-generated loads, and final URLs. Its documentation identifies speculative navigation and browser subresource risk. | Official documentation states that request interception is not a network firewall: redirects, popups, Service Workers, and background resources can still require egress isolation or a policy proxy. | Adopted strict defaults and explicit exceptions. Modified browser interception into a separate acceptance boundary; it is not Runtime-fetch evidence. |
| Claude Code | WebFetch(domain:...) supports allow/deny. Before a request, the hostname is sent to the Anthropic safety blocklist; the result is cached for exactly five minutes, and the owner may disable the preflight. | The Bash sandbox uses a Seatbelt or bubblewrap HTTP/SOCKS proxy that child processes inherit. The proxy trusts the client-provided hostname and does not inspect TLS content, so broad domains and domain fronting remain risks. | Adopted layered tool authorization and child-process egress policy. Rejected hostname permission as DNS/IP/connect-time SSRF evidence. |
| Codex | The audited mainline does not substitute one WebFetch string check for a network boundary. | codex-network-proxy supplies HTTP/SOCKS5 proxying, exact or restricted wildcard domains, private-resolution rejection, limited read-only methods, MITM header hooks, Unix-socket control, and structured audit; the OS sandbox constrains loopback bypass paths. | Adopted structured deny reasons, audit, and managed-policy concepts. Retained consumer-aware Runtime safe clients rather than relying only on a child-process proxy. |
| CodeBuddy 2.109.3 | WebFetch applies Security Center domain policy and rejects IP literals, localhost, and selected private ranges. WeCom media uses an official CDN allowlist with redirects disabled. | Sandbox allowedDomains intersects with WebFetch permission. The audited bundle's general WebFetch path checks literal hosts; the snapshot does not confirm full hostname A/AAAA validation, peer binding, or hop-by-hop redirect enforcement. | Adopted fixed channel-service allowlists and the intersection of permission and network policy. Rejected literal-IP-only protection for general fetches. |
| Gemini CLI | Official tool documentation warns that web_fetch can reach localhost and private networks; Plan Mode can require confirmation. The local 0.35.3 core contains IP classification and DNS utilities, but the warning shows they do not form a general WebFetch private-target boundary. | User confirmation is a permission decision and does not constrain the connected peer. | Adopted explicit risk documentation. Rejected confirmation as a substitute for deterministic target enforcement. |
| OpenCode | webfetch permissions use the complete requested URL and support allow, ask, or deny. Current official permissions documentation does not establish private-IP classification, full DNS results, hop-by-hop redirects, or peer binding. | shell uses the host user's network privileges; official documentation calls string rules best effort, not network isolation. | Adopted uniform resource-permission semantics. A permitted URL must still pass independent transport policy. |
| pi-mono | The fixed snapshot does not confirm a built-in arbitrary-URL fetch in the core product; network capability primarily comes from shell commands, installers, or user extensions. | The core snapshot does not confirm an OS egress policy. Third-party pi-sandbox is not counted as built-in pi-mono behavior. | Rejected it as a Runtime SSRF algorithm reference. Its extension boundary supports keeping third-party code outside safe-client completion. |
| pi-ai | This is protocol reference source, not a runnable product, and has no session or Runtime URL consumer. | Not applicable. | Excluded from product-level SSRF conclusions while retaining its matrix column to preserve the audited unit distinction. |
| weclaw | The fixed snapshot is a messaging bridge to external ACP/CLI agents and does not confirm an independent general URL fetch. | It primarily inherits the launched CLI's tools and network privileges; the delegated product's controls are not counted again for weclaw. | Retained channel-media and external-process launch as its relevant boundaries; rejected it as a general safe-HTTP reference. |
| cc-internal / cx-internal / gm-internal | cc-internal and cx-internal wrap upstream launchers; gm-internal substantially reuses Gemini CLI. The audited packages do not confirm independent strengthening of general URL-fetch policy. | They inherit most Claude Code, Codex, or Gemini CLI permission and network behavior. Added authentication or endpoints do not themselves create an SSRF boundary. | Audited only their additional endpoints, credential forwarding, and configuration differences; rejected double-counting them as three independent security designs. |
| OWASP | Separates known-service allowlisting from arbitrary-public-target validation, restricts protocols, validates every A/AAAA result, and prefers allowlists for trusted services. | A single DNS preflight still has DNS pinning and time-of-check/time-of-use risk; network controls are also recommended. | Adopted separate trust classes and connect-time peer constraints. Rejected one preflight DNS result as the only boundary. |
Primary sources: Hermes url_safety.py, Hermes Security, OpenClaw Security, Claude Code Sandboxing, Claude Code WebFetch safety check, Codex network proxy, OpenCode Permissions, Gemini CLI tools, and OWASP SSRF Prevention.
Versioned local snapshot evidence: @tencent-ai/codebuddy-code@2.109.3 dist/codebuddy.js,
CHANGELOG.md, dist/web-ui/docs/cn/cli/permissions.md; @google/gemini-cli@0.35.3 core/dist/src/utils/fetch.js;
references/pi-mono@3eb002766fff; references/weclaw@10711d0c6b7e; and references/pi-ai.
04Adopted, modified, and rejected choices
| Decision | Mechanism | Reason |
|---|---|---|
| Adopted | One pure URL/IP policy; all DNS answers must pass; DNS failure is closed; every redirect is reevaluated; errors are origin-only; decoded bodies are bounded | These checks cover the Runtime's target, redirect, credential, and resource boundaries consistently. |
| Adopted | Distinct untrusted-public, fixed-public-service, configured-service, and loopback-callback trust classes | Owner-configured localhost/private services remain usable without granting arbitrary public consumers private-network access. |
| Adopted | Claude Code and CodeBuddy permission/network layering, Codex structured deny reasons, and CodeBuddy fixed channel-media allowlists | Permission answers who may request a resource; transport policy answers which peer receives the connection and credentials. |
| Modified | Hermes process-wide allow_private_urls | Replaced by an exact consumer plus normalized origin or canonical private/local CIDR; metadata and link-local targets remain non-exceptable. |
| Modified | OpenClaw browser interception and Codex whole-process egress proxy | Browser controls remain a separate gate; a policy proxy is optional, owner-declared, separately peer constrained, visible in doctor output, and has no direct fallback. |
| Rejected | URL-string-only checks, one DNS preflight, implicit redirects, environment proxy inheritance, TLS verification disablement, or raw fallback | Each leaves a target, rebinding, credential, or bypass path. |
| Rejected | User confirmation or hostname allow/deny as target enforcement | Neither validates every resolved address nor constrains the connected peer. |
| Rejected | Applying a public-only rule to every HTTP client | This would break explicitly configured local model endpoints, MCP servers, and intranet services. |
| Rejected | Counting Origin/Host, CORS, owner authentication, browser checks, or sandbox networking as Runtime SSRF completion | Those controls protect different directions or execution boundaries. |
The Runtime URL-fetch implementation is complete through Task 8. This section records that implementation evidence first,
then lists separate future boundaries. The matrix remains ◐ because those separate boundaries are excluded.
05Completed plan: implementation evidence
Registry-keyed caller
four trust classes
exact configured or callback origin
Pure policy decision
one complete DNS answer set
exact exception and reason code
Managed transport
original Host, SNI, and certificate
manual redirect and bounded response
05.1 Transport and policy behavior
- Public requests allow only registry-declared HTTP methods, schemes, and ports; all resolved addresses must be global, with explicit rejection for metadata, loopback, private, link-local, CGNAT, multicast, reserved, unspecified, documentation/test, mapped, ambiguous, and malformed targets.
- Sync and async
httpcorebackends dial only the approved IP tuple, verify the actual socket peer, and retain the normalized hostname for HTTP Host, TLS SNI, and certificate validation. Pool identity includes consumer, trust class, normalized origin, approved IPs, and proxy identity. - Redirects are manual and each hop receives a new policy decision. Cross-origin configured-service redirects fail; public credentials are stripped; URL userinfo is rejected; logs and public errors retain no query, fragment, token path, cookie, or peer-controlled body.
- Headers, MIME, content encoding, decoded bytes, connect/read/write/pool/overall timeout, and temporary-file lifecycle are bounded. Downloads replace the destination only after full validation, flush,
fsync, and cleanup-safe completion.
05.2 Consumer and SDK disposition evidence
| Disposition | Consumers | Enforcement |
|---|---|---|
| Injected transport | provider.openai.sdk, provider.anthropic.sdk, provider.google.sdk, mcp.configured.http, mcp.configured.sse | Public SDK construction receives a scoped managed client or factory; real SDK-chain tests reach the constrained backend. |
| Exact origin | Telegram, WeChat, Feishu, Matrix, Slack, Discord, fixed/configured TTS, and other fixed/configured REST consumers | The normalized owner or shipped origin is frozen before credentials are attached; redirects cannot move credentials to another origin. |
| Derived URL extraction | Attachment, generated-asset, and image-result consumers | The SDK or upstream response yields a URL, then Runtime performs the download or upload through a dedicated managed registry consumer. |
| Disabled | provider.amazon_bedrock.sdk, channel.slack.gateway_sdk, channel.discord.gateway_sdk, tts.edge_sdk | Startup or invocation fails with UNMANAGED_TRANSPORT before importing or using the uninjectable network SDK. Bedrock covers both the registered stream and model-list entry before boto3/botocore credential or client activity. |
05.3 Owner exceptions, policy proxy, doctor, and inventory
Owner configuration accepts only a registered consumer plus exactly one normalized origin or canonical private/local CIDR. It rejects wildcards, suffixes, credentials, unknown fields, public/default-route networks, metadata, link-local ranges, and exception forms not allowed by the consumer. Existing owner-authenticated settings paths remain the only mutation authority.
A policy proxy is usable only when owner configuration sets enforces_target_policy: true. Runtime evaluates the
target before proxy use, constrains the proxy peer separately, reports delegation through doctor/audit state, and never retries direct.
| Inventory exclusion | Owner and narrow reason |
|---|---|
security/safe_http.py | Runtime managed transport; the only allowed direct httpcore pool/proxy construction |
functions/tools/browser/_chrome_bootstrap.py | Browser control; exact browser opener construction and loopback CDP liveness dial |
_cli_cmds/mcp.py | Owner control plane; authenticated backend opener only |
cli_ink.py, _cli_cmds/doctor.py, _cli_cmds/rescue.py | Owner control plane; exact loopback worker liveness socket only |
The checker fails for an unregistered raw call, active unmanaged SDK transport, registry entry without a classified consumer,
or stale exclusion. Doctor exposes stable runtime-http-registry, runtime-http-owner-exceptions,
runtime-http-policy-proxy, runtime-http-recent-denials, and runtime-http-unmanaged-transport labels.
05.4 Verification snapshot
| Command | Recorded result |
|---|---|
uv run --extra dev pytest -q tests/security | 804 passed |
uv run --with pip --with boto3 pytest -q tests/meta_functions tests/providers tests/unit tests/webui tests/integration/test_mcp_client.py | 3482 passed, 12 skipped, 1 xfailed |
uv run --extra dev --with boto3 pytest -q | 4923 passed, 14 skipped, 2 deselected, 1 xfailed |
uv run python scripts/check_runtime_http.py | unregistered=0 active_unmanaged=0 registry_without_consumer=0 stale_exclusions=0 |
uv run python scripts/check_feature_matrix.py docs/reference/design/feature-matrix.html | features=160 openprogram_score=78 openprogram_gaps=73 openprogram_only=6 |
◐ because the verified scope is Runtime-owned HTTP rather than the
separate browser, arbitrary-code, plugin, external-CLI, and package-manager boundaries.06OpenProgram future plan
| Future boundary | Required work before claiming completion |
|---|---|
| Browser navigation and page-derived traffic | Define and test navigation, popup, Service Worker, background resource, redirect, download, CDP, and browser-profile policy across every browser backend; use an enforcing egress boundary where browser interception is insufficient. |
| Plugin and arbitrary-code networking | Keep the trust and sandbox model separate from Runtime safe HTTP; add an OS or policy-proxy egress boundary if these processes need restricted network access. |
| Disabled third-party SDK network features | Enable only after the SDK supports managed transport injection or an owner-configured enforcing policy proxy has equivalent tests. Do not replace the disabled disposition with an unverified SDK guarantee. |
Related documents: Sandbox design, network proxy design, and feature matrix.