Repository navigation
Releases: cn0xroot/CC-Monitor
Release list
v2.0.0
新增
-
跨工作目录行为检测(新模块
cc_monitor/workdir.py+policy.py新增
match: "workdir"规则类型):已有规则全是"对某个字段做正则",看不见 hook 输入里的
cwd,判断不了"这个路径相对当前项目在哪"。新模块把一次工具调用要碰的所有路径抠出来
(Read/Write/Edit/Glob/Grep 直接看file_path/path;Bash 复用split_shell_segments
按子命令拆、shlex认 token、~/$HOME/$PWD展开、同一条命令里的cd会改后续
相对路径的基准、>/>>重定向和rm/mv/cp/tee/sed -i/tar -xC/git clone
等识别为写、bash -c "..."递归拆),解析成绝对路径跟 cwd 比对,项目外的按位置分四档
(homeDotfile家目录隐藏文件、otherUserHome别的用户家目录、system系统目录、
otherProject其它)× 读/写。临时目录、/dev、系统目录里的解释器本身
(/usr/bin/python3 x.py)、Claude Codepermissions.additionalDirectories里用户
授权过的目录一律不报。evaluate()新增cwd参数,hook 和 rematch 都会传。 -
四条新默认规则(排在所有具体规则之后兜底,读
~/.ssh这类被更具体规则先命中的
仍算在原规则里):workdir_escape_write_sensitive(high/confirm,往家目录隐藏
文件/别人家目录/系统目录写)、workdir_escape_write_other(medium/log,往
其它项目目录写——真实历史里一个会话跨到旁边项目改几百个文件很常见,默认只记录,
想拦的话把 action 改成 confirm)、workdir_escape_read_sensitive(medium/log)、
workdir_escape_read_other(low/log)。每条都带scopes/access/ignore_paths
字段,用户可以在rules.json里自己调档位、加白名单。规则数从 70 增至 74。
claude 用 root 跑、项目却放在/home/<user>下的场景专门处理过:cwd 所在的那个
用户家目录也算"自己家",旁边的项目是otherProject,不会误判成"别的用户的家"。 -
跨工作目录检测的降噪(用 2500 多条真实历史事件做的调优):把"cd 进子目录后误报
整个项目"这一类彻底消掉(实测占误报的约七成),加上系统目录只读、Claude 自身状态目录
等豁免;剩下的基本都是真的跨出去了(比如在 A 项目目录里读写 B 项目的文件——这种即便
两个目录是同一份工作的不同副本,工具也无从判断,需要的话用规则的ignore_paths把
某个目录并进来)。- 按项目根算边界,不按 hook 给的 cwd:Claude Code 的 Bash 工具
cd进子目录之后,
hook 输入里的cwd也跟着变成子目录(比如proj/webui),这时候改proj/README.md
会被当成跨目录——占了原来七成的误报。现在从 cwd 往上找.git/.hg/.svn/
CLAUDE.md/.claude标记,取家目录之下最靠上的那一层当项目根。 - 只读系统目录里的读不报(
/usr、/lib*、/bin、/opt、/snap、/nix、
/System、/Library、/Applications,以及~/.cache):查共享库、看头文件、跑程序
都是读这些地方;往这些地方写照样按system档报。/etc、/proc、/var、/root这类
放配置/状态的目录读仍然报。 ~/.claude/projects整体忽略:Claude Code 自己的会话记录/自动记忆/todo,读写它们是
它正常工作的一部分;~/.claude下别的东西(settings、凭证)照常报。- 忽略目录/项目内的路径按字面判定、不追符号链接:
ln -sf /usr/lib/x.so /tmp/build/x.so
之前会被 realpath 解析成"往 /usr/lib 写"。 /本身归到system档(find /、ls /)。
- 按项目根算边界,不按 hook 给的 cwd:Claude Code 的 Bash 工具
-
CC-Monitor workdir子命令:按规则小计 + 逐条列出最近的跨工作目录文件操作。 -
审批弹窗用通俗易懂的内容概述需要审批的操作指令:74 条默认规则每条新增
title/
desc(及title_en/desc_en),用一句通俗的话说明这条规则拦的是什么操作、为什么
要确认(比如git_force_push→
"git 强制推送:会覆盖远程分支历史,别人已拉取的提交可能丢失")。AI 审批台的每张卡
先显示"需要确认:<title>",下面一行解释,规则 id 和工具名退到最后一行小字;Claude Code
原生权限确认(没命中规则)按工具名给一句说明;审批历史表的"命中规则"列也显示通俗标题
(悬停看 id)。终端里的确认提示和桌面通知同样带上 title/desc。新增/api/rules/meta
(webui/lib/rules.js读~/.cc-monitor/rules.json,用户改过的文案也能跟上)。
老用户的rules.json会通过默认规则同步机制自动补上这些字段;用户自定义的规则没写
title 就退回显示 id。 -
首页"跨工作目录操作"卡片 +
/api/drilldown/workdir-escape:跟"高级威胁检测"
同一套matched_rule归类/下钻模板,四个分类对应上面四条规则。 -
tests/test_workdir.py:分档、文件类工具、Bash 路径抽取(含 heredoc 正文/字符串
里的路径不误报、cd跟踪、同一路径写覆盖读)、规则映射与优先级的回归测试。 -
截图检测大幅扩充:原来只认
scrot/gnome-screenshot那一撮桌面截图工具,实测
394 条记录里 391 条其实是"读取图片文件",真正的截图命令只识别出 3 条。现在按三类覆盖:
宿主机屏幕(X11 / Wayland / macOS / Windows 截图工具、屏幕录制 ffmpeg x11grab 等、
framebuffer 直读、脚本里的ImageGrab.grab()/pyautogui.screenshot()/mss 等抓屏库)、
网页(headless 浏览器--screenshot、CDPcaptureScreenshot、Playwright/Puppeteer/
Selenium 的截图 API、shot-scraper/wkhtmltoimage/gowitness 等工具)、虚拟机与远程设备
(QEMUscreendump(HMP 与 QMP 两种形式)、virsh screenshot、VBoxManage、VMware、
VNC、adb shell screencap、idevicescreenshot、simctl)。识别数从 3 → 209,其中
QEMU 虚拟机截屏 174 条此前完全没有被检测到。误报为 0:命中前会剥掉
sudo/env/timeout/路径前缀等包装,grep/rm/ls等纯检索命令即使提到关键字也
不算,ImageMagick 的import必须带-window/-screen或图片文件名才算(避免撞上
Python 的import)。详情页新增按方式分类的小计与彩色徽章,真截屏不再被图片读取淹没。 -
账号信息一键打码:「Anthropic 账号 & 额度」分组新增"隐藏敏感信息"按钮,把姓名 /
邮箱 / 组织 / 组织角色 / 套餐 / 额度档位 / 计费方式 / 账号创建时间 / 订阅开始时间
全部显示成***,方便截图和演示。状态存本浏览器,只改显示、不动数据,额度百分比不打码。 -
额度进度条配色方案:10 种可选(默认 / 分段色阶 / 红黄绿三档 / 蓝青 / 紫粉霓虹 /
日落暖 / 森林绿 / 光谱 / 灰度 / 主题强调色),同时作用于额度卡、"已使用百分比"和
"重置时间"三种进度条;选默认时后两者保持原来的 severity 配色。 -
claude 进程明细增加信息:运行时间、模型 ID、事件数、内存占用(
ps的etime/rss,
POSIX 字段,Linux 与 macOS 通用;模型按 cwd 关联审计会话的 transcript 解析)。 -
被拦截的高危操作详情增加统计:累计 / 最近 24 小时 / 最近 7 天三张卡,外加按命中规则、
按工具、按工作目录三个维度的分类小计。
变更
-
首页改成紧凑仪表盘(先用 Claude Design 出稿确认方向再落地;配色/圆角/字号/卡片
边框全部沿用原来的主题变量,没有新颜色):- 顶部一条态势摘要:审计状态 + claude 进程数、已监测会话(附进行中的 Web UI 终端
会话数)、待审批(点击直达审批台)、已拦截 + 疑似绕过、单次额度剩余——原来这几个
"现在怎么样"的数字散在页面四处。 - 原来四条各带一段说明文字的控制条(审计开关/归档清空/同步更新/远程访问)收成一行
工具栏,说明文字进 ⓘ 悬停气泡(新增data-i18n-tip,中英文照常切换)。 - 每个分组改成短标题 + ⓘ,不再每组占一整行长说明;安全类六张红卡并排,卡片下多一行
小字提示覆盖范围(跨工作目录那张实时显示读/写/敏感位置计数,/api/overview新增
workdirEscapeBreakdown);活动/命令类用紧凑卡(1.5rem 数字),MCP/Glob·Grep/TodoWrite
三个低频计数合并成一张卡(每个数字仍各自可点开下钻);文件操作/软件安装收进两个盒子。 - 账号信息 + 额度卡 + 限额明细并成一行两栏。页面高度约减半,所有下钻入口和元素 id 都
保留(app.js 的绑定不受影响)。
- 顶部一条态势摘要:审计状态 + claude 进程数、已监测会话(附进行中的 Web UI 终端
-
其它七个标签页统一成同一套页头(
.page-head:短标题 + ⓘ 说明 + 行内控件):
状态信息页改成跟首页一样的两栏账号/额度布局,模型使用统计和会话状态用分组标题;
网络流量页的 GeoIP 精度徽章进页头、汇总卡改紧凑卡、地图图例移到分组标题右侧;
AI 审批台的桌面通知按钮进页头,审批历史表格时间/结果/处理方式列不再折行、长文本列
限宽省略(悬停看全文);Log 审计 / Claude Tap 的开关和会话下拉框收进页头,Claude Tap
没选会话时给一句提示而不是整页空白;终端会话侧栏标题同款;历史数据页头同款。
所有页面的长段说明文字都从正文移进 ⓘ。 -
下钻弹窗加载失败时给出说明:之前接口 404/无响应会一直停在"加载中…"(典型场景:
刚更新过代码但正在运行的 Web UI 服务还是旧版本、没有新路由);现在换成一句提示,
告诉用户重启 Web UI。 -
下钻弹窗放宽到 1400px:会话列表有 8 列(完整工作路径、36 位 UUID、时间范围…),
原来跟"选择文件夹"那种窄弹窗共用 760px,列头被压成一列一个字、UUID 折成四行,一屏
只看得到 10 行。现在单独放宽(一屏 24 行),窄屏改为横向滚动而不是继续挤;
"选择文件夹"弹窗保持 760px 不变。 -
事件列表分段上色:时间戳、文件夹名、session id、完整路径、命中规则各自配色(原来
整行灰字),并抽出共用函数统一了 5 处手拼代码;下钻列表的操作标签复用 Log 审计页已有的
按操作类型配色(读/写/编辑/Bash/删除),不再是没有颜色的裸标签。 -
生命体征指示器放大:心形 14→20px、心电图 40×14→54×20,颜色深浅(也就是"有多活跃")
在表格里终于看得出层次。 -
"已监测的 Claude Code 会话"后面加了 Claude 星芒标记(内联 SVG,无外部资源)。
-
规则文案回退:用户改过的规则不会被默认规则合并覆盖,于是拿不到新版加的
title/desc,审批台只能显示规则 id。展示时按 id 回退到内置默认文案(Python 与
Node 两侧),不写回用户的rules.json。
修复
- Web UI 终端会话的健康状态永远是灰色:
/api/sessions从来没返回statusAgoMs,
而vitalHeat()拿到undefined一律返回 0,导致颜色插值退化成纯灰、心电图拉直线,
不管会话多活跃都一样。现在按"PTY 最近有输出"和"该目录最近有审计事件"取更近的那个返回。 - 账号额度卡片"剩余 · 单次额度"在窗口刚到期时显示 0%:接口在 5 小时窗口过期、
还没有新请求刷新数字的那段时间里,仍会把上一个窗口末尾的用量(用满就是 100%)连同
已经过去的resets_at一起原样返回,前端按它算"剩余"就是 0%。现在resets_at已
过就按 0 已用处理,并把用量夹在 0~100 之间。
本版本还包含以下未单独发版的改动(1.8.0 / 1.7.2 / 1.7.1)
1.8.0
新增
- "高级威胁检测"首页卡片:复用
matched_rule架构(不重复写正则),把此前几批新加的
高危规则统一归类展示——cryptoMining(挖矿)、dbFileWrite(数据库任意文件写入)、
webshell(webshell 代码特征)、reverseEscapeShell(反弹/逃逸 shell)、
downloadExec(下载后执行)、c2Framework(C2 框架工具)、postExploitation
(后渗透/内网横向工具)、suspiciousMcp(可疑 MCP 工具名)、pentestRecon
(扫描/爆破工具)、covertTunnel(隐蔽隧道工具)十个分类,新增
/api/drilldown/advanced-threat。 policy.py的evaluate()新增field: "tool_name"支持:规则可以直接匹配
tool_name本身,不再局限于tool_input里的字段——MCP 工具调用的tool_name
是运行时才知道的动态字符串(mcp__<server>__<tool>),没法像 Bash/Write 那样枚举进
tools列表。- 新增
mcp_suspicious_tool_name规则(high/confirm):MCP 工具名里出现
reverse-shell/c2/beacon/backdoor字样就标出来——直接对应分析
AIPentest/CyberStrikeAI 时发现的反向 Shell MCP Server(官方文档明确支持接入 Claude
Code 的.mcp.json)。 - 新增
post_exploitation_tool_execution规则(high/confirm):
linpeas/netexec/bloodhound/sharphound/smbmap/rpcclient/enum4linux-ng
这几个后渗透/内网横向阶段的标准工具,c2_framework_execution加了pacu(AWS 云
攻击框架),pentest_recon_tool_execution加了 18 个子域名枚举/Web 模糊测试工具
(rustscan/amass/subfinder/ffuf/feroxbuster/dirsearch等)。 - 新增"逆向分析工具调用"首页卡片:识别 IDA/Ghidra/radare2/rizin/GDB 以及
Binary Ninja/Hopper/x64dbg/WinDbg/dnSpy/JADX/apktool/Frida/binwalk/checksec 等
一批逆向分析工具的调用,纯可见性统计(这些是专业逆向工程师/CTF/合规安全测试里的日常
工具,不代表风险,不参与 confirm/block)。 - 新增
shell_escape_via_utility规则(high/block,参考
GTFOBins):find -exec、
awk system()、perl exec、Python 的pty.spawn/os.system、
tar --checkpoint-action=exec、vim -c ':!sh'、zip --unzip-command、
script -c、ssh的ProxyCommand这几种"用一个看起来无害的日常工具逃逸出
shell"的经典手法,"高级威胁检测"同步加了reverseEscapeShell分类(把这条和已有的
reverse_shell_pattern归到一起)。规则数从 58 增至 70。 - 首页新增"同步更新"按钮:
POST /api/sync-update用execFile固定参数数组在
项目源码目录跑git pull(不经过 shell,不接受任何请求参数拼进命令行),把
GitHub 上的新代码/新规则同步下来;二次确认弹窗,把 git 自己的输出/报错原样展示,
不做任何自动冲突处理。 - 下钻详情统一视觉强化:GitHub/SSH/下载/Docker/压缩归档/网络诊断/逆向分析/
进程管理/敏感操作/敏感数据/高级威胁检测这十个共用同一套下钻模板的分组,事件明细
里的分类徽章和"行为操作"标签统一标红加粗。
修复
decision.submitted缺中英文翻译:UserPromptSubmit生命周期事件的
decision固定是"submitted",之前漏加了对应的 i18n key,中文界面下显示成了
英文单词。- 逆向分析工具调用分类器的几个精度问题(用真实历史数据发现的):真实场景里 IDA
几乎都是绝对路径调用(比如/opt/idapro-9.0/idat64),原来只认裸文件名开头会
全部漏检,补了路径前缀剥离;LD_LIBRARY_PATH=... gdb ...这种环境变量赋值前缀
也会把剥离逻辑带偏,补了环境变量赋值剥离;rizin 家族只认了rz-bin/rz-asm
两个子工具,扩到rz-\w+;frida-ls-devices这类多段连字符的 frida 子命令
漏匹配;新增 macOSopen -a "IDA Pro"等 GUI 启动方式识别。 env_dump规则过度匹配:env VAR=val VAR2=val2 command这种设置环境变量
启动子进程的常见写法(调试/构建脚本里很常见)被误判成"在 dump 环境变量",加了
负向前瞻排除掉赋值形式。用真实数据验证:某台机器上的历史误判从 134 条降到
14 条,v1.7.1 加的自动重判机制自动生效,未手动干预。- 归属地信息中文界面下显示英文(比如
Tseung Kwan O, HK):geoip.js之前
写死取 MaxMind 数据的英文名,改成跟着页面语言选(中文界面优先取
.names["zh-CN"]);另外新增一张 250 个国家/地区代码到中文名的静态映射表,
给 DB-IP Lite 这类没有多语言字段的扁平格式数据源兜底(国家/地区一级能翻,城市名
因为数据源限制没有中文版本,如实保留英文)。 shell_escape_via_utility规则设计过程中修了两个正则精度 bug:
script -qc /bin/sh这种合并短选项写法一开始没匹配上;
ssh -o ProxyCommand='ssh -W %h:%p jump' target(合法跳板机用法)被误判成
shell 逃逸——懒惰匹配的通配符吃到了 "ssh" 这个词自己的尾巴 "sh",加了词边界
卡死。
1.7.2
新增
- 参考 suricata-rules 分类思路新增 9 条规则:这是一份网络层面的
Suricata IDS 规则集,跟 CC-Monitor 不是同一层检测(那边看网络包内容,这边看 Claude
Code 自己发起的工具调用),规则文本本身不能照搬,但按它的目录分类找出了几个此前
完全没覆盖的攻击技术类别:crypto_miner_pool_domain_command/_write(medium/confirm):命令行或写入内容
里出现已知挖矿矿池域名(pool.minexmr.com/monerohash.com/xmrpool.eu等,从该仓库
Crypto_miner_pool目录提取)或stratum://协议 URI。db_arbitrary_file_write(high/confirm,...
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(same `medium...
v1.6.0
新增
- npm 安装统计新增本地安装识别,不再只认全局:
default_rules.json新增
npm_local_install规则(risk: low,action: log),匹配不带-g/--global的
npm install/npm i,排在已有的npm_global_install(risk: medium,
action: confirm)后面,全局规则先命中的命令不会重复计到本地这边。以前本地
npm install完全没有任何规则覆盖——不拦截、不记录、不统计,首页"软件安装
统计"里的"npm 全局安装"卡片对本地安装是彻底的盲区,即使npm install的
preinstall/postinstall生命周期脚本跟全局安装是同一个执行权限、同样是真实
存在的供应链投毒入口(event-stream、ua-parser-js这些真实事件都是本地安装
阶段就已经中招,不需要等到全局安装那一步)。首页卡片文案改成"npm 安装"(原来是
"npm 全局安装"),数字是本地+全局的合计;点开详情不会把两种混在一起平铺,而是
分成"全局安装"/"本地安装"两组分别列出——风险等级不一样的东西不该在界面上看起来
一样重。INSTALL_RULE_GROUPS.npm同步更新为两条规则的并集。用真实
WebSocket/无头浏览器验证过:首页卡片数字正确合并,点开详情两组分类完全正确
(sudo npm install -g pm2、npm install -g ccstatusline归到"全局",
npm install、npm install lodash --save、npm i react归到"本地")。已有用户
的~/.cc-monitor/rules.json是首次运行时拷的副本,不会自动更新——想要这条新
规则生效,删掉它让它重新生成,或者手动加进去。 - "终端会话"新增"新建窗口"按钮:跟"新建会话"共用同一个选工作目录的弹窗
(弹窗标题/说明文字跟着按钮动态换),唯一的行为区别是不会自动往新建的 PTY 里
敲claude\r——原来"新建会话"打开的窗口固定会自动进 Claude Code 会话,单纯
想要个终端跑跑脚本、看看文件的场景之前没有对应的入口。SessionManager. create()新增launchClaude(默认true,不传就是原来的行为)参数,
POST /api/sessions透传launchClaude: false即可跳过。语言切换时弹窗的
动态标题/说明文字也会跟着重新套用当前语言(复用syncGridToggleBtnText()
这些"状态相关文案"函数同一个模式,不能只靠data-i18n静态属性)。用真实
WebSocket 连接分别抓了两种模式下的终端输出对比:launchClaude:false只有
裸 shell 提示符,launchClaude:true能看到claude\r被写入终端。 - 世界地图新增"本机 ↔ 目的地"连线弧光点动画:参考
BeeEye(同一个作者的另一个项目)的
WorldMap.jsx直接移植——每条连接画一条从示意起点到目的地的二次贝塞尔弧线
(arc2d(),往极点方向鼓一点,有大圆航线的弧度感),头部带一个跑动的光点:
方向跟着这条连接上传/下载哪个字节数更多走(下载为主时光点从目的地往回跑,
推断出来的命令文本目标没有真实字节数,默认往外跑),2.2 秒一个周期,光点
用独立一次drawArrays批量画(复用目的地发光点同一套pointProg/
FRAG_POINT着色器),不是只给弧线本身调透明度——第一版只调透明度做的效果,
实测截图确认过肉眼基本看不出哪段更亮,加了这个独立光点才是真的"看起来在跑"。
"本机"在这张图上没有真实地理位置可言(这是运行 Claude Code 的这台机器,不是
流量真正经过的公网出口),固定钉在 (0, 0)(南大西洋几内亚湾外海,"Null
Island")——不会为了这个另外发请求去问"我的公网 IP 在哪",那样等于把用户的
真实位置发给了第三方,图例里明确标注这不是真实位置。WebGL2 不可用时自动退化
成 Canvas 2D 画同一套内容(海岸线/弧线/光点/发光点全都有,用
globalCompositeOperation='lighter'模拟 GL 那边的加法混合),地图不会因为
拿不到 WebGL2 就整个消失。用无头浏览器实测截图验证过两条渲染路径:GL 模式和
2D 模式下光点都正确沿弧线移动、方向都正确(上传为主的连接往外跑,下载为主的
从目的地往回跑)。 - 首页新增"SSH 操作统计"和"下载行为统计":SSH 操作拆成 ssh(远程登录/执行)/
scp(文件复制)/ sftp(文件传输)/ 密钥管理(ssh-keygen/ssh-copy-id/ssh-add/
ssh-agent)/ 其它(autossh/sshpass)五张卡片;下载行为拆成 wget / curl(只在
带-o/-O/--output落盘参数时才算,裸 curl 调 API 不算)/ aria2 / 其它
(axel/lftp/ftp/http)四张卡片。识别方式跟已有的 GitHub 操作统计
(classifyGithubOp)完全一致:按;/&/|/换行拆成子命令,只看子命令开头,
不对整条命令文本做子串匹配。新增webui/lib/audit.js的cc_ssh_op/
cc_download_opSQL 自定义函数,/api/drilldown/ssh-op/:type、
/api/drilldown/download-op/:type接口。 - "AI 轨迹"卡片和世界地图新增命令文本推断的网络目标:之前"AI 轨迹"完全依赖
系统层探针(eBPF/nettop)的实测数据,很多人从没手动启动过探针(需要额外
sudo ./bin/CC-Monitor-probe),即使 Claude 明明执行过一堆 wget/curl/git clone/
ssh/scp 这类联网命令,这张卡片和世界地图也一直是空的。现在会从这些命令的文本里
提取目标主机名(extractCommandHosts():URL 里的 host、user@host形式、
host:path形式,特意不认裸主机名——第一版曾经把curl -o out.tar.gz ...的
输出文件名、scp file.txt user@host:/path的本地源文件名都当成了"主机名",因为
这些字符串本身也是带点的、后面跟着空白,形状上没法用纯正则跟真主机名区分开,
收紧成"必须有@前缀或紧跟冒号"两种明确写法后才不再误判),惰性 DNS 解析
(dnscache.js,带缓存和超时,不在 hook 里做——避免拖慢每次 Bash 调用)+ 查
GeoIP,跟探针实测数据合并进同一套network.js的listTraffic()/summary()/
geoPairs()管线,用inferred:true标记,前端渲染成"推断"徽章(不会冒充成
探针确认过的真实流量——命令有没有真的连通、字节数多少都无法得知)。已实测验证
完整链路:真实 DNS 解析 github.com/example.com 到正确 IP、GeoIP 查到 Toronto/
Singapore、"AI 轨迹"下钻里 Session/文件夹信息正确关联到发起命令的会话。 - 首页新增"截屏审计"卡片:Claude Code 没有内置"截图"工具,识别靠三条独立信号——
① Bash 命令调用截图类 CLI 工具(scrot/gnome-screenshot/import/spectacle/
flameshot/maim/grim/xwd、macOS 的screencapture,以及 Wayland 下常见的
gdbus/dbus-send调用org.freedesktop.portal.Screenshot,判断方式跟已有的
commandDeletesFiles()一个思路,按;/&/|/换行拆成子命令分别看开头,不对
整条命令文本做子串匹配,避免echo输出内容被误判);②Read工具打开的文件
本身是图片(.png/.jpg/.gif/.webp/.bmp,范围比纯截图宽——用户明确要求
把"查看已有图片内容"也算进来);③ MCP/"computer use" 类工具的截图动作(工具名
带screenshot字样,比如 Playwright/Puppeteer 这类浏览器自动化 MCP server 暴露
出来的工具,或者 Anthropic Computer Use 的computer工具、action字段等于
"screenshot")。新增webui/lib/audit.js的cc_is_screenshotSQL 自定义函数、
/api/drilldown/screenshot接口。点开详情只显示命令/文件路径等基本信息,不
读取、不展示截图本身的图像内容——这是用户明确要的尺度,截图很可能带敏感桌面
信息,网页详情页不该把它 serve 出来。用隔离测试数据库 + 无头浏览器验证过端到端
链路:首页卡片计数正确、点击详情正确列出匹配事件、不匹配的普通命令(ls -la、
纯文本文件读取)正确排除在外。 - 新增 kill/pkill 监控进程检测规则:
default_rules.json新增两条规则——
kill_monitoring_process(risk: high,action: confirm)专门匹配
kill/pkill/killall后面跟着 CC-Monitor 自身进程名(探针二进制、
probe_linux.bt/probe_darwin.py、cc_monitor.probe、bpftrace)的命令——
这是"正在试图关掉安全监控本身"这个场景,用confirm而不是block,因为
正常维护/重启探针本来就是合理操作,不该被硬拦截,只是需要人确认一下;通用的
kill/pkill/killall(process_kill,risk: low,action: log)单独放
在后面兜底,只做记录不打扰,kill/pkill在日常开发里太常见,全部要求确认
会造成警报疲劳。两条规则插在disable_security_controls和sudo_pip_install
之间,规则顺序保证专门针对 CC-Monitor 自身进程的这条先匹配。已有用户的
~/.cc-monitor/rules.json是首次运行时拷的副本,不会自动更新,想要这两条新
规则生效,删掉它让它重新生成,或者手动加进去。 - 首页新增"Docker 操作统计":跟 SSH/下载行为统计同一套思路,按
;/&/
|/换行拆成子命令只看开头,拆成 run(启动容器)/ build(构建镜像)/ exec
(进入容器执行)/ compose(docker compose/docker-compose)/ 其它
(ps/logs/images等只读查看类)五张卡片。run/build/exec 单独拆出来是
因为这三个是"会执行任意外部镜像/Dockerfile/容器内命令",风险跟纯只读查看不是
一个量级。新增webui/lib/audit.js的classifyDockerOp/dockerOpsStats/
dockerOpsDetails、cc_docker_opSQL 自定义函数,/api/drilldown/docker-op/:type
接口。用隔离测试数据库验证过 9 条真实命令(含一条echo "docker run..."的
字符串输出,确认不会被误判成真的执行了 docker run)分类结果全部正确,用无头
浏览器截图验证过首页卡片计数和点击详情弹窗都正确。 - 敏感文件读取检测扩展到 Bash 命令:之前
sensitive_file_read规则只覆盖
Read工具直接打开.ssh//.aws/credentials/.env/私钥等敏感路径的场景,
用cat/less/head等 Bash 命令读同样的文件完全是盲区。新增
sensitive_file_read_bash(risk: medium,action: log)覆盖
cat/less/more/head/tail/strings/xxd/hexdump/od后面跟着这些
敏感路径的命令;另外新增env_dump(risk: low,action: log)记录
env/printenv/export -p这类会把当前进程全部环境变量(可能包含 API key/
token)打印到终端的命令。两条都插在已有的sensitive_file_read和
system_config_write之间。这几条规则复用了policy.py现有的re.search
纯正则匹配,没有像 webui 那边的splitShellSegments()一样做引号/heredoc
感知——这是 Python 策略引擎全部 30+ 条规则共有的既有特点,不是这几条新规则
引入的新问题,之后如果要修,得是对整个策略引擎的单独改造,不在这次范围内。
同样地,已有用户的~/.cc-monitor/rules.json不会自动更新,想要这两条新
规则生效需要手动同步。 - 新增
su/pkexec提权检测:之前只有sudo_usage这一条规则覆盖提权场景,
su/pkexec这两个跟sudo效果等价(切换/以其它用户身份执行命令,通常是
root)的提权方式完全漏检。新增su_pkexec_privilege_escalation
(risk: medium,action: confirm),只认命令开头或;/&&/||之后紧跟的
su/pkexec(写法跟已有sudo_usage的(^|;|&&|\|\|)\s*sudo\b完全一致),
不会把echo su这类字符串输出、或subprocess.run(...)里当成普通标识符出现的
"su" 误判成真的在提权。插在sudo_usage后面。 - 新增单文件
chmod 777(非递归)检测:原来chmod_world_writable_recursive
只认目标路径以/或~开头的写法,chmod_recursive_generic只认带-R的
调用——chmod 777 file.txt(相对路径、单文件、不递归)这种真实存在的场景恰好
两条都没覆盖到。新增chmod_777_single_file(risk: medium,action: confirm),
插在chmod_recursive_generic之后,靠规则顺序保证只在前两条都没命中时才轮到它
(带-R的、目标是//~开头的都已经在更早的规则里被更高级别处理过,不会
重复触发或降级)。 - 新增数据库直连破坏性命令检测:
mysql/psql/redis-cli/mongo/mongosh/
sqlite3这类数据库命令行客户端接DROP/DELETE/TRUNCATE(SQL)或
FLUSHALL/FLUSHDB(Redis)之前完全没有任何规则覆盖——能直接读写生产数据,
风险性质跟"删文件"是一回事,但完全在雷达外。新增db_destructive_command
(risk: high,action: confirm),插在history_tampering和
docker_privileged_or_host_mount之间。用 23 个正负测试用例验证过匹配和跟
既有规则的优先级交互(比如chmod -R 777 subdir正确命中更早的
chmod_recursive_generic而不是新规则,chmod 777 ~/.ssh/id_rsa正确命中
chmod_world_writable_recursive),node --test5/5 全部通过,不是回归。
这三条同样复用policy.py现有的纯正则匹配,没有 webui 那边的引号/heredoc
感知,理由跟上面sensitive_file_read_bash/env_dump一致;已有用户的
~/.cc-monitor/rules.json同样不会自动更新。 - 新增历史指令读取检测:
cat ~/.bash_history/cat .history这类读取 shell
历史文件的命令、以及直接执行裸history命令(会把当前会话的历史命令原样打印
到 stdout),之前完全没有规则覆盖——命令历史里经常留着过去输入过的密码/token
等明文凭据,是真实存在的信息泄露路径。新增history_read(risk: low,
action: log),覆盖cat/less/more/head/tail/strings读取
.bash_history/.zsh_history/.python_history/.mysql_history/
.psql_history/.node_repl_history/任意*.history文件、以及命令开头或
;/&&/||之后紧跟的裸history(history -c这种清空历史的破坏性用法
已经由更早的history_tampering规则单独覆盖,靠规则顺序 + 显式排除
(?!\s*-c\b)避免重复触发)。插在history_tampering之后。用 13 个正负
测试用例验证过匹配和优先级,不是回归。 - 加强反弹 shell / 后门执行检测:原来的
reverse_shell_pattern只认
nc ... -e /bin/sh这一种写法,覆盖面偏窄——真实攻击/红队工具箱里还有好几种
常见变体完全漏检:nc/ncat/netcat换用-c而不是-e(部分 nc 变体的
执行命令写法)、ncat/netcat这两个 nc 的别名工具本身没被认到、socat用
exec:目标做反弹 shell(很多加固过的系统没有带-e支持的 nc,socat是
最常见的替代品)、以及不带-e/-c参数、靠mkfifo建一个命名管道配合
nc+shell 手动拼出来的反弹 shell(更隐蔽,规避了对-e/-c参数的检测)。
扩展了reverse_shell_pattern的正则覆盖这四种新变体,risk/action维持
原来的high/block不变。用 17 个正负测试用例验证过匹配(含原有nc -e/
/dev/tcp/sh -i写法必须继续命中,不能因为改动引入回归;以及nc -zv/
nmap/ncat --ssl这类正常网络诊断用途不能被误伤)。范围上刻意不做的:
Python/Perl/PHP/Ruby 这类脚本语言的一行反弹 shell(python3 -c "import socket,subprocess...")——这类命令的"危险性"完全取决于脚本内容的
语义,纯正则匹配要么漏掉绝大多数变体、要么把大量合法用到socket模块的
脚本也拦下来,误报代价太高,不在这次范围内。 - 首页新增"压缩/归档操作统计":跟 SSH/下载/Docker 操作统计同一套思路,拆成
tar / zip(含 unzip)/ 7z / gzip(含 gunzip/zcat)/ 其它
(bzip2/xz/zstd/rar 等)五张卡片,按 Bash 命令文本识别。新增
webui/lib/audit.js的classifyArchiveOp/archiveOpsStats/
archiveOpsDetails、cc_archive_opSQL 自定义函数,
/api/drilldown/archive-op/:type接口。 - 首页新增"网络诊断工具统计":nc(含 ncat/netcat 别名)/ nmap / telnet /
其它(socat)四张卡片,纯粹是"用过这些工具没有"的可见性统计,跟上面的反弹
shell 风险判断是两回事——nc -zv example.com 443这种正常端口探测也会被计入
这张卡片,不代表危险。新增classifyNetdiagOp/netdiagOpsStats/
netdiagOpsDetails、cc_netdiag_opSQL 自定义函数,
/api/drilldown/netdiag-op/:type接口。 - 首页新增"进程管理/后台驻留统计":nohup / disown / 后台任务(命令末尾裸
&)/ 其它(setsid)四张卡片。前两个按子命令开头识别,跟其它分类器一个思路;
"后台任务"不一样——裸&不是某个命令的名字,是整条命令末尾的 shell 语法
标记,`spli...
v1.5.0
新增
-
AI 审批台接管 Claude Code 原生确认框:新注册
PermissionRequesthook(install.py
重跑会自动补上这一条,已有的两条不动)。以前审批台只镜像规则表里confirm的操作,
Claude Code 自己要弹的 "Do you want to proceed?"(没命中任何规则的那些)在网页上完全
看不到——PreToolUse阶段根本不知道它接下来会不会弹。现在这类询问以kind='permission'
出现在审批台,按钮跟 confirm 一样(允许/拒绝/10、30 分钟/一直允许,"一直允许"按
session + 工具名记),网页或终端给了答案就通过hookSpecificOutput.decision.behavior
替用户答掉;没人答(90s 超时)或终端里敲回车,hook 静默退出、原生确认框照常弹出。
审批历史新增 "已转回原生确认框" 状态。 -
macOS 系统层探针(网络部分):新增
cc_monitor/probe_darwin.py,bin/CC-Monitor-probe
在 macOS 上自动切过去。用系统自带的nettop -d -L 0每 2 秒采样 claude 进程树(claude- 子进程,每次采样重新算)每条连接的远端 IP:port 和上传/下载字节增量,写进跟 Linux
探针同一张network_traffic表和同一种os_net事件,网络流量页/世界地图/AI 轨迹在
Mac 上不再永远是空的。不需要 root。没有execve观测(verify的绕过检测仍是 Linux
独有),域名只能靠反向 DNS 兜底。SIGTERM 时会把 nettop 子进程一起收掉。
Web UI 上网络页的几条提示文案改成会告诉你"去跑 bin/CC-Monitor-probe",并提到走本地
代理时远端全是 127.0.0.1、地图上不会有点这种情况。
- 子进程,每次采样重新算)每条连接的远端 IP:port 和上传/下载字节增量,写进跟 Linux
-
install.sh自动安装并接线 ccstatusline:新增第 1 步,检测系统上有没有
ccstatusline(command -v ccstatusline),
没有就npm install -g ccstatusline;装好后如果~/.claude/settings.json里还没有
statusLine配置,install.py的configure_statusline()会自动写入一份(command: "ccstatusline",padding: 0,refreshInterval: 10)。两个条件都要满足才会真的写——
没装就不接(接了也是空跑),已经有statusLine(不管是不是 ccstatusline、不管什么参数)
就绝不覆盖,尊重用户已有的定制。--skip-ccstatusline/CC_MONITOR_SKIP_CCSTATUSLINE=1
能把安装和接线两件事一起跳过(内部会转成install.py --skip-statusline透传下去)。
GeoIP 数据库下载顺延成第 5 步。 -
install.sh第 5 步自动下载 GeoIP 数据库:DB-IP Lite(CC BY 4.0,约 60MB)下到
~/.cc-monitor/dbip-city.mmdb(尊重CC_MONITOR_HOME)。已有任何.mmdb就跳过;
先下到.part再改名、小于 1MB 视为失败并删掉,不会留半截文件让 geoip.js 静默打不开;
失败只提示不中断。--skip-geoip/CC_MONITOR_SKIP_GEOIP=1跳过,CC_MONITOR_GEOIP_URL
换镜像。 -
system_package_install规则覆盖 macOS 的包管理器:brew install/reinstall/uninstall/ remove/rm/upgrade/tap/untap/bundle(含brew cask …)和 MacPorts 的port [-flags] install/uninstall/upgrade/activate/deactivate/selfupdate(port installed、lsof -i :port
这类不会误命中)。首页"软件安装统计"的"系统包管理器"卡片标签同步。顺手修了pacman -Syu不命中的问题(原正则要求 S/R 是最后一个字母)。已有用户的~/.cc-monitor/rules.json
是首次运行时拷的副本,不会自动更新——想要新规则的话删掉它让它重新生成,或者手动改。
修复
- 桌面版(Electron)在 macOS 上 AI 审批台完全没有提醒:页面里走的是浏览器 Notification
API,Electron 渲染进程里Notification.permission永远是 "granted"、new Notification()
也不报错,但底层 UNUserNotificationCenter 对未正式签名的 app(npm run electron用的
node_modules 里那个 Electron.app 只有 ad-hoc 签名)直接拒绝,实测主进程侧是 failed 事件
UNErrorDomain error 1(NotificationsNotAllowed),渲染进程侧静默失败。改为主进程自己
每 2 秒轮询pending_approvals:新请求 →shell.beep()+ Dock 图标跳动 + 角标数字(这
三样不需要任何授权),能弹系统通知就一并弹(点击跳到审批台),弹失败只警告一次不再重试。
页面按 UA 识别 Electron 后关掉浏览器那条路,按钮改成说明文字,避免两边重复。 - Web 终端"新建会话"直接 500(
posix_spawnp failed.):node-pty 靠
prebuilds/<平台>/spawn-helper这个小程序起 pty,npm 解包时(实测 macOS + npm 11)
会丢掉它的可执行位。新增webui/scripts/fix-node-pty-perms.js,作为postinstall
跑一次,lib/sessions.js加载时再幂等地跑一次(打包后没有 postinstall)。 - macOS 上额度/套餐永远显示"没找到 Claude Code 的登录凭证":Claude Code 在 macOS 上
不写~/.claude/.credentials.json,OAuth 凭证存在登录钥匙串里(service
Claude Code-credentials,内容是同一份 JSON)。全新的 Mac 必现。新增
webui/lib/credentials.js:先读文件,没有再security find-generic-password -s "Claude Code-credentials" -w读钥匙串,usage.js/account.js共用。跟有没有装
ccstatusline 无关——我们不调用它,只是复刻它的读取逻辑(它在 macOS 上走的正是钥匙串)。 - macOS 上终端里永远看不到 CC-Monitor 的
[y/N]确认提示:notify.py用文本模式
open("/dev/tty", "r+"),这会套一层要求可 seek 的 BufferedRandom;Linux 上对 tty
lseek返回 0 所以没事,macOS 返回ESPIPE,open()直接抛io.UnsupportedOperation
(OSError子类)被静默吞掉,tty 一直是 None,只剩网页一条路。改成"r+b", buffering=0
的裸 FileIO。 - 终端处于 raw 模式时敲 y/N 可能把 hook 卡死:Claude Code 的 TUI 让终端处于 raw 模式,
回车发过来的是\r不是\n,原来的readline()会一直等那个不来的换行,连网页那条
路的轮询也一起卡住。改为直接读当前可读的字节、看首字符。
English
Added
-
AI Approvals now covers Claude Code's native permission dialog: a new
PermissionRequesthook is registered (re-runninginstall.pyadds it; the existing two
entries are left alone). Previously the approvals page only mirrored operations our rule
table flags asconfirm; Claude Code's own "Do you want to proceed?" prompts (calls that
matched no rule) never showed up — atPreToolUsetime there is no way to know whether a
prompt is coming. These now appear askind='permission'with the same buttons as confirm
(allow / deny / 10 or 30 min / always — "always" is remembered persession + tool name).
An answer from the web page or the terminal is returned through
hookSpecificOutput.decision.behavior; if nobody answers (90s timeout) or Enter is
pressed at the terminal, the hook exits silently and the native dialog appears as usual.
Approval history gains a "Handed back to native dialog" status. -
macOS system-layer probe (network part): new
cc_monitor/probe_darwin.py;
bin/CC-Monitor-probeswitches to it automatically on macOS. It samples the claude process
tree (claude + descendants, recomputed each sample) every 2s with the built-in
nettop -d -L 0, recording each connection's remote IP:port and upload/download byte
deltas into the samenetwork_traffictable andos_netevents the Linux probe uses, so
the traffic page / world map / AI trajectory are no longer permanently empty on a Mac. No
root required. Noexecveobservation (the bypass detection behindverifystays
Linux-only); hostnames fall back to reverse DNS. SIGTERM also tears down the nettop child.
The network page hints now tell you to runbin/CC-Monitor-probe, and mention that with a
local proxy every remote is 127.0.0.1 so the map has nothing to plot. -
install.shnow installs and wires up ccstatusline: new step 1 checks whether
ccstatusline is already onPATH
(command -v ccstatusline) and runsnpm install -g ccstatuslineif not; once it's
available,install.py'sconfigure_statusline()writes astatusLineentry into
~/.claude/settings.jsonif one isn't already there (command: "ccstatusline",
padding: 0,refreshInterval: 10). Both conditions have to hold before anything is
written — not installed means nothing to wire up, and an existingstatusLine(whether
it's ccstatusline or something else, with any settings) is never overwritten, respecting
whatever the user already configured.--skip-ccstatusline/
CC_MONITOR_SKIP_CCSTATUSLINE=1skips both the install and the wiring (internally passed
through asinstall.py --skip-statusline). The GeoIP download step is renumbered to 5. -
install.shstep 5 downloads the GeoIP database: DB-IP Lite (CC BY 4.0, ~60MB) to
~/.cc-monitor/dbip-city.mmdb(honoursCC_MONITOR_HOME). Skipped if any.mmdbis
already present; downloads to a.partfile first and treats anything under 1MB as a
failure (deleted), so a truncated file can never make geoip.js fail silently; a failed
download warns without aborting.--skip-geoip/CC_MONITOR_SKIP_GEOIP=1skips it,
CC_MONITOR_GEOIP_URLpoints at a mirror. -
system_package_installrule now covers macOS package managers:brew install/reinstall/uninstall/remove/rm/upgrade/tap/untap/bundle(incl.brew cask …) and
MacPortsport [-flags] install/uninstall/upgrade/activate/deactivate/selfupdate(things
likeport installedorlsof -i :portdon't match). The "system package manager" card on
the home page is relabelled accordingly. Also fixedpacman -Syunot matching (the old
regex required S/R to be the last flag letter). Note that an existing
~/.cc-monitor/rules.jsonis a copy made on first run and is not updated automatically —
delete it to regenerate, or edit it by hand.
Fixed
- Desktop app (Electron) on macOS gave no alert at all for AI Approvals: the page used
the browser Notification API; in an Electron rendererNotification.permissionis always
"granted" andnew Notification()doesn't throw, but the underlying
UNUserNotificationCenter refuses apps that aren't properly signed (npm run electronruns
the ad-hoc-signed Electron.app from node_modules) — the main-process side gets afailed
event withUNErrorDomain error 1(NotificationsNotAllowed), the renderer side fails
silently. The main process now pollspending_approvalsevery 2s itself: a new request →
shell.beep()+ Dock bounce + badge count (none of which need permission), plus a system
notification when possible (click → approvals tab); a failed notification warns once and
isn't retried. The page detects Electron via UA, disables the browser path and turns the
button into an explanatory label so the two never double-notify. - Web terminal "new session" returned 500 (
posix_spawnp failed.): node-pty spawns
the pty through its bundledprebuilds/<platform>/spawn-helperbinary, and npm dropped
its executable bit when unpacking (seen on macOS + npm 11). New
webui/scripts/fix-node-pty-perms.jsruns aspostinstalland again, idempotently, when
lib/sessions.jsloads (packaged builds have no postinstall step). - macOS always showed "Claude Code credentials not found" for usage/plan: on macOS
Claude Code does not write~/.claude/.credentials.json; the OAuth credential lives in
the login Keychain (serviceClaude Code-credentials, same JSON payload). Every fresh Mac
hit this. Newwebui/lib/credentials.jsreads the file first and falls back to
security find-generic-password -s "Claude Code-credentials" -w; shared byusage.js
andaccount.js. Unrelated to whether ccstatusline is installed — we never invoke it, we
only replicate its lookup (which uses the Keychain on macOS too). - macOS never showed CC-Monitor's
[y/N]prompt in the terminal:notify.pyopened
/dev/ttyin text mode"r+", which wraps aBufferedRandomthat requires a seekable
stream;lseekon a tty returns 0 on Linux butESPIPE...
v1.4.3
新增
- 发布编译好的 Linux x64 二进制:GitHub Release 现在附带打包好的
CC-Monitor-*.AppImage,配一个cc-monitor-start.sh启动包装脚本——AppImage
是单个可执行文件,双击/直接运行时不经过任何 npm 脚本,v1.4.2 里给npm run electron用的 node launcher 在这种运行方式下用不上(那个 launcher 依赖的是
npm 脚本这一层,AppImage 根本不经过它)。这个脚本按运行身份决定要不要给
AppImage 加--no-sandbox --disable-gpu-sandbox,是 root 才加,其它身份原样
启动,沙箱保护不受影响。已实测验证:直接双击/裸跑 AppImage 在 root 下依旧会
FATAL 退出,必须用这个包装脚本或者手动加这两个参数启动。
修复
electron-main.js里遗留的 root 沙箱兜底代码其实完全不起作用:v1.4.2 里
留了一段"就算 launcher 没生效,这里再兜底加一次--no-sandbox"的代码,实测
验证发现这段 JS 根本没有机会在 Chromium 的原生 FATAL 检查前执行到——这个检查
发生在 Electron 启动的原生阶段,比electron-main.js里任何一行 JS(包括文件
最开头)都早,连"在最开头用 Node 自己重新拉起带参数的自己"这种自愈写法都测过,
同样来不及。删掉了这段无效代码,改为纯注释说明:这个开关只能从外部、在真正
spawn 出 electron 进程之前带入 argv,npm run electron走
scripts/electron-start.js,打包后的二进制走上面新增的cc-monitor-start.sh。
English
Added
- Published a compiled Linux x64 binary: the GitHub Release now ships a built
CC-Monitor-*.AppImagealongside acc-monitor-start.shlauncher wrapper.
An AppImage is a single self-contained executable — double-clicking or running
it directly bypasses any npm script, so the Node launcher added in v1.4.2 for
npm run electron(which relies on that npm-script layer) never gets a chance
to run for the packaged binary. This wrapper checks the caller's uid and only
appends--no-sandbox --disable-gpu-sandboxwhen actually running as root,
leaving the sandbox untouched for everyone else. Verified: running the raw
AppImage directly as root still FATAL-exits — the wrapper (or passing those
flags manually) is required.
Fixed
- Leftover root-sandbox fallback code in
electron-main.jsdid nothing:
v1.4.2 kept a "just in case the launcher didn't apply it, add--no-sandbox
here too" block. Testing showed this JS never gets a chance to run before
Chromium's native FATAL check fires — that check happens at Electron's native
startup stage, earlier than any JS inelectron-main.js, even the very first
line of the file; even a self-re-exec pattern placed at the top of the file
was too late. Removed the dead code and replaced it with a comment explaining
that this flag can only be supplied from outside, before the electron process
is spawned —npm run electrongoes throughscripts/electron-start.js, and
the packaged binary goes through the newcc-monitor-start.shabove.
完整更新日志见 CHANGELOG.md (English)。
v1.4.2
修复
npm run electron在 root 下无法启动:Chromium 在原生启动阶段就会检查是否
以 root 身份运行且没带--no-sandbox,不满足直接 FATAL 退出——这个检查比
electron-main.js里任何 JS 代码都先执行,运行时用app.commandLine. appendSwitch()加这个开关完全没用,必须在真正 spawn electron 二进制那一刻
的进程参数(argv)里就带上。解决这层后又暴露第二层:GPU 进程有自己独立的
沙箱,root 下同样会失败(GPU process isn't usable. Goodbye.),还得加
--disable-gpu-sandbox。新增了一个小 launcher 脚本(webui/scripts/ electron-start.js),按process.getuid()判断身份,只有真的是 root 时才
附加这两个开关,非 root 用户运行时完整沙箱保护不受任何影响。已在当前 root
环境下用wmctrl/xdotool验证真实起了标题为 "CC-Monitor" 的独立窗口。
English
Fixed
npm run electronfailed to launch under root: Chromium checks whether it's
running as root without--no-sandboxat native startup and FATAL-exits if so —
this check runs before any JS inelectron-main.jsexecutes, so setting the flag
at runtime viaapp.commandLine.appendSwitch()does nothing; it has to be present
in the actual process argv at the moment the electron binary is spawned. Fixing
that surfaced a second layer: the GPU process has its own independent sandbox and
fails the same way under root (GPU process isn't usable. Goodbye.), requiring
--disable-gpu-sandboxas well. Added a small launcher script
(webui/scripts/electron-start.js) that checksprocess.getuid()and only
appends these two flags when actually running as root — non-root users keep the
full sandbox untouched. Verified under the current root environment with
wmctrl/xdotoolthat a real window titled "CC-Monitor" comes up.
完整更新日志见 CHANGELOG.md (English)。
v1.4.1
修复
- 界面字号设置之前不是真正全局生效:样式表里 62 处按钮/卡片数字/表格文字等
的字号是写死的 px,只改body自己的font-size根本碰不到它们,调节滑块时
页面看起来"没什么变化"。修复过程中还发现一个连带问题:把这 62 处全部换成相对
根字号的rem单位后,第一版验证时发现即使 CSS 变量本身已经正确更新,
<html>的实际字号还是纹丝不动——原因是html, body { ... font-size: 1rem }
这条联合选择器规则会同时套到html和body头上,而1rem用在根元素自己
身上不是"相对自己"、是相对浏览器默认的 16px,这条规则排在后面,把专门给
html写的字号规则覆盖掉了。现在用全新浏览器验证过:默认 14px 时按钮是
13px、卡片数字 28px;调到 18px 后按钮变成 16.7px、卡片数字变成 36px——整站
所有文字同比例联动缩放。 - 界面字体设置对导航栏/按钮类文字不生效:
<button>/<select>/<input>/
<textarea>这几个表单控件元素,浏览器自己的默认样式表压根不会让它们继承正文
的font-family(会摆烂用系统 UI 控件字体,实测是 Arial),这是每个浏览器的
标准行为,不是继承链断了——"首页"/"状态信息"这些导航按钮、"新建会话"这类按钮
换字体跟没换一样,根因就是这个。加一条button, input, select, textarea { font-family: inherit; }就解决了,截图验证过导航文字确实跟着变成楷体的手写
笔画风格。
新增
- 界面字体新增三个中文选项:楷体、黑体(思源黑体)、宋体(思源宋体)。没有
把字体文件打包进项目——完整 CJK 字库一个 17~21MB,直接打包会让首次切字体的
下载很慢——改成纯字体名引用,访问者的系统/浏览器本地装了对应字体就会生效
(这台 Linux 机器本身已经装了 AR PL UKai/UMing、Noto Sans/Serif CJK、霞鹜文楷
这些开源中文字体,同机或同局域网内访问能直接命中;Windows 的"微软雅黑"/
"宋体"、Mac 的"苹方"/"华文楷体"也在各自字体栈里)。没有加"柳体"——系统上没有
真正的柳公权风格书法字体,网上找到的几款标榜"柳体"的免费字体授权条款未经
核实,不确定能不能安全打包,先跳过。
English
Fixed
- The interface font-size setting wasn't actually global: 62 places in the stylesheet
(buttons, card numbers, table text, …) had hardcoded pixel font-sizes, so changing only
body's ownfont-sizenever touched them — the slider looked like it barely did
anything. Fixing it surfaced a second, related bug: after converting all 62 torem
units relative to the root, the first verification pass showed the CSS variable updating
correctly while<html>'s actual font-size never moved — because the combined
html, body { ... font-size: 1rem }rule applies to both elements, and1remon the
root element itself doesn't mean "relative to itself," it resolves against the browser's
16px default; that rule came later in the cascade and silently overrode the dedicated
htmlfont-size rule. Verified in a fresh browser profile: at the 14px default, buttons
render at 13px and card numbers at 28px; at 18px, buttons become 16.7px and card numbers
36px — every piece of text scales together now. - The interface font setting had no effect on nav-bar/button text: browsers'
built-in default stylesheet never lets<button>/<select>/<input>/<textarea>
inherit the surroundingfont-family— they fall back to the OS's native UI control
font instead (Arial, in practice), and that's standard behavior in every browser, not a
broken inheritance chain. So switching fonts did nothing for the "Home"/"Status" nav
buttons or things like the "New Session" button. Fixed with a single
button, input, select, textarea { font-family: inherit; }rule — verified via
screenshot that the nav text now actually picks up Kaiti's handwritten brush strokes.
Added
- Three new Chinese font options in the interface font setting: Kaiti (calligraphy-
style), Heiti (Source Han Sans), and Songti (Source Han Serif). No font files were
bundled into the project — a full CJK glyph set runs 17–21MB each, and bundling would
make the first font switch painfully slow to download — so these are plain font-name
references instead; they render correctly wherever the visitor's system/browser already
has a matching font installed (this Linux machine itself has AR PL UKai/UMing, Noto
Sans/Serif CJK, and LXGW WenKai, so same-machine or same-LAN access picks them up
directly; Windows's "Microsoft YaHei"/"SimSun" and macOS's "PingFang SC"/"STKaiti" are
also in the respective stacks). A "Liu style" (柳体, Liu Gongquan calligraphy) option was
not added — no such font is installed on this system, and the free fonts online claiming
that style have unverified licensing, so bundling one wasn't safe to do without
confirmation.
完整更新日志见 CHANGELOG.md (English)。
v1.4.0
新增
- 外观设置弹窗(顶栏 ⚙ 按钮):主题配色改成可视化色块网格(10 个主题各自的
强调色圆点 + 高亮选中态),新增界面字体(系统默认/等宽/衬线/圆体)和
界面字号(12~18px 滑块)两个之前完全没有的设置项,弹窗里带实时预览文字,
两个新设置存localStorage,刷新后保留;原来顶栏那个主题下拉框还在,两边保持
同步,不是二选一的替换关系。 - 单次额度(5 小时窗口)改成显示"剩余百分比"(原来是"已使用"),颜色换成
conky 风格的分段色阶——5% 以下很红、85% 以上很绿,中间每 10% 一档,固定鲜艳色值,
不跟主题走(这是专门给这一个进度条用的,跟其它额度条的连续渐变是两套独立配色)。
其它额度条(周额度全部模型/Sonnet/Opus/Fable)颜色改成红→黄→绿连续渐变,换算
成"健康度"(已用越多越红),渐变的三个锚点直接读当前主题的--red/--yellow/
--green,换主题这套颜色也跟着联动。所有额度卡片的文字标签都补上"已用"/"剩余"
前缀,不会再看不出这个百分比到底是哪个方向。 - Fable 模型的额度信息:Anthropic 的用量接口没有给 Fable 专门的顶层字段(不像
Opus/Sonnet 那样有seven_day_opus/seven_day_sonnet),额度只挂在limits[]
里一条kind="weekly_scoped"的记录上——现在会动态从这条记录里摘取模型名和额度,
不写死"Fable",以后 Anthropic 加别的按模型限额也能自动跟着显示出来。 - 状态信息页新增"模型使用统计"表格:按模型(Sonnet/Opus/…)汇总所有已监测会话
的 token 用量(输入/输出/缓存/总计),同一 session 中途换过模型的话分开算。 - 状态信息页新增"额度明细(limits)"表格(跟首页那份同一份数据),进度条也用
加长加粗的版本;"重置时间"那一列新增一条紫色的"这个窗口已经过去多少"进度条。 - 状态信息页会话状态行新增"上下文窗口使用率"和"Context compaction 次数":前者
是当前对话实际带着的上下文大小占比,按 200K 标准上下文窗口估算(Claude Code 只在
它自己的 statusLine 输入里才带精确窗口大小,我们的 hooks 拿不到,这是近似值,
UI 上用 hover 提示说清楚);后者是真的从 transcript 里检测
type=system, subtype=compact_boundary事件数出来的,含自动/手动次数拆分和
累计精简的 token 数,不是估算。 - 网络流量域名捕获改用
uprobe:libc:getaddrinfo:之前只对连上的 IP 做事后反向
DNS(PTR 记录),很多云厂商/CDN 出口 IP 根本没配 PTR,反向解析注定拿不到域名
(实测 Anthropic 自己的 API IP 就是这样);现在改成在应用层调用getaddrinfo()
解析域名的那一刻就用 uprobe 记下来,按 pid 存最近一次问的域名,接到 CONNECT 事件
时直接查表——域名从连接真正发生之前就已经知道了,不管出口 IP 有没有配 PTR 都能
拿到(用一个改名成 "claude" 的可执行文件模拟真实调用链,实测抓到了 Cloudflare
出口 IP 上的example.com,验证有效)。 - 网络流量页"连接次数"可以点开看明细:每个目标地址一行的连接次数、以及页面
顶部"总连接次数"/"不同 IP 数"两张汇总卡片,现在都能点开看这个目标/全部目标的
每次连接时间、发起进程、PID。 - 首页新增"GitHub 操作统计":git push / git clone / git commit / git pull-fetch
/ gh CLI(PR/Issue/API…)/ 其它 git 操作六张卡片。大部分 git/gh 命令本来就不会
命中任何规则(不违反任何 policy,压根不会打上matched_rule),没法像软件安装
统计那样直接复用规则引擎的判断结果,改成新写一个按子命令分类的识别函数(;/
&/|/换行 切开分别看开头,避免把echo "git push 很危险"这种字符串输出也
当成真的执行了 git push),注册成 SQLite 自定义函数cc_github_op()直接在查询
里用。点开每张卡片能看到具体是哪个 session、哪个文件夹、什么时候、执行的什么
命令(复用文件操作/软件安装统计已经在用的下钻渲染逻辑)。
修复
- 额度明细(limits)表格里"已使用百分比"和"级别"两列文字重叠——加长版进度条用
width: 100%在表格的自动布局里跟其它列打架(百分比宽度的 flex 子元素不参与
表格列的内在宽度计算,列被判定得很窄,导致条又想撑满又撑不开),改成固定
220px宽度,两边都不会再互相挤。 - Log 审计 / Claude Tap 的内容列表容器之前写死
max-width: 900px(明明叫
".wide"却限死宽度),宽屏下右边留一大截空白、滚动条也卡在页面中间不上不下,
去掉这个限制改成撑满可用宽度。 - 首页"Claude Code 运行身份检测"和"Anthropic 账号信息"标题旁边两句冗长的说明
文字按反馈精简掉了。
English
Added
- Appearance settings dialog (⚙ button in the top bar): the color theme picker is now
a visual swatch grid (each of the 10 themes shown as its own accent-color dot, with an
active highlight) instead of only a plain dropdown; added interface font (system
default / monospace / serif / rounded) and interface font size (12–18px slider) —
neither existed before. The dialog includes a live preview. Both new settings persist to
localStorageand survive a reload; the original top-bar theme dropdown still works too
and stays in sync, it's not a replacement. - Session quota (5-hour window) now shows "remaining %" (was "used"), colored with a
conky-style stepped palette — very red below 5%, very green above 85%, one fixed vivid
color step per 10% in between. The other quota bars (weekly all-models/Sonnet/Opus/Fable)
switched to a continuous red→yellow→green gradient computed from "health" (more used =
more red), with the three gradient anchors read live from the current theme's
--red/--yellow/--greentokens so it re-colors on a theme switch. Every quota card's
label now carries an explicit "Used"/"Remaining" prefix. - Fable model quota: the usage API has no dedicated top-level field for Fable (unlike
Opus/Sonnet, which getseven_day_opus/seven_day_sonnet) — its quota only shows up as
onekind="weekly_scoped"entry insidelimits[]. This is now extracted dynamically by
model name rather than hardcoding "Fable", so any future per-model quota Anthropic adds
shows up automatically too. - Model Usage table on the Status page: token usage (input/output/cache/total) summed
per model (Sonnet/Opus/…) across every monitored session; a session that switched models
mid-way counts separately per model. - Limits table on the Status page (same data as the Home page's), with a longer, bolder
progress bar; the "Resets" column gained a purple "how far through this window" progress
bar. - Context window usage % and Context compaction count on each session row on the Status
page: the former is how much context the conversation is actually carrying right now,
estimated against a standard 200K context window (Claude Code only reports the exact
window size to its own statusLine input, which our hooks don't receive — this is an
approximation, called out with a hover hint); the latter is a real detection of
type=system, subtype=compact_boundaryevents in the transcript, with auto/manual counts
split out and cumulative tokens reclaimed — not an estimate. - Network domain capture switched to
uprobe:libc:getaddrinfo: previously relied on
reverse DNS (a PTR lookup) after the fact, which fails for most cloud/CDN egress IPs that
never had a PTR record configured (confirmed on Anthropic's own API IP, among others); now
the actual hostname the application asked to resolve is captured the moment it calls
getaddrinfo(), keyed by pid, and looked up when the matching CONNECT event fires — the
domain is known before the connection even happens, regardless of whether the egress IP
has a PTR record (verified live against a Cloudflare-fronted IP forexample.comusing an
executable renamed to "claude" to simulate the real process-tree ancestry check). - "Connections" is now clickable on the Network tab, both per target-row and on the two
summary cards ("Total connections" / "Distinct IPs") — opens a detail list of every
connection's time, originating process, and PID. - New "GitHub Operations" stats on the Home page: git push / git clone / git commit /
git pull-fetch / gh CLI (PR/Issue/API…) / other git operations, six cards. Most git/gh
commands don't violate any policy rule, so they never get amatched_ruleand couldn't
reuse the same trick as the install-ops stats — classification is done with a new
per-sub-command matcher (split on;/&/|/newline and check each segment's start, so
echo "git push is dangerous"doesn't get miscounted as a realgit push), registered as
a SQLite custom functioncc_github_op()used directly in the query. Each card drills down
to the exact session, folder, timestamp, and command (reusing the same detail rendering
already used by file-ops/install-ops).
Fixed
- The limits table's "Used %" and "Severity" columns visually overlapped — the enlarged
progress bar usedwidth: 100%, which doesn't participate in a table's automatic column
sizing (a percentage-width flex child contributes no intrinsic width, so the column was
sized far too narrow while the bar still tried to fill 100% of it), causing it to spill
into the next column. Switched to a fixed220pxso neither column crowds the other. - The Log Audit / Claude Tap content-list containers had a leftover
max-width: 900px
(despite the class being named ".wide") that left a huge dead gutter on wide screens with
the scrollbar stranded mid-page — removed, now fills the available width. - Trimmed two overly long hint captions next to "Claude Code identity check" and "Anthropic
account info" on the Home page.
完整更新日志见 CHANGELOG.md (English)。
v1.3.2
新增
- Anthropic 账号信息新增姓名/邮箱/组织/套餐:不是新的网络请求,是 Claude Code
自己维护的本地全局配置文件~/.claude.json(oauthAccount字段)——反编译
ccstatusline 的 "Claude Account
Email" 挂件确认的路径和字段名。新增字段:姓名、邮箱、组织名称、组织角色、套餐类型
(比如claude_max)、组织额度档位、计费方式、账号创建时间、订阅开始时间。首页放在
"检测到的 claude 进程"下面,状态信息页放在"账号额度"前面。 - AI 审批台新增历史记录:
pending_approvals表本来就没有任何清空逻辑(首页"清空
当前数据"按钮只清events表),所以历史记录天然就是长期保存的,这次只是把它显示
出来——时间、Session、工具、命中规则、匹配内容、结果、处理方式。对于notify类
记录(比如AskUserQuestion),新增resolved_value字段,从对应PostToolUse
事件的tool_response.answers里把用户在终端里实际选的答案摘出来存下,历史记录里
不止看得到当时问了什么,也看得到最后答了什么(旧记录没有这个数据,新产生的才有)。 - 额度明细(limits)百分比换成圆角胶囊 + 发光描边的进度条样式,颜色跟着当前配色
主题的 accent/yellow/red 变量联动(不是写死的固定色)。 - 状态信息页会话状态新增
Σ Total: X.XM · Cached: X.XM每会话 token 统计,跟
ccstatusline 的 TokensTotal/
TokensCached 挂件同一个计算口径(Total = input+output+cached,Cached =
cache_read+cache_creation),之前只统计了 cache_read,漏了 cache_creation。 - 事件明细表格(工具调用/MCP 调用/Skill 调用统计的下钻、审批历史记录)的 Session ID
前面统一带上文件夹名(比如 "webui · 6ca9e412…"),比一串截断的 UUID 好认。
修复
- CC-Monitor 自己的
disk_overwrite规则正则表达式\b(dd|mkfs|fdisk|parted)\b
会把dd-table这种带连字符的 CSS 类名误判成dd磁盘命令拦截(正则的\b词
边界把连字符当成了单词分隔符)——开发这次功能时被自己的规则拦了才发现,改成用
(?<![\w-])...(?![\w-])精确匹配独立的命令名。 - AI 审批台的桌面通知按钮、审批历史记录表格,切换中英文界面语言时不会重新翻译(imperative
设置的textContent,不在data-i18n的自动刷新范围内),已经把它们接进语言切换
时的刷新流程。 - Log 审计 / Claude Tap 工具栏的会话筛选下拉框之前用
margin-left: auto想推到最
右边,但它在 DOM 里排在自动滚动开关前面,实际效果是开关跑到了它右边——加上
order让它排到真正最后。 - 首页账号信息的"账号创建时间"/"订阅开始时间"用
toLocaleString().slice(0, 10)
截取日期部分,截出来的字符串带了个多余的逗号("3/28/2026,"),改用
toLocaleDateString()。
变更
- 之前给文件操作统计/软件安装统计/Anthropic 账号信息这几组统计卡片加过整卡片背景
染色(先是跟随主题色,后来改成固定的霓虹配色,又发现忘了主题选择器本来就能改这个
颜色),来回几轮后按最新反馈整体去掉了,卡片恢复成跟其它统计一样的纯色背景。
English
Added
- Anthropic account info now shows name/email/organization/plan: not a new network
call — it's read from Claude Code's own local global config file~/.claude.json(the
oauthAccountfield), confirmed by decompiling ccstatusline's
"Claude Account Email" widget for the exact path and field names. New fields: name,
email, organization name, organization role, plan type (e.g.claude_max), organization
rate-limit tier, billing type, account creation date, subscription start date. Placed
below "Detected claude processes" on the Home page, and before "Account quota" on the
Status page. - Approval history on the AI Approvals page: the
pending_approvalstable was never
actually deleted from anywhere (the Home page's "clear current data" button only clears
theeventstable), so history was already being kept long-term — this just surfaces it:
time, session, tool, matched rule, matched value, outcome, resolved via. Fornotify-kind
records (e.g.AskUserQuestion), a newresolved_valuefield captures the user's actual
answer from the correspondingPostToolUseevent'stool_response.answers— the history
now shows not just what was asked but what was actually answered (existing records predate
this and won't have it; new ones will). - The limits percentage now renders as a rounded, glowing pill-shaped progress bar,
colored from the current theme's accent/yellow/red tokens (not a fixed palette). - Per-session token stats on the Status page: added
Σ Total: X.XM · Cached: X.XM,
matching ccstatusline's TokensTotal/
TokensCached widgets exactly (Total = input+output+cached, Cached =
cache_read+cache_creation) — the previous implementation only counted cache_read, missing
cache_creation. - Session ID cells in event-detail tables (Tool/MCP/Skill call drilldowns, approval history)
now show the folder name in front of the ID (e.g. "webui · 6ca9e412…") instead of a bare
truncated UUID.
Fixed
- CC-Monitor's own
disk_overwriterule regex,\b(dd|mkfs|fdisk|parted)\b, false-positived
on hyphenated CSS class names likedd-table(\btreats a hyphen as a word boundary,
same as whitespace) — discovered when this very feature got blocked by our own rule while
grepping the stylesheet. Fixed with(?<![\w-])...(?![\w-])so it only matches an isolated
command name. - The desktop-notification button and approval-history table on the AI Approvals page didn't
re-translate on a language switch (their text is set imperatively, outside thedata-i18n
auto-refresh path) — wired into the language-switch refresh now. - The session-filter dropdown on the Log Audit / Claude Tap toolbars used
margin-left: auto
to push itself right, but it sat before the autoscroll toggle in DOM order, so the toggle
ended up to its right instead — addedorderso it renders truly last. - The Home page's "Account created" / "Subscription started" dates were sliced out of
toLocaleString()output, leaving a stray trailing comma ("3/28/2026,") — switched to
toLocaleDateString().
Changed
- Colored card backgrounds for the file-ops/install-ops/Anthropic-account stat groups (added,
then re-themed to a fixed neon palette, then it turned out the theme selector already
covered this) were removed entirely after a few rounds of feedback — these cards are back
to the same plain background as every other stat card.
完整更新日志见 CHANGELOG.md (English)。
v1.3.1
新增
- 工具调用 / MCP 调用 / Skill 调用 / AI 轨迹下钻明细新增 Session ID、文件夹路径、
时间戳的事件级列表(不只是按类型/server/skill 分组的汇总数字)。AI 轨迹的连接
事件来自内核层探针,没有 Session 概念,这两列改成显示说明文字,换成显示发起连接
的进程/命令名(比如pip3、apt、curl)做替代的归因方式。 - 首页导航栏顺序调整为:首页 / 状态信息 / 网络流量 / 终端会话 / AI 审批台 / Log 审计
/ Claude Tap / 历史数据。 - 文件操作统计 / 软件安装统计 / Anthropic 账号信息这几组统计卡片背景改成跟随当前
配色主题的彩色底色(后续版本里又整体去掉了,见上面"未发布"的"变更")。
修复
- 连接目的地世界地图的等距柱状投影没有按画布实际宽高比缩放,画布不是标准 2:1 比例
时地图会被拉伸变形;改成按画布宽高比在 clip space 里补一次缩放,多出来的部分用
留白而不是拉伸填满。
English
Added
- Tool Calls / MCP Calls / Skill Calls / AI Trajectory drilldowns now include an
event-level list with Session ID, working directory, and timestamp (not just the
grouped-by-type/server/skill summary counts). AI Trajectory's connection events come from
the kernel-level probe and have no session concept, so those two columns show an
explanatory note instead, replaced with the originating process/command name (e.g.pip3,
apt,curl) as an alternative form of attribution. - Home page nav reordered to: Home / Status / Network / Terminal / AI Approvals / Log Audit
/ Claude Tap / Archives. - File-ops / install-ops / Anthropic-account stat cards got a themed colored background
(later removed entirely — see "Changed" under Unreleased above).
Fixed
- The connection-destination world map's equirectangular projection wasn't corrected for the
canvas's actual aspect ratio, so it stretched whenever the canvas wasn't exactly 2:1. Fixed
by scaling in clip space to match the canvas aspect, letterboxing/pillarboxing instead of
stretching to fill.
完整更新日志见 CHANGELOG.md (English)。