fix: reject unsafe configurable AI promotion headers - #974
Conversation
…xis-proxy#554) Configurable effective/original model headers could previously name Content-Length, Host, or hop-by-hop headers and conflict with framing. Share the blocked-name check with API-client forward-header validation and reject duplicate promotion names. Signed-off-by: mkoushni <mkoushni@redhat.com>
The postgres vLLM job co-locates Postgres, vLLM, and OGX; the shipped chat file-search example still uses a 60s IRR step and 5s callout, so a slow CPU inference can 504 before the 300s OpenAI client timeout. Match the native file-search template deadlines in the CI-only patcher. Signed-off-by: mkoushni <mkoushni@redhat.com>
Compose the promotion-header denylist on http_hop plus Host and Content-Length after outbound hop-by-hop sanitization landed on main. Signed-off-by: mkoushni <mkoushni@redhat.com>
leseb
left a comment
There was a problem hiding this comment.
P1: promotion.rs:29 still permits authorization and unrelated internal x-praxis-* targets. A client model such as Bearer attacker can overwrite upstream credentials or routing facts.
P1: The class-level fix is missing from Anthropic config:122 and Responses-format config:112, which retain syntax-only validation before promoting body-derived values. Issue #554 explicitly requires these sibling sinks to be fixed.
P2: Generated documentation:17 omits the new requirement that original/effective header names must differ.
…-promotion-headers Signed-off-by: mkoushni <mkoushni@redhat.com>
Block Authorization and x-praxis-* names outside the AI fact namespaces so a client model cannot overwrite credentials or routing. Apply the same validator to Responses and Anthropic format classifiers, and document that original/effective rewrite headers must differ. Signed-off-by: mkoushni <mkoushni@redhat.com>
|
> P1: promotion.rs:29 still permits authorization and unrelated internal x-praxis-* targets. A client model such as Bearer attacker can overwrite upstream credentials or routing facts. P1: The class-level fix is missing from Anthropic config:122 and Responses-format config:112, which retain syntax-only validation before promoting body-derived values. Issue #554 explicitly requires these sibling sinks to be fixed. P2: Generated documentation:17 omits the new requirement that original/effective header names must differ. P1 credentials / internal headers. Promotion targets now reject authorization/cookie/set-cookie/www-authenticate and x-praxis-* names outside the AI fact namespaces (x-praxis-ai-, x-praxis-responses-). Defaults such as x-praxis-ai-effective-model and x-praxis-responses-mode stay allowed. forward_headers still uses only the transport list, so Authorization can still be forwarded to APIs. P1 sibling sinks. openai_responses_format and anthropic_messages_format use the same validate_promotion_header helper. P2 docs. Generated model-rewrite docs now say effective_model and original_model must use distinct names. |
CI clippy -D unwrap_used failed on unwrap_err in the new authorization rejection test. Match the crate's existing test-module suppressions. Signed-off-by: mkoushni <mkoushni@redhat.com>
…-promotion-headers Signed-off-by: mkoushni <mkoushni@redhat.com>
…-promotion-headers Signed-off-by: mkoushni <mkoushni@redhat.com>
leseb
left a comment
There was a problem hiding this comment.
P1 — Credential headers remain promotable. promotion.rs:64 omits x-api-key, api-key, and x-goog-api-key, although the repository explicitly classifies them as credentials in header_policy.rs:168. A client-controlled model can therefore replace an upstream API key.
P1 — Cross-purpose routing headers remain promotable. promotion.rs:68 permits every x-praxis-ai-* and x-praxis-responses-* destination. For example, configuring effective_model: x-praxis-ai-format writes a client-derived model into the format-routing fact through model_rewrite/mod.rs:411.
…-promotion-headers Signed-off-by: mkoushni <mkoushni@redhat.com>
Model rewrite could still share AI fact namespaces, and sibling sinks only syntax-checked header names. Require each fact's dedicated header or a custom non-x-praxis name, share the credential denylist with fixture sanitization, and fail closed on unsafe targets. Signed-off-by: mkoushni <mkoushni@redhat.com>
…-promotion-headers Signed-off-by: mkoushni <mkoushni@redhat.com>
praxis-bot
left a comment
There was a problem hiding this comment.
Security Hardening Review
Solid security fix. The centralized denylist in promotion.rs is well-structured, the dedicated-allowlist pattern prevents cross-fact overwrites, and test coverage is thorough across all six call sites. Three items below.
leseb
left a comment
There was a problem hiding this comment.
LGMT, just need to address the bot review. Thanks!
…-promotion-headers Signed-off-by: mkoushni <mkoushni@redhat.com>
Shared fields cannot share a destination header. Config validation lowercases once, and the forward-header path uses the lowercase transport check so it does not allocate per request. Signed-off-by: mkoushni <mkoushni@redhat.com>
…-promotion-headers Signed-off-by: mkoushni <mkoushni@redhat.com>
Summary
Configurable promotion headers could receive a client-derived model (or other body fact) on transport, credential, and other Praxis routing headers.
openai_responses_model_rewritewas the original #554 case (content-length,host, hop-by-hop); the same class of config is now rejected on every body-derived promotion sink.Host, credential, and provider API-key names (authorization,x-api-key,api-key,x-goog-api-key, …) at config load.x-praxis-*header (or a custom non-x-praxis-*name). Model rewrite cannot overwritex-praxis-ai-format; formatmodelcannot overwrite format/rewrite facts.model_to_header, A2A, and MCP.is_credential_headerwith inference-fixture sanitization so the denylist cannot drift.Default dedicated headers (
x-praxis-ai-effective-model,x-praxis-ai-format,X-Model, A2A/MCP defaults) andforward_headersforAuthorizationare unchanged.Related issue
Closes #554
Validation
make lintCovered by
promotion,openai_responses_model_rewrite,openai_responses_format,anthropic_messages_format,model_to_header, A2A, MCP, and fixtureheader_policyunit tests.cargo clippy -p praxis-ai-apis -p praxis-ai-filters -p praxis-test-utils --lib --tests -- -D warningspassed.cargo xtask lint-filter-docsis clean. No new example config is required; existing defaults andformat: x-praxis-ai-formatexamples remain valid.Checklist
Signed-off-bytrailer.Breaking changes
Configs that name a transport, credential, API-key, or other internal
x-praxis-*header as a promotion target now fail at filter construction. Dedicated defaults and custom non-x-praxis-*names continue to work. Using the same header for both rewriteeffective_modelandoriginal_modelis also rejected.