中文EN

dsh-write-protect

security已测通过npmMIT

为 DSH 沙箱增加工作区子路径只读保护 (例如 .git), 支持工作区根下的只读规则文件, 可在 workspace-write 下声明额外可写根, 并允许模型申请本会话的可写授权; 命令沙箱与 write/edit 工具都生效.

下载量 / Downloads
1,089
依赖体积 / node_modules
16.77 MB
测试时间 / Tested
2026-11-06
DSH 版本 / Version
0.2.2-rc.1

安装

dsh plugin --profile web add dsh-write-protect

沙箱测试结果

阶段结果
1 安装✓
2 列装✓
3 挂载✓
4 启动—

四阶段在隔离沙箱里跑:安装(有网)→ 列装 → 挂载 → 启动(后三步断网)。

README

dsh-write-protect

给 DSH 沙箱补上工作区里某一段路径的只读保护, 典型用途是不让模型改 .git. 也可以在 workspace-write 下声明工作区外的额外可写根, 让 bash 与 write / edit 写到相邻目录, 而不必切到 danger-full-access.

工作区根还能放一份只读规则文件 (默认 .readonly), 与插件页配置里的保护路径同语义; 任务要反复写同一片受保护区域时, 模型也可以申请本会话的可写授权 (见下文两节). 会话区还有一个 "写入权限" tab: 在那里可以手动加临时可写根, 也可以把某条授权撤回, 撤回会通知模型.

write / edit 工具在所有平台都会挡住保护路径, 并放行额外可写根. bash 等命令在 Linux / macOS 上同样生效; Windows 上 bash / pwsh 既挡不住 .git, 也拿不到额外可写根. 读取不受影响.

官方沙箱只有 "整个工作区可写" 和 "全只读" 两档, 管不到工作区内部的某一段, 也不能把工作区外的个别目录并进 allow-list; Codex 一类实现默认会保护 .git, 本插件补这一块.

安装

Web 端装进 web profile:

dsh plugin --profile web add azazo1/dsh-write-protect

固定版本:

dsh plugin --profile web add azazo1/dsh-write-protect#v0.3.0

GitHub Release 同时挂不带版本号的预构建包, 安装时跳过 allowBuilds:

dsh plugin --profile web add https://github.com/azazo1/dsh-write-protect/releases/latest/download/dsh-write-protect.tgz

桌面端装进 desktop profile. 它由 Electron 应用独占管理, dsh plugin 会拒绝 --profile desktop, 所以要用应用内的插件管理器: 在插件页的安装入口填上面命令里对应的包名或 tarball 地址. 装上后重启应用, 窗口刷新一次.

安装后会接管沙箱策略和 write / edit 围栏, Linux / macOS 上还会接管命令沙箱. 改配置即时生效, 不用重启 dsh web. web 与 desktop 两个 profile 跑的是同一套 Web 应用, 桌面端只是多起一个 Host 子进程并给 <html> 打上平台标记, 所以同一份包在两边通用, 不需要分别构建.

引擎版本线要求 @deepseek-ai/dsh-* 不低于 0.2.0-rc.1, 且仍在 0.2.x 上 (peerDependencies 与 devDependencies 都写作 >=0.2.0-rc.1 <0.3.0). 已在 0.2.0-rc.2 上日常验证. 官方 SandboxProvider.confine() 自 0.1.6-alpha.1 起改为异步 (Promise<ConfinedArgv> 加 signal 参数), 插件配置自 0.1.7 起走 volatile Config (插件页的配置卡片经 configForms 读写); 本插件的覆写与配置面都按这条线走. 仍在 0.1.7 引擎线上的部署请继续用 v0.2.0, 更早的部署请继续用 v0.1.1.

配置

保护路径, 额外可写根, 规则文件名, 各项上限与三个开关的默认值都定义在 src/constants.ts 的 DEFAULT_*, patch 的 policy 行与插件页保存的值都由它们兜底; 需要部署级覆盖时在 patch 行显式给出字段 (整行替换, mode / workspaceRoot 必须带上):

- id: dsh-write-protect-policy
  name: dsh-write-protect
  config:
    mode: !!js process.env.DSH_PERMISSION_MODE ?? 'workspace-write'
    workspaceRoot: !!js process.cwd()
    # 部署级覆盖示例.
    # readOnlyPaths: ['.git', '//etc/pki']
    # writablePaths: ['../shared-scratch', '//tmp/dsh-extra']
    # hardenBroker: false
    # readonlyFileName: '.readonly'   # 置空即关闭工作区规则文件识别
    # maxReadOnlyEntries: 200
    # maxGrants: 8
    # allowWritableRequests: true   # 置 false 即不许模型申请可写路径
    # watchProtectedPaths: true   # 置 false 即不监听工作区变化, 只按时间兜底
    # watchTtlMinMs: 2000
    # watchTtlMaxMs: 30000

readOnlyPaths 的每一项是一行 gitignore 语义的模式, 数组逐行合并为生效文本:

  • 不含 / 的条目 (如 .git, vendor) 在工作区内任意层级匹配, 覆盖嵌套仓库等场景.
  • 以 / 开头或含中间 / 的条目锚定到工作区根 (如 /.git, dist/a.txt); 字面条目即使尚不存在也保留保护, 例如 git init 之前的 /.git.
  • // 开头的条目是文件系统绝对路径 (如 //etc/pki), 这是本插件额外支持的写法, gitignore 没有这种形态.
  • 尾部 / 表示只匹配目录 (如 build/).
  • 通配: * 匹配单段内任意字符, ? 匹配单字符, [...] 字符类 (含 [:alpha:] 等 POSIX 类), 独立成段时递归 (如 a//b); \ 转义下一字符 (\#, \!, 尾部空格用 \ 保留).
  • ! 开头剔除匹配项, 按 gitignore 的 last-match-wins 顺序解释; 受保护目录内部无法通过取反重新放行后代.
  • write / edit 按模式逐路径判定, 与路径是否存在无关; bash / pwsh 侧的保护路径是枚举出来的, 只覆盖展开时刻已存在的路径. 解析时会解开符号链接并去重.
  • 置为空列表 [] 即停用保护 (插件仍在, 只是不再多挡任何路径).

writablePaths 的每一项是一行字面路径, 不是 gitignore glob:

  • 行首 ~ 或 ~/... 展开为当前用户家目录; ~other 不支持.
  • $NAME 与 ${NAME} 展开为环境变量; 未设置或空值的变量整行丢弃并告警. \$ 保留字面 $.
  • 宿主绝对路径 (/tmp/extra) 或 // 前缀 (//tmp/extra) 按文件系统解析.
  • 其余相对当前会话工作区, 含 .. (如 ../sibling-project).
  • 工作区内的路径本来就可写, 展开时忽略并告警; 文件系统根 (/ 或盘符根) 拒绝, 避免把只读宿主根整棵翻成可写.
  • 不支持通配与 ! 取反. 不存在的路径仍保留词法形态: write / edit 与 Seatbelt 可按前缀放行, bwrap / Landlock 在叠加时跳过并告警.
  • 只在 workspace-write 下并进 allow-list, 不打穿 read-only. 保护路径优先: 额外根内部仍可被保护.
  • 置为空列表即不额外放行.

hardenBroker 是 macOS broker 逃逸加固的部署 base, 布尔值, 缺省 true:

  • 开启时在 Seatbelt profile 末尾追加 broker 拒绝形式 (见 "保护范围").
  • 关掉后命令按官方 profile 运行, 只影响 macOS, 只影响这一个加固; 保护路径与额外可写根照常.
  • 用户在插件页拨动开关后该值不再生效.

readonlyFileName / maxReadOnlyEntries / maxGrants / allowWritableRequests / watchProtectedPaths / watchTtlMinMs / watchTtlMaxMs 同理, 是只读规则文件, 可写申请与命令侧刷新的部署 base (见后两节), 用户保存过对应字段后该值不再生效.

源码分三块, 边界是"有没有文件系统依赖":

文件 · 职责 · 依赖

src/gitignore.ts · gitignore 语义的解析与逐路径匹配 (PatternSet.match), 规则文件校验与可写申请的保护判定共用 · 纯字符串/正则, 零运行时依赖

src/patterns.ts · 把模式枚举成具体路径, 供保护路径清单与命令沙箱使用 · node:fs、canonicalPath

src/fs.ts / src/policy.ts / src/provider.ts · 三个挂载点: write/edit 围栏、沙箱策略、进程沙箱 argv 叠加 · DSH 引擎

src/readonly-file.ts 负责工作区只读规则文件的读取与缓存, src/path-expand.ts 负责额外可写根的字面路径展开, src/request-writable-path.ts 是可写申请的授权表与 request_writable_path 工具, src/grants-route.ts 与 src/grant-notice.ts 分别是会话面板的 /api 路由与撤回通知.

插件页配置

<img src="https://raw.githubusercontent.com/azazo1/dsh-write-protect/HEAD/docs/screenshots/settings-page.png" alt="写入保护插件页配置" width="520">

插件页里 dsh-write-protect 卡片上的配置表单, 从上到下是: 保护路径与额外可写根两个多行文本, 工作区只读规则文件名与最多条目数, 单会话可写授权上限, macOS broker 逃逸加固, 模型申请可写路径, 监听工作区变化, 以及刷新下界与上界两个毫秒值. 草稿只留在卡片里, 点 "保存" 才写进 profile 的 patch 层并实时生效; 预览面板按当前草稿展开, 不必先保存:

# 保护路径
.git
secrets/*.pem
!secrets/example.pem
# 额外可写根
../shared-scratch
~/scratch
$HOME/scratch
/tmp/dsh-extra
  • 保护路径: # 开头是注释, 空行忽略; ! 排除, 按最后匹配生效; 不能在仍受保护的目录内部重新放行后代. 通配与锚定语义同 "配置" 一节. Windows 上的绝对条目写作 //C:/Users/me/secret: gitignore 语义里 \ 是转义符, / 才是分隔符.
  • 额外可写根: 每行一条字面路径, 不要通配. ~ / ~/... 为家目录, $NAME / ${NAME} 为环境变量; 绝对路径按文件系统解析, 相对路径 (含 ..) 相对当前会话工作区. Windows 上 \ 是分隔符而不是转义符, C:\Users\me\caches 与 ~\caches 都按字面解析; 盘符相对路径 (C:caches) 的落点取决于进程当前目录, 会被拒绝并出现在 "未生效" 里.
  • 两份文本都按当前会话的工作区根解析, 每个会话各自生效. 开关是全局的, 与工作区无关.
  • 预览按钮把当前草稿交给 Host 展开, 不必先保存: 列出生效的保护路径与额外可写根, 工作区规则文件的条目, 本会话已批准的可写授权, 以及被忽略或拒绝的行. 展开使用当前选中会话的 cwd (即被主视图持有的那条会话记录); 没有选中会话时预览直接报错, 不回退部署工作区根 —— 部署根是进程 cwd, 可能是一棵极大的树, 在那里同步展开会堵住 Host 事件循环. 这一页只列不改: 加临时可写根与撤回授权在会话区的 "写入权限" tab 上 (见下节).

mode 与 workspaceRoot 是官方 policy 行字段的复述 (patch 对整行配置做替换, 必须带上), 取值语义与 base bundle 一致.

只读规则文件

工作区根可以放一份与 "保护路径" 同语义的规则文件 (默认 .readonly, 可在插件页配置里改名或置空关闭), 逐行追加在保护路径文本之后:

# <工作区根>/.readonly
secrets/
/vendor
!vendor/public/**
**
  • 只认工作区根这一份, 每个会话各读一份; 整段保护工作区写 或 /,

…(truncated)

以上内容来自 npm 上的 README,我们只做格式化,不改内容。

版本

npm 上共发布:4,最后更新 2026-10-03

链接

GitHub · Homepage · npm

同类插件