# Defense-in-Depth Score: ZAP MCP add-on

**Repo:** https://github.com/zaproxy/zap-extensions (`addOns/mcp`) · **Commit:** `a7f2fcf19557a6b455aa7f1d4fe1ad218179fade` (0.4.0) · **Reviewed:** 2026-10-03
**What it is:** ZAP add-ons repo; the MCP Integration add-on lets AI assistants drive ZAP spider/active scans
**Category:** Cybersecurity
**Scored configuration:** MCP Integration add-on 0.4.0 with the listener enabled by the operator and otherwise default options (security key required, HTTPS only, history recording off).
**Agent surface (default):** code execution no · filesystem write yes · network egress yes · external credentials no · persistent memory yes · untrusted input yes · third party extensions no · sub agents no · external communication yes

## Score: 3.8 / 10.0 (Minimal)

| # | Criterion | S | C | D | B | Raw | Cap | Score | Confidence |
|---|---|---|---|---|---|---|---|---|---|
| C1 | Identity & least privilege | L1 | L2 | L2 | L2 | 0.42 | — | **0.42** | High |
| C2 | Approval gates | L0 | L0 | L0 | L1 | 0.05 | — | **0.05** | High |
| C3 | Tool & action scoping | L1 | L1 | L0 | L1 | 0.20 | — | **0.20** | High |
| C4 | Code-execution isolation | SA | SA | SA | SA | 1.00 | — | **1.00** (SA) | High |
| C5 | Untrusted input blast radius | L1 | L0 | L0 | L0 | 0.07 | C5-WORSTCASE | **0.07** | High |
| C6 | Memory, context & configuration integrity | L1 | L1 | L1 | L1 | 0.25 | — | **0.25** | High |
| C7 | Third-party extensions | SA | SA | SA | SA | 1.00 | — | **1.00** (SA) | High |
| C8 | Secrets & sensitive-data protection | L1 | L0 | L2 | L1 | 0.23 | — | **0.23** | High |
| C9 | Audit & traceability | L2 | L2 | L0 | L1 | 0.35 | G1 | **0.35** | High |
| C10 | Limits & kill switch | L1 | L1 | L1 | L1 | 0.25 | — | **0.25** | High |

Controls where a risk surface exists: 1.83 / 8.0 (23%); 2 criteria scored SA (surface absent).

The server is off until enabled and, once on, requires a random key over HTTPS, but it then exposes ZAP's full capability to any key holder: unrestricted active scans and spidering of any URL, report writes to any path, and unredacted target traffic. It gives the host no risk annotations, previews or limits to gate on, and it returns target-controlled content with no provenance. Treat it as a localhost-only tool for a trusted client that must be approved at the host.

## Critical gaps
- A hijacked model can drive unattended scans or spidering of any URL and write report files, with no server-side gate, annotations or egress limits. (ASI01, ASI02, LLM01; C5) — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStartScanTool.java:88-104](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStartScanTool.java#L88-L104); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGenerateReportTool.java:96](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGenerateReportTool.java#L96)

## Criterion details

### C1 Identity & least privilege — 0.42 (high)

The add-on holds no downstream credentials of its own; every tool runs with the full authority of the ZAP process that hosts it. Callers are authenticated with one randomly generated 32-character key compared in constant time and the listener is HTTPS-only by default, but there is no per-tool, per-user or read/write separation: whoever has the key can start scans, change contexts, write report files and read all recorded traffic. The key check can be switched off by an options change or by an empty key in the config job, and the server itself is off until the operator enables it.

- **S L1:** Authority is a single static shared key over ambient ZAP authority; no scoped roles, read-only credential, or per-tool narrowing. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpParam.java:132-136](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpParam.java#L132-L136); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpHttpMessageHandler.java:84-97](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpHttpMessageHandler.java#L84-L97) (verified)
  - *To reach the next level:* No role or per-capability scoping (read tools and write tools share one credential).
- **C L2:** Every JSON-RPC call (tools, resources, prompts) passes the same single authentication check in the HTTP handler before dispatch. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpHttpMessageHandler.java:84-122](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpHttpMessageHandler.java#L84-L122); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpRequestHandler.java:94-126](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpRequestHandler.java#L94-L126) (verified)
  - *To reach the next level:* Only authentication, not authorization: no per-tool or per-principal policy is evaluated, and the key is not tied to a requesting principal.
- **D L2:** Default posture is reasonable: server disabled, key required, random key generated on first load, HTTPS-only, bound to ZAP default address (inferred loopback). — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpParam.java:48-53](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpParam.java#L48-L53); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpParam.java:80-82](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpParam.java#L80-L82); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/automation/McpConfigJob.java:101-104](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/automation/McpConfigJob.java#L101-L104); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/ExtensionMcp.java:251](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/ExtensionMcp.java#L251) (verified)
  - *To reach the next level:* Widening is an operator config change without warning (options panel or an empty key in the config job turns the key requirement off).
- **B L2:** A compromised or leaked key gives full control of one system (ZAP): scans, scope changes, report file writes and read access to recorded traffic; independent layers are the opt-in server, HTTPS-only and localhost bind (the bind address lives in another add-on and was not verified). — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpRequestHandler.java:176-197](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpRequestHandler.java#L176-L197); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/ExtensionMcp.java:143-159](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/ExtensionMcp.java#L143-L159); [addOns/mcp/src/main/javahelp/org/zaproxy/addon/mcp/resources/help/contents/mcp.html:26](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/javahelp/org/zaproxy/addon/mcp/resources/help/contents/mcp.html#L26) (verified)
  - *To reach the next level:* Full ZAP control is reachable with one key; no read-only mode or per-capability credential narrows it.
- **Cap:** none
- **Notes:** No token passthrough: the key is checked locally and is not forwarded; the importer sends a user-supplied key to the MCP server under test, which is an operator-driven job rather than a model-reachable tool.

### C2 Approval gates — 0.05 (high)

The server gives the host no risk signalling to build an approval gate on. Tool listings carry only a name, description and input schema, with no read-only or destructive hints, and there is no dry-run, preview or server-side read-only mode. Read, start and stop operations are at least split into separate, clearly named tools, but a host cannot tell that zap_generate_report writes an arbitrary file or that zap_start_active_scan attacks a live target without reading descriptions.

- **S L0:** Tool listing exposes only name, description and inputSchema; no risk annotations of any kind. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpRequestHandler.java:160-163](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpRequestHandler.java#L160-L163); searched `rg -n -i 'readOnlyHint|destructiveHint|annotations'` in `addOns/mcp/src/main/java` → 0 hits (no annotation fields anywhere in main sources) (verified)
  - *To reach the next level:* No accurate readOnlyHint/destructiveHint on any tool (L2 needs them on every tool).
- **C L0:** No tool carries any signal the host could gate on; mutating tools (active scan, report write, context replace) are indistinguishable by protocol from read tools. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpRequestHandler.java:155-174](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpRequestHandler.java#L155-L174); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/ExtensionMcp.java:143-159](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/ExtensionMcp.java#L143-L159) (verified)
  - *To reach the next level:* Every mutating tool would need a flag or annotation for the host to gate on.
- **D L0:** No server-enforced confirmation or approval mode exists, on or off; approval is entirely the host's job. — searched `rg -n -i 'confirm|approv|readOnly|dryRun'` in `addOns/mcp/src/main/java` → 0 hits (no approval, confirmation or dry-run handling in main sources) (verified)
  - *To reach the next level:* No server-side confirmation step or read-only mode that a host must complete.
- **B L1:** Active scans against live targets cannot be undone, existing contexts are deleted and replaced by name, and the report tool writes to a caller-chosen path; there is no preview, dry run or rate limit, though scans can be stopped. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapCreateContextTool.java:111](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapCreateContextTool.java#L111); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGenerateReportTool.java:96](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGenerateReportTool.java#L96); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStartScanTool.java:119](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStartScanTool.java#L119) (verified)
  - *To reach the next level:* No dry-run or preview for destructive or outward-facing operations and no rate limits on them.
- **Cap:** none
- **Notes:** Tool-server anchors used: the host owns the gate. C2-POWERBYPASS not applied separately because the server has no gate to bypass and the cap would not bind (total is below 0.30).

### C3 Tool & action scoping — 0.20 (high)

Argument handling is mostly presence checks and syntax parsing. Scan targets are only parsed as URIs; there is no host allowlist, scope check or internal-address blocking, and the report tool takes any file path. The one tool with real validation is the history reader, which allowlists field names, parses integers and windows large bodies. All 17 tools, including write and active-scan tools, are always registered with no way to enable only a read-only subset.

- **S L1:** Validation is syntactic: required-argument checks and URI parsing; only get_history uses an allowlist and bounds; targets, regexes, policy and report path are unconstrained. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStartScanTool.java:88-96](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStartScanTool.java#L88-L96); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGenerateReportTool.java:74-79](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGenerateReportTool.java#L74-L79); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGetHistoryTool.java:191](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGetHistoryTool.java#L191) (verified)
  - *To reach the next level:* No allowlist or containment on target hosts or report path; no internal-address blocking.
- **C L1:** Only the history tool validates against an allowlist; others check presence and URI syntax only; tool schemas type every property as string or string array. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpTool.java:86-96](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpTool.java#L86-L96); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpRequestHandler.java:187-197](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpRequestHandler.java#L187-L197) (verified)
  - *To reach the next level:* Most tools do not apply real validation; no shared validation layer.
- **D L0:** All 17 tools including write, report and active-scan tools are registered unconditionally; there is no option to disable individual tools or select a read-only group. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/ExtensionMcp.java:143-159](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/ExtensionMcp.java#L143-L159); searched `rg -n -i 'enabledTools|disableTool|toolFilter'` in `addOns/mcp/src/main/java` → 0 hits (no per-tool enablement setting in McpParam or elsewhere) (verified)
  - *To reach the next level:* No read-only default tool set or per-tool enablement switch.
- **B L1:** Tools are fixed-function (scan, spider, report, history read) but their destinations are unbounded: any URL can be scanned or spidered and any path written. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStartScanTool.java:103](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStartScanTool.java#L103); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGenerateReportTool.java:96](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGenerateReportTool.java#L96) (verified)
  - *To reach the next level:* No target scope limits, quantity limits or path containment; destinations unbounded.
- **Cap:** none

### C4 Code-execution isolation — 1.00 (high)

No model-reachable path in this add-on interprets text as code: there is no shell, process spawn, script engine, dynamic class loading or eval in the main sources, and the tools only build fixed automation jobs (spider, AJAX spider, active scan) and call the report generator. The shipped extender-script templates are run only if the operator enables them in ZAP's scripting add-on. The scanner itself sends test requests to targets, which is outbound traffic scored under tool scoping and untrusted input, not local code execution.

- **Structural absence:** searched `rg -n -i 'ProcessBuilder|Runtime\.getRuntime|ScriptEngine|URLClassLoader|ServiceLoader|Class\.forName|loadClass|\beval\('` in `addOns/mcp/src/main/java` → 0 hits (no process, script or dynamic-load primitives in the add-on's main Java sources); searched `rg -n -i 'ExtensionScript|ScriptWrapper'` in `addOns/mcp/src/main/java` → 0 hits (the add-on never executes scripts itself; templates under zapHomeFiles are only run if the operator enables them)
- **Notes:** Absence by design, not a control. Regex arguments are compiled as patterns and passed to ZAP contexts, not evaluated as code.

### C5 Untrusted input blast radius — 0.07 (high)

The server returns content that originates from scanned targets, including HTTP bodies and headers from history and alert evidence, as plain text or JSON with no provenance or untrusted marker, and offers no read-only or no-egress mode. A hijacked model reading that content can start spiders or active scans against any URL, which is both an outbound channel and an irreversible action on third parties, and can write report files, all without any server-side approval. The tool descriptions themselves are static and contain no directives.

- **S L1:** Outputs are plain text or JSON with no source or untrusted flag; get_history and resources do separate fields but nothing marks body content as target-controlled. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpRequestHandler.java:207](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpRequestHandler.java#L207); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGetHistoryTool.java:146-153](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGetHistoryTool.java#L146-L153); searched `rg -n -i 'untrusted|provenance|taint'` in `addOns/mcp/src/main/java` → 0 hits (no untrusted-content marking in main sources) (verified)
  - *To reach the next level:* No provenance or untrusted flag on returned content, and no mode that drops a Rule-of-Two leg.
- **C L0:** No untrusted source is distinguished: history bodies, alert evidence and site-tree data from targets enter the model context with the same standing as status output. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/resources/HistoryEntryResource.java:99-101](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/resources/HistoryEntryResource.java#L99-L101); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/resources/AlertInstancesResource.java:121-122](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/resources/AlertInstancesResource.java#L121-L122) (verified)
  - *To reach the next level:* Sources such as history bodies, alert evidence and sites data would need provenance handling.
- **D L0:** No mitigating mode exists to be on or off by default. — searched `rg -n -i 'readOnly|noEgress|restricted|safeMode'` in `addOns/mcp/src/main/java` → 0 hits (no restrictive modes in main sources) (verified)
  - *To reach the next level:* No default control; nothing to be on by default.
- **B L0:** If a hijack succeeds, target-controlled text can steer unattended scans or spidering of arbitrary URLs (a data-carrying egress channel plus actions that cannot be undone on third-party systems) and report file writes, with nothing server-side requiring a human. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStartScanTool.java:88-104](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStartScanTool.java#L88-L104); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGenerateReportTool.java:96](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGenerateReportTool.java#L96); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGetHistoryTool.java:132-137](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGetHistoryTool.java#L132-L137) (verified)
  - *To reach the next level:* No egress or state-change restriction after untrusted content has been read; destinations unrestricted.
- **Cap:** C5-WORSTCASE — B is L0: leaked traffic data and irreversible scans or file writes are reachable with no server-side human step.

### C6 Memory, context & configuration integrity — 0.25 (high)

The add-on does not load instruction files or project config from the working directory, but state the model can change persists inside the ZAP session: zap_create_context and the scan tools create contexts that define scan scope, and an existing context with the same name is deleted and replaced without any check, including contexts the operator set up. Nothing validates or gates these writes and nothing logs them. Persisted contexts affect later scans started by any client.

- **S L1:** Context writes by tools are neither validated nor gated, and an existing context of the same name is deleted first; no provenance or approval. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapCreateContextTool.java:109-126](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapCreateContextTool.java#L109-L126); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStartScanTool.java:97-104](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStartScanTool.java#L97-L104) (verified)
  - *To reach the next level:* Writes are not gated or validated and carry no provenance; replacing an operator context is silent.
- **C L1:** One persistent surface (session contexts) with no control; no instruction-file or repo-config auto-loading was found. — searched `rg -n -i 'AGENTS\.md|CLAUDE\.md|\.cursorrules|dotenv|load_dotenv'` in `addOns/mcp/src/main/java` → 0 hits (no auto-loaded instruction files or env files); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapCreateContextTool.java:113](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapCreateContextTool.java#L113) (verified)
  - *To reach the next level:* The context store is uncontrolled; protections would need to cover every write path (create_context and scan tools).
- **D L1:** Contexts are one global namespace in the ZAP session shared by all MCP clients and the operator; any client can overwrite any context by name. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapCreateContextTool.java:109-111](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapCreateContextTool.java#L109-L111) (verified)
  - *To reach the next level:* No per-client namespace; model can overwrite operator-created contexts.
- **B L1:** A replaced or widened context persists in the session and changes the scope of every later scan, which can be triggered by any client without a gate. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStartScanTool.java:103-105](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStartScanTool.java#L103-L105) (verified)
  - *To reach the next level:* Persisted context changes are not reviewed or reversible by the server.
- **Cap:** none
- **Notes:** Configuration (key, port, enable flag) is changeable only through ZAP options or the automation config job, not through an MCP tool.

### C7 Third-party extensions — 1.00 (high)

The add-on does not launch or download any third-party code: it starts no MCP server subprocesses, fetches no remote tools or models, and installs no packages. The importer only connects to an MCP server the operator names, as a test target. The tool registry is public, so other ZAP add-ons or operator-enabled extender scripts in the same process can register tools with full ZAP authority, which is ZAP's own extension model and is not verified or gated by this add-on.

- **Structural absence:** searched `rg -n -i 'ProcessBuilder|Runtime\.getRuntime|URLClassLoader|ServiceLoader|Class\.forName|loadClass|ObjectInputStream|pip install|npm '` in `addOns/mcp/src/main/java` → 0 hits (no subprocess launch, dynamic load or deserialization of downloaded code); searched `rg -n -i 'download|installAddOn|marketplace'` in `addOns/mcp/src/main/java` → 0 hits (no download or install of extensions by the add-on)
- **Notes:** Absence by design. Residual: McpToolRegistry.registerTool is public and unauthenticated for in-process callers, so any other loaded ZAP add-on or enabled script can add tools; not scored as a control.

### C8 Secrets & sensitive-data protection — 0.23 (high)

The security key is stored in ZAP's configuration as plaintext and shown and copyable in the options panel; there is no masking or redaction anywhere. Request and response payloads are logged only at debug level, and request recording into ZAP history is off by default. Tool and resource results return target traffic unredacted, including headers that may hold session tokens or credentials of the tested application. No telemetry beyond local counters was found.

- **S L1:** Key generated with SecureRandom but kept as a plain config string; no masking, redaction or secret scanning in any path. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpParam.java:126-129](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpParam.java#L126-L129); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpParam.java:86-92](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpParam.java#L86-L92); searched `rg -n -i 'redact|mask|sanitiz|scrub'` in `addOns/mcp/src/main/java` → 0 hits (no redaction helpers in main sources) (verified)
  - *To reach the next level:* No secret manager, encryption at rest or redaction before model-bound output.
- **C L0:** No path is protected: debug logs record request and response bodies unredacted, and history tools return headers and bodies as-is; the key is not logged only because the logging statements do not print headers. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpHttpMessageHandler.java:116-123](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpHttpMessageHandler.java#L116-L123); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGetHistoryTool.java:132-137](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGetHistoryTool.java#L132-L137) (verified)
  - *To reach the next level:* No log, model-bound or error path has redaction.
- **D L2:** Debug-level payload logging and history recording are off by default and no telemetry exists; redaction does not exist so cannot be left on. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpParam.java:52](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpParam.java#L52); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpRequestHandler.java:198](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpRequestHandler.java#L198) (verified)
  - *To reach the next level:* Redaction is absent rather than always on; stored data is not minimised.
- **B L1:** The key is long-lived, rotatable by the operator, and grants full control of ZAP; target secrets in history reach the model only when a tool or resource asks for them. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpParam.java:131-136](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpParam.java#L131-L136); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/resources/HistoryEntryResource.java:99-101](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/resources/HistoryEntryResource.java#L99-L101) (verified)
  - *To reach the next level:* Key is long-lived and full-scope rather than scoped and short-lived.
- **Cap:** none
- **Notes:** C8-MODELSECRETS not applied: the server's own key is never sent to the model; target traffic is returned only on request. Not examined: how ZAP core stores config.xml permissions.

### C9 Audit & traceability — 0.35 (high)

Tool calls are not recorded by default beyond debug-level log lines and per-tool success and failure counters that carry no arguments or caller. An optional setting records each MCP HTTP exchange, including rejected requests, into ZAP history with full request and response, which would give a structured record of calls, but it is off by default, has no actor or approver attribution, does not cover the event-stream GET path, and is best-effort. The model has no tool to delete history, but the record lives in the same ZAP session it can read.

- **S L2:** When enabled, every POST exchange is stored in ZAP history with its full JSON-RPC request, response and timestamp. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpHttpMessageHandler.java:217-226](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpHttpMessageHandler.java#L217-L226); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpHttpMessageHandler.java:130](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpHttpMessageHandler.java#L130) (verified)
  - *To reach the next level:* No actor attribution, approver or correlation IDs, and no tamper-evident or exported storage.
- **C L2:** The handler records successful and rejected requests (auth, HTTPS, method, empty body) but the GET event-stream path returns without recording. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpHttpMessageHandler.java:92-95](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpHttpMessageHandler.java#L92-L95); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpHttpMessageHandler.java:99-102](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpHttpMessageHandler.java#L99-L102) (verified)
  - *To reach the next level:* Not every path is recorded, and there are no approval or config-change records.
- **D L0:** Recording is off by default; default observability is debug-level logging and counters only. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpParam.java:52](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpParam.java#L52); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpHttpMessageHandler.java:116-120](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpHttpMessageHandler.java#L116-L120) (verified)
  - *To reach the next level:* Recording would need to be on by default.
- **B L1:** History insertion is best-effort and does not gate execution; the call has already run when it is recorded. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpHttpMessageHandler.java:122-130](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/McpHttpMessageHandler.java#L122-L130) (verified)
  - *To reach the next level:* Records are not durable per action and high-risk actions do not wait for the record.
- **Cap:** G1 — The only structured record of tool calls (history recording) is opt-in.
- **Notes:** Counters via Stats.incCounter are aggregate and carry no arguments.

### C10 Limits & kill switch — 0.25 (high)

The server enforces few bounds on its own work. The history tool defaults to a 4000-character body window but the caller can raise it, scans have no server-side time, concurrency or count limits, and resources are returned in full. Scan start waits at most 10 seconds for an ID, which bounds waiting rather than work. Stop tools exist for spider, AJAX spider and active scan and cancel the running job, which is the main damage-limiting control.

- **S L1:** Limits are mostly optional or caller-adjustable: max_body_chars has a default but can be raised; the 10-second wait does not bound the scan itself. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGetHistoryTool.java:110](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGetHistoryTool.java#L110); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStartScanTool.java:122](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStartScanTool.java#L122); searched `rg -n -i 'RateLimit|Semaphore|maxConcurrent|MAX_SCANS'` in `addOns/mcp/src/main/java` → 0 hits (no rate or concurrency limiters) (verified)
  - *To reach the next level:* No server-enforced ceilings on scan duration, concurrency or output size.
- **C L1:** Only a couple of tools have any bound; started scans, spiders and resource reads run without server caps. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStartScanTool.java:118-119](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStartScanTool.java#L118-L119); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStopActiveScanTool.java:73](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStopActiveScanTool.java#L73) (verified)
  - *To reach the next level:* Scans and background plans are not counted against any budget.
- **D L1:** The one default (4000 body characters) can be raised by the caller; no other defaults exist. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGetHistoryTool.java:104-110](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapGetHistoryTool.java#L104-L110) (verified)
  - *To reach the next level:* Defaults the caller cannot raise and sensible scan ceilings.
- **B L1:** There is no ceiling on how many scans can be started or how long they run; stopping a scan cancels the job but only on an explicit call, and stopping the server was not shown to cancel running plans. — [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStopActiveScanTool.java:67-73](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/tools/ZapStopActiveScanTool.java#L67-L73); [addOns/mcp/src/main/java/org/zaproxy/addon/mcp/ExtensionMcp.java:260-270](https://github.com/zaproxy/zap-extensions/blob/a7f2fcf19557a6b455aa7f1d4fe1ad218179fade/addOns/mcp/src/main/java/org/zaproxy/addon/mcp/ExtensionMcp.java#L260-L270) (verified)
  - *To reach the next level:* No tight time or count ceilings; no automatic stop.
- **Cap:** none
- **Notes:** Request-body size limits in the shared network add-on were not examined.

## Rule-of-Two check
[A] untrusted input: Target HTTP bodies and alert evidence returned by history and alert tools (tools/ZapGetHistoryTool.java:146) · [B] sensitive data/systems: Recorded traffic and session data in ZAP history (resources/HistoryEntryResource.java:99) · [C] state change / egress: Active scan, spider and report-write tools to arbitrary targets and paths (tools/ZapStartScanTool.java:119) · Same default session? Yes

## Highest-impact improvements
1. Add readOnlyHint/destructiveHint annotations to every tool so hosts can gate scans, context replacement and report writes. — C2 S L0→L2, +0.150 before caps (Playbook 5)
2. Restrict scan targets to operator-configured scope or contexts and contain report paths to a configured directory. — C3 S L1→L3, +0.150 before caps (Playbook 3)
3. Record every tool call (arguments, result, caller) by default outside the model's reach. — C9 D L0→L2, +0.100 before caps (Playbook 1 step 3)
4. Return target-derived content with provenance and an untrusted flag, and offer a read-only mode. — C5 S L1→L3, +0.150 before caps (Playbook 1)
5. Add server-side caps on concurrent scans and scan duration that callers cannot raise. — C10 S L1→L3, +0.150 before caps (Playbook 3 step 3)

## Re-audit log
- No changes.

## Limitations
- Static source review of the pinned commit only; nothing was executed, installed, or probed.
- Only addOns/mcp was reviewed. The listener's bind address (Server.DEFAULT_ADDRESS), TLS handling, request size limits and ZAP's own modes and configuration permissions live in other add-ons or ZAP core and were not examined; localhost binding is inferred from the add-on help text.
- C4 and C7 are structurally absent for this add-on; the total therefore includes two full-credit absent surfaces and the applicable score is the better comparison.
- No reviewer-directed instructions were found in the reviewed files.
