{{ message }}
Repository navigation
feat(agent): HTTP body conditions and allow_fields - #79
Merged
Merged
Conversation
An optional body on [[secrets.*.rules]] and [[personal_credentials.*.rules]] uses the MCP argument conditions on a JSON request body. When a matching rule has body conditions, the local proxy buffers the body (at most 1 MiB), checks it strictly before injecting the credential, and forwards the exact bytes. Unreadable bodies fail closed. Denials name the rule and field, never values. AgentHttpRule now rejects unknown fields, and --remote refuses body conditions until the control plane enforces them.
CLIs before body conditions silently ignore 'body' on credential rules, which would turn a conditioned allow into an unconditional one. They reject unknown top-level keys, so validation now requires min_cli_version >= 0.22.0 when any rule has 'body', and refuses profiles that need a newer CLI than the one running.
allow_fields on MCP tool rules and HTTP credential rules lists the only fields a call or body may contain besides those with conditions; any other field fails the rule (unlisted_field:<prefix>.<field>). Same key and semantics for both, allow rules only. MCP rules send allow_fields to the control plane. [[policy_tests]] cases accept a body (and content_type), evaluated like the proxy evaluates a request body.
Body conditions only read JSON, so a test body is always JSON.
Match agent mcp tools: green/red markers and allow/deny, dimmed reasons and labels, plain text when color is off. A test case without a body now reports body unreadable: no_body.
The control plane now enforces body and allow_fields on credential rules, so --remote no longer refuses profiles that use them.
HTTP rule body/allow_fields and MCP rule allow_fields were missing from ProxyPolicy::fingerprint, so profiles differing only in conditions got the same fingerprint in audit records.
Profiles no longer need a min_cli_version key to use body or allow_fields.
With allow_fields = ["project.id"] and no condition on it, an array or
scalar at project skipped the nested check, so {"project": [{...}]}
passed. Only null is accepted where just descendant paths are listed.
In remote mode the relay skipped reading the body but still authorized locally as if no body was sent, so conditioned allow rules could never match and valid requests got a local 403. The relay now refuses only what no body could allow and leaves body conditions to the remote proxy, which enforces the same rules.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.

Summary
Credential rules can now check what a request sends, not only where it goes. The design is the same as the MCP argument conditions from #78.
bodyon[[secrets.*.rules]]and[[personal_credentials.*.rules]]. It uses the same conditions as MCParguments: a single value, a list (one of),{ min, max }, and{ absent = true }. When a matching rule has conditions, the local proxy buffers the body (at most 1 MiB) and checks it strictly (JSON object, no encoding, no duplicate keys) before injecting the credential. It then forwards the exact bytes. An unreadable body fails closed. Requests whose rules have no conditions still stream unchanged.allow_fieldson HTTP rules and MCPtool_rules. It is strict mode: no fields other than those named are allowed. Allow rules only.(rule 1: unlisted body.bcc). Audit reasons areallow_condition_failed,unlisted_field,deny_condition_matchedandbody_unreadable, and work withagent logs --reason.[[policy_tests]]accept a JSONbody.agent policy testoutput is now colored.agent validaterejects malformed conditions andallow_fieldson deny rules, and warns about moot or best-effort rules.agent explainreports body-dependent decisions as conditional.agent mcp toolsshowsallow_fieldsand warns about undeclared fields.--remotesendsbodyandallow_fieldsto the control plane.AgentHttpRulenow rejects unknown fields.docs/agent-profiles/request-conditions.md, plus runnable examples for httpbin and the GitHub REST API.Depends on
radim10/backend-monorepo#314 must be deployed before this is released. An older backend drops
bodyandallow_fields, so with--remotea conditioned allow would apply to every request.Release note
Profiles using
bodyorallow_fieldsrequire CLI 0.22.0 or newer. Older CLIs ignore these keys and would treat the rules as unconditional allows.Tests
cargo test: 664 + 25 + 33 pass. Verified live:--remoteon staging against GitHub. Create with label, comment and rename were allowed. Extra label, assignees and close were refused with the right field.