# dsh-sandbox-allowlist 三种权限规则使用说明 本插件在官方 DSH 沙箱之上追加了三类**可配置权限规则**,全部在设置页 「沙箱授权」分节(设置 namespace `sandbox-allowlist`,落盘 `$DSH_HOME/settings.yaml`)编辑,保存后实时生效: | 规则 | 解决什么问题 | 一句话 | 面向对象 | |---|---|---|---| | **授权目录** `allowedDirs` | 想让代理写工作区之外 | 额外放行哪些目录可**写** | 目录 | | **命令规则** `commands` | 想让某些命令免审批/必审批/被拦 | 哪些命令 **allow / ask / deny** | shell 命令 | | **禁读规则** `noRead` | 不想让代理读到某些东西 | 哪些文件/目录**不许读**(或需批准) | 文件 / 目录 | 三者相互独立、可叠加:授权目录管"写不写得了",命令规则管"命令问不问/拦不拦", 禁读规则管"读不读得到"。禁读规则**故意没有 `allow` 动作**——官方默认本来就允许 读,配一个 allow 等于没配;需要"放行一次"时用 `ask`(弹人工审批)。 > 术语:**审批** = 工具执行前弹给用户的人工确认;**沙箱** = 文件系统级的写边界 > (read-only / workspace-write / danger-full-access 三档)。 --- ## 1. 授权目录(allowedDirs)—— 允许写工作区之外 ### 用途 DSH 默认只允许在工作区内(外加临时目录)写入。`allowedDirs` 追加若干 **受信可写目录**:沙箱内的 CLI 命令与 write/edit 工具写这些目录**无需审批**。 ### 语法 ```yaml sandbox-allowlist: allowedDirs: - 'D:\Shared\Tools' # 字面目录:整棵子树可写 - 'D:\Shared\**' # 显式子树(含未来新建的子目录) - 'D:\Data\logs\*' # 现存的一级子目录 - 'D:\Archive\202?' # ? 匹配单个非分隔符字符 - '/opt/tools/**' # POSIX 同样支持 ``` ### 注意 - **放行的是写**,与读无关(官方本就允许读);目录必须**已存在且归当前用户所有**; - Windows:插件会把"工作区写 SID 的写 ACE"物化到这些目录(含子目录继承), 受限 CLI 才能写;从清单删除目录会自动回收该 ACE(对账持久化,重启补账); - Linux:bwrap 追加 `--bind <目录> <目录>`; - 只在 `workspace-write` 模式放行;`read-only` 仍全拒写,`danger-full-access` 无沙箱则无需本清单; - 通配符每次调用前懒展开(TTL 缓存);锚定盘符/根且带 `**` 的模式会被拒绝 (防整盘遍历)。 ### 场景示例 1. **工具/脚本目录**:代理需要维护 `D:\Shared\Tools` 下的脚本与批处理: ```yaml allowedDirs: - 'D:\Shared\Tools' ``` 2. **数据落盘**:让测试产物写进 `D:\Data\exports`(一级子目录按年份分开): ```yaml allowedDirs: - 'D:\Data\exports\*' ``` 3. **个人笔记库(示例占位路径,请替换成你的实际目录)**:工作区外自己的 Obsidian 知识库 `D:\Notes\MyVault` 整棵可写(含未来目录用 `**`): ```yaml allowedDirs: - 'D:\Notes\MyVault\**' ``` --- ## 2. 命令规则(commands)—— 免审批 / 必审批 / 拦截命令 ### 用途 dsh 的 bash / pwsh 工具执行命令前可能弹审批;命令被沙箱拒绝后 AI 还会带 `sandbox_permissions` 重试,那会弹第二次(**沙箱升级**审批:命令将在沙箱外运行)。 `commands` 决定这两处怎么办:哪些命令直接跑、哪些必须问、哪些直接拦。 判定不是"整串匹配":命令先被拆成一条条独立命令,每条按**能力分类** (只读 / 写工作区 / 跑仓库工具链 / 跨网络 / 执行不可见代码 / 破坏性 / 未知)判定, 再按 `deny > ask > allow > default` 聚合。所以 `git status && git log` 能整体放行, `git status && git push` 会被拦住,PowerShell 管道也不必逐条枚举 cmdlet。 ### 语法 ```yaml sandbox-allowlist: commands: default: delegate # 未命中规则时:delegate=维持现状(默认)| allow | ask | deny escalation: capability # 沙箱升级自动放行:capability(默认)| never baseline: true # 内置能力基线(默认开) sessionCache: true # 会话级命令缓存(默认开) rules: # 按声明顺序求值,最后一条命中的生效 - tool: bash # 可选:bash / pwsh;省略 = 两种都生效 pattern: 'git status*' action: allow # 窄规则:只放行 git status / git status -s… 这类形状 - pattern: 'git push*' action: ask # 同前缀下更细的规则放后面覆盖 - pattern: 'rm -rf *' action: deny # 两种 shell 都拦 ``` ### 三种动作 | 动作 | 含义 | |---|---| | `allow` | 免询问运行,**并且该形状的沙箱升级请求也自动放行(命令可在沙箱外运行)** | | `ask` | 强制弹审批 | | `deny` | 拦截并给出原因 | ### 注意 - **命令字符串会**规范化**(折叠空格;Windows 大小写不敏感),程序 token 会先归一化 (去路径、去引号、去 `.exe`/`.cmd` 类后缀)再参与匹配——`git *` 也能匹配 `"C:\Program Files\Git\git.exe" status`; - `*` 匹配任意多字符、`?` 匹配单个字符;规则锚定在命令开头(`git *` 不会误匹配 `gitdb status`); - 复合命令按 `;` `|` `&&` `&` `||` 与换行**分段判定**,任何一段 deny ⇒ 整体拦, 任何一段 ask ⇒ 整体询问; - `$( … )` / 反引号里的子命令**递归展开后一起判**(所以 `echo $(whoami)` 里的 `whoami` 会命中针对 `whoami` 的 deny 规则);重定向**解析目标路径**并与工作区 ∪ 授权目录比对;heredoc 正文按数据处理; - **无法解析的结构一律 fail-closed**(进程替换 `<(...)`、`$((`、引号不配对、递归超深); - ⚠️ **宽 pattern 的授权力度 = 全机执行**:`git *` → `allow` 会把该前缀下**任意** 命令(含 git 协议注入 `ext::sh`)授权到沙箱外,`pnpm *` → `allow` 等于放行 `pnpm run <任意脚本>`。请用窄规则(`git status*`、`pnpm test*`)表达精确意图; - 规则只管 **bash / pwsh 两个 shell 工具**,其它工具与命令规则无关。 ### 内置能力基线(`baseline`) 开启时(默认),只读命令(`ls`/`cat`/`grep`/`git status`/`Get-ChildItem`/`Where-Object`…) 与"只写工作区/授权目录内路径"的命令**无需任何规则**即可识别。关掉则完全按规则判定。 ### 沙箱升级自动放行(`escalation`) | 值 | 含义 | |---|---| | `capability`(默认) | 每条独立命令都命中 `allow` 规则(**allow 自带升级授权**),或都是良性能力类(只读、或只写工作区/授权目录内路径)⇒ 自动放行;其余(解释器、网络、包管理器、未知程序、解析不了的结构)弹审批 | | `never` | 永不自动放行 | 三条硬约束(**任何规则都覆盖不了**,与配置无关永远生效): 1. 命令引用了 **noRead 禁读目标**时绝不自动放行——升级会一并解除读取限制, `allow` 规则也不解除这条轨道; 2. 写入/重定向目标落在**工作区与授权目录之外**时不自动放行——正道是把它加入 「沙箱授权 → 授权目录」,那样命令根本不会被沙箱拒绝; 3. 任一段命中 `deny` / `ask` 规则时不自动放行。 另有一条保守规则:**只读命令只要点名了工作区/授权目录之外的路径,也不自动走能力类 兜底**(`uniq 输入 输出`、`git log --output=…`、`tree -o 文件` 这类"看着是读其实 会写"的参数位置无法逐程序解析,宁可多问一次)。 **环境变量前缀**(`FOO=bar cmd`)只拦能力类兜底:显式写的 `allow` 规则照常生效。 **元程序展开**:`pnpm run