feat(filter): add identity header guard filter - #709
Conversation
leseb
left a comment
There was a problem hiding this comment.
would core be more suitable for this instead of this repo? it's not so much "ai" related
praxis-bot
left a comment
There was a problem hiding this comment.
Clean implementation — the filter logic is correct and the security-critical behavior (stripping before forwarding, namespaced metadata to avoid collision with verified auth) is well thought out. Three medium findings, all related to test coverage gaps.
|
Flagging a likely conflict: this filter's If both land as-is, the pipeline would end up with two filters independently capturing/stripping the same headers into different metadata shapes. Might be worth the two of you syncing on which owns the canonical |
praxis-bot
left a comment
There was a problem hiding this comment.
One new medium finding on duplicate-header handling. The previous review's three findings (misleading default-namespace test, missing registry assertion, non-UTF-8 edge case) still apply and are not repeated here.
|
@yossiovadia please address the bot's review or dismiss with a reason |
|
Good catch @jordigilh. This filter and #581 are designed to work together, not independently:
The overlap in stripping is intentional redundancy: if The canonical metadata namespace is cc @noyitz — this is the same pattern as IPP's |
|
Any reason why this feature couldn't be expressed by using the core filter Header? https://github.com/praxis-proxy/praxis/blob/main/filter/src/builtins/http/transformation/header/mod.rs#L82 Potentially adding wildcard and "move to metadata" as a feature? |
|
Good question. Looked at the core
Could these be added to the core filter? Yes — as a
I'm fine either way — if the team prefers this as a core cc @alexsnaps |
|
CI's green but there's a merge conflict with main — mind rebasing? This blocks #130/#104. Also still open: the standalone-vs-core-filter question from @aslakknutsen (Aug 13) — needs a call from @alexsnaps. |
ea02554 to
dd29829
Compare
|
Rebased onto latest Intent is unchanged from the original PR: this stays the self-contained temporary bridge @leseb green-lit in #708, writing captured identity to On the standalone-vs-core-filter question from @aslakknutsen (Aug 13): still needs a call from @alexsnaps. I'm happy to refactor into a core |
a6bca42 to
86c4dc9
Compare
praxis-bot
left a comment
There was a problem hiding this comment.
One new medium finding on untested validation logic. All previous findings are resolved.
86c4dc9 to
7e85535
Compare
praxis-bot
left a comment
There was a problem hiding this comment.
Re-review (5/5)
One new medium finding. The previous finding (#6, empty-namespace validation test) is still open and not repeated here.
8428c38 to
0389131
Compare
|
@yossiovadia please rebase |
- Add registry assertion for identity_header_guard in build_ai_registry_includes_ai_and_builtin_filters test - Add test for non-UTF-8 header values (stripped but not captured) - Add test for duplicate headers (last-value-wins behavior) - Clarify default namespace test with comment explaining the indirect verification via captures_matching_headers_to_metadata Signed-off-by: Yossi Ovadia <yovadia@redhat.com>
Signed-off-by: Yossi Ovadia <yovadia@redhat.com>
Signed-off-by: Yossi Ovadia <yovadia@redhat.com>
…filters Signed-off-by: Yossi Ovadia <yovadia@redhat.com>
Reflowed the use list after the rebase conflict resolution; the manual wrap did not match nightly rustfmt's line-filling. Signed-off-by: Yossi Ovadia <yovadia@redhat.com>
0389131 to
e44cc69
Compare
|
Rebased onto current main (post-#581). Conflict resolution was limited to the three registration sites (register.rs import list, examples/README.md table, integration suite mod.rs) — all both-sides-added-a-line merges, verified against the xtask sync checks. All gates green locally: filter unit + doctests + integration example, clippy (both feature sets), nightly fmt, machete, xtask suite. One note for reviewers: this force-push also removes four previously stacked, now-superseded commits (MCP metadata block / credential redaction, #436/#438/#578, plus a chores commit) — upstream reimplemented that redaction/SSRF work in faa043f. The PR now contains only the identity_header_guard work. @shaneutt fresh eyes when you get a chance — the rebase supersedes the earlier review. |
|
@leseb can you confirm and approve this one? |
praxis-bot
left a comment
There was a problem hiding this comment.
Re-review (6/6)
All previous findings except #6 (empty-namespace validation test, still open) are resolved. One new medium finding.
| Severity | Findings |
|---|---|
| Critical | 0 |
| Large | 0 |
| Medium | 1 |
| @@ -0,0 +1,128 @@ | |||
| // SPDX-License-Identifier: MIT | |||
| // Copyright (c) 2026 Praxis Contributors | |||
There was a problem hiding this comment.
[Medium] All four new files (config.rs, mod.rs, tests.rs, identity_header_guard.rs) use SPDX-License-Identifier: MIT, but the repository license is Apache-2.0 and every other source file in the codebase uses the Apache-2.0 header. The cargo deny license allowlist permits both, so CI will not catch this, but shipping MIT-licensed files in an Apache-2.0 project creates licensing ambiguity -- downstream consumers who rely on the repo-level license may not realize these files carry different terms.
Change the headers to SPDX-License-Identifier: Apache-2.0 to match the rest of the codebase.
There was a problem hiding this comment.
Fixed in d43ece0 — all four new files now carry SPDX-License-Identifier: Apache-2.0, matching the repo convention. Copyright lines were already correct.
…tion Signed-off-by: Yossi Ovadia <yovadia@redhat.com>
|
Heads-up after the license fixup ( |
Schema validation on main rejects loopback endpoints unless insecure_options.allow_private_endpoints is set; match the pattern used by the other local-backend examples so all_example_configs_parse passes. Signed-off-by: Yossi Ovadia <yovadia@redhat.com>
|
Another fixup pushed ( Heads-up for anyone with write access: the CI workflows on this head are sitting in |
…raxis-proxy#709) Signed-off-by: Sébastien Han <seb@redhat.com>
Summary
identity_header_guardfilter that captures headers matching a configurable prefix intofilter_metadataand strips them before upstream forwardingx-tenant-username,x-tenant-group) from leaking to LLM providersexternal_metering(feat(filter): add external metering filter for usage reporting and balance checks #577)Fixes #698
Motivation
The
external_meteringfilter reads tenant identity from request headers for per-user usage attribution. These headers are set by an upstream auth layer and must not reach the upstream provider.reserved_headersin core only handles hardcodedx-praxis-*prefixes with no metadata capture and no configurable prefixes (core TODO #186).Design
x-tenant-)filter_metadataunder a configurable namespace (prevents collision with verified auth metadata)request_headers_to_removeWhat's included
filters/src/identity_guard/— filter, config, 11 unit tests + 1 doctesttests/integration/tests/suite/examples/identity_header_guard.rs— 3 integration tests (config parse + header capture + strip)examples/configs/identity-header-guard.yaml— example configTest plan
cargo xtask lint-example-tests— passes (example config has test coverage)cargo xtask lint-filter-docs— passes (generated docs up to date)cargo clippy -p praxis-ai-filters -- -D warnings— zero warningsexternal_meteringconsuming captured identity