Repository navigation
[P0] Preserve bounded end-to-end HTTP headers across the gateway proxy #440
Description
Verified buyer/security gap
Fresh protected-source review on main@f8260f1e03836039ff9463dd99fa982e4e270c4b found that Wardnet's generic gateway is not yet an HTTP-semantics-preserving reverse proxy.
gateway() receives the inbound HeaderMap, but uses it only for client-IP attribution. It then calls proxy_request(&state, &route, &method, gateway_path, uri.query(), body) without the request headers. proxy_request() constructs the upstream request from only method, target URL, and body. On the return path it reads only the upstream status and complete body bytes and emits (status, bytes), so all upstream response fields are discarded.
That means a buyer route can lose required end-to-end semantics such as request Content-Type / Accept and response Content-Type, Location, Retry-After, authentication challenge or other explicitly admitted application metadata. A JSON API can therefore receive bytes without the caller's media type, and a client can receive upstream bytes/status without the upstream representation metadata.
A naive "forward every header" repair would create a separate security defect. Wardnet must never relay management credentials such as X-Admin-Token, attacker-controlled forwarding identity, Host, framing fields, proxy credentials, hop-by-hop fields, or any field named by Connection. Header mediation must therefore be explicit, bounded and independently testable.
Ownership boundary
This is Wardnet-owned generic gateway / SOC control-plane HTTP mediation. It does not move reusable outbound URL/address/DNS/peer/redirect/proxy/TLS/resource authorization from EgressWeave and must not copy EgressWeave source. It does not overlap #89's contextual-orchestrator-specific LLM seam, #435's Coraza enforcement sidecar contract, or #180/#181 route segment selection. The generic gateway should expose one narrow end-to-end-header policy that those specialized seams may consume without becoming duplicate transport-policy authorities.
Hostile realistic RED
Create a serialized Draft repair only after re-reading the then-current gateway parents and active writers. First keep production source byte-identical and use a loopback upstream fixture that proves the current behavior:
- a benign JSON request with
Content-Type: application/jsonandAccept: application/jsonreaches the upstream without those fields today; the acceptance contract requires the explicitly admitted end-to-end fields to arrive unchanged; - an upstream JSON response with
Content-Type: application/jsonplus a bounded application response field reaches the Wardnet client without those fields today; the acceptance contract requires the admitted response fields to survive; - hostile request fields
X-Admin-Token,X-Forwarded-For,X-Real-IP,Proxy-Authorization,Host,Connection,Transfer-Encoding,TE,Trailer,Upgradeand any field nominated byConnectionmust never be forwarded merely because header preservation is added; - hostile upstream hop-by-hop /
Connection-nominated response fields must never be reflected to the client; - duplicate/sensitive/oversized header cases fail closed according to the bounded policy rather than being silently merged into a different security meaning;
- a genuine required end-to-end field remains distinguishable from a stripped security-sensitive field in SOC/audit evidence;
- existing route selection, local threat/DNSBL denial, Coraza authority and body-size gates remain unchanged.
Runner/bootstrap/format noise is not semantic RED. The test must reach the real loopback upstream/client assertions.
Minimum causal GREEN contract
Implement one Wardnet-owned header-mediation boundary rather than ad-hoc copies in each adapter.
- Define an explicit bounded request/response end-to-end field policy. Default behavior must be least-authority; authorization/cookie or other credential-bearing propagation must require an explicit reviewed route/application contract rather than blanket passthrough.
- Strip RFC hop-by-hop fields and every field dynamically nominated by
Connectionon both request and response paths. - Strip Wardnet management/authentication fields, untrusted forwarding identity, proxy credentials,
Hostand message-framing authority regardless of application allowlists. - Preserve exact field multiplicity where protocol semantics require it; do not comma-join fields whose grammar does not permit that transformation.
- Keep outbound destination/DNS/peer/redirect/proxy/TLS authorization delegated to released EgressWeave contracts; header mediation cannot authorize transport.
- Do not introduce cross-service SQL, mutable sibling dependencies, source copy, ambient proxy inheritance or a second LLM/provider-routing boundary.
- Keep response streaming as the target architecture; do not treat the current whole-body buffering behavior as a reason to broaden this slice. If streaming is still independently unresolved, record it as a dependent gateway gap rather than hiding it.
Exact-head acceptance
On one unchanged candidate require cargo fmt --check, locked workspace tests, strict Clippy, Fuzz, security/SAST/CodeQL under live governance, current reviews/threads, 100% owned-production statement/branch/edge/public-rustdoc evidence for the new mediation code, and a realistic async loopback buyer path that remains within Wardnet's standing p95 <=20 ms target.
Keep the issue open until the effective code/test/doc/traceability delta reaches protected main through normal non-force governance. No self/model approval, force push, destructive rebase, routine bypass, gate weakening, no-op redispatch churn, predecessor-evidence promotion or release claim from a Draft head.
TRACEABILITY
- Fielding, R., Nottingham, M., & Reschke, J. (2022). HTTP Semantics (RFC 9110), especially connection-specific field handling and intermediary requirements. Internet Engineering Task Force. https://www.rfc-editor.org/rfc/rfc9110
- Thomson, M., & Nottingham, M. (2022). HTTP/1.1 (RFC 9112), message framing and connection management. Internet Engineering Task Force. https://www.rfc-editor.org/rfc/rfc9112
- National Institute of Standards and Technology. (2022). Secure Software Development Framework (SSDF) Version 1.1 (NIST SP 800-218), PW.8 verification context. https://doi.org/10.6028/NIST.SP.800-218
- MITRE. (2025). CWE-441: Unintended Proxy or Intermediary ('Confused Deputy') Problem. https://cwe.mitre.org/data/definitions/441.html
Activity
seonghobae commentedon Sep 21, 2026
2026-09-21 KST RED-lane repair update for #441.
Exact
d8fca38bdb57a10d8cfffc69e8b18b2ceef43427did acquire a hosted runner in CI35521507793, but rust job106106284520failed at Check formatting; test and Clippy never ran. That specimen is therefore not semantic RED and not a runner defect.Minimum repair changed only canonical rustfmt import ordering in
tests/gateway_header_mediation.rs; production source remains byte-identical to protectedmain@f8260f1e03836039ff9463dd99fa982e4e270c4b. Current #441 exact head is766e11d3427a062861511619e3b5d919ada6409c. Exact-head CI35597826514, Security35597826609, and SAST35597826518remain queued; CodeQL35597826488remains pending. No semantic RED/GREEN is claimed and no predecessor status transfers.The unchanged-head runner-admission specimen is already handed to canonical central owner
ContextualWisdomLab/.github#712; this leaf must not add wake/no-op commits, blind redispatch, local workflow copies, synthetic status, gate weakening, or bypass.Source repair remains serialized behind the active
src/lib.rsowners #435/#140. When this unchanged test-only head reaches the real loopback assertions, preserve that hosted semantic RED before adding one bounded mediation boundary plus hostile strip/fail-closed cases. Keep EgressWeave transport authority and other foreign-owner logic out of this lane.seonghobae commentedon Sep 22, 2026
2026-09-22 KST exact-current RED update for serialized #441.
Hosted CI
35679113962/ rust job106591966349checked out the PR merge candidate for exact branch head70f5aaa2d8b7c841097b78e2397943475cca71bb, passed rustfmt and all 145 library unit tests, then ran the real loopbackgateway_header_mediationintegration binary. Five of seven hostile cases failed semantically:X-Admin-Token: upstream accepted request (202) instead of Wardnet failing closed (400);Content-Type: upstream accepted request (202) instead of400;Content-Type: application/jsonwas dropped (Noneupstream);Content-Type: application/jsonwas collapsed to synthesizedapplication/octet-stream;x-wardnet-app-meta>16 KiB was forwarded (202) instead of failing closed (400).The two stripping cases pass because current production proxying drops essentially all request/response metadata; they are not positive evidence that bounded mediation exists. Clippy was skipped only because the semantic test step failed. Production source on this head remains byte-identical to parent #435 exact
e7e9d9b87a03c05f802a3d50c0917f75a89f7022.Minimum causal GREEN remains one Wardnet-owned generic HTTP mediation boundary: explicit request/response allowlists with multiplicity preservation; dynamic
Connectionnomination stripping; unconditional stripping of management credentials, blanket auth/cookies, forwarding identity, proxy credentials, attackerHost, framing and hop-by-hop fields; duplicate singleton and over-budget admitted request metadata fail closed before outbound I/O; malformed/ambiguous upstream singleton or over-budget admitted response metadata fails as upstream error. Do not add EgressWeave transport-authorization semantics, Coraza authority, streaming/backpressure (#443), or mutable sibling logic to this fix.After the production fix, require fresh exact-head fmt + locked workspace tests + strict Clippy and the hosted loopback hostile cases GREEN before advancing; predecessor GREEN does not transfer.
seonghobae commentedon Sep 22, 2026
@jules Continue the now-proven RED only through the existing serialized writer PR #441 (
test/440-gateway-header-mediation-red), current exact heade32a620fa66232405adbd1603327bb5abcf55b4astacked on #435 exacte7e9d9b87a03c05f802a3d50c0917f75a89f7022. Append ordinary commits only; do not force-push, rebase destructively, open a parallelsrc/lib.rssource PR, or touch #140. If your tooling cannot append safely to #441, stop rather than creating a competing writer.Hosted RED is CI
35701602003/ rust job106660704883: all 145 library tests passed, then five gateway mediation cases failed semantically. Implement the minimum Wardnet-owned causal boundary only: validate duplicate singleton/management headers and the 16,384-byte admitted request metadata budget before any upstream I/O, returning 400; request allowlistContent-Type,Accept, repeatablex-wardnet-app-meta, preserving multiplicity; strip Wardnet management credentials, Authorization/Cookie, proxy credentials, untrusted forwarding identity, attacker Host, framing/hop-by-hop fields, plus every case-insensitiveConnection-nominated field; reconstruct only admitted upstream response metadata (Content-Type, repeatablex-wardnet-app-meta,Location,Retry-After,WWW-Authenticate) with multiplicity and the same dynamic Connection stripping. Ambiguous/over-budget admitted upstream singleton/metadata must fail as a bounded upstream error, not be silently downgraded.Do not absorb #442/#443 streaming/backpressure, #435 Coraza authority, EgressWeave transport authorization, quarantine isolation, contextual-orchestrator routing, appguardrail logic, or mutable sibling code. Keep DDD/API/test docs consistent if the production boundary introduces a named contract. Preserve exact-head evidence: run fmt,
cargo test --locked --workspaceincludinggateway_header_mediation, strict Clippy, and any owned coverage/rustdoc checks available on the branch. Do not claim GREEN from predecessor runs. Leave the PR Draft until exact-head hosted GREEN exists and the dependent stack is reconciled normally.seonghobae commentedon Sep 23, 2026
2026-09-23 KST exact-current serialized repair update for #441.
#441 remains the sole production writer and is now exact
a24065743b6697a91e16a1be54e62f8dc79d95d0on exact Coraza parent #43511e7fabfca76e0051fa883905228a0dbe472f4df. The earlier real loopback semantic RED remains valid. The current head is still a temporary one-shot synthesis-helper head: predecessora63b540...acquired hosted compute in synthesis35816872244/ job107040157306, passed guard/synthesis/formatting, and failed in the local proof stage before any production-source commit.a240657...changes only that executed helper/proof-stage root cause (test-only legacy wrapper, unused generated test import, scratch formatting and explicit source scope); product mediation semantics and hostile assertions are unchanged.Current exact synthesis
35829979981, CI35829985939, and Fuzz35829986084remain queued. They are incomplete evidence, not GREEN, and predecessor results do not transfer. The unchanged queue specimen has been handed to canonical runner owner.github#712; this Wardnet lane will not use wake/no-op commits, blind reruns, runner-selector changes, timeout inflation, central workflow copies, synthetic status or bypass.Two follow-on test-only gaps remain serialized behind the settled #441 source head rather than competing with it: #446 (ambiguous duplicate response
Location/Retry-After) and #447/#448 (Content-Encodingrepresentation integrity). Their latest hosted runs failed formatting/base checks before their hostile semantics executed, so neither currently authorizes additional production repair. #443 retains the independently proven streaming/backpressure semantic RED. Keep #440 open until the effective mediation code/test/docs reach protected main through normal governance.seonghobae commentedon Sep 23, 2026
Evidence correction / fresh exact-head status (2026-09-24 KST):
Hosted #446 CI
35893328787, rust job107290948038, on exact child8ae44f90dec252140ad65075a96ad1c8b256f0eapassed formatting, all 151 library tests, the existing seven-case gateway mediation suite, and the repaired duplicate-Retry-Afterbuyer-path case. The duplicate-Locationcase still returned404, but the fixture used302 Found; Wardnet's generic proxy uses ordinary reqwest redirect behavior, so reqwest followed the upstream Location beforeadmit_response_headers()could inspect the original duplicated field set. The historical 404 is therefore not valid evidence that the repaired mediation boundary relayed duplicate Location.#446 exact
b8b5804b655a424967252d8ac22172eb4a1338fbrepairs only the hostile fixture to201 Createdwith duplicate Location. That is a protocol-valid Location-bearing response that does not invoke redirect handling, so it isolates the Wardnet-owned header mediation boundary. Current exact CI35918347719is queued; do not claim current-child GREEN until it executes.The automatic-redirect observation is a distinct outbound-authority integration gap. Exact run/job/root-cause evidence and RED/GREEN acceptance were handed to canonical EgressWeave Rust-consumer issue
ContextualWisdomLab/EgressWeave#237. Wardnet must not solve it by copying EgressWeave destination/DNS/redirect/proxy/TLS policy; EgressWeave's current GitHub Release inventory remains empty, so an immutable consumer bump is not yet available.Parent #441 remains the sole Wardnet production mediation writer at exact
29a35163fc5bab860f247a68a6b1d5f797d73182, with the minimum owned fix rejecting duplicateContent-Type,Location, andRetry-After. Keep #440 open until exact current source/test evidence reaches protected main through normal governance.