Summary
pipeline.Context.Path holds a different value for the same request depending on the
listener:
| listener (mode) |
Path from |
query string? |
decoded? |
| forward / reverse proxy (proxy-sidecar) |
r.URL.Path |
no |
yes |
| ext_proc (envoy-sidecar), 4 sites |
raw :path pseudo-header |
yes |
no |
| ext_authz (waypoint) |
AttributeContext.HttpRequest.path (documented as the full request target) |
yes |
no |
GET /api/x?secret=1 gives plugins Path == "/api/x" under proxy-sidecar but
"/api/x?secret=1" under envoy-sidecar/waypoint. There is no Query field on
pipeline.Context, so any plugin behavior keyed on Path silently differs by
deployment mode.
Impact
Consumers diverging per mode today:
- context-guru: exact/suffix path gates miss
/v1/messages?beta=true under
Envoy modes only;
- opa: policies matching
input.path == "/api/x" pass under proxy-sidecar,
fail under envoy-sidecar/waypoint;
- ibac: the judge prompt interpolates
pctx.Path, so under Envoy modes the
judge LLM sees query parameters — potentially secrets carried in URLs.
Why it survived this long
Plugins kept absorbing it locally instead: bypass.Matcher strips the query since
birth (b7f79e49, as bypass-evasion canonicalization — accidentally shielding JWT
validation, the highest-traffic consumer), inference-parser hit it and fixed it
locally on 2026-08-26 (e957afd4 — its comment states the root cause verbatim:
"The listeners disagree on what Path holds"), and tool-prune hit it again on
2026-09-02 (ae3f0a35). Two independent plugin-level fixes for the same root cause
within one week says it's structural: Path should be fixed at the listeners, once.
Expected behavior
pctx.Path means the same thing in every listener mode: the URL path only, exactly
as the proxies produce it (query dropped, percent-decoded). Plugins that need the
query string should get an explicit channel (e.g. a future Query field) rather
than parsing Path.
Assisted-By: Claude (Anthropic AI) noreply@anthropic.com
Summary
pipeline.Context.Pathholds a different value for the same request depending on thelistener:
Pathfromr.URL.Path:pathpseudo-headerAttributeContext.HttpRequest.path(documented as the full request target)GET /api/x?secret=1gives pluginsPath == "/api/x"under proxy-sidecar but"/api/x?secret=1"under envoy-sidecar/waypoint. There is noQueryfield onpipeline.Context, so any plugin behavior keyed onPathsilently differs bydeployment mode.
Impact
Consumers diverging per mode today:
/v1/messages?beta=trueunderEnvoy modes only;
input.path == "/api/x"pass under proxy-sidecar,fail under envoy-sidecar/waypoint;
pctx.Path, so under Envoy modes thejudge LLM sees query parameters — potentially secrets carried in URLs.
Why it survived this long
Plugins kept absorbing it locally instead:
bypass.Matcherstrips the query sincebirth (
b7f79e49, as bypass-evasion canonicalization — accidentally shielding JWTvalidation, the highest-traffic consumer), inference-parser hit it and fixed it
locally on 2026-08-26 (
e957afd4— its comment states the root cause verbatim:"The listeners disagree on what Path holds"), and tool-prune hit it again on
2026-09-02 (
ae3f0a35). Two independent plugin-level fixes for the same root causewithin one week says it's structural:
Pathshould be fixed at the listeners, once.Expected behavior
pctx.Pathmeans the same thing in every listener mode: the URL path only, exactlyas the proxies produce it (query dropped, percent-decoded). Plugins that need the
query string should get an explicit channel (e.g. a future
Queryfield) ratherthan parsing
Path.Assisted-By: Claude (Anthropic AI) noreply@anthropic.com