Fix cloaking transcript leak + scope-blind status/audit + scanner/sweep false positives - #315
Merged
Conversation
Five issues surfaced while exercising a fresh enforce+global install with a real Claude Code agent in the loop on an isolated host: 1. decloak.sh inlined the resolved secret into the command string it handed back to Claude Code, which records tool_input.command verbatim in ~/.claude/projects/*.jsonl. The raw value never reached the model but did reach disk. Resolution now happens in the child (decloak-exec.sh); the recorded command keeps the placeholders. Regression test added (fails against the old hook, passes now); leak re-audit shows 0 raw-secret occurrences across the transcripts. 2. cloak status and audit looked only at project scope, so a global-scope install (what setup --scope global writes) reported not-installed / a CRITICAL no-hooks finding while prismor status said the opposite. Both now check project+user, matching status. 3. hook_installed() text-searched for the inline dispatcher marker, which openclaw/hermes/opencode never have (they register a plugin path in config[plugins] and write the dispatcher into the scaffolded plugin). They read as unhooked forever, under-reporting coverage and re-triggering ensure_global_coverage every run. Now recognises the plugin-path form. 4. prismor scan flagged Prismor's OWN installed skill as CRITICAL shell injection + credential access, because the skill documents the very commands and paths the rules look for. Skip it while byte-identical to the bundled copy; an edited copy is scanned again. Malicious-skill positive control still fires. 5. sweep-gitleaks.toml mistral/together rules used bare length classes gated only by a file-level keyword, so one mention of mistral in a file matched every 32-char run in it (88 phantom secrets in one transcript). Bound both to an assignment context; positive control on a real key still detects.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Five issues surfaced while exercising a fresh
enforce+ global-scope install with a real Claude Code agent in the loop, on an isolated host. All are runtime/CLI fixes; no behavior change for anyone not hitting the specific paths.cloaking/hooks/decloak.sh~/.claude/projects/*.jsonlcli.py,audit.pycloak status/auditwere project-scope-only → a global install reported "not installed" whileprismor statussaid the oppositehooks.pyhook_installed()missed plugin-path agents (openclaw/hermes/opencode) → coverage under-reported,ensure_global_coveragere-ran every timescanner.pyprismor scanflagged Prismor's own installed skill as CRITICAL shell-injectionsweep-gitleaks.toml1. Cloaking transcript leak (the important one)
decloak.shhas to hand Claude Code a runnable command, and Claude Code records that command verbatim in its on-disk transcript. Substituting the real secret into that string therefore wrote the raw value to disk — the exact leak the cloaking layer exists to prevent (it never reached the model, but it did reach~/.claude/projects/*.jsonl).Fix: resolution moves into the child process (
decloak-exec.sh); the recorded command keeps its@@SECRET:name@@placeholders.Before — the wrapped command handed back contains the raw key (highlighted):
After — placeholders preserved, resolution deferred to
decloak-exec.sh:End-to-end check with a real agent: after asking Claude to use the key, a value-grep of every transcript file returns 0 raw-secret occurrences.
A regression test is added that fails against the old hook and passes now (
tests/test_cloak_output_scrub.py).2 + 3. Scope-blind status/audit & plugin-path coverage
prismor setup --scope globalwrites hooks to~/.claude, butaudit/cloak statusonly looked at project scope, andhook_installed()only recognised the inline-command form (openclaw/hermes/opencode register a plugin path instead). Net effect on a correctly-installed global machine:Before — CRITICAL "no hooks" + HIGH "cloaking not installed", both false:
After — hooks detected in both scopes and across plugin-path agents:
4. Scanner flagged Prismor's own skill
prismor setupinstalls Prismor's own skill, which documents the very commands and credential paths the skill rules look for — so a clean install scanned itself as CRITICAL. Now skipped while byte-identical to the bundled copy; an edited copy is scanned again, and a malicious-skill positive control still fires.Before (own skill flagged) → After (clean):
5. Sweep false positives
mistral-api-key/together-api-keyused bare length classes ([A-Za-z0-9]{32}) gated only by a file-level keyword, so one mention of "mistral" anywhere in a file matched every 32-char run in it (88 phantom hits in a single Claude transcript). Both are now bound to an assignment context; a positive control on a realMISTRAL_API_KEY=…still detects.Cloaking, end to end
Testing
453 passed, 2 skippedacross the cloak/audit/sweep/scanner/hooks/surface subset (the one remaining failure,test_cloak_add_env_file_imports_each_entry, is pre-existing onmain— unrelated).python -m buildconfirms the newdecloak-exec.shships in the wheel.