Anthropic has released the Claude Security plugin for Claude Code in beta. The plugin runs a multi-agent vulnerability scan of a repository from inside an existing Claude Code session, then turns the findings you select into patch files that you review and apply yourself. Anthropic emphasized the tool’s versatility upon announcement, highlighting its capability to either run a comprehensive scan across the full codebase or inspect changes from the terminal right before a commit.
What plugin adds
The plugin adds a single command, /claude-security, which opens a menu of three jobs, per the official documentation:
- Scan codebase — the whole repository or a scoped subset of it
- Scan changes — a branch’s diff, a pull request’s diff, or a single commit
- Suggest patches — turn a report’s findings into .patch files
Installation is two commands from the official Anthropic marketplace:
/plugin install claude-security@claude-plugins-official
/reload-plugins
If the marketplace is not found, run /plugin marketplace add anthropics/claude-plugins-official first. The plugin source is public in the claude-plugins-official repository, currently at version 0.10.0.
How the scan pipeline is structured
The scan is implemented as a dynamic workflow — a JavaScript orchestration script that fans work out across subagents. The script declares six phases:
- Inventory: partition the repository into components. Every top-level directory must be either scanned or explicitly skipped with a reason.
- Threat model: one modeler per component, producing entry points, sinks, trust boundaries, and files a researcher must read in full.
- Research: one researcher per component × category cell.
- Sweep: gap-fill over what the matrix did not cover.
- Panel: three-lens adversarial verification, one voter per lens.
- Adversarial: max effort only: re-panel marginal keeps, then red-team every survivor.
Research runs against four fixed categories: injection-and-input, auth-and-access, memory-and-unsafe, and crypto-and-secrets. The memory-and-unsafe lens is dropped for components written entirely in memory-safe languages, so a pure Python or TypeScript component gets three lenses instead of four.
The operational scale of a run is dictated by four distinct effort tiers: low, medium, high, and max. Depending on the selected tier, specific thresholds are enforced: the maximum number of components is capped at 12 for low and medium tiers, expanding to 24 for high and max tiers; matrix cells are assigned 1 researcher, which increases to 2 at the high and max levels; and the number of gap-fill sweeps scales from 0 at low, to 1 at medium, up to 2 for high and max. When dealing with a limited scope or a small diff, the process condenses into a single-researcher configuration instead of deploying the entire matrix. This ensures the evaluation remains strictly proportionate to the target while maintaining the identical verification standard.
The system employs model-tiered agents: the orchestrator runs on Opus, while the repository cartographer and read-only code explorer run on Sonnet. Furthermore, the session model is inherited by researchers and verifiers, and scan agents are restricted exclusively to read-only tools.
How a finding earns its place in the report
This is the part worth understanding closely. A candidate finding does not go into the report because a researcher found it. It goes in only after surviving a panel.
Each candidate is handed to three independent verifiers, one per lens: REACHABILITY, IMPACT, and DEFENSES. Each returns a structured verdict of TRUE_POSITIVE or FALSE_POSITIVE with one or two lines naming the decisive file:line. The keep quorum is 2 of 3. If fewer than three voters return, the candidate is not keepable at all.
The panel result also caps the finding’s stated confidence. A unanimous 3/3 panel allows a confidence ceiling of high; a 2/3 quorum caps it at medium. A finding cannot claim more confidence than its verification earned.
Critically, the tally is computed in Python by the report renderer, not asserted by the model that produced the findings. The revision stamp’s verification.status is set to verified only when the vote record proves the panel ran for every finding in the report; otherwise it is unverified with a stated reason. That makes the report’s own account of its rigor something you can check rather than something you take on trust.
<div class="pane" id="p-panel" role="tabpanel" aria-labelledby="t-panel" hidden>
<p class="lede">A researcher finding something is not enough. Every candidate goes to <b>three independent verifiers</b>, one per lens, each told to disprove it. Flip the verdicts and watch the rule resolve.</p>
<div id="voters"></div>
<div class="out" id="panel-out"></div>
<p class="note">Keep quorum is <b>2 of 3</b>. If fewer than three voters return, the candidate is not keepable at all. A unanimous panel allows a confidence ceiling of <b>high</b>; a bare quorum caps it at <b>medium</b> — a finding cannot claim more confidence than its verification earned.</p>
</div>
<div class="pane" id="p-effort" role="tabpanel" aria-labelledby="t-effort" hidden>
<p class="lede">Two things size a scan: <b>scope</b> (how much of the tree) and <b>effort</b> (how much work it does there). Set the tier and the repository shape to see the plan the workflow would log.</p>
<div class="ctl">
<span class="lbl">EFFORT TIER</span>
<button class="chip" data-tier="low">low</button>
<button class="chip" data-tier="medium" aria-pressed="true">medium</button>
<button class="chip" data-tier="high">high</button>
<button class="chip" data-tier="max">max</button>
</div>
<div class="ctl">
<span class="lbl">COMPONENTS FOUND BY INVENTORY <span class="v" id="comp-v">14</span></span>
<div class="range"><input type="range" id="comp" min="1" max="30" value="14" aria-label="Components found by inventory"></div>
</div>
<div class="ctl">
<span class="lbl">LANGUAGE PROFILE</span>
<button class="chip" id="memsafe" aria-pressed="false">all components memory-safe</button>
</div>
<div class="out" id="effort-out"></div>
<p class="note">The <code>memory-and-unsafe</code> lens is dropped for components written entirely in memory-safe languages, so a pure Python or TypeScript component is researched against three categories instead of four. A small diff or narrow scope collapses to a single-researcher shape — proportionate to the target, verified to the same standard.</p>
</div>
<div class="pane" id="p-report" role="tabpanel" aria-labelledby="t-report" hidden>
<p class="lede">A scan writes exactly one directory into your checkout. It ships with its own <b>.gitignore</b>, so a stray <code>git add</code> cannot sweep a report into a commit.</p>
<div class="out" id="report-out"></div>
<table>
<thead><tr><th>FIELD</th><th>WHAT IT CARRIES</th></tr></thead>
<tbody>
<tr><td class="k">id</td><td class="v">Finding identifier, e.g. F1 — the handle you pass to the patch job</td></tr>
<tr><td class="k">severity</td><td class="v">HIGH, MEDIUM, or LOW</td></tr>
<tr><td class="k">confidence</td><td class="v">HIGH, MEDIUM, or LOW — capped by the panel vote</td></tr>
<tr><td class="k">cweId</td><td class="v">The CWE class, e.g. CWE-89</td></tr>
<tr><td class="k">file, line</td><td class="v">Repository-relative path and the exact sink line</td></tr>
<tr><td class="k">exploitScenario</td><td class="v">How the flaw is reached and abused</td></tr>
<tr><td class="k">preconditions</td><td class="v">What must be true for the exploit to work</td></tr>
<tr><td class="k">recommendation</td><td class="v">Outcome-focused fix guidance</td></tr>
<tr><td class="k">verification</td><td class="v">Vote tally, computed in code by the report renderer — not asserted by the model</td></tr>
</tbody>
</table>
<p class="note">Patches land in <code>patches/</code> as one <code>F<n>.patch</code> per finding, built in a scratch clone so your working tree is never touched. Nothing is applied for you: run <code>git apply</code> yourself, one patch per pull request.</p>
</div>
<div class="foot">
<small>Verified 22 July 2026 against the
<a href="https://code.claude.com/docs/en/claude-security" target="_blank" rel="noopener">plugin docs</a> and
<a href="https://github.com/anthropics/claude-plugins-official/tree/main/plugins/claude-security" target="_blank" rel="noopener">source</a>.</small>
<span class="brand">■ Built by Marktechpost</span>
</div>
</div>
<script>
(function(){
var root = document.getElementById(‘mtp-cs-term’);
/* ———- tabs ———- */
var tabs = [].slice.call(root.querySelectorAll(‘[role=tab]’));
tabs.forEach(function(tab){
tab.addEventListener(‘click’, function(){
tabs.forEach(function(t){
var pane = document.getElementById(t.getAttribute(‘aria-controls’));
var on = (t === tab);
t.setAttribute(‘aria-selected’, on ? ‘true’ : ‘false’);
pane.hidden = !on;
});
resize();
});
});
/* ———- 1. pipeline ———- */
var PHASES = [
{ n:’PHASE 01′, t:’Inventory’, cmd:’inventory –cap 12|24′,
body:’Partition the repository into components, ordered by attacker-reachable surface.nnEvery top-level directory must be either scanned or named in a skip ledger with a reason.nThat accounting is checked before the search begins, not taken on trust.nnAgent: scan-inventory (Sonnet, read-only: Read, Glob, Grep)’ },
{ n:’PHASE 02′, t:’Threat model’, cmd:’threat-model –per-component’,
body:’One modeler per component. Returns a structured map:nn entryPoints …… file:line where untrusted input entersn sinks ………… file:line of dangerous operationsn assumptions …… validation the code assumes happened elsewheren trustBoundaries .. where the trust level changesn hotFiles ……… files a researcher must read in full’ },
{ n:’PHASE 03′, t:’Research’, cmd:’research –matrix component x category’,
body:’One researcher per component x category cell. Four categories:nn injection-and-input .. SQLi, command and code injection, XSS, XXE,n deserialization, template injection, ReDoS,n path traversal, prompt injectionn auth-and-access …… auth bypass, missing/wrong authz checks, IDOR,n privilege escalation, CSRF, SSRF, open redirect,n race conditions in access decisionsn memory-and-unsafe …. buffer overflow, OOB access, use-after-free,n integer overflow, type confusion, unsafe FFIn crypto-and-secrets … weak or misused crypto, weak randomness,n key/nonce reuse, timing side channels,n hardcoded secrets, credential exposure’ },
{ n:’PHASE 04′, t:’Sweep’, cmd:’sweep –gap-fill’,
body:’A gap-fill pass over what the component x category matrix did not cover.nn 0 sweeps at low effortn 1 sweep at mediumn 2 sweeps at high and maxnnOn a large repository, attention goes to code an attacker can reach.nTests, fixtures, generated code, and vendored trees are treated asnbackground rather than targets — plus a dedicated secrets pass thatnstill checks fixtures for real committed keys.’ },
{ n:’PHASE 05′, t:’Panel’, cmd:’panel –lenses REACHABILITY,IMPACT,DEFENSES’,
body:’Three-lens adversarial verification, one voter per lens.nnEach verifier works from the code rather than from the report of it,nand is told to call a candidate a false positive unless it can confirmna real path to exploitation.nnVerdict enum: TRUE_POSITIVE | FALSE_POSITIVEnKeep quorum: 2 of 3nnFindings that survive are what you read. The rest are discarded andnnever shown — which is why the reports stay short.’ },
{ n:’PHASE 06′, t:’Adversarial’, cmd:’adversarial –effort max’,
body:’Runs at max effort only.nn 1. Re-panel marginal keeps — anything that passed 2/3 rather than 3/3n goes back to a fresh panel.n 2. Red-team every survivor.nnAt every other tier the run ends after the panel.’ }
];
var stagesEl = document.getElementById(‘stages’);
var stageOut = document.getElementById(‘stage-out’);
PHASES.forEach(function(p, i){
var b = document.createElement(‘button’);
b.className = ‘stage’;
b.type = ‘button’;
b.setAttribute(‘aria-pressed’, i === 0 ? ‘true’ : ‘false’);
b.innerHTML = ‘<span class="n">’ + p.n + ‘</span><span class="t">’ + p.t + ‘</span>’;
b.addEventListener(‘click’, function(){
[].slice.call(stagesEl.children).forEach(function(c){ c.setAttribute(‘aria-pressed’,’false’); });
b.setAttribute(‘aria-pressed’,’true’);
renderStage(i);
});
stagesEl.appendChild(b);
});
function esc(s){ return String(s).replace(/&/g,’&’).replace(/</g,’<’).replace(/>/g,’>’); }
function renderStage(i){
var p = PHASES[i];
stageOut.innerHTML =
‘<span class="c">$</span> <span class="w">’ + esc(p.cmd) + ‘</span>nn’ +
‘<span class="d">’ + esc(p.body) + ‘</span>’;
resize();
}
renderStage(0);
/* ———- 2. panel ———- */
var LENSES = [
{ k:’REACHABILITY’, d:’Can untrusted input actually reach this sink?’ },
{ k:’IMPACT’, d:’If it is reached, what does an attacker gain?’ },
{ k:’DEFENSES’, d:’Does something upstream already stop it?’ }
];
var votes = [true, true, false];
var votersEl = document.getElementById(‘voters’);
var panelOut = document.getElementById(‘panel-out’);
LENSES.forEach(function(l, i){
var row = document.createElement(‘div’);
row.className = ‘vote’;
row.innerHTML = ‘<div class="name">[v’ + (i+1) + ‘] ‘ + l.k + ‘<em>’ + l.d + ‘</em></div>’;
var btn = document.createElement(‘button’);
btn.type = ‘button’;
btn.className = ‘toggle’;
btn.addEventListener(‘click’, function(){ votes[i] = !votes[i]; paintVotes(); renderPanel(); });
row.appendChild(btn);
votersEl.appendChild(row);
});
function paintVotes(){
var btns = votersEl.querySelectorAll(‘.toggle’);
for (var i = 0; i < btns.length; i++){
btns[i].textContent = votes[i] ? ‘TRUE_POSITIVE’ : ‘FALSE_POSITIVE’;
btns[i].className = ‘toggle ‘ + (votes[i] ? ‘tp’ : ‘fp’);
btns[i].setAttribute(‘aria-label’, LENSES[i].k + ‘: ‘ + btns[i].textContent + ‘. Click to flip.’);
}
}
function pad(s, n){ s = String(s); while (s.length < n) s += ‘.’; return s; }
function renderPanel(){
var t = votes.filter(Boolean).length;
var f = 3 – t;
var kept = t >= 2;
var ceiling = t >= 3 ? ‘HIGH’ : ‘MEDIUM’;
var lines = ”;
LENSES.forEach(function(l, i){
lines += ‘ [v’ + (i+1) + ‘] ‘ + pad(l.k + ‘ ‘, 18) + ‘ ‘ +
(votes[i] ? ‘<span class="c">TRUE_POSITIVE</span>’ : ‘<span class="w">FALSE_POSITIVE</span>’) + ‘n’;
});
var verdict = kept
? ‘<span class="c">KEEP</span> written to CLAUDE-SECURITY-RESULTS.md as F1’
: ‘<span class="w">DISCARD</span> never shown; it is not in the report at all’;
var conf = kept
? ‘ ‘ + pad(‘confidence ceiling ‘, 22) + ‘ <span class="c">’ + ceiling + ‘</span>’ +
(t >= 3 ? ‘ <span class="d">(unanimous)</span>’ : ‘ <span class="d">(quorum, not unanimous)</span>’) + ‘n’
: ”;
panelOut.innerHTML =
‘<span class="c">$</span> <span class="w">panel –finding F1 –voters 3 –quorum 2</span>nn’ +
lines + ‘n’ +
‘ ‘ + pad(‘tally ‘, 22) + ‘ ‘ + t + ‘ true / ‘ + f + ‘ falsen’ +
‘ ‘ + pad(‘verdict ‘, 22) + ‘ ‘ + verdict + ‘n’ +
conf;
resize();
}
paintVotes(); renderPanel();
/* ———- 3. effort planner ———- */
var tier = ‘medium’, memsafe = false;
var compEl = document.getElementById(‘comp’);
var compV = document.getElementById(‘comp-v’);
var effortOut = document.getElementById(‘effort-out’);
var memBtn = document.getElementById(‘memsafe’);
[].slice.call(root.querySelectorAll(‘[data-tier]’)).forEach(function(b){
b.addEventListener(‘click’, function(){
tier = b.getAttribute(‘data-tier’);
[].slice.call(root.querySelectorAll(‘[data-tier]’)).forEach(function(x){
x.setAttribute(‘aria-pressed’, x === b ? ‘true’ : ‘false’);
});
renderEffort();
});
});
memBtn.addEventListener(‘click’, function(){
memsafe = !memsafe;
memBtn.setAttribute(‘aria-pressed’, memsafe ? ‘true’ : ‘false’);
renderEffort();
});
compEl.addEventListener(‘input’, function(){ compV.textContent = compEl.value; renderEffort(); });
function renderEffort(){
var big = (tier === ‘high’ || tier === ‘max’);
var cap = big ? 24 : 12;
var found = parseInt(compEl.value, 10);
var used = Math.min(found, cap);
var cats = memsafe ? 3 : 4;
var per = big ? 2 : 1;
var sweeps = tier === ‘low’ ? 0 : (big ? 2 : 1);
var low = (tier === ‘low’);
var researchers = used * cats * per;
var dropped = found > cap
? ‘n <span class="w">inventory cap:</span> keeping ‘ + cap + ‘ of ‘ + found + ‘ components; the rest are merged or dropped’
: ”;
var researchLine = low
? ‘ ‘ + pad(‘shape ‘, 24) + ‘ <span class="w">single-researcher</span> <span class="d">(no component matrix)</span>’
: ‘ ‘ + pad(‘category matrix ‘, 24) + ‘ ‘ + cats + ‘ categories × ‘ + per + ‘ researcher’ + (per > 1 ? ‘s’ : ”) + ‘ per celln’ +
‘ ‘ + pad(‘researchers dispatched ‘, 24) + ‘ <span class="c">’ + researchers + ‘</span>’;
effortOut.innerHTML =
‘<span class="c">$</span> <span class="w">/claude-security scan codebase –effort ‘ + tier + ‘</span>nn’ +
‘ ‘ + pad(‘component cap ‘, 24) + ‘ ‘ + cap + ‘ <span class="d">(12 at low/medium, 24 at high/max)</span>n’ +
‘ ‘ + pad(‘components scanned ‘, 24) + ‘ ‘ + used + ‘ of ‘ + found + dropped + ‘n’ +
researchLine + ‘n’ +
‘ ‘ + pad(‘gap-fill sweeps ‘, 24) + ‘ ‘ + sweeps + ‘n’ +
‘ ‘ + pad(‘verification ‘, 24) + ‘ 3-voter panel per surviving candidaten’ +
‘ ‘ + pad(‘adversarial pass ‘, 24) + ‘ ‘ + (tier === ‘max’
? ‘<span class="c">on</span> <span class="d">(re-panel marginal keeps, red-team survivors)</span>’
: ‘<span class="d">off — max effort only</span>’) + ‘nn’ +
‘<span class="d"> Every tier clears the same verification bar. A thorough scan coversn’ +
‘ more ground; it does not lower the standard a finding must meet.</span>’;
resize();
}
renderEffort();
/* ———- 4. report tree ———- */
document.getElementById(‘report-out’).innerHTML =
‘<span class="c">$</span> <span class="w">ls -R CLAUDE-SECURITY-20260722-1412/</span>nn’ +
‘ <span class="c">CLAUDE-SECURITY-RESULTS.md</span> <span class="d">the readable report</span>n’ +
‘ <span class="c">CLAUDE-SECURITY-RESULTS.jsonl</span> <span class="d">one JSON object per finding</span>n’ +
‘ <span class="c">CLAUDE-SECURITY-REVISION-a1b2c3d4e5f6.json</span>n’ +
‘ <span class="d">commit, effort, severity counts,</span>n’ +
‘ <span class="d">and how thoroughly it was verified</span>n’ +
‘ <span class="c">patches/</span>n’ +
‘ <span class="c">F1.patch</span> <span class="c">F1.md</span> <span class="d">one patch + note per finding</span>n’ +
‘ <span class="c">.gitignore</span> <span class="d">so a stray git add cannot commit it</span>nn’ +
‘<span class="d"> The revision filename carries -dirty when uncommitted changes weren’ +
‘ part of the scanned tree, so a report is always tied to the code itn’ +
‘ describes.</span>’;
/* ———- auto-resize for WordPress iframe ———- */
function resize(){
try {
var h = root.offsetHeight + 40;
if (window.parent && window.parent !== window) {
window.parent.postMessage({ mtpEmbedHeight: h, id: ‘mtp-cs-term’ }, ‘*’);
}
} catch (e) {}
}
window.addEventListener(‘load’, resize);
window.addEventListener(‘resize’, resize);
setTimeout(resize, 120);
setTimeout(resize, 600);
})();
</script>
</body>
</html>
“>
What a scan writes to disk
Every scan writes a timestamped CLAUDE-SECURITY-<timestamp>/ directory into the repository containing three artifacts:
- CLAUDE-SECURITY-RESULTS.md — the human-readable report. Each finding carries an ID such as F1, plus severity (HIGH/MEDIUM/LOW), confidence, CWE ID, the exact sink line, impact, exploit scenario, preconditions, and a recommendation.
- CLAUDE-SECURITY-RESULTS.jsonl — the same findings, one JSON object per line.
- CLAUDE-SECURITY-REVISION-<commit>.json — the revision stamp: which commit was scanned, at what effort, the severity counts, and how thoroughly the run was verified. The filename carries -dirty when uncommitted changes were part of the scanned tree.
That directory is the only change a scan makes to your checkout, and it ships with its own .gitignore so a stray git add cannot sweep a report into a commit. Deleting that one .gitignore lets you commit the report for an audit trail.
Patches, and the three claims each one must earn
The fix job develops each patch in a scratch clone of the repository, so your working tree and index are never touched. An agent independent of the one that wrote the patch then reviews the staged diff and runs the project’s own test suite against the change.
A patch file is written only if the verifier can state all three of these with confidence: the change addresses that one finding, it introduces no new vulnerability, and behavior is otherwise unchanged — where a change to which inputs the code accepts counts as a behavior change. Any change that weakens security while claiming to fix it, such as a loosened auth check or a disabled test, is an automatic reject. When the verifier cannot vouch for all three, you get a short note explaining why instead of a patch.
Patches land in the report’s patches/ folder as F<n>.patch. Nothing is applied automatically:
git apply CLAUDE-SECURITY-<timestamp>/patches/F1.patch
The patch note explicitly indicates if no repository test covers the modified code, clarifying that the verification relies on code review rather than test execution. Anthropic suggests that each patch should be applied using an individual pull request.
Requirements, cost, and the trust model
The plugin requires a paid plan with Claude Code v2.1.154 or later, with dynamic workflows enabled in /config. It also needs Python 3.9.6 or later on your PATH as python3 (using only the standard library) and Git for change scans and patching. Linux, macOS, and Windows are supported, and scans count against your plan’s token limits.
Running in your session under your permissions, the scan adds no isolation of its own, meaning committed .claude/ settings, hooks, and CLAUDE.md still apply. While it treats repository content as data rather than instructions, this is not a defense against hostile repositories; use sandbox-runtime to sandbox unfamiliar codebases.
Additionally, scans are nondeterministic and do not replace traditional static analysis, dependency scanning, or code reviews.
Where it sits in the stack
| Stage | Tool | What it covers |
|---|---|---|
| In session | Security guidance plugin | Common vulnerabilities in code Claude writes, fixed in the same session |
| On demand, single pass | /security-review | One-time security pass on the current branch |
| On demand, deep scan | Claude Security plugin | Multi-agent scan of a repository or diff, independently reviewed findings and patches |
| On pull request | Code Review (Team, Enterprise) | Multi-agent correctness and security review with full codebase context |
| Managed | Claude Security (Enterprise) | Hosted scanning that monitors connected repositories |
| In CI | Your existing SAST and dependency scanners | Language-specific rules, supply-chain checks, policy enforcement |
Key Takeaways
- The Claude Security plugin runs a six-phase multi-agent scan locally in a Claude Code session, installed with /plugin install claude-security@claude-plugins-official.
- Findings reach the report only after a 3-voter adversarial panel (REACHABILITY, IMPACT, DEFENSES) clears a 2-of-3 quorum; a non-unanimous panel caps stated confidence at medium.
- The verification tally is computed in code by the report renderer, not asserted by the model, and stamped into a per-commit revision file.
- Patches are built in a scratch clone, independently verified against three claims, and never applied automatically.
- The scan adds no isolation of its own — sandbox the session before scanning code you do not control.
Sources: Claude Security plugin documentation, Claude Security product page, Plugin source, claude-plugins-official, @claudeai announcement and Claude Security public beta blog
The post Anthropic Releases Claude Security Plugin for Claude Code in Beta: A Multi-Agent Vulnerability Scanner That Runs in Your Terminal appeared first on MarkTechPost.