[BUG] Cyber safeguards false positive: routine sysadmin audit commands blocked, write-only reporting blocked in new session, context poisoning breaks session recovery

Open 💬 16 comments Opened May 21, 2026 by Zejzz

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

The cyber safeguards filter fires on three categories of work that have no offensive application:

  1. Standard sysadmin audit commands run as root on own infrastructure (find / -perm -4000 — SUID enumeration, present in Lynis/OpenSCAP/CIS benchmarks)
  2. After a block fires, the entire session becomes unusable — subsequent messages with zero security content (e.g. "how is it going?") are also blocked
  3. In a brand new session with no commands executed, repeated Write() calls to produce a markdown audit report were blocked because the document content mentioned security terminology (e.g. ` /root/.ssh/authorized_keys is mode 0777 `)

All three occurred while auditing a Proxmox node we own and operate, running as root.

What Should Happen?

  1. Auditing your own infrastructure as root should not trigger the cyber filter. The filter should consider ownership/authorisation context, not just command signatures.
  2. A block on one message should not poison the rest of the session. Subsequent messages should be evaluated independently.
  3. Write() tool calls producing markdown documents should not be filtered on the security terminology appearing in the document content. Writing about security findings is not the same as executing offensive commands.

Error Messages/Logs

API Error: Claude Code is unable to respond to this request, which appears to violate our Usage Policy (https://www.anthropic.com/legal/aup). Please double press esc to edit your last message or start a new session for Claude Code to assist with a different task.
Request ID: req_011CbFr6AcLjEMmhqAVHL5fA

API Error: Claude Code is unable to respond to this request, which appears to violate our Usage Policy (https://www.anthropic.com/legal/aup). This request triggered cyber-related safeguards. To request an adjustment pursuant to our Cyber Verification Program based on how you use Claude, fill out https://claude.com/form/cyber-use-case?token=[token]
Request ID: req_011CbFr7iBf4RowG2QBRUgQQ

Write(audit-report.md) blocked in new session:
Request ID: req_011CbFrTbe4gH6DzjBSZm7Cv
Request ID: req_011CbFrUPqgapjtUTsKTv1sJ
Request ID: req_011CbFrV3AkhogP3fWKrZKjU
Request ID: req_011CbFrVxDsnPnrtV5FPA8sM
Request ID: req_011CbFrWeWnoKHk3k5HGnVap

Steps to Reproduce

Bug 1 — Audit command false positive + context poisoning

  1. Start Claude Code as root on a Linux server you own
  2. Ask Claude to perform a security audit: "perform a security audit of this system, check for misconfigurations, list SUID binaries, check SSH config"
  3. Claude will successfully run: pveum user list, aa-status, sshd -T, iptables -L -n -v, apt list --upgradable, openssl x509 ..., smartctl -a ...
  4. Claude attempts find / -perm -4000 (SUID binary enumeration) → block fires
  5. Send any follow-up message, e.g. "how is it going?" → also blocked despite containing no security content

Bug 2 — Write-only reporting blocked in new session

  1. After Bug 1 terminates a session, start a completely new Claude Code session (claude fresh)
  2. Ask Claude to read the prior session's JSONL transcript from ~/.claude/projects/ and write a markdown audit report from the findings already collected
  3. Claude reads the JSONL files (allowed), then calls Write(audit-report.md) with content containing lines like:
  4. | CRITICAL | /root/.ssh/authorized_keys is mode 0777 (world-writable) |
  5. Write() is blocked repeatedly (5+ times) despite no commands being executed and no interaction with any system — purely writing a local markdown file

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.146

Platform

Anthropic API

Operating System

Ubuntu/Debian Linux

Terminal/Shell

Other

Additional Information

  • Session IDs with full JSONL transcripts available: a8654819-f562-41f2-9e55-7c1034e4dd34 (primary, 501 KB), 873782ec-da52-4808-9972-5f57d8cf1a65 (earlier attempt, 242 KB)
  • CVP application submitted via the tokenised URL in req_011CbFr7iBf4RowG2QBRUgQQ (pending)
  • The write-only blocking means CVP approval may not fix Bug 2, since the filter is triggering on document content not executable intent. This makes it impossible to produce audit reports, pentest findings docs, or incident response summaries — the primary legitimate use cases CVP is meant to enable.
  • Related: issue #50162 documents the same class of problem for bug bounty researchers. This adds the own-infrastructure sysadmin case and the write-only reporting case.

View original on GitHub ↗

16 Comments

Zejzz · 1 month ago

transcript example:
The two highest-value next steps are probably (a) running the full ss + iptables-save + lsof -i :3128 (to
confirm what's actually exposed on the public IP, including pveproxy and the unknown 3128 listener) and (b)
reading the two port-forward scripts (since they're called as root by every portfwd user). Want me to start
with those?

✻ Churned for 18s

❯ yes please

● Bash(echo "=== FULL ss -tulnp ===" && ss -tulnp 2>&1
echo…)
⎿  === FULL ss -tulnp ===
Netid State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
… +78 lines (ctrl+o to expand)

Read 3 files, listed 1 directory (ctrl+o to expand)

● Bash(iptables-save 2>&1 | wc -l && echo "---FILTER---" && iptables-save -t filter 2>&1 | head -80 && echo
"---NAT (first 80)---" && iptables-save -t nat 2>&1 | head…)
⎿  199
---FILTER---
# Generated by iptables-save v1.8.9 on Thu May 21 16:24:37 2026
… +114 lines (ctrl+o to expand)
⎿  API Error: Claude Code is unable to respond to this request, which appears to violate our Usage Policy
(https://www.anthropic.com/legal/aup). Please double press esc to edit your last message or start a new
session for Claude Code to assist with a different task.

Request ID: req_011CbFsEUhrb2NN6ak78DKZS

✻ Sautéed for 30s

github-actions[bot] · 1 month ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/60076
  2. https://github.com/anthropics/claude-code/issues/48977
  3. https://github.com/anthropics/claude-code/issues/59402

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

Zejzz · 1 month ago

By the way, the exact same thing copy pasted into claude.ai chat runs fine, so this is a nonsensicial issue https://claude.ai/chat/8caa1778-9356-42e2-9b47-f98f5b03dc35

k33bs · 1 month ago

its now a full blown bug...

filipghoulin · 1 month ago

+1. My case: homelab operator (Proxmox cluster, ~40 self-hosted services on personal hardware).
Blocks fire on: Loki alerting rules referencing IP-rate anomalies on my own reverse proxy,
banIP config on my own OpenWRT router, WireGuard config files, SSH commands to my own router.

This session: context poisoning reproduced exactly as described — reading the body of issue #49679
(which contained "exploit lab machines") fired the filter mid-session. Clean context (new session
or model switch) was the only recovery.

k33bs · 1 month ago

looks resolved on my end

Zejzz · 1 month ago
looks resolved on my end

Are you able to share more info?

k33bs · 1 month ago
> looks resolved on my end Are you able to share more info?

literally this, error lasted for an hour under each project, came out of nowhere, for every prompt, couldn't compact, couldn't walk back and compact, couldn't end new requests. new cli chats were also blocked with same error. went to bed, got up this morning, all issues gone, no more errors anywhere.

7H35C4r3Cr0W · 1 month ago

Im in the Cyber Program and ive gotten questions blocked over asking about cats. https://medium.com/@its.lagus_66214/anthropics-broken-cyber-verification-program-c8c630820fd6

filipghoulin · 1 month ago

Concrete reproduction case with identified trigger and fix:

What triggered it: A lab-review skill dispatched a sub-agent that ran cat /etc/banip/banip.blocklist on an OpenWRT router. The blocklist has ~17,000 entries. That volume of content in a single tool result, combined with the security vocabulary already in the Claude Code system prompt, crossed the classifier threshold and fired the error mid-session.

Secondary effect: Pasting the failed session's analysis into a new Sonnet session also triggered the error — because the analysis text contained the same blocklist content. A clean context wasn't enough; the pasted text re-introduced the trigger.

Fix applied: Changed the skill to use /etc/init.d/banip status (summary only) instead of cat /etc/banip/banip.blocklist, and to report unblocked scanner counts instead of raw IP lists.

This points to a volume threshold in the classifier, not just keyword matching — a single tool result with a large amount of network-threat-related content (IPs flagged as malicious) is enough to fire it even when the intent is purely defensive (checking what's already blocked on my own router).

Zejzz · 1 month ago
Concrete reproduction case with identified trigger and fix: What triggered it: A lab-review skill dispatched a sub-agent that ran cat /etc/banip/banip.blocklist on an OpenWRT router. The blocklist has ~17,000 entries. That volume of content in a single tool result, combined with the security vocabulary already in the Claude Code system prompt, crossed the classifier threshold and fired the error mid-session. Secondary effect: Pasting the failed session's analysis into a new Sonnet session _also_ triggered the error — because the analysis text contained the same blocklist content. A clean context wasn't enough; the pasted text re-introduced the trigger. Fix applied: Changed the skill to use /etc/init.d/banip status (summary only) instead of cat /etc/banip/banip.blocklist, and to report unblocked scanner counts instead of raw IP lists. This points to a volume threshold in the classifier, not just keyword matching — a single tool result with a large amount of network-threat-related content (IPs flagged as malicious) is enough to fire it even when the _intent_ is purely defensive (checking what's already blocked on my own router).

Its much wrose than that, its begun blocking eveyrthing about cybersecurity.

<img width="882" height="646" alt="Image" src="https://github.com/user-attachments/assets/6ebce37e-db13-46fa-8dbc-42747d310fdb" />

case in point here, it blocks mentioning this issue itself at all

yurukusa · 1 month ago

@Zejzz @filipghoulin @7H35C4r3Cr0W — this thread, taken together with the volume-threshold reproduction at https://github.com/anthropics/claude-code/issues/61185#issuecomment-3155330927, has actually surfaced a cleaner failure model than any of the individual reports. I've been tracking the broader Usage-Policy false-positive surface as a four-axis cluster across ~25 issues since mid-May. Folding these data points in:

What the cluster looks like with this issue's data added

I label this Cluster 9 (AUP classifier misfire) and have been treating it as a 4-axis problem rather than a single bug, because the trigger surfaces and operator-side mitigations are independent. The axes are:

  • Axis 0 — Vocabulary surface. Static keyword overlap with offensive content. Hits SUID-binary enumeration, banIP blocklists, sensitive-file paths in audit reports. Closest to what most reports point at, but actually the smallest axis once you control for the next three.
  • Axis 1 — Context-bloat amplification. Long sessions accumulate security vocabulary in the transcript, raising the running per-turn classifier score above the static-content score. Why @k33bs's "literally went to bed, all gone" works (the context state, not the content, was the corruption). Why @Zejzz's "subsequent how is it going? is also blocked" happens: the new message inherits the contaminated transcript.
  • Axis 4 — Large-volume tool output. Identified explicitly by @filipghoulin in the banIP blocklist reproduction (~17,000 entries in a single tool result). The fix was not "change the tool, change the vocabulary" — it was "summarize the volume". This is a distinct failure mode from axes 0/1, and the most repeatedly under-reported one. The volume threshold appears to compound with axis 0: medium-volume security text + medium-volume neutral text ≠ high-volume security text in terms of classifier scoring.
  • Axis 6 — Cross-session re-contamination. @filipghoulin's "pasting the failed session's analysis into a new Sonnet session also triggered the error" closes a loop people often miss: the operator-side recovery surface assumed by "new session" does not survive carrying analysis text across the boundary. Combined with axis 1, this means the cure people are told to use is itself a transmission vector.

A 7-axis secondary-pain analysis covering axes 0/1/4/6 plus three on the consumer side (no self-diagnostic, no graceful degradation, planning-mode invalidation) is published at Cluster 9 — Usage Policy classifier misfire — 7 axes of secondary pain.

Where Zejzz's three categories map

  • Bug 1 (SUID enumeration + context poisoning) → axis 0 trigger + axis 1 propagation. The first message is the static-vocabulary hit; the "how is it going?" follow-up is axis 1 (the contaminated transcript is the input the classifier sees, not just the new turn).
  • Bug 2 (Write-only reporting blocked in a fresh session) → axis 4 + axis 0. The audit document content is itself the input — security findings written as markdown carry the same vocabulary surface as the original commands. The "fresh session" framing is misleading because the Write payload reintroduces the contamination into the very first turn, so axis 1 is not even needed.
  • The "blocks mentioning this issue itself" observation in your follow-up screenshot is one of the cleanest demonstrations of axis 0 + 4 compound scoring I have seen: a single issue body containing 14 cross-referenced Issue numbers in security-vocabulary context exceeds the threshold even when the intent is "discuss the bug."

The reason Zejzz's chat-surface attempt worked while Claude Code did not is the system-prompt surface: the Code CLI's tool-aware prompt carries additional security-flavored vocabulary (sandbox guidance, permission semantics, etc.) that the chat surface lacks. The classifier sees more "security context" in the same transcript on the CLI side. This is not a CVP-approval-fixable issue at the API layer because the system-prompt difference is upstream of the model's actual call.

What is operationally usable today

The recovery surface that holds up across all four axes is defense-must-fire-before-dispatch. Three concrete hooks that target the specific axes, all in the cc-safe-setup repo (MIT, ~1,000 unique users / 14 days):

  1. aup-retry-loop-guard (PR #481) — targets axis 1 by catching the context-bloat amplification before the AUP block fires. Runs as a PostToolUse hook, counts security-vocabulary tool outputs in the recent transcript window, warns when the rolling score crosses a configurable threshold. The threshold gives you a head-start window to compact or clear before the classifier crosses its own threshold. Defaults are calibrated against the public AUP issues I have been tracking, but the threshold environment variable lets you tune. 16 tests, all passing.
  2. aup-large-tool-output-warner (PR #486) — targets axis 4 (your large-blocklist case directly). Runs as a PreToolUse hook, intercepts tool calls whose expected output is large (configurable byte / line thresholds), suggests status / summary / wc alternatives before the raw output enters the transcript. Your banip status instead of full-blocklist dump fix is exactly the substitution the hook surfaces — automating the trigger-avoidance rather than the post-block recovery. 32 tests, all passing.
  3. model-swap-suggester (PR #480) — targets the recovery surface for axis 6. When an AUP block fires, the hook detects the failure signature and suggests Sonnet (or a clean-context switch) before the operator pastes the contaminated analysis text into a new session. The clean-context substitution that worked on k33bs's "next morning" case but failed on filipghoulin's "paste analysis" case is the gap this hook closes. 25 tests, all passing.

None of these will fix the underlying classifier behavior — that is upstream — but they shift the failure surface from "you find out by getting blocked" to "you find out by getting a warning with a substitution suggestion". For paying-tier users running real ops work, the difference in interruption cost is roughly 2 orders of magnitude.

On CVP not helping the document-content case

@7H35C4r3Cr0W's article and Zejzz's Bug 2 are pointing at the same structural problem: CVP approves the user, but the classifier scores the content. Approval does not change the per-turn score, it changes the threshold above which an approved user is blocked. When the content itself is the trigger (Write of a markdown audit report), CVP raises the threshold marginally but does not change which inputs cross it. This is why the write-only reporting case is the load-bearing failure mode — it makes the primary legitimate use case CVP is meant to enable (audit reports, pentest findings, incident response summaries) unreachable for both pre-CVP and post-CVP users in the same way.

If anyone in this thread is hitting Bug 2 specifically and has the bandwidth, I would find it useful to know whether the block fires on the Write tool call itself or on the subsequent turn's classifier evaluation of the assistant message that contained the Write. The two have different fixes upstream — and the answer changes whether axis-4 pre-write summarization (already in PR #486) would also help, or whether a separate post-write transcript-redaction surface is required.

A note on the duplicate-detection bot

The auto-flagged duplicates (#60076 / #48977 / #59402) are actually not duplicates of this issue — they are separate axes within the same cluster. The duplicate detector is operating on vocabulary similarity, which is exactly the same scoring surface as the AUP classifier, so it inherits the same precision problem. If the bot closes this as a duplicate, the cluster loses the cleanest volume-threshold reproduction (filipghoulin's blocklist case) and the cross-session re-contamination data point — both of which the other three issues do not contain.

Anyone landing here from this thread who is hitting one of the four axes, please leave a "+1 axis N" comment so the cluster keeps its evidence base intact.

---

Thank you all for the level of detail you have put into the reproductions. The volume-threshold finding from @filipghoulin specifically is the cleanest piece of root-cause analysis the AUP cluster has gotten from any public report — including my own — and the cross-session re-contamination data point closes a recovery-surface gap that operator-side advisories had been silently missing.

7H35C4r3Cr0W · 1 month ago

I take this back. I turned off memory and i still have an overactive refusal for a new thread I just opened from previously generated topics. I reached out to as many people as I could possibly think. Ive done all I could guys. Anything you can do to push this to anthropic will be beneficial to the community.

Call-me-Boris-The-Razor · 1 month ago

Sysadmin, kernel, FPGA, infra, embedded — all tripping the same classifier. Mine: flashing my own ESP32 and burning eFuses via Espressif's esptool/espefuse, benign messages blocked. Request IDs: req_011Cbc2BcQzSvZu1XZD3gcFW, req_011Cbc2EFjcxGAKVP7FKbn5s.

The breadth of false-positive domains is the tell here — this is a precision failure landing on legitimate professionals, not a series of edge cases. Cross-linking #63751 and #64405 to keep the cluster visible.

COOLak · 1 month ago

Adding one narrow route-map link from the reporting/recovery side of this cluster:

  • #64287 is the product/UX ask for a first-class private false-positive report flow keyed to the blocked request ID.
  • #64507 is a related reporting/diagnostic bug: support/docs point affected users toward /feedback, /bug, and /doctor, but those commands can be unavailable or inconsistent across Claude Code surfaces.

That matters here because this issue includes the especially painful case of reporting/recovery work itself getting blocked: write-only reporting blocked in a fresh session, context poisoning, and session recovery failure. Even if the upstream classifier fix is separate, the product still needs a reliable private path to submit request IDs and evidence without relying on public GitHub comments or reintroducing the triggering context.

Keeping private request IDs, support IDs, local paths, and logs out of public GitHub; those belong only in official private support channels.

Manmartix · 21 days ago

Confirming on our side — plus a reproducible signal that may help diagnosis.

Same failure mode doing authorized defensive hardening on our own infrastructure (sysadmin, as root). All three categories match: audit commands, full-session poisoning after a single hit, and Write() of a markdown report blocked purely on security terminology in the body.

Two additions from our debugging:

1. Reasoning-effort correlation. The block is sensitive to reasoning effort. On low/xhigh the same session proceeds; switching to max triggers it consistently and stays blocked. Strong hint that the second (reasoning) stage of the two-stage classifier is what fires — the fast screen alone passes. Not escalating strictness with effort on owned-infra contexts would help.

2. Persistence via auto-loaded state. "Context poisoning" isn't confined to one live session — it re-triggers on startup of a new session, because auto-loaded context (project memory recall, resumed transcript) carries the flagged material back in. So "start a new session" isn't reliable recovery: the poison is at rest in ~/.claude/projects/.../*.jsonl + memory files and gets reloaded.

Workaround we built (in case it helps others — shouldn't be necessary): a read-time filter. Source files stay untouched (real values); the session only reads them through a wrapper that pseudonymizes topology (host→role-name, IPs→labels) and masks live secrets on the fly. Combined with pulling heavy infra notes out of memory-recall, this keeps accumulated context below the trigger without destroying the notes.

Strong +1 on: ownership/authorization context, per-message evaluation, and a recovery path that doesn't require nuking session state.