Repository navigation
v1.7.0
新增
- 新增 Claude Code 自身配置文件篡改检测:改
~/.claude/settings.json(或项目内
.claude/settings.json/settings.local.json)、.claude/hooks/目录下的脚本、
CLAUDE.md之前完全没有规则覆盖。这是目前发现的最大缺口——改配置文件删掉 hook
注册,比kill -9探针进程更隐蔽(不需要杀任何进程,Claude Code 下次工具调用
时应用层监测就已经悄悄失效了),跟已有的kill_monitoring_process是同一个
"防绕过"目标,但覆盖的是配置层面而不是进程层面。新增claude_config_tamper
(risk: high,action: confirm),tools: ["Write", "Edit", "NotebookEdit"],
field: "file_path"。用confirm而不是block,是因为正常给自己项目加一个
新 hook、改CLAUDE.md本来就是合理操作。插在shell_rc_write之后。 - Docker 特权/挂载检测新增 Docker socket 挂载识别:
docker_privileged_or_host_mount
原来只认--privileged和-v /:/这两种写法,-v /var/run/docker.sock:/var/run/docker.sock
这个更常见、更经典的容器逃逸手法(挂载宿主机 docker socket 等于把宿主机 root
权限直接送进容器)完全漏检。扩展正则加上docker.sock这个子串匹配,同时把
这条规则的risk从medium提到high(--privileged/docker.sock挂载都是
宿主机 root 等价的风险,medium偏低了;action维持confirm,兼容合法的
DinD/CI 场景)。 - 新增写入内容密钥格式扫描:之前所有规则全部按文件路径/命令文本判断,Claude
把 API key 写进任意一个不带敏感文件名特征的文件(比如config.py、
notes.txt)完全不会触发任何规则。新增secret_pattern_in_write
(risk: high,action: confirm),tools: ["Write", "Edit", "NotebookEdit"],
新增的field: "content"扫描私钥文件头(-----BEGIN ... PRIVATE KEY-----)、
AWS Access Key(AKIA/ASIA前缀)、GitHub token(ghp_/gho_/ghu_/ghs_/
ghr_/github_pat_)、Anthropic/OpenAI key(sk-ant-/sk-proj-/sk-)、Slack
token(xox[baprs]-)、Google API key(AIza)、npm token(npm_)、Stripe
live key(sk_live_)这几类高置信度的固定前缀格式。cc_monitor/policy.py的
FIELD_CANDIDATES新增"content": ["content", "new_string", "new_source"]
映射——Write 用content、Edit 用new_string、NotebookEdit 用
new_source,三个工具语义上都是"即将写进文件的内容",一条规则要同时认这三个
字段名,跟已有file_path的多候选写法是同一个道理。已经写进.env/.ssh/
这类本来就被sensitive_file_write覆盖的路径不会重复触发——规则顺序保证更
具体的路径规则先命中。局限性:按字符类型+长度的固定前缀匹配,不看信息熵,
文档里的示例占位符密钥(比如全用x填充的假 key)如果长度凑巧够长也会被
误判,这是所有轻量级密钥扫描工具(gitleaks/trufflehog 的非熵值模式)共有的
局限,action: confirm而不是block也是为了给这种误判留人工确认的余地。 - 新增 git hooks / git config 持久化攻击面检测:
core.hooksPath(把 git hooks
重定向到别的目录)、url.<url>.insteadOf(悄悄把依赖源换成攻击者控制的仓库,
真实供应链攻击手法)、直接用 Bash 重定向写入.git/hooks/,之前都没有覆盖——
跟已有的crontab_persistence/systemd_persistence是同一类"植入持久化后门"
风险,但 git 生态里的对应手法完全是盲区。新增两条规则:git_hooks_persistence
(risk: medium,action: confirm,tools: ["Bash"],覆盖上面三种命令行写法)
和git_hooks_file_write(同样medium/confirm,tools: ["Write", "Edit", "NotebookEdit"],field: "file_path",覆盖直接用 Write/Edit 工具往
.git/hooks/底下写文件这个命令行规则覆盖不到的写法)。git_hooks_persistence
插在git_hard_reset_clean之后。
以上四项用 26 个正负测试用例、外加 13 个覆盖全部旧规则类别的回归测试,全部
直接调用cc_monitor/policy.py的真实evaluate()函数验证(不是简化版
正则复现),确认新规则匹配正确、FIELD_CANDIDATES新增"content"映射没有
影响command/file_path/url这些既有字段的解析,node --test5/5 全部
通过。已有用户的~/.cc-monitor/rules.json不会自动更新,想要这些新规则生效
需要手动同步。 - 落地 slowmist-agent-security
致谢里提到的 5 条规则思路:credential_grep_scan(grep -r/rg -r之类递归
搜索 password/secret/api_key/token 等凭据关键字,medium/confirm)、
npx_pipx_ephemeral_run(npx/pnpm dlx/bunx/pipx run/uvx这类跳过本地
安装痕迹直接执行远程包的一次性执行,medium/confirm)、proc_env_read(读取
其它进程的/proc/<pid>/environ/cmdline,跨进程偷凭据的经典手法,high/
confirm)、browser_credential_read+browser_credential_read_bash(Chrome/
Chromium/Brave/Edge/Firefox 的Cookies/Login Data/cookies.sqlite/
logins.json/key4.db等登录态文件,分别覆盖 Read 工具和 Bash 命令行两种访问
方式,high/confirm)、dynamic_exec_in_write(写入内容里出现eval(/
exec(/os.system(/subprocess.*shell=True/new Function(/
child_process.exec(这类动态执行代码,常见于植入后门,但正常代码里也很常见,
故意压低到medium/log不打扰正常写代码)。 - 新增编码混淆执行检测:
encoded_payload_exec(base64 -d/xxd -r -p解码后
管道给sh/bash/zsh/python3,是curl_pipe_shell最常见的绕过变体——同样
是"下载/构造一段东西直接丢给解释器执行",只是用编码绕开了对curl|wget关键字
的文本匹配,high/block,跟curl_pipe_shell同等对待)。 - 新增 SSH 隧道/反向代理检测:
ssh_tunnel_reverse_proxy(ssh -R/-D/-L
建隧道、socat、chisel client/server,medium/confirm——很多合法用途
比如连内网数据库,所以没有做成block)。插在reverse_shell_pattern之后,
跟已有更严格的 socat 反弹 shell 特征是同一个"隐蔽出网通道"主题,评估顺序上
reverse_shell_pattern更精确的匹配优先命中。 claude_config_tamper扩大覆盖面到 MCP/Skills 配置:原来只认
.claude/settings*.json/.claude/hooks//CLAUDE.md,现在加上项目级
.mcp.json和.claude/skills/目录——写入一个恶意 MCP server 配置或恶意
skill 定义,是比改 hooks 更隐蔽的持久化后门手法,也正是 slowmist 清单聚焦的
攻击面。
以上共新增 8 条规则(default_rules.json从 49 条增至 57 条)+ 1 条既有规则的
patch,用真实policy.evaluate()跑了 18 个正负测试用例全部通过验证。已有
用户的~/.cc-monitor/rules.json不会自动更新,需要手动同步。- 接入 6 个会话生命周期 hook:
UserPromptSubmit/SessionStart/SessionEnd/
PreCompact/Stop/SubagentStop——之前只接了PreToolUse/PostToolUse/
PermissionRequest,纯对话、没有触发任何工具调用的轮次完全没有审计留痕。
这 6 个新 hook 全部是纯审计留痕,不参与 confirm/block(生命周期事件没有
"允许/拒绝"语义):UserPromptSubmit记录用户原始输入(唯一能看到"用户到底
让 Claude 干了什么"的钩子);SessionStart/SessionEnd记录会话的开始来源
(startup/resume/clear/compact)和结束原因;PreCompact在长会话被
压缩前记一笔(避免审计细节随上下文压缩丢失);Stop/SubagentStop记录主
任务/子代理的结束。cc_monitor/hook.py新增对应 6 个handle_*函数,
bin/CC-Monitor-hook <mode>新增prompt/session_start/session_end/
precompact/stop/subagent_stop六种 mode;install.py的merge_hooks()
改成用extra_hooks字典批量注册,新老用户重跑一遍install.py/install.sh
就会补上这几条(已装的PreToolUse/PostToolUse/PermissionRequest不动)。
事件分别落到source="hook_prompt"(UserPromptSubmit)和
source="hook_lifecycle"(其余 5 个),cc_monitor/format.py和
webui/lib/format.js同步加了对应的TOOL_LABELS/STAGE_LABELS/describe()
分支,Web UI 的i18n.js也补了中英文标签——首页"日志类型分布"、Log 审计页、
终端CC-Monitor tail都能直接看到这些新事件,不需要额外改 UI 代码(沿用
events表已有的通用 source/tool_name/detail 结构)。用独立CC_MONITOR_HOME
测试目录端到端验证过 6 个 hook 的 stdin→SQLite 落库全过程,以及 Python/JS 两份
describe()输出完全一致。
English
Added
- New detection for tampering with Claude Code's own config: editing
~/.claude/settings.json(or a project's.claude/settings.json/settings.local.json),
scripts under.claude/hooks/, orCLAUDE.mdhad zero rule coverage before. This is the
biggest gap found so far — rewriting the config to drop a hook registration is stealthier
thankill -9-ing the probe process (no process needs to die; the application-layer
monitoring just silently stops firing on the next tool call). Same "anti-bypass" goal as the
existingkill_monitoring_process, just at the config layer instead of the process layer.
Addedclaude_config_tamper(risk: high,action: confirm),
tools: ["Write", "Edit", "NotebookEdit"],field: "file_path".confirmrather than
blocksince adding a new hook to your own project or editingCLAUDE.mdis a completely
normal action. Inserted right aftershell_rc_write. - Docker privileged/mount detection extended to Docker-socket mounts:
docker_privileged_or_host_mountpreviously only recognized--privilegedand-v /:/—
-v /var/run/docker.sock:/var/run/docker.sock, a more common and classic container-escape
technique (mounting the host's Docker socket hands the container host-root-equivalent
control), was a complete blind spot. Extended the regex with adocker.socksubstring match,
and bumped this rule'sriskfrommediumtohigh(both--privilegedand a docker.sock
mount are host-root-equivalent risk,mediumundersold it;actionstaysconfirmto
accommodate legitimate DinD/CI use cases). - New secret-format scanning on written content: every existing rule judged by file path or
command text — Claude writing an API key into a file with no sensitive-looking name (e.g.
config.py,notes.txt) triggered nothing at all. Addedsecret_pattern_in_write
(risk: high,action: confirm),tools: ["Write", "Edit", "NotebookEdit"], using a new
field: "content"to scan for private-key headers (-----BEGIN ... PRIVATE KEY-----), AWS
access keys (AKIA/ASIAprefix), GitHub tokens (ghp_/gho_/ghu_/ghs_/ghr_/
github_pat_), Anthropic/OpenAI keys (sk-ant-/sk-proj-/sk-), Slack tokens
(xox[baprs]-), Google API keys (AIza), npm tokens (npm_), and Stripe live keys
(sk_live_) — high-confidence, fixed-prefix formats. Added a
"content": ["content", "new_string", "new_source"]mapping toFIELD_CANDIDATESin
cc_monitor/policy.py— Write usescontent, Edit usesnew_string, NotebookEdit uses
new_source, all three semantically "the content about to be written," so one rule needs to
recognize all three field names, the same trick already used forfile_path. Writes to paths
already covered bysensitive_file_write(like.env/.ssh/) won't double-fire — rule
ordering ensures the more specific path rule wins first. Limitation: this is a fixed-prefix,
character-class-and-length match, not an entropy check, so a sufficiently long placeholder key
in documentation (allxs, say) can false-positive — the same limitation every lightweight,
non-entropy secret scanner (gitleaks/trufflehog's non-entropy patterns) shares;confirm
rather thanblockleaves room for a human to wave off exactly that case. - New git-hooks/git-config persistence-attack-surface detection:
core.hooksPath
(redirecting git hooks to another directory),url.<url>.insteadOf(quietly swapping a
dependency's source for an attacker-controlled repo — a real supply-chain technique), and
writing directly into.git/hooks/via Bash redirection all had zero coverage before — the
same "plant a persistent backdoor" risk category as the existingcrontab_persistence/
systemd_persistence, but the git-ecosystem equivalent was a complete blind spot. Added two
rules:git_hooks_persistence(risk: medium,action: confirm,tools: ["Bash"], covering
the three command-line forms above) andgit_hooks_file_write(samemedium/confirm,
tools: ["Write", "Edit", "NotebookEdit"],field: "file_path", covering writing into
.git/hooks/directly via the Write/Edit tool — a form the command-based rule can't see).
git_hooks_persistenceis inserted right aftergit_hard_reset_clean.
All four items above were verified with 26 positive/negative test cases, plus 13 regression
cases covering every pre-existing rule category, all callingcc_monitor/policy.py's real
evaluate()directly (not a simplified regex re-implementation) — confirming the new rules
match correctly and that adding the"content"mapping toFIELD_CANDIDATESdidn't disturb
how the existingcommand/file_path/urlfields resolve.node --teststill passes 5/5.
As with prior rule additions, an existing user's~/.cc-monitor/rules.jsonwon't auto-update
to pick these up. - Implemented the 5 rule ideas named in the slowmist-agent-security
acknowledgment:credential_grep_scan(recursivegrep -r/rg -rsearches for
password/secret/api_key/token-style keywords,medium/confirm);npx_pipx_ephemeral_run
(npx/pnpm dlx/bunx/pipx run/uvx— one-shot execution of a remote package without a
local install footprint,medium/confirm);proc_env_read(reading another process's
/proc/<pid>/environ/cmdline— a classic cross-process credential-theft technique,
high/confirm);browser_credential_read+browser_credential_read_bash(Chrome/
Chromium/Brave/Edge/FirefoxCookies/Login Data/cookies.sqlite/logins.json/key4.db
files, covering both the Read tool and Bash command-line access paths,high/confirm);
dynamic_exec_in_write(eval(/exec(/os.system(/subprocess.*shell=True/
new Function(/child_process.exec(appearing in written content — common in planted
backdoors, but also extremely common in ordinary code, so deliberately kept at
medium/logto avoid interrupting normal coding). - Added encoded-payload execution detection:
encoded_payload_exec(base64 -d/
xxd -r -pdecoding piped intosh/bash/zsh/python3— the most common bypass variant
ofcurl_pipe_shell, same "fetch/construct something and hand it straight to an interpreter"
pattern, just obfuscated past the literalcurl/wgettext match;high/block, treated
the same ascurl_pipe_shell). - Added SSH tunnel / reverse-proxy detection:
ssh_tunnel_reverse_proxy(ssh -R/-D/
-Ltunnels,socat,chisel client/server;medium/confirm— plenty of legitimate
uses like reaching an internal database, so not ablock). Inserted right after
reverse_shell_pattern; the more specific existing socat reverse-shell pattern still wins
evaluation order when both could match. - Extended
claude_config_tamperto cover MCP/Skills config too: previously only matched
.claude/settings*.json/.claude/hooks//CLAUDE.md; now also covers project-level
.mcp.jsonand the.claude/skills/directory — planting a malicious MCP server config or
a malicious skill definition is a more subtle persistence backdoor than editing hooks, and is
exactly the attack surface the slowmist checklist is focused on.
Together this is 8 new rules (default_rules.jsongrows from 49 to 57) plus one patch to an
existing rule, verified with 18 positive/negative test cases against the real
policy.evaluate(). As always, an existing user's~/.cc-monitor/rules.jsonwon't
auto-update — resync manually to pick these up. - Wired up 6 session-lifecycle hooks:
UserPromptSubmit/SessionStart/SessionEnd/
PreCompact/Stop/SubagentStop— previously onlyPreToolUse/PostToolUse/
PermissionRequestwere wired, so a purely conversational turn that never called a tool left
zero audit trail. All 6 new hooks are pure audit trail, never confirm/block (lifecycle
events have no "allow/deny" semantics):UserPromptSubmitrecords the user's raw input (the
only hook that can show what the user actually asked Claude to do);SessionStart/
SessionEndrecord how a session started (startup/resume/clear/compact) and why it
ended;PreCompactrecords a marker right before a long session gets compacted (so audit
detail doesn't silently vanish with the compaction);Stop/SubagentStoprecord when the
main task or a subagent finishes.cc_monitor/hook.pygained 6 correspondinghandle_*
functions, andbin/CC-Monitor-hook <mode>gainedprompt/session_start/session_end/
precompact/stop/subagent_stopmodes;install.py'smerge_hooks()now takes an
extra_hooksdict to register them in bulk — existing users just need to rerun
install.py/install.shto pick these up (the already-installedPreToolUse/
PostToolUse/PermissionRequestentries are left untouched). Events land under
source="hook_prompt"(UserPromptSubmit) andsource="hook_lifecycle"(the other 5);
cc_monitor/format.pyandwebui/lib/format.jsgot matchingTOOL_LABELS/STAGE_LABELS/
describe()branches, andi18n.jsgot the matching bilingual labels — the home page's
"event type breakdown", the Log page, and terminalCC-Monitor tailall show these new
events out of the box, no further UI wiring needed (they reuse theeventstable's existing
generic source/tool_name/detail shape). Verified end-to-end against an isolated
CC_MONITOR_HOMEtest directory (stdin → SQLite for all 6 hooks), and confirmed the Python
and JSdescribe()implementations produce identical output.
完整更新日志见 CHANGELOG.md (English)。