Skip to content

Releases: cn0xroot/CC-Monitor

v2.0.0

Choose a tag to compare

@cn0xroot cn0xroot released this 16 Sep 14:50

新增

  • 跨工作目录行为检测(新模块 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 Code permissions.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 /)。
  • 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、CDP captureScreenshot、Playwright/Puppeteer/
    Selenium 的截图 API、shot-scraper/wkhtmltoimage/gowitness 等工具)、虚拟机与远程设备
    (QEMU screendump(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 的绑定不受影响)。
  • 其它七个标签页统一成同一套页头(.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 子命令
    漏匹配;新增 macOS open -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,...
Read more

v1.7.0

Choose a tag to compare

@cn0xroot cn0xroot released this 16 Sep 14:50

新增

  • 新增 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 --test 5/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/, or CLAUDE.md had zero rule coverage before. This is the
    biggest gap found so far — rewriting the config to drop a hook registration is stealthier
    than kill -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
    existing kill_monitoring_process, just at the config layer instead of the process layer.
    Added claude_config_tamper (risk: high, action: confirm),
    tools: ["Write", "Edit", "NotebookEdit"], field: "file_path". confirm rather than
    block since adding a new hook to your own project or editing CLAUDE.md is a completely
    normal action. Inserted right after shell_rc_write.
  • Docker privileged/mount detection extended to Docker-socket mounts:
    docker_privileged_or_host_mount previously only recognized --privileged and -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 a docker.sock substring match,
    and bumped this rule's risk from medium to high (both --privileged and a docker.sock
    mount are host-root-equivalent risk, medium undersold it; action stays confirm to
    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. Added secret_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/ASIA prefix), 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 to FIELD_CANDIDATES in
    cc_monitor/policy.py — Write uses content, Edit uses new_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 for file_path. Writes to paths
    already covered by sensitive_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 (all xs, say) can false-positive — the same limitation every lightweight,
    non-entropy secret scanner (gitleaks/trufflehog's non-entropy patterns) shares; confirm
    rather than block leaves 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 existing crontab_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) and git_hooks_file_write (same `medium...
Read more

v1.6.0

Choose a tag to compare

@cn0xroot cn0xroot released this 16 Sep 14:50

新增

  • 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_op SQL 自定义函数,/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_screenshot SQL 自定义函数、
    /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_op SQL 自定义函数,/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 --test 5/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_op SQL 自定义函数,
    /api/drilldown/archive-op/:type 接口。
  • 首页新增"网络诊断工具统计":nc(含 ncat/netcat 别名)/ nmap / telnet /
    其它(socat)四张卡片,纯粹是"用过这些工具没有"的可见性统计,跟上面的反弹
    shell 风险判断是两回事——nc -zv example.com 443 这种正常端口探测也会被计入
    这张卡片,不代表危险。新增 classifyNetdiagOp/netdiagOpsStats/
    netdiagOpsDetails、cc_netdiag_op SQL 自定义函数,
    /api/drilldown/netdiag-op/:type 接口。
  • 首页新增"进程管理/后台驻留统计":nohup / disown / 后台任务(命令末尾裸
    &)/ 其它(setsid)四张卡片。前两个按子命令开头识别,跟其它分类器一个思路;
    "后台任务"不一样——裸 & 不是某个命令的名字,是整条命令末尾的 shell 语法
    标记,`spli...
Read more

v1.5.0

Choose a tag to compare

@cn0xroot cn0xroot released this 16 Sep 14:50

新增

  • AI 审批台接管 Claude Code 原生确认框:新注册 PermissionRequest hook(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、地图上不会有点这种情况。
  • 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
    PermissionRequest hook is registered (re-running install.py adds it; the existing two
    entries are left alone). Previously the approvals page only mirrored operations our rule
    table flags as confirm; Claude Code's own "Do you want to proceed?" prompts (calls that
    matched no rule) never showed up — at PreToolUse time there is no way to know whether a
    prompt is coming. These now appear as kind='permission' with the same buttons as confirm
    (allow / deny / 10 or 30 min / always — "always" is remembered per session + 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-probe switches 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 same network_traffic table and os_net events the Linux probe uses, so
    the traffic page / world map / AI trajectory are no longer permanently empty on a Mac. No
    root required. No execve observation (the bypass detection behind verify stays
    Linux-only); hostnames fall back to reverse DNS. SIGTERM also tears down the nettop child.
    The network page hints now tell you to run bin/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.sh now installs and wires up ccstatusline: new step 1 checks whether
    ccstatusline is already on PATH
    (command -v ccstatusline) and runs npm install -g ccstatusline if not; once it's
    available, install.py's configure_statusline() writes a statusLine entry into
    ~/.claude/settings.json if 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 existing statusLine (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=1 skips both the install and the wiring (internally passed
    through as install.py --skip-statusline). The GeoIP download step is renumbered to 5.

  • install.sh step 5 downloads the GeoIP database: DB-IP Lite (CC BY 4.0, ~60MB) to
    ~/.cc-monitor/dbip-city.mmdb (honours CC_MONITOR_HOME). Skipped if any .mmdb is
    already present; downloads to a .part file 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=1 skips it,
    CC_MONITOR_GEOIP_URL points at a mirror.

  • system_package_install rule now covers macOS package managers: brew install/reinstall/uninstall/remove/rm/upgrade/tap/untap/bundle (incl. brew cask …) and
    MacPorts port [-flags] install/uninstall/upgrade/activate/deactivate/selfupdate (things
    like port installed or lsof -i :port don't match). The "system package manager" card on
    the home page is relabelled accordingly. Also fixed pacman -Syu not matching (the old
    regex required S/R to be the last flag letter). Note that an existing
    ~/.cc-monitor/rules.json is 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 renderer Notification.permission is always
    "granted" and new Notification() doesn't throw, but the underlying
    UNUserNotificationCenter refuses apps that aren't properly signed (npm run electron runs
    the ad-hoc-signed Electron.app from node_modules) — the main-process side gets a failed
    event with UNErrorDomain error 1 (NotificationsNotAllowed), the renderer side fails
    silently. The main process now polls pending_approvals every 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 bundled prebuilds/<platform>/spawn-helper binary, and npm dropped
    its executable bit when unpacking (seen on macOS + npm 11). New
    webui/scripts/fix-node-pty-perms.js runs as postinstall and again, idempotently, when
    lib/sessions.js loads (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 (service Claude Code-credentials, same JSON payload). Every fresh Mac
    hit this. New webui/lib/credentials.js reads the file first and falls back to
    security find-generic-password -s "Claude Code-credentials" -w; shared by usage.js
    and account.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.py opened
    /dev/tty in text mode "r+", which wraps a BufferedRandom that requires a seekable
    stream; lseek on a tty returns 0 on Linux but ESPIPE...
Read more

v1.4.3

Choose a tag to compare

@cn0xroot cn0xroot released this 16 Sep 14:50

新增

  • 发布编译好的 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-*.AppImage alongside a cc-monitor-start.sh launcher 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-sandbox when 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.js did 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 in electron-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 electron goes through scripts/electron-start.js, and
    the packaged binary goes through the new cc-monitor-start.sh above.

完整更新日志见 CHANGELOG.md (English)。

v1.4.2

Choose a tag to compare

@cn0xroot cn0xroot released this 16 Sep 14:50

修复

  • 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 electron failed to launch under root: Chromium checks whether it's
    running as root without --no-sandbox at native startup and FATAL-exits if so —
    this check runs before any JS in electron-main.js executes, so setting the flag
    at runtime via app.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-sandbox as well. Added a small launcher script
    (webui/scripts/electron-start.js) that checks process.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/xdotool that a real window titled "CC-Monitor" comes up.

完整更新日志见 CHANGELOG.md (English)。

v1.4.1

Choose a tag to compare

@cn0xroot cn0xroot released this 16 Sep 14:50

修复

  • 界面字号设置之前不是真正全局生效:样式表里 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 own font-size never touched them — the slider looked like it barely did
    anything. Fixing it surfaced a second, related bug: after converting all 62 to rem
    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, and 1rem on 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
    html font-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 surrounding font-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

Choose a tag to compare

@cn0xroot cn0xroot released this 16 Sep 14:50

新增

  • 外观设置弹窗(顶栏 ⚙ 按钮):主题配色改成可视化色块网格(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
    localStorage and 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/--green tokens 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 get seven_day_opus/seven_day_sonnet) — its quota only shows up as
    one kind="weekly_scoped" entry inside limits[]. 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_boundary events 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 for example.com using 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 a matched_rule and 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 real git push), registered as
    a SQLite custom function cc_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 used width: 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 fixed 220px so 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

Choose a tag to compare

@cn0xroot cn0xroot released this 16 Sep 14:50

新增

  • 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
    oauthAccount field), 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_approvals table was never
    actually deleted from anywhere (the Home page's "clear current data" button only clears
    the events table), so history was already being kept long-term — this just surfaces it:
    time, session, tool, matched rule, matched value, outcome, resolved via. For notify-kind
    records (e.g. AskUserQuestion), a new resolved_value field captures the user's actual
    answer from the corresponding PostToolUse event's tool_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_overwrite rule regex, \b(dd|mkfs|fdisk|parted)\b, false-positived
    on hyphenated CSS class names like dd-table (\b treats 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 the data-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 — added order so 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

Choose a tag to compare

@cn0xroot cn0xroot released this 16 Sep 14:50

新增

  • 工具调用 / 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)。