Fix: Strip query string from pctx.Path in ext_proc and ext_authz - #882
Fix: Strip query string from pctx.Path in ext_proc and ext_authz#882JoshSag wants to merge 2 commits into
Conversation
pipeline.Context.Path meant different things depending on the listener: the forward and reverse proxies populate it from r.URL.Path (query-free, percent-decoded by net/http's parser), while ext_proc used the raw :path pseudo-header and ext_authz used AttributeContext.HttpRequest.path — both of which carry the full request target, query string included. Any plugin behavior keyed on Path therefore differed by deployment mode. Three consumers had already grown defensive strips (bypass matcher, tool-prune's gate, inference-parser's dialect dispatch), while others were still exposed: context-guru's suffix gate misses /v1/messages?beta=true under Envoy modes, OPA policies exact-matching input.path break only there, and ibac's judge prompt includes query parameters only there. Run the raw request target through url.ParseRequestURI — the same parser net/http runs for the proxy listeners — at pctx construction in both Envoy-fed listeners, so Path is byte-identical across listener modes. The invariant is documented on Context.Path and pinned by new tests in both fixed listeners (red before this change). inference-parser's defensive strip stays as defense in depth for contexts constructed outside a listener; its comment now reflects the guaranteed invariant. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: YehoshuaSagron <ysagron@gmail.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (6)
🚧 Files skipped from review as they are similar to previous changes (1)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughExt_authz and ext_proc now populate ChangesPath normalization
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: ⚪ Minimal · up to Ext_authz and ext_proc now provide query-free, percent-decoded paths consistently across listener modes, with malformed targets retaining the documented query-strip fallback. Current coverage exercises the changed behaviors and no merge-blocking risk remains. Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant EnvoyOrAuthRequest
participant Listener
participant httpxPathOnly
participant Pipeline
EnvoyOrAuthRequest->>Listener: provide request target
Listener->>httpxPathOnly: normalize target
httpxPathOnly-->>Listener: decoded path without query
Listener->>Pipeline: store Context.Path
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
…ests Review-hardening pass on the previous commit: - Hoist pathOnly to a single exported httpx.PathOnly used by both Envoy-fed listeners. The function now defines a cross-listener invariant documented on pipeline.Context.Path; two private copies could drift silently. - Hedge the Context.Path and PathOnly doc comments: values are identical across listener modes modulo unparseable targets, which net/http rejects with 400 before any pipeline runs while the Envoy-fed listeners keep them query-stripped but otherwise raw. - Table-drive both listener tests and extend them to pin all three behaviors per listener: query strip, percent-decoding, and the unparseable-target fallback (previously uncovered — a regression there would have passed green). - Run each ext_proc test request on its own mock stream, matching the one-request-per-stream production shape instead of relying on incidental cross-request state handling. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: YehoshuaSagron <ysagron@gmail.com>
|
@abigailgold Thanks — both points acted on.
Assisted-By: Claude (Anthropic AI) noreply@anthropic.com |
Fixes #881.
The problem
pipeline.Context.Pathdiverged by listener mode: the proxies parse the requesttarget (
r.URL.Path— query-free, percent-decoded), while ext_proc and ext_authzpassed the raw target through, query string included. Full table, timeline, and
impact in #881.
The change
Run the raw request target through
url.ParseRequestURI— the same parsernet/httpruns for the proxy listeners — at pctx construction in both Envoy-fed listeners
(shared helper
httpx.PathOnly).Pathis now identical across all four listenermodes, decoding included:
/api/hello%20world?x=1yields/api/hello worldeverywhere. Targets that parser rejects (which
net/httpanswers with 400 before anyproxy-mode pipeline runs) keep a plain query-strip fallback — no worse than today.
Two behavior notes for Envoy-mode deployments, both alignments with proxy-sidecar's
longstanding semantics:
Decoding affects matching:
/%68ealthznow matches a/healthzbypass/policypattern, as it always has under the proxies.
The raw query — which routinely carries tokens and secrets — no longer reaches the
ibac judge LLM; the flip side is that policy loses query visibility entirely until
the
Queryfield lands (follow-up below).ext_proc:
Path: httpx.PathOnly(getHeader(headers, ":path"))at all fourconstruction sites.
ext_authz:
path := httpx.PathOnly(httpReq.GetPath()).pipeline.Context.Pathdoc comment now states the invariant, so plugins maymatch/log/policy-feed
Pathwithout stripping a query themselves.inference-parser's one-line defensive strip is kept (defense in depth forcontexts constructed outside a listener; the failure mode it guards is silent),
with its comment updated to reflect the new invariant. The other existing strips
(bypass matcher, tool-prune) are untouched.
No behavior change for the forward/reverse proxy listeners.
Evidence
New table-driven tests
TestExtProc_PathMatchesProxyListenersandTestCheck_PathMatchesProxyListenersdrive each fixed listener through a captureplugin and pin all three behaviors per listener — query strip (
/api/x?secret=1),percent-decoding (
/api/hello%20world?secret=1&b=2), and the unparseable-targetfallback (
/a%zz?secret=1) — assertingpctx.Pathholds exactly what the proxylisteners produce for the same wire bytes.
On
main(before):On this branch: both pass, and the same capture-plugin probe run against the forward
and reverse proxy listeners confirms they already produced these values. Full
go vet ./... && go test -count=1 -race ./listener/... ./pipeline/... ./plugins/...green.
Proposed follow-up (not in this PR)
Plugins that legitimately need query parameters currently have no channel for them —
the proxies drop the query on the floor. A follow-up could add a
Query stringfieldto
pipeline.Context, populated by all four listeners (fromr.URL.RawQuery/ therequest target), so query-aware plugins opt in explicitly instead of parsing
Path.This PR deliberately only restores the invariant; adding the field is a separate,
additive decision.
Assisted-By: Claude (Anthropic AI) noreply@anthropic.com
Summary by CodeRabbit
Bug Fixes
Documentation