一、要解决的问题
安全, dsh 原生只有二元审批(ask/never 全局策略)+ sandbox 模式,缺一层「细粒度、可分层配置的 allow/deny/ask 规则」。本插件补的就是这一层,并按 skill(tool-permission-system)把五层体系在 dsh 上重建。
二、参考模型:Claude Code 的 5 层工具安全体系
|-------|-------------------------------------------|------------------------------------------------------|
| 第 1 层 | 「判断工具前是否有强行拦截......优先级最高,通常把最危险的事情进行强行拦截」 | PreToolUse hook |
| 第 2 层 | 「判断某操作是否被明确禁止」 | settings.json 的 deny,三级规则来源 |
| 第 3 层 | 「判断当前的安全模式......全局的总开关」 | bypassPermissions 等模式 |
| 第 4 层 | 「判断工具是否在 setting 里的 allow 白名单」 | permissions.allow |
| 第 5 层 | 「工具自身的安全属性......在定义工具的时候就确定了」 | isReadOnly / isDestructive / isConcurrencySafe |
三、五层体系在 dsh 上的落地对照
dsh 的工具执行流水线是 tools/pre-execute(可扩展的允许/拒绝/询问门禁)→ guard → execute → post-execute。本插件把五层判断压进 tools/pre-execute 一个监听器里,产出 dsh 的 PreToolDecision(allow / deny / ask)。逐层对照:
|------------------|--------------------------------------------------------------------------------------------------------------------|-----------------------------------------|
| 第 1 层 强行拦截(hook) | 暂不复刻(dsh 已有独立 hooks 包,避免重叠);其职责由本插件的 safety 黑名单承担一部分 | v1 非目标 |
| 第 2 层 明确禁止(deny) | decide() 流水线第 1 步:命中 deny → 拒 | 见下 |
| 第 2 层 分层规则来源 | 复用 dsh 的 ctx.settings 分层解析(policy→user→project→local),注册 tool-permission namespace,allow/deny/ask 三个规则数组 | 「本地级 > 项目级 > 用户层级」同构,并在其上多一层企业 policy |
| 第 3 层 安全模式 | dsh 原生 sandbox/approval 模式;本插件默认行为 defaultBehavior(passthrough/deny/ask)对齐"未命中时的模式" | 复用宿主 |
| 第 4 层 allow 白名单 | decide() 流水线第 4 步:命中 allow → 放行 | 见下 |
| 第 5 层 工具自身属性 | dsh 的 defineTool 已内建 isConcurrencySafe 等;本插件不重复 | 复用宿主 |
| Auto Mode + 熔断 | v1 非目标 (YAGNI);ask 直接交 dsh 的 ctx.approval,不引入 AI 分类器 | 后续可加 |
决策流水线:顺序即安全
decide() 按固定顺序判决,顺序本身就是安全策略:
1. deny 命中 → 拒绝 ← 最高优先:企业层 deny 不可被下层 allow 翻案
2. ask 命中 → 弹确认框
3. safety 命中 → 强制弹确认 ← 硬编码黑名单,命中后即使配了 allow 也拦不住
4. allow 命中 → 放行
5. 默认 → defaultBehavior(passthrough 交回 dsh / deny / ask)
-
deny 在 allow 之前:「本地级 > 项目级 > 用户层级」的分层覆盖------各层同名数组 union 后,deny 恒胜,企业管理员的禁令不会被用户的 allow 翻案。
-
safety 在 allow 之前 : 第 1 层「把最危险的事情进行强行拦截」精神的延续------一份硬编码危险黑名单(
.git/hooks、.bashrc/.zshrc、.claude/、.dsh/、.mcp.json、rm -rf等),命中一律强制ask,配了 allow 也拦不住。堵的是两类:代码执行(改 shell 启动脚本/hook)与自我提权(AI 借"正常编辑文件"改自己的护栏)。
规则格式与跨平台
规则沿用课/Claude Code 的 ToolName 或 ToolName(content) 形态:
tool-permission:
allow: [read, bash(git:*), bash(pnpm:*)] # 只读/安全命令自动放行
deny: [bash(rm:*)] # 危险命令直接拒
ask: [bash(curl:*), write] # 敏感操作弹确认
内容匹配:shell 工具走命令前缀(bash(git:*) 命中 git 开头),fs 工具走路径 glob(edit(/etc/**))。
一条贯穿的铁律:fail-closed
授权是安全边界,拉不到权限 / 规则损坏 / 无应答者时一律拒 ,绝不"拿不准就放行"。唯一例外是纯便利性的规则读取降级(降到 defaultBehavior + 告警),但 safety 黑名单永远运行、不可降级。
四、应用方案
同一决策内核,换"规则来源"覆盖两类场景:
A. 命令级权限管控 (本插件):管 dsh 自带工具(bash/read/edit...),规则读本地 settings.yml,纯离线、不依赖任何后端。真正有价值;交互式使用下 dsh 原生 ask/never + sandbox 已够,增量主要是细粒度规则 + safety 黑名单。
B. 业务功能级权限管控 (配套 demo):管自研业务工具(查天气/订票/退款...),规则来源可切换------static(身份→工具白名单写本地配置,离线)/ api(调后端权限服务,超时/不可达 fail-closed)。通用 Agent 注册全量业务工具,但每个用户按身份拿到授权子集,清单外功能一律禁。面向多租户/多角色业务 Agent。
五、安装与使用
体验者:拿到 tgz 后
dsh plugin --profile web add ./dsh-tool-permission-0.1.0.tgz
