【免费下载链接】clui-cc
Clui CC — Command Line User Interface for Claude Code
Clui CC 是 Claude Code 的桌面图形化界面,其 PermissionServer(权限服务器)通过 127.0.0.1 本地 HTTP Hook 拦截工具调用,依靠"双 Token 鉴权 + 5 分钟自动拒绝"实现安全的 Human-in-the-loop 权限审批。本文将拆解这套安全设计的工作原理。
一、Clui CC 是什么:给 Claude Code 加一层"审批弹窗"
Clui CC 用 macOS 悬浮层包裹本地的claudeCLI:每个标签页对应一个独立的 Claude 会话。当 Claude 想执行Bash、Edit、Write这类有实际影响的操作时,不会直接在终端里静默执行,而是弹出权限卡片,由你点"允许"或"拒绝"。
实现这个能力的关键,就是本文主角——权限服务器。它的源码只有一个文件:permission-server.ts,完整架构可参考 docs/ARCHITECTURE.md。
二、为什么用 HTTP Hook:PreToolUse 拦截机制
🔌 核心思路是"借道" Claude Code 原生支持的PreToolUse Hook:
- 权限服务器在本地启动,默认监听
127.0.0.1:19836(端口被占用时自动顺延 +1)。 - 每次发起对话前,Clui CC 动态生成一个临时配置文件,把 Hook 指向该地址,并通过
--settings参数传给claude -p子进程。 - Claude 想用工具时,CLI 会先
POST到 Hook URL,等服务器返回allow或deny才决定是否执行。
Hook 地址长这样(<appSecret>和<runToken>都是随机 UUID):
http://127.0.0.1:19836/hook/pre-tool-use/<appSecret>/<runToken>绑定127.0.0.1而非0.0.0.0,意味着服务器只接受本机回环流量,局域网内其他设备无法访问——这是网络安全面的第一道闸门。
三、双 Token 鉴权:两道随 URL 传递的"钥匙"
🔒 本地回环不代表绝对安全:同机其他进程理论上也能连到127.0.0.1。权限服务器因此在 URL 路径里埋了两级凭证:
| 凭证 | 生命周期 | 作用 |
|---|---|---|
appSecret(应用密钥) | 每次启动 App 重新生成 | 证明请求"来自本实例的 Hook 配置",防止本地伪造 |
runToken(运行令牌) | 每次对话(run)单独生成 | 把请求路由到正确的标签页,防止"串台" |
两者在 permission-server.ts#L234-L238 定义:appSecret在构造函数里用randomUUID()随机生成;runToken由 control-plane.ts 在每次派发对话时调用registerRun()注册,并同时落盘生成对应的临时 Hook 配置文件。
请求到达后服务器逐段校验 URL:appSecret不匹配 →403 Invalid credentials;runToken找不到注册记录 →403 Unknown run。校验通过后,服务器才能根据runToken知道这个请求属于哪个标签页,从而把权限卡片弹到正确的界面上。
四、5 分钟自动拒绝:Fail-Closed 的兜底设计
⏱️ 权限卡片弹出后,服务器不会无限等待。超时、异常、崩溃——所有失败路径的最终答案只有一个:deny。
1. 5 分钟超时自动拒绝
PERMISSION_TIMEOUT_MS = 5 * 60 * 1000定义在 permission-server.ts#L28。如果你离开电脑 5 分钟没点按钮,请求自动以"Permission timed out after 5 minutes"被拒绝,Claude 会跳过该工具继续工作,而不是悬挂。
2. 所有失败路径一律拒绝
- 非
POST请求 →404+ deny - URL 路径格式错误 → deny
- 请求体超过 1MB →
413+ deny - JSON 解析失败 / 缺少必填字段 / 事件名不是
PreToolUse→ deny - 用户点了一个不存在的选项 → deny(合法决策仅限
allow、allow-session、allow-domain、deny四种)
3. 主动拒绝的场景
- 服务器停止时:所有挂起请求统一以"Server shutting down"拒绝(stop())。
- 标签页被关闭:ControlPlane 立即回
deny 'Tab closed',避免用户已看不到卡片却等满 5 分钟。 - 一次对话结束:
unregisterRun()会拒绝该 run 名下所有未决请求并删除临时配置。
这种"默认拒绝"(fail-closed)思路保证:任何不确定的状态,都按最保守的安全方向处理。
五、两个贴心设计:只读命令自动放行 & 敏感字段脱敏
🛡️ 审批系统最怕"弹窗疲劳"。权限服务器用两道机制减少噪音:
只读 Bash 命令免审批:内置一份安全命令白名单(ls、grep、git status等),纯只读命令直接自动放行;而git push、npm install这类会改变状态的子命令仍会弹窗。判断逻辑见 isSafeBashCommand(),它会逐段检查;、&&、|分隔的复合命令,并拦截重定向写文件。
敏感字段脱敏:工具输入里凡是键名匹配token、password、secret、api_key等正则的字段,在展示到界面前被替换为***,由 maskSensitiveFields() 递归完成。更妙的是前端 PermissionCard.tsx 还做了一次同样的掩码——双层防御,防止日志或界面意外泄露密钥。
六、一次完整审批的旅程
把上面所有环节串起来,就是权限请求的完整链路:
Claude 想用 Bash 工具 → CLI 按 Hook 配置 POST 到 127.0.0.1:19836(URL 含双 Token) → 权限服务器校验 appSecret + runToken → 只读命令?→ 直接返回 allow → 否则生成 questionId,等待用户决策(5 分钟倒计时) → ControlPlane 把请求转发到对应标签页 → 界面弹出 PermissionCard(选项已脱敏) → 你点 Allow / Deny → 决策沿原路返回,HTTP 响应带回 allow/deny → Claude 执行或跳过该工具事件路由的关键接线在 control-plane.ts#L97-L130:服务器发出permission-request事件后,ControlPlane 依据runToken注册时记录的tabId直接定位标签页,无需再靠 session_id 反查。
七、安全设计速览与延伸阅读
📌 把这套设计浓缩成一张清单:
- ✅仅本地:绑定
127.0.0.1,不暴露到网络 - ✅双 Token:appSecret 防伪造、runToken 防串台,每次启动/每次运行都重新随机
- ✅Fail-Closed:超时 5 分钟、路径错、Token 错、格式错……一律 deny
- ✅最小打扰:只读命令自动放行,会话级"允许本会话"减少重复审批
- ✅密钥脱敏:前后端双层掩码敏感字段
相关文档:架构全景见 docs/ARCHITECTURE.md,安全属性与网络面清单见 SECURITY.md,共享类型定义见 src/shared/types.ts。
理解了"为什么本地 Hook 也要鉴权"和"为什么所有异常都往拒绝方向倒",你就能读懂 permission-server.ts 里每一行防御代码的意图——它不追求功能最全,只追求在任何意外下,Claude 都不会替你做出你没同意的操作。
【免费下载链接】clui-cc
Clui CC — Command Line User Interface for Claude Code
相关推荐
Civitai OAuth 授权服务器与 Scoped Token 位掩码权限体系:设计原理与源码实现
Civitai OAuth 授权服务器与 Scoped Token 位掩码权限体系:设计原理与源码实现 本篇基于仓库中的规划文档 oauth scoped to
后端前端AI 应用为什么TrumpScript拒绝root权限运行?安全设计理念
为什么TrumpScript拒绝root权限运行?安全设计理念 你是否曾遇到过程序坚持拒绝以管理员权限运行的情况?在开源项目中,这种设计选择往往蕴含着深刻的安全
编程语言编译器Dexter权限拒绝处理:永久拒绝与临时拒绝的差异指南
Dexter权限拒绝处理:永久拒绝与临时拒绝的差异指南 在Android应用开发中,权限管理是一个至关重要的环节。Dexter作为一款优秀的Android权限请
移动开发认证鉴权
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考