DEV Community

Cover image for # Agent Firewall v2.5: I Attacked My Own AI Agent Security Boundary
Shubhbhangoo
Shubhbhangoo

Posted on

# Agent Firewall v2.5: I Attacked My Own AI Agent Security Boundary

AI agents can call APIs, use MCP tools, access databases, modify files, and trigger real-world actions.

So the important security question isn't just:

Is the agent trustworthy?

It is:

Is this specific action authorized right now?

That is the problem I built Agent Firewall to solve.

How it works

The architecture is intentionally centered around one authorization boundary:

AI Agent
   |
   v
Tool / API Request
   |
   v
FirewallSDK.authorize()
   |
   +--> identity
   +--> task
   +--> capability
   +--> provenance
   +--> delegation
   +--> constraints
   +--> validity
   +--> revocation
   +--> policy
   +--> security state
   |
   v
ALLOW / DENY
   |
   v
Execution
Enter fullscreen mode Exit fullscreen mode

The important part is not the number of checks.

It is where authority comes from.

Security analysis can provide evidence and context.

It cannot create an authorization decision.

A risk score cannot become an allow.

An LLM cannot become an allow.

Monitoring cannot become an allow.

Aegis cannot become an allow.

The canonical authorization boundary remains responsible for the decision.

Capabilities

Agent Firewall treats permissions as signed capabilities.

A capability can carry:

identity
action
constraints
issuer
expiration
delegation
nonce
signature
Enter fullscreen mode Exit fullscreen mode

For example:

payments.send
amount_max = 1000
expires_at = ...
Enter fullscreen mode Exit fullscreen mode

Authority can be delegated or attenuated, but not widened.

A child capability can become:

amount_max = 100
Enter fullscreen mode Exit fullscreen mode

It cannot turn itself into:

amount_max = 10000
Enter fullscreen mode Exit fullscreen mode

The same idea applies across delegation lineage.

Every step must preserve or reduce authority.

Aegis

v2.4 introduced Aegis, the adaptive authority control plane.

It allows live authority to be:

narrowed
suspended
revalidated
revoked
Enter fullscreen mode Exit fullscreen mode

while a task is running.

But Aegis has a very deliberate limitation.

It can restrict authority.

It cannot grant authority.

Its integration with the SDK is essentially:

Aegis
   |
   v
deny-only gate
   |
   v
FirewallSDK.authorize()
Enter fullscreen mode Exit fullscreen mode

There is no Aegis → ALLOW path.

That separation is important because otherwise the system would eventually have two authorization engines.

Then came v2.5

I decided to stop adding features and attack what already existed.

The v2.5 mission was:

Attack the shipped boundary until a guarantee breaks, the attack is contained, or a limitation becomes clear.

I ran 22 attacks against v2.4.

And we found real problems.

An expired capability could be allowed

The most serious finding was an expiry check that could silently abstain when the firewall could not establish the current time.

That meant a legitimate signature could still result in:

expired capability
        |
        v
authorized
Enter fullscreen mode Exit fullscreen mode

v2.5 changes that to:

clock unavailable
        |
        v
DENY
Enter fullscreen mode Exit fullscreen mode

Internal security state could raise exceptions

Failures in:

  • revocation
  • delegation lineage
  • issuer trust
  • risk state
  • refusal state

could previously escape authorize().

That matters because an exception isn't a security decision.

v2.5 turns those failures into explicit denials such as:

revocation_state_unavailable:{Type}
Enter fullscreen mode Exit fullscreen mode

The boundary decides.

The caller does not.

Authorization and execution could disagree

Three integrations had another interesting problem.

The firewall authorized one representation of a request, while the handler could receive another.

For example:

authorize({"amount": 10})
        |
        v
ALLOW

handler({"amount": 5000})
Enter fullscreen mode Exit fullscreen mode

The authorization boundary itself was correct.

The integration around it wasn't.

v2.5 fixes this structurally by normalizing or settling the request once and giving the same object to both authorization and execution.

Authority envelopes

v2.4 also introduced AuthorityEnvelope.

It provides a projection of what a capability can still do after considering its constraints and delegation lineage.

But an envelope is deliberately not an authorization decision.

The guarantee is one-way:

Envelope excludes request
        |
        v
authorize() must deny
Enter fullscreen mode Exit fullscreen mode

Not:

Envelope does not exclude request
        |
        v
authorize() must allow
Enter fullscreen mode Exit fullscreen mode

That distinction prevents an analytical projection from becoming a second authorization path.

The security model

The deeper principle behind the project is:

Evidence
   |
   v
Context
   |
   v
Authorization
   |
   v
Authority
Enter fullscreen mode Exit fullscreen mode

Not:

Evidence
   |
   v
Authority
Enter fullscreen mode Exit fullscreen mode

A signature is evidence.

A risk score is evidence.

A monitoring result is evidence.

An AI-generated assessment is evidence.

None of them should automatically become authority.

And the tests?

v2.5 has 16 named security invariants covering properties such as:

AUTHORIZATION_UNIQUENESS
MODEL_NON_AUTHORITY
CONTROL_PLANE_INTEGRITY
PROVENANCE_INTEGRITY
FAIL_CLOSED
ENVELOPE_SOUNDNESS
DELEGATION_MONOTONICITY
CAPABILITY_MONOTONICITY
REVOCATION_MONOTONICITY
POLICY_NON_WIDENING
AEGIS_STATE_TRANSITIONS
REVALIDATION_CONSISTENCY
Enter fullscreen mode Exit fullscreen mode

The interesting rule is that:

UNVERIFIABLE != PASS
Enter fullscreen mode Exit fullscreen mode

If an invariant cannot establish its claim, it does not quietly report success.

That is a small implementation detail with a large security consequence.

What v2.5 taught me

The hardest bugs weren't always sophisticated attacks.

Some were caused by something much simpler:

unknown
   |
   v
nothing obviously wrong
   |
   v
allow
Enter fullscreen mode Exit fullscreen mode

That is the dangerous direction.

The safer model is:

unknown
   |
   v
cannot establish
   |
   v
deny / restrict / revalidate
Enter fullscreen mode Exit fullscreen mode

Agent Firewall v2.5 is therefore less about adding another security feature and more about making the existing boundary harder to escape.

The goal remains simple:

AI agents can have powerful capabilities. Those capabilities should be explicit, bounded, and enforceable.

github:https://github.com/Shubhbhangoo/agent-firewall

Top comments (0)