-
Notifications
You must be signed in to change notification settings - Fork 36
feat: add CPEX policy filter #615
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
araujof
wants to merge
16
commits into
praxis-proxy:main
Choose a base branch
from
araujof:feat/cpex
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
701bb47
feat: add CPEX security filter for agentic identity and access control
terylt a5afff2
fix(security): harden CPEX response-phase body handling
araujof 532b187
feat(cpex): add CEL PDP, session taint, and filter docs
araujof 6641259
feat(cpex): add Valkey session-store backend
araujof 63310ce
build(cpex): pin cpex crates to the 0.2.0-alpha.3 release tag
araujof 516e693
refactor(cpex): depend on the single cpex facade crate
araujof 33f0cd1
chore: cleanup
araujof 5e6a58f
build(cpex): pin cpex to rev 702ab16
araujof 8d134b2
fix(cpex): satisfy lints enabled on main after rebase
araujof 067325f
fix(cpex): move policy doc to a test fixture so schema sweep passes
araujof 0982620
chore: auto generate examples/README.md
araujof c932c86
chore: generate filter docs
araujof 229279e
fix(cpex): address PR review findings
araujof 95d7caf
chore: merge main
araujof ed78afb
chore: merge 'main'
araujof dbcd8db
build: bump quinn-proto to 0.11.15 for RUSTSEC-2026-0185
araujof File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,4 +35,6 @@ highlight = "all" | |
| unknown-registry = "deny" | ||
| unknown-git = "deny" | ||
| allow-registry = ["https://github.com/rust-lang/crates.io-index"] | ||
| allow-git = [] | ||
| # CPEX policy-runtime crates (feature-gated behind `--features cpex`). | ||
| # Pinned by exact rev in Cargo.toml until the crates are published. | ||
| allow-git = ["https://github.com/contextforge-org/cpex"] | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This exception exists solely because of the git-rev pin above. An external filter crate wouldn't require any changes to Praxis's |
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| <!-- Generated by: cargo xtask generate-filter-docs --> | ||
| <!-- Do not edit manually --> | ||
|
|
||
| # `cpex` | ||
|
|
||
| Configuration block for a `cpex` filter slot in a Praxis filter chain. | ||
|
|
||
| Requires Cargo feature: `cpex`. | ||
|
|
||
| ## Configuration Notes | ||
|
|
||
| Praxis filter configs are flat: the filter's typed fields sit directly under the `- filter:` entry alongside the structural keys (`name`, `conditions`), not nested under a `config:` wrapper. See `examples/configs/security/cpex.yaml` for a runnable example. | ||
|
|
||
| The referenced YAML is the CPEX policy document — plugins, routes, and identity-source declarations. The filter loads it once at construction and rejects misconfigured policy at server startup (fail-fast rather than at first request). | ||
|
|
||
| ## Configuration | ||
|
|
||
| | Field | Type | Required | Description | | ||
| |-------|------|---------|-------------| | ||
| | `config_path` | string | yes | Filesystem path to the CPEX YAML policy document. | | ||
| | `body_access` | `read_only` \| `read_write` | no | Body-access tier. `ReadOnly` (default) lets APL inspect request and response bodies for routing / policy decisions but discards any mutations. `ReadWrite` enables the CMF → JSON-RPC re-serialization round-trip so APL field mutators (e.g. `args.ssn: redact(!perm.view_ssn)`) rewrite the upstream body and response. Pay the round-trip cost only when needed. | | ||
| | `require_mcp_metadata` | bool | no | Fail-closed policy gate for misconfigured chains. When `true` (default), `on_request_body` rejects any request that reaches it without `mcp.method` filter-metadata. The metadata is set by praxis's built-in `mcp` filter, so its absence means either (a) the `mcp` filter is missing from the chain, or (b) it is ordered AFTER `cpex` instead of before. Either is a misconfiguration that would silently bypass CMF/APL policy. Set to `false` only when intentionally fronting non-MCP traffic through `cpex` for identity-only enforcement (legacy behavior). Note: MCP methods that legitimately carry no entity (e.g. `tools/list`, `initialize`, `prompts/list`) still pass — `require_mcp_metadata` only rejects when the metadata is missing entirely. | | ||
| | `init_timeout_secs` | u64 | no | Maximum time, in seconds, to wait for `PluginManager::initialize` at filter construction. Identity plugins fetch JWKS over HTTPS during init; a reachable-but-unresponsive identity provider would otherwise hang startup or hot-reload indefinitely. On expiry, filter construction returns an error and the server fails fast. 30s is generous for legitimate cold-cache JWKS fetches over the public internet, while short enough that misbehavior is noticed during the deploy. | | ||
| | `max_buffer_bytes` | usize | no | Maximum request/response body bytes buffered in `ReadWrite` mode. `ReadWrite` uses `StreamBuffer` to accumulate the whole body before APL field mutators run; without a cap an oversized payload could exhaust memory. Ignored in `ReadOnly` mode, which streams. The pipeline rejects an unbounded buffer at config load, so this always carries a concrete ceiling. | | ||
|
|
||
| ## Example | ||
|
|
||
| ```yaml | ||
| filter: cpex | ||
| config_path: /etc/praxis/cpex.yaml | ||
| body_access: read_write # optional; default read_only | ||
| require_mcp_metadata: true # optional; default true | ||
| init_timeout_secs: 30 # optional; default 30 | ||
| max_buffer_bytes: 10485760 # optional; default 10 MiB (read_write only) | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| # CPEX Security Filter | ||
| # | ||
| # Embeds the CPEX policy runtime in-process to enforce multi-source | ||
| # JWT identity, APL route policy, RFC 8693 OAuth 2.0 token exchange, | ||
| # PII scanning, audit emission, and (under `body_access: read_write`) | ||
| # request / response body rewriting. | ||
| # | ||
| # The cpex filter consumes `mcp.method` / `mcp.name` metadata stashed | ||
| # by praxis's built-in `mcp` filter, which MUST be ordered before it | ||
| # in the chain. | ||
| # | ||
| # `config_path` points at the CPEX policy YAML (plugins + routes) that | ||
| # the filter loads once at construction. It is operator-supplied, not | ||
| # shipped here: a real policy carries your own issuers, secrets, routes, | ||
| # PDPs, and delegators. See the praxis-demos repo | ||
| # (`demos/cpex/cpex.yaml`) for a fully-featured policy, and | ||
| # `tests/integration/fixtures/cpex-policy.yaml` for the minimal | ||
| # single-HS256-identity-plugin policy the integration test loads. | ||
| # | ||
| # `require_mcp_metadata: true` (the default) fail-closes when the | ||
| # `mcp` filter is missing or ordered after `cpex` — this guards | ||
| # against a misconfigured chain silently bypassing policy. | ||
| # | ||
| # This example builds only when the `cpex` cargo feature is enabled: | ||
| # | ||
| # cargo run --features cpex -p praxis -- \ | ||
| # -c examples/configs/security/cpex.yaml | ||
| # | ||
| # Exercise (assumes the backend at :3000 echoes 200): | ||
| # | ||
| # # Missing Authorization → 401 with WWW-Authenticate and | ||
| # # X-Cpex-Violation header: | ||
| # curl -i -X POST http://localhost:8080/mcp \ | ||
| # -H "Content-Type: application/json" \ | ||
| # -d '{"jsonrpc":"2.0","id":1,"method":"tools/call", | ||
| # "params":{"name":"echo","arguments":{}}}' | ||
| # | ||
| # # Token rejected by HS256 signature mismatch → 401: | ||
| # curl -i -X POST http://localhost:8080/mcp \ | ||
| # -H "Authorization: Bearer bogus.token.bytes" \ | ||
| # -H "Content-Type: application/json" \ | ||
| # -d '{"jsonrpc":"2.0","id":1,"method":"tools/call", | ||
| # "params":{"name":"echo","arguments":{}}}' | ||
| # | ||
| # Supply your own policy document at `config_path` before running this | ||
| # example; the minimal test fixture uses a placeholder shared secret | ||
| # that must not be used beyond local experimentation. | ||
|
|
||
| listeners: | ||
| - name: default | ||
| address: "127.0.0.1:8080" | ||
| filter_chains: | ||
| - main | ||
|
|
||
| filter_chains: | ||
| - name: main | ||
| filters: | ||
| - filter: mcp | ||
| # Default mode parses MCP JSON-RPC bodies and stashes | ||
| # mcp.method / mcp.name in filter_metadata. Must precede cpex. | ||
|
|
||
| - filter: cpex | ||
| config_path: /etc/praxis/cpex-policy.yaml | ||
| # Fail-closed when mcp.method is missing. Set to `false` only | ||
| # when intentionally fronting non-MCP traffic through cpex for | ||
| # identity-only enforcement. | ||
| require_mcp_metadata: true | ||
|
|
||
| - filter: router | ||
| routes: | ||
| - path_prefix: "/" | ||
| cluster: backend | ||
|
|
||
| - filter: load_balancer | ||
| clusters: | ||
| - name: backend | ||
| endpoints: | ||
| - "127.0.0.1:3000" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This git-rev pin is the core of the coupling concern.
As an external crate using
export_filters!, this dependency would live in the CPEX filter crate's ownCargo.toml, not here.