Skip to content

v0.22.0

Choose a tag to compare

@github-actions github-actions released this 19 Oct 09:04
· 20 commits to master since this release

Overview

Request conditions: control what an agent sends, not only where (#78, #79)

Credential rules and MCP tool rules can now check the content of a request: the JSON body of an HTTP request, or the arguments of an MCP tool call. A host, method and path can't tell a draft from a sent email, or creating an issue from closing one. Conditions can.

# Inject MAIL_KEY only into drafts to listed recipients, with no other fields.
[[secrets.MAIL_KEY.rules]]
effect = "allow"
hosts = ["api.mail.example"]
methods = ["POST"]
paths = ["/v1/messages"]
body = { send = false, to = ["support@acme.com", "ops@acme.com"] }
allow_fields = ["subject", "text"]
# Allow Linear's save_issue only for creating issues in one team.
[[mcp_servers.linear.tool_rules]]
effect = "allow"
tools = ["save_issue"]
arguments = { team = ["Engineering"], id = { absent = true } }
allow_fields = ["title", "description", "priority"]
  • body on [[secrets.*.rules]] and [[personal_credentials.*.rules]], and arguments on the new [[mcp_servers.*.tool_rules]]. Both use the same four conditions:

    Condition Holds when the field
    send = false equals the value, with the same type
    to = ["a", "b"] is one of the values, or a non-empty array of only those values
    amount = { min = 1, max = 5000 } is a number in the range (inclusive; either bound optional)
    id = { absent = true } is missing or null

    Dotted paths ("destination.type") reach nested fields. All conditions in a rule must hold, separate rules are alternatives, and a matching deny always wins.

  • allow_fields lists the only other fields a request may contain. Without it, fields the rule doesn't mention pass unchecked; with it, any unlisted field (a bcc, an assignees) is refused. Allow rules only.

  • Fails closed. When a matching rule has conditions, the proxy reads the body before injecting the credential. It must be JSON, uncompressed, at most 1 MiB, an object, and have no repeated keys. Anything else, including no body, can't satisfy an allow rule with conditions. The upstream receives exactly the bytes that were checked. Rules without conditions work as before, and their bodies are never read.

  • Denials name the field, never the value. The 403 says The supplied credential is not allowed for this request (rule 1: unlisted body.bcc). The audit log records reasons such as allow_condition_failed:body.send, unlisted_field:body.bcc, deny_condition_matched:… and body_unreadable:json.

  • agent logs, logs list and logs summary --reason <code> filter by those reasons. A bare code like allow_condition_failed also matches the variants that name a field.

  • Testable without requests. [[policy_tests]] cases accept a JSON body, and agent policy test now prints its results in color.

  • Tooling:

    • agent validate rejects malformed conditions and allow_fields on deny rules. It warns when an allow rule without conditions makes others pointless, and that deny rules with conditions are best-effort.
    • agent explain reports requests that depend on the body as "eligible only if the request body meets the rule's body conditions".
    • agent mcp tools --tool <name> shows a tool's arguments, types and the rules for it, and warns about rule fields the tool doesn't declare. An unknown server or tool is now a JSON input error.
  • Works with --remote. The remote Agent Proxy enforces the same conditions with the same messages and audit reasons.

  • Guide: Request conditions, with examples for the GitHub REST API and httpbin. GraphQL endpoints need an exact-query allowlist; see the guide's limits.

Changes

  • Credential rule hosts no longer need to be in egress_hosts (#75). A host in a secret's rules is reachable by requests that carry that secret and match its rules. egress_hosts now only covers requests without a credential. Before, every credential host had to be listed in egress_hosts, which opened it to any request.
  • The Docker sandbox uses the repository's git identity (#76). Commits inside the container used your global user.name and user.email even when the repository sets its own. The identity is now resolved like git commit resolves it on the host: worktree, repo-local, global (including includeIf), then system.

Compatibility

  • Profiles using body or allow_fields need CLI 0.22.0 or newer. Older CLIs ignore these keys and would apply such a rule to every request it names. MCP tool_rules are refused by older CLIs.
  • Unknown keys in credential rules are now an error. A typo like methds = [...] used to be silently ignored.
  • The policy fingerprint changes once for profiles with HTTP rules or MCP tool rules, because conditions are now part of it.
  • JSON output changes in agent explain and agent policy test (#75):
    • agent explain: connection: "no_egress_restriction" is removed. allowed_with_credential, egress_hosts_configured and reachable_hosts are new.
    • agent policy test: reason denied_by_egress_hosts is removed.

Pull requests

  • fix(agent): let secret rule hosts bypass the egress allowlist — #75
  • fix(agent): forward repo-local git identity into docker sandbox — #76
  • feat(agent): MCP tool argument conditions — #78
  • feat(agent): HTTP body conditions and allow_fields — #79