Skip to content

fix(proxy): L4 and forward-proxy 403/502 responses should include a body #807

Description

@johntmyers

Agent Diagnostic

Loaded skills: openshell-cli, create-github-issue. Explored credential placeholder resolution in the L7 proxy to verify header-level injection for a user report. While testing with curl -s from inside a sandbox, discovered that policy denials and upstream failures return empty-body HTTP responses, making them indistinguishable from each other (and from "no response" to the user).

Searched all 403/502 response sites in proxy.rs and l7/rest.rs. The L7 inspection path (l7/rest.rs:344-381, send_deny_response()) already returns a rich JSON body:

{"error":"policy_denied","policy":"...","rule":"GET /path","detail":"GET /path not permitted by policy"}

The inference denial path (proxy.rs:1285-1293) also returns a JSON body:

{"error":"connection not allowed by policy"}

But the L4 CONNECT and forward-proxy paths write bare status lines with no body at all.

Description

Actual behavior: 9 denial sites in proxy.rs return HTTP/1.1 403 Forbidden\r\n\r\n (empty body). The 502 at line 2366 returns HTTP/1.1 502 Bad Gateway\r\n\r\n (also empty). With curl -s, the user sees zero output — no way to distinguish "denied by policy" from "upstream unreachable" from "no response."

Empty-body 403 sites in proxy.rs:

  • Line 466 — OPA policy denial (CONNECT)
  • Line 521 — SSRF: allowed_ips check failed (CONNECT)
  • Line 556 — SSRF: invalid allowed_ips in policy (CONNECT)
  • Line 597 — SSRF: internal address without allowed_ips (CONNECT)
  • Line 2074 — OPA policy denial (forward proxy)
  • Line 2202 — L7 policy denial (forward proxy)
  • Line 2257 — SSRF: allowed_ips check failed (forward proxy)
  • Line 2295 — SSRF: invalid allowed_ips in policy (forward proxy)
  • Line 2337 — SSRF: internal IP (forward proxy)

Empty-body 502:

  • Line 2366 — upstream TCP connect failed (forward proxy)

Expected behavior: All denial and error responses should include a JSON body consistent with the L7 path, e.g.:

{"error":"policy_denied","detail":"CONNECT api.example.com:443 not permitted by policy"}
{"error":"upstream_unreachable","detail":"connection refused: api.example.com:443"}

This makes proxy errors diagnosable without -v and gives tools/SDKs a machine-readable error to surface.

Proposed Design

Add a helper (or extend the existing respond()) that writes a JSON body with Content-Type: application/json and Content-Length headers. Each site should include an error code and a detail string. Pattern after the existing send_deny_response() in l7/rest.rs.

Suggested error codes:

  • policy_denied — request blocked by network policy
  • ssrf_denied — resolved IP failed allowed_ips or internal-address check
  • upstream_unreachable — TCP connect to upstream failed
  • upstream_error — upstream returned an error or closed prematurely

Alternatives Considered

  • Plain-text body (e.g., "not permitted by policy") — simpler but not machine-readable.
  • Status code only, rely on logs — current state; insufficient for curl -s and SDK users.

Activity

  1. added 2 commits that reference this issue on Apr 10, 2026
    5cb8a38
    29e8159
  2. added a commit that references this issue on Apr 13, 2026
    09af1b6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:sandboxSandbox runtime and isolation work

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions