---
title: "The Model Proposed the Action. The Broker Decided Whether It Could Exist."
description: "A practical architecture for AI-assisted pentest execution: resolve scope outside the model, classify side effects, issue short-lived capabilities, deny high-impact authority, and preserve a decision record that can be independently verified."
date: 2026-08-23
author: Sevban Dönmez (@jankesec)
canonical: https://jankesec.com/posts/ai-pentest-execution-broker/
---

## The execution broker in 60 seconds

The dangerous component in an AI pentest system is not necessarily the model. It is the function
that turns a model response into authority.

An agent can correctly identify a cross-tenant authorization weakness and still choose an unsafe
proof. It can correctly understand an Active Directory relationship and still run the wrong
command under an inherited privileged session. Better reasoning reduces some errors. It does not
make a general-purpose credential safe to hand to a probabilistic planner.

The previous article in this route established the operating rule: the model proposes, policy
authorizes, a least-privileged worker executes, and an independent path verifies. This article
turns that rule into an implementable boundary.

The companion [Execution Broker Lab](/tools/ai-pentest-execution-broker/) is a target-free,
browser-only simulation. It contains no cloud API, directory connection, shell, or credentials. It
is a reference policy model—not a production security product, a measured benchmark, or evidence
that these controls have protected a live engagement.

## Start with the security property

“The agent asks for confirmation” is not a sufficient security property. A confirmation dialog can
hide the resolved target, inherit the wrong identity, approve a broad class of actions, or appear
after the system already obtained a privileged token.

The property we want is narrower:

> A model-directed worker can execute only a typed operation for a resolved in-scope target, using
> a short-lived capability whose identity, effect, object count, and expiry were independently
> authorized.

That sentence separates four responsibilities:

1. **The model** supplies a proposal and rationale.
2. **The broker** resolves trusted facts and makes a deterministic decision.
3. **The worker** receives only the capability required for one bounded job.
4. **The verifier** observes scope and state through a separate path.

The model does not resolve its own target, grant its own scope, select its own effective identity,
approve its own side effect, or grade its own result.

## Four planes, four different trust decisions

An execution broker is easier to reason about when it is split into four planes.

### Proposal plane

The model produces structured intent: operation, candidate target, expected effect, reason, and
evidence goal. Everything in this plane is untrusted. A proposal may be mistaken, manipulated by
retrieved content, derived from stale memory, or entirely fabricated.

### Policy plane

The broker replaces aliases with trusted facts. It looks up the engagement, resolves the target,
maps the operation to a typed tool contract, determines the effective identity, calculates the
maximum object count, and classifies the side effect. These facts come from registries the model
cannot edit.

### Execution plane

An isolated worker receives a narrowly scoped capability after policy allows the request. It does
not inherit the operator's shell, browser, SSH agent, cloud CLI session, Kubernetes context, or
directory token. A capability is bound to one resource audience, operation, scope, expiry, and job.

### Evidence plane

The original proposal, resolved facts, decision, approval, capability, worker result, independent
state observation, negative control, and cleanup status form one append-only record. A denial is
also evidence: it must show that no capability was issued and no worker job was created.

<figure class="diagram">
<svg viewBox="0 0 780 500" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="A four-plane AI pentest execution broker. The untrusted proposal plane sends structured intent to a deterministic policy plane. The policy plane resolves scope, identity, effect, and object count. Denied requests stop without credentials. Allowed requests receive a short-lived capability for an isolated worker. A separate evidence plane records proposal, decision, execution, verification, and cleanup.">
<defs><marker id="broker-plane-a" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0 L8,4 L0,8 z" class="dg-arrow-accent" /></marker><marker id="broker-plane-r" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0 L8,4 L0,8 z" class="dg-arrow-crit" /></marker></defs>
<text x="4" y="20" class="dg-accent">EXECUTION BROKER</text><text x="158" y="20" class="dg-muted">REASONING, AUTHORIZATION, EXECUTION, AND PROOF ARE SEPARATE</text>
<text x="4" y="54" class="dg-muted">PROPOSAL PLANE · UNTRUSTED</text>
<rect x="0" y="68" width="214" height="112" rx="10" class="dg-box-crit"/><text x="18" y="98" class="dg-label">Model proposal</text><text x="18" y="125" class="dg-mono">operation · alias · reason</text><text x="18" y="151" class="dg-mono">expected effect · evidence goal</text><text x="18" y="172" class="dg-crit">NO AUTHORITY</text>
<line x1="216" y1="124" x2="260" y2="124" class="dg-line-crit" marker-end="url(#broker-plane-r)"/>
<text x="264" y="54" class="dg-muted">POLICY PLANE · DETERMINISTIC</text>
<rect x="264" y="68" width="252" height="198" rx="10" class="dg-box-accent"/><text x="282" y="98" class="dg-label">Resolve trusted facts</text><text x="282" y="127" class="dg-mono">engagement + target</text><text x="282" y="153" class="dg-mono">tool contract + effect</text><text x="282" y="179" class="dg-mono">effective identity + count</text><line x1="282" y1="198" x2="498" y2="198" class="dg-line"/><text x="282" y="225" class="dg-label">Policy decision</text><text x="282" y="250" class="dg-accent">ALLOW · HOLD · DENY</text>
<path d="M518 114 C548 114 548 104 574 104" class="dg-line-crit" marker-end="url(#broker-plane-r)"/>
<rect x="578" y="68" width="202" height="74" rx="10" class="dg-box-crit"/><text x="596" y="98" class="dg-label">Explicit denial</text><text x="596" y="124" class="dg-mono">no token · no job</text>
<path d="M518 220 C548 220 548 226 574 226" class="dg-line-accent" marker-end="url(#broker-plane-a)"/>
<text x="578" y="178" class="dg-muted">EXECUTION PLANE</text>
<rect x="578" y="192" width="202" height="108" rx="10" class="dg-box-accent"/><text x="596" y="222" class="dg-label">Bounded capability</text><text x="596" y="248" class="dg-mono">one scope · one operation</text><text x="596" y="274" class="dg-mono">one worker · expiry</text><text x="596" y="294" class="dg-accent">NO AMBIENT SESSION</text>
<rect x="0" y="342" width="780" height="116" rx="10" class="dg-box"/><text x="18" y="372" class="dg-label">Evidence plane · append-only record</text>
<rect x="18" y="392" width="132" height="44" rx="7" class="dg-box-accent"/><text x="34" y="419" class="dg-mono">proposal</text>
<line x1="152" y1="414" x2="174" y2="414" class="dg-line-accent" marker-end="url(#broker-plane-a)"/><rect x="178" y="392" width="132" height="44" rx="7" class="dg-box-accent"/><text x="194" y="419" class="dg-mono">decision</text>
<line x1="312" y1="414" x2="334" y2="414" class="dg-line-accent" marker-end="url(#broker-plane-a)"/><rect x="338" y="392" width="132" height="44" rx="7" class="dg-box-accent"/><text x="354" y="419" class="dg-mono">execution</text>
<line x1="472" y1="414" x2="494" y2="414" class="dg-line-accent" marker-end="url(#broker-plane-a)"/><rect x="498" y="392" width="132" height="44" rx="7" class="dg-box-accent"/><text x="514" y="419" class="dg-mono">verification</text>
<line x1="632" y1="414" x2="654" y2="414" class="dg-line-accent" marker-end="url(#broker-plane-a)"/><rect x="658" y="392" width="104" height="44" rx="7" class="dg-box-accent"/><text x="674" y="419" class="dg-mono">cleanup</text>
<text x="4" y="488" class="dg-muted">A DENIAL IS COMPLETE ONLY WHEN THE RECORD CONFIRMS THAT NO CAPABILITY EXISTED.</text>
</svg>
<figcaption>The proposal is evidence of model intent, not permission. The broker resolves trusted facts before any execution identity exists.</figcaption>
</figure>

## The tool catalog is a security boundary

An agent cannot exercise functionality it cannot reach. That makes tool inventory reduction more
valuable than adding another sentence to the system prompt.

[OWASP's Excessive Agency guidance](https://genai.owasp.org/llmrisk/llm062025-excessive-agency/)
identifies excessive functionality, permission, and autonomy as root causes. A document reader that
also exposes delete, a database connector that permits mutation, or a legacy extension left in the
catalog increases impact even if the model normally selects the safe operation.

Every tool contract should declare at least:

- a stable operation name;
- accepted and rejected argument shapes;
- side-effect class;
- allowed environments;
- maximum object and result count;
- required identity class;
- whether retry is safe;
- approval class;
- evidence emitted on success, failure, and timeout.

```json
{
  "operation": "directory.object.inspect",
  "effect": "read",
  "environments": ["lab", "production"],
  "identity_class": "audit_reader",
  "max_objects": 50,
  "automatic_retry": false,
  "approval": "none",
  "evidence": ["scope_id", "query_id", "result_count", "verifier_result"]
}
```

Do not describe a general-purpose shell as `read_only` because the intended command reads data. The
capability includes every executable, file, credential, socket, and inherited session available to
the process. That is a worker isolation problem, not a naming problem.

The current [MCP Tools specification](https://modelcontextprotocol.io/specification/draft/server/tools)
says users should be able to deny tool invocations and applications should clearly expose tools,
show invocation, and present confirmations. It also allows the visible tool set to vary with the
authorization on the request. That supports a useful design: the model should not even discover
high-impact tools when its current capability is L0 or L1.

Protocol support does not create policy by itself. The application must still classify operations,
resolve targets, constrain identities, and decide what never becomes autonomous.

## Resolve before approval

The model may propose `customer-backup`, `prod-east`, or `corp.local`. Those are aliases, not safe
authorization inputs. The broker resolves them through trusted engagement and asset registries.

Before showing an approval, the system should know:

| Proposed value | Broker-resolved fact |
| --- | --- |
| Target alias | Canonical tenant, subscription, account, domain, host, and object identifier |
| Tool name | Versioned operation and effect class from the catalog |
| “Current user” | Effective worker identity and granted scopes |
| “A few objects” | Exact object set and enforced maximum |
| “Can be reverted” | Tested recovery reference and current recovery status |
| “Safe to retry” | Idempotency contract and request identifier semantics |

The approval binds to the resolved facts. If the target, operation, identity, object count, expected
state difference, recovery reference, or expiry changes, the approval is invalid.

This is where a generic “Are you sure?” fails. It approves intent while the dangerous details remain
unresolved.

## Use a capability ladder, not a safe/unsafe flag

A single `dangerous: true` property cannot express the operational difference between offline
analysis, bounded inventory, a reversible lab change, and production deletion. The lab uses four
levels:

### L0 — Offline

No target connection or credential exists. Saved-artifact analysis, hypothesis generation, schema
comparison, code review, and report drafting belong here. These tasks can be autonomous within data
handling constraints because they cannot create an external side effect.

### L1 — Bounded read

The broker may issue a short-lived read identity when the target matches scope, the worker is
isolated, the operation has a result cap, and a separate path verifies scope and result. Read-only
is an operation contract, not an assumption based on an HTTP method or command name.

### L2 — Reversible lab action

One named, engagement-owned canary in a controlled lab may receive a single-use capability. The
broker requires a verified recovery path, parameter-bound approval, no automatic retry, a lab-only
identity, and independent verification of both the state change and rollback.

### L3 — High impact

Deletion, production mutation, credential reset, privilege change, broad data access, and
administrative shells are denied to model-directed execution. The model may prepare a plan or
evidence package. It does not receive the capability.

This is a policy decision, not a statement that L3 work can never be performed by an authorized
human workflow. It says L3 authority should not be created for ordinary autonomous execution.

<figure class="diagram">
<svg viewBox="0 0 780 480" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="An execution broker decision ladder. Offline L0 work is allowed without credentials. L1 bounded reads require scope, isolation, and a result cap. L2 reversible lab actions also require a named canary, recovery, parameter-bound approval, and independent verification. L3 destructive, production, privilege-changing, and administrative-shell actions are denied to autonomous execution.">
<defs><marker id="broker-ladder-a" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0 L8,4 L0,8 z" class="dg-arrow-accent" /></marker></defs>
<text x="4" y="20" class="dg-accent">CAPABILITY LADDER</text><text x="164" y="20" class="dg-muted">AUTHORITY INCREASES ONLY AFTER DETERMINISTIC GATES</text>
<rect x="0" y="60" width="176" height="128" rx="10" class="dg-box-accent"/><text x="18" y="90" class="dg-accent">L0 · OFFLINE</text><text x="18" y="119" class="dg-label">No credential</text><text x="18" y="146" class="dg-mono">saved artefacts</text><text x="18" y="171" class="dg-mono">analysis + drafting</text>
<line x1="178" y1="124" x2="198" y2="124" class="dg-line-accent" marker-end="url(#broker-ladder-a)"/>
<rect x="202" y="60" width="176" height="128" rx="10" class="dg-box-accent"/><text x="220" y="90" class="dg-accent">L1 · READ</text><text x="220" y="119" class="dg-label">Short-lived reader</text><text x="220" y="146" class="dg-mono">scope + isolation</text><text x="220" y="171" class="dg-mono">cap + verifier</text>
<line x1="380" y1="124" x2="400" y2="124" class="dg-line-accent" marker-end="url(#broker-ladder-a)"/>
<rect x="404" y="60" width="176" height="128" rx="10" class="dg-box-accent"/><text x="422" y="90" class="dg-accent">L2 · LAB</text><text x="422" y="119" class="dg-label">Single-use capability</text><text x="422" y="146" class="dg-mono">canary + recovery</text><text x="422" y="171" class="dg-mono">bound approval</text>
<line x1="582" y1="124" x2="602" y2="124" class="dg-line-accent" marker-end="url(#broker-ladder-a)"/>
<rect x="606" y="60" width="174" height="128" rx="10" class="dg-box-crit"/><text x="624" y="90" class="dg-crit">L3 · HIGH IMPACT</text><text x="624" y="119" class="dg-label">No agent capability</text><text x="624" y="146" class="dg-mono">delete · production</text><text x="624" y="171" class="dg-mono">privilege · admin shell</text>
<text x="4" y="232" class="dg-muted">DECISION GATES</text>
<rect x="0" y="250" width="242" height="82" rx="9" class="dg-box"/><text x="18" y="279" class="dg-label">Resolve</text><text x="18" y="306" class="dg-mono">scope · target · identity · count</text>
<rect x="269" y="250" width="242" height="82" rx="9" class="dg-box"/><text x="287" y="279" class="dg-label">Classify</text><text x="287" y="306" class="dg-mono">effect · environment · retry</text>
<rect x="538" y="250" width="242" height="82" rx="9" class="dg-box"/><text x="556" y="279" class="dg-label">Authorize</text><text x="556" y="306" class="dg-mono">approval · expiry · recovery</text>
<rect x="0" y="374" width="780" height="72" rx="10" class="dg-box-crit"/><text x="18" y="403" class="dg-label">Absolute autonomous denials</text><text x="18" y="429" class="dg-mono">scope mismatch · ambient administrator · destructive effect · production mutation · unsafe retry</text>
<text x="4" y="472" class="dg-muted">THE MODEL CAN REVISE A PLAN. IT CANNOT WAIVE A GATE.</text>
</svg>
<figcaption>The ladder controls whether authority can exist. High-impact requests are not waiting for a better prompt; they are outside the autonomous capability model.</figcaption>
</figure>

## Authorization should be incremental and resource-bound

The current [MCP authorization draft](https://modelcontextprotocol.io/specification/draft/basic/authorization)
uses protected resource metadata, explicit resource indicators, least-privilege scope selection,
and step-up authorization when an operation needs more scope. It also limits repeated retries after
an authorization failure.

Those ideas translate well to a pentest broker:

- obtain no target credential during offline planning;
- request a read scope only when the L1 operation is ready;
- bind the token to the intended resource server rather than accepting it for any connector;
- issue short-lived capabilities after policy, not before proposal generation;
- track authorization upgrades so a planner cannot loop until a user accepts;
- never let accumulated scopes silently turn an L1 session into an L3 session.

Transport authorization answers whether a client can reach a protected server. Object and
engagement authorization still have to answer whether this identity may perform this operation on
this resolved target during this assessment.

## A retry is another action

Tool orchestration often treats timeouts as infrastructure noise. That is safe only when the
operation is idempotent and the server's semantics are known.

A mutating request can reach the target, commit, and lose the response. If the agent retries, it may
repeat a change, advance a state machine twice, create another object, or act on a target whose
preconditions have already changed.

The broker therefore records three outcomes separately:

- **failed before send** — no target action occurred; a bounded retry may be safe;
- **definitive target rejection** — no state change occurred; remediation or revised authorization
  may be needed;
- **ambiguous after send** — execution pauses and an independent read path determines state.

Automatic retry is disabled for L2. L3 never receives a capability. A model cannot reinterpret an
ambiguous result as permission to try again.

## Approval must expose the real blast radius

An L2 approval should show:

- engagement and policy version;
- canonical target and named canary owner;
- operation and side-effect class;
- effective worker identity;
- exact object count and enforced maximum;
- expected pre-state and post-state;
- recovery reference and last verified time;
- automatic retry status;
- independent verification path;
- expiry and immutable approval identifier.

The approval record is hashed or otherwise bound to these parameters. Any change creates a new
proposal and requires a new decision.

The [NIST concept paper on software and AI agent identity](https://www.nccoe.nist.gov/publications/other/accelerating-adoption-software-and-ai-agent-identity-and-authorization-concept)
highlights the risk created when agents receive access to diverse tools, applications, and data,
and frames agent identity and authorization as a distinct control problem. In pentesting, written
rules of engagement remain the source of authority. The broker makes part of those constraints
machine-enforceable; it does not expand them.

## Walk the six lab scenarios

The [interactive lab](/tools/ai-pentest-execution-broker/) includes six synthetic proposals.

1. **Offline hypothesis review** receives L0 approval because no identity or external effect exists.
2. **Scoped directory inspection** receives an L1 read capability only while scope, isolation,
   count, and independent verification remain valid.
3. **Reversible lab canary** receives one L2 capability because every lab gate is present. Disable
   recovery, named canary, bound approval, worker isolation, or verification and the decision moves
   to hold.
4. **Production cloud deletion** is denied because the effect is destructive, the identity is
   broadly privileged, the worker is not isolated, approval is generic, and retry is unsafe.
5. **Domain Admin shell** is denied because a general shell under ambient Domain Admin authority is
   outside the autonomous capability model.
6. **Prompt-influenced scope drift** is denied even though the requested effect is read-only. The
   resolved target is outside the engagement registry.

Change one field at a time. The point is not to find a combination that tricks the simulator into
allowing deletion. The point is to see which facts belong to deterministic policy and which risks
cannot be reduced by model confidence.

The lab can download its current decision as JSON. The record is created locally in the browser and
contains only the synthetic or user-edited values on the page. Do not paste real credentials,
tokens, customer data, or production identifiers into it.

## The field kit

Two reusable artefacts accompany the lab:

- [Policy contract JSON](/downloads/ai-pentest-execution-broker-policy.json) — capability levels,
  mandatory gates, denial rules, proposal fields, approval binding, and decision records.
- [Evidence worksheet CSV](/downloads/ai-pentest-execution-broker-evidence.csv) — a working record
  for proposal, resolved scope, identity, decision, capability, execution, verification, negative
  control, rollback, and evidence limits.

They are intentionally deployment-neutral. They do not issue tokens or connect to a target. Adapt
them to the organization's identity provider, engagement system, tool server, worker isolation,
logging, retention, and incident response model.

## Evidence matrix

| Security claim | Positive evidence | Negative control | Failure signal | Explicit limit |
| --- | --- | --- | --- | --- |
| The model cannot authorize its own tool call | Proposal exists without a credential; broker decision precedes capability issuance | Change an approved parameter after decision and confirm the approval becomes invalid | A worker token exists before policy resolution | The browser lab demonstrates logic, not production isolation |
| Scope is resolved outside model context | Canonical target and engagement ID come from a protected registry | Propose an alias that resolves outside scope and confirm denial | Model-provided target is accepted without lookup | Registry integrity requires separate controls |
| L1 reads are bounded | Read identity, object cap, expiry, isolated worker, and verifier are recorded | Exceed the object cap and confirm no capability is issued | Result set grows without a policy event | Read-only does not prove absence of resource consumption or sensitive disclosure |
| L2 changes are reversible lab actions | Named canary, pre-state, bound approval, post-state, rollback, and verifier result exist | Remove recovery verification and confirm hold | Lab change executes with generic confirmation | Lab rollback does not prove production recoverability |
| L3 authority cannot reach autonomous execution | Delete and Domain Admin proposals produce denial and no capability/job | Submit an allowed L1 proposal and confirm the normal worker path still functions | High-impact tool remains discoverable or executable | Human-controlled high-impact workflows are outside this model |
| Ambiguous mutation does not retry | Timeout state pauses and independent verification determines target state | Repeat a known idempotent read within its retry cap | Second mutation occurs before state resolution | Target-specific idempotency must be independently documented |

## Implementation sequence

Do not begin by placing a policy prompt in front of an existing privileged agent. Build the control
path in this order:

1. Inventory every currently exposed tool and effective identity.
2. Remove unused, overlapping, destructive, and general-purpose functionality.
3. Define versioned operation contracts and side-effect classes.
4. Build an engagement registry that resolves canonical targets and testing windows.
5. Separate L0, L1, and L2 workers and credentials. Keep L3 outside model-directed execution.
6. Implement deterministic denial rules before approval UX.
7. Bind approval to resolved parameters and expiry.
8. Issue one short-lived, resource-bound capability per allowed job.
9. Record proposal, policy, approval, capability, worker result, and verification separately.
10. Test negative controls: scope mismatch, cap exceeded, stale approval, ambient administrator,
    unsafe retry, and unavailable recovery.
11. Add a kill switch that prevents new capability issuance and revokes active short-lived access.
12. Rehearse ambiguous results, cleanup, and incident notification in the controlled lab.

Only after those controls pass should the organization consider increasing automation. Model
benchmark performance is not a substitute for control-plane testing.

## What this design does not solve

An execution broker reduces one class of excessive agency. It does not make every connected system
safe.

The engagement registry can be wrong. A tool can misclassify its side effect. The identity provider
can issue an excessive token. A worker can escape isolation. The target API can behave differently
from its documentation. Logs can omit the decisive event. A malicious tool server can lie about
its schema or result. An operator can approve a dangerous request.

Each boundary requires its own test and evidence. The broker is valuable because it creates a place
to enforce and observe those decisions. It is not a universal trust anchor.

## The operating conclusion

The safest AI pentest agent is not the one with the longest prohibition prompt. It is the one whose
ordinary execution path cannot create broad authority.

Let the model explore hypotheses and prepare structured proposals. Let deterministic policy resolve
scope, identity, effect, count, retry, approval, and recovery. Give isolated workers the smallest
short-lived capability that can accomplish the approved proof. Verify through another path. Record
denials as carefully as executions.

If the model proposes production deletion or a Domain Admin shell, the broker's job is not to ask
more persuasively. It is to ensure that capability never exists.

## MITRE ATT&CK mapping

| Tactic | Technique ID | Technique name | Verification signal |
| --- | --- | --- | --- |
| Execution | `T1059` | Command and Scripting Interpreter | Deterministic broker denies shell execution from model plan |
| Privilege Escalation | `T1548` | Abuse Elevation Control Mechanism | Parameter-bound capability issuance gates worker elevation |
| Defense Evasion | `T1078` | Valid Accounts: Ambient Authority | Short-lived single-use credentials prevent ambient credential reuse |

<div class="compact-panel lab-callout">
  <div class="lab-callout-head">
    <span class="signal-pill">Interactive Simulator</span>
    <strong>AI Pentest Execution Broker Workbench</strong>
  </div>
  <p>Test capability ladder decisions, policy gates, and scope boundaries interactively in your browser with reproducible execution scenarios.</p>
  <a class="btn" href="/tools/ai-pentest-execution-broker/">Launch Interactive Broker ↗</a>
</div>