Skip to content

Releases: azazo1/dsh-write-protect

dsh-write-protect v0.3.0

Choose a tag to compare

@github-actions github-actions released this 03 Oct 06:59

dsh-write-protect v0.3.0

Date: 2026-10-03

这一版把引擎版本线从 @deepseek-ai/dsh-* 0.1.7 抬到 0.2.0-rc: peer 写成 >=0.2.0-rc.1 <0.3.0, 覆盖 0.2.0-rc.1 与 0.2.0-rc.2. 会话区新增 "写入权限" tab, 可以手动加临时可写根, 也可以把已批准的授权撤回并通知模型. 仍在 0.1.7 引擎线上的部署请继续用 v0.2.0.

Highlights

  • 引擎版本线抬到 @deepseek-ai/dsh-* >=0.2.0-rc.1 <0.3.0; 已在 0.2.0-rc.2 上日常验证.
  • 会话区 "写入权限" tab: 列出本会话授权, 手动加临时可写根, 逐条撤回; 撤回后向会话投一条模型可见的通知, 空闲会话不唤醒.
  • README 安装节补上 web 与 desktop 两个 profile 的写法.

Breaking Changes

  • 引擎版本线: peerDependencies 与 devDependencies 由 0.1.7-rc.* 抬到 >=0.2.0-rc.1 <0.3.0. 迁移方式: 先把 DSH 升到 0.2.0-rc.1 或 0.2.0-rc.2; 仍在 0.1.7 引擎线上的部署继续用 v0.2.0, 不要升到本版.

Upgrade Notes

  1. 升级前确认引擎版本: 本版要求 @deepseek-ai/dsh-* 不低于 0.2.0-rc.1, 且仍在 0.2.x 上. 停在 0.1.7 的部署请继续用 v0.2.0; 更早的部署请继续用 v0.1.1.
  2. 从 GitHub 固定版本安装时把 tag 换成 v0.3.0; 走 releases/latest 的预构建包会在本 Release 发布后自动指向这一版.
  3. 升级后打开会话区的 "写入权限" tab, 确认本会话授权列表, 撤回与手动添加可用.
  4. 验证方式: 让模型写 .git 里的文件, 应当被拒; 在面板里手动加一条临时可写根后再写, 应当放行; 撤回后模型应收到通知, 再次写入应当被拒.

Features

  • 会话写入权限面板: POST /api/dsh-write-protect.grants, 一次请求一个动作: list 列本会话授权, add 手动加临时可写根, revoke 撤回并投通知.
  • 路径解析与硬保护复用模型申请那一套: 工作区内的目标记保护旁路 (writableOverrides), 工作区外记额外可写根 (writablePaths), 规则文件本身仍不受理.
  • 撤回通知走 agent.inject, 以 write-protect 来源与 notice 形态排进模型下一步, 空闲会话不唤醒; 消息按 user/message 落进会话日志, 所以 dsh 重启, 同一会话恢复后模型仍看得到.
  • client 半区注册会话区 tab (conversation.view, order 20): 列出授权与 maxGrants 占用, 逐条撤回, 手动加临时可写根, read-only 模式提示, 并如实显示通知是否投出.

Known Limitations

  • 授权仍是会话级内存态: 面板加的临时可写根与模型申请批准的一样, 不跨会话, 也不写配置; 重启 dsh 后消失.
  • 撤回不影响已经跑起来的命令: 命令侧的挂载与 profile 在启动时就定好了.

Full Changelog: v0.2.0...v0.3.0

dsh-write-protect v0.2.0

Choose a tag to compare

@github-actions github-actions released this 25 Sep 03:37

dsh-write-protect v0.2.0

Date: 2026-09-25

这一版把写入保护从 "一份静态清单" 扩成 "可声明, 可申请, 可保鲜" 的一整套: 工作区根可以放一份只读规则文件, 模型能在任务确实需要时申请本会话的可写授权, 命令侧的清单也会跟着工作区变化及时重算. 配置入口从 Web 设置页的独立页面迁到插件页的配置卡片, 引擎版本线抬到 @deepseek-ai/dsh-* 0.1.7-rc.1 —— 仍在更早引擎线上的部署请先读 Upgrade Notes, 继续使用 v0.1.1.

Highlights

  • 新增 request_writable_path 工具: 任务要反复写同一片受保护区域时, 由模型提出申请, 你在审批弹窗里逐次决定; 授权按路径生效并覆盖其下全部后代, 且只在本会话内存里.
  • 新增工作区只读规则文件 (默认 .readonly): 与 "保护路径" 同语义, 随工作区走, 因此可以配成 "整个工作区只读 + 逐个目录批准"; 这份文件自身是唯一硬保护, 任何授权都不放行.
  • 命令侧清单开始保鲜: macOS 上字面条目改按 Seatbelt 正则拒绝, 会话中途 git init 出来的 .git 也挡得住; 全平台按 agent 运行状态监听工作区, 变化之后立即重算.
  • 配置迁到插件页卡片: 两个多行文本, 规则文件名与各项上限, 三个开关, 刷新下界与上界都在 dsh-write-protect 卡片上, 预览面板按草稿展开, 不必先保存.
  • danger-full-access 下完全不再介入 (含规则文件硬保护), 与 "保护只在 read-only / workspace-write 下生效" 的口径对齐.

Breaking Changes

  • 引擎版本线: 依赖抬到 @deepseek-ai/dsh-* 0.1.7-rc.1 (cordis ^4.0.4), 配置面改用 volatile Config 与 configForms, 并新增 @deepseek-ai/dsh-tools 这条 peer. 迁移方式: 先把 DSH 升到 0.1.7-rc.1 一档; 仍在 0.1.6 及更早引擎线上的部署继续用 v0.1.1, 不要升到本版.
  • 配置入口迁移: Web 设置页里独立的 "写入保护" 页被移除, 改由插件页的 dsh-write-protect 卡片承载. 旧页面保存过的值 (保护路径, 额外可写根, broker 加固开关) 走的是 settings namespace, 本插件不再读取; patch 层的 readOnlyPaths / writablePaths / hardenBroker 照常作为部署 base 生效. 迁移方式: 打开插件页, 在卡片上按 Upgrade Notes 重新填写.
  • danger-full-access 行为变更: 该模式下不再应用写保护围栏, 也不再应用规则文件硬保护 (此前 write / edit 仍会拒绝保护路径). 迁移方式: 需要保护就改用 read-only 或 workspace-write, 这个模式的语义就是 "不设限".
  • 预览不再回退部署工作区根: 没有当前会话 (或会话记录里没有 cwd) 时预览直接报错, 不再拿 dsh web 的启动目录去展开. 迁移方式: 先在界面上选中一个会话再点预览.

Upgrade Notes

  1. 升级前确认引擎版本: 本版要求 @deepseek-ai/dsh-* 0.1.7-rc.1 一档 (peerDependencies 同号). 停在这条线之前的部署请继续用 v0.1.1.
  2. 升级后打开插件页, 在 dsh-write-protect 卡片上重新填写保护路径与额外可写根, 并按需调整规则文件名, 各项上限与三个开关; 旧设置页保存过的值不会被读取. 保存后实时生效, 不用重启 dsh web.
  3. 若之前依赖 "danger-full-access 下 write / edit 仍然挡保护路径", 请把模式改回 read-only 或 workspace-write.
  4. 想在项目里用 "按需申请" 的玩法: 在工作区根放一份 .readonly, 用 ** (或 /**) 保护整段工作区, 再让模型按目录逐个申请; 注意这份文件本身任何工具与命令都改不了, 要改只能在 DSH 之外编辑, 或换掉 readonlyFileName.
  5. 验证方式: 让模型写 .git 里的文件, 应当被拒并提示可以申请授权; 让它在会话中途执行 git init 后立刻写 .git, macOS 与 bwrap 可用的 Linux 上都应当被拒; 用卡片的预览按钮确认生效路径, 规则文件条目与本会话授权三组列表.

Features

  • 可写申请: request_writable_path 接受 path (字面路径, 支持 ~ / $NAME / // / 相对含 ..) 与 justification (给你看的一句话理由), 经审批弹窗逐次同意. 工作区外的目标成为本会话的额外可写根 (bash 与 write / edit 都能写); 工作区内被保护的目标只放开那棵子树. 授权按路径生效并覆盖后代, 批过父目录之后再写子目录不会再次弹窗; 上限 maxGrants (默认 8), 只存在内存里, 不跨会话也不写配置.
  • 申请开关 allowWritableRequests (默认开启): 关掉后工具仍然注册, 但任何调用都被拒且不弹审批, 提示词改为告知模型本部署不授予额外写权限.
  • 命令沙箱认本会话授权: bwrap 在保护路径的只读挂载之后追加授权子树的 --bind, Seatbelt 在保护 deny 之后追加授权 allow, 顺序保证授权不被只读压回去; Landlock 是纯 allow-list, 不叠加并告警一次 (这种 runner 下授权只作用于 write / edit).
  • 工作区只读规则文件: 只认工作区根那一份, gitignore 语义, 逐行追加在卡片文本之后 (因此既能新增保护条目, 也能用 ! 放行卡片里的条目). 只接受普通文件 (符号链接拒绝); // 绝对条目与越出工作区的 .. 条目拒绝; 条目超过上限 (默认 200) 截断; 三类情况都只告警. 文件名与上限可在卡片或 patch 里配置, 文件名置空即关闭这项识别.
  • 命令侧清单保鲜: macOS 上字面条目直接翻译成 (deny file-write* (regex ...)), 覆盖条目本体与全部后代, 与路径当时是否存在, 展开缓存新旧都无关; 含通配或转义的条目, 以及文本里出现 ! 取反时, 退回枚举清单. 全平台另按 agent 运行状态给工作区根装递归监听 (同一根被多个会话共用时最后一个结束才摘), 事件在 50ms 窗口内合并后立即后台重扫; 没有事件时按 "上次展开耗时 x 10" 兜底, 夹在 watchTtlMinMs (默认 2 秒) 与 watchTtlMaxMs (默认 30 秒) 之间.
  • write / edit 围栏改为按保护模式原文逐路径判定, 不再依赖扫盘得到的清单: 深层嵌套与尚不存在的匹配一样挡得住, 每条写入只做几次正则; 拒绝信息会指出命中的模式与围栏起点, 并提示可以申请本会话授权 (#1, @readlotusx).
  • 插件页卡片: 保护路径与额外可写根两个多行文本, 只读规则文件名, 最多条目数, 单会话可写授权上限, macOS broker 逃逸加固, 模型申请可写路径, 监听工作区变化, 刷新下界与上界; 预览面板新增规则文件条目与本会话授权两组, 并按当前草稿展开.
  • 提示词: 改为陈述生效的保护模式原文 (不再是枚举出来的绝对路径), 并说明规则文件来源, 本会话授权与申请工具的用法; 保护文本为空时不输出该段.

Bug Fixes

  • 兼容 DSH 0.1.6 起改为异步的 confine(): 官方把 SandboxProvider.confine() 的返回值换成 Promise<ConfinedArgv> 并加了 signal 参数之后, 插件原先的同步覆写会在那个 Promise 上读 .argv 并直接抛错, 于是每条命令都失败, 表现为载入插件后沙箱整体不可用; 现在 await 官方结果后再叠加保护路径与额外可写根, 并把官方 signal 原样透传, 取消语义一并恢复 (#1, @readlotusx).
  • macOS 命令侧: 会话中途才出现的受保护路径 (展开当时还不存在的 .git) 以前挡不住, 叠加展开缓存窗口后表现为时灵时不灵; 字面条目现在按正则拒绝, 不经过枚举, 也不受缓存影响.
  • provider 取 policy service 改用 ctx.get('sandboxPolicy'): 之前直接读 ctx.sandboxPolicy, 真实实例里会因为官方 LocalSandboxProvider 未声明该 inject 而抛错并落到退路 (单测的 Context 不活跃, 走的是另一条分支, 所以没暴露).
  • 可写申请里的相对路径先按工作区根解析: 之前申请工作区内的相对路径会被判成工作区外 (审批文案说错), 批准之后写入仍然被拒 —— 授权按相对形态记录, 下一次展开又把它当成工作区内条目丢掉.
  • 保护路径展开不再回退部署根: 从家目录启动 dsh web 时, 无会话的 resolve() 会把 .git 这类非锚定条目展开到部署根上, 实测同步扫了 101.5 秒, 期间事件循环被占死 (审批弹窗不出现, HTTP 全挂). 现在没有会话 cwd 就不展开, 预览也必须带当前会话 cwd, 否则报 400.
  • 预览改用被主视图持有的那条会话判定当前会话: sessions.list 快照里没有 current 字段, 之前读它恒为 undefined, 于是预览请求不带工作区根, Host 侧一律报 400.
  • 会话授权覆盖其下全部后代: 批过父目录之后再申请它的子目录不再重复弹窗 (此前 override 那一路会漏).
  • 只读展开缓存与会话授权解耦: 授权批准不再作废展开缓存, 也就不会紧接着触发一次同步全量扫盘, 批准后的卡顿消失.
  • danger-full-access 下不再应用写保护围栏, 规则文件硬保护也不再介入, 与该模式 "用户显式选择不设限" 的语义一致.

Performance

  • 保护路径展开不再发生在同步的 resolve() 里: 遍历改走 fs.promises, 每次 readdir / lstat 让出事件循环, 结果进缓存; confine() 与预览走异步的 materialize(). 此前工作区一大, 一次 resolve() 就能把 Host 事件循环占死几十秒到几分钟, 表现为整个 Web 连首页都打不开.
  • 监听只服务正在运行 agent 的会话, agent 跑完或会话销毁即摘除, 空闲时不占资源; 同一次展开的并发请求会合并到同一次扫描上.
  • write / edit 的每条判定只做几次正则, 与工作区大小无关.

Known Limitations

  • Windows 上 bash / pwsh 既挡不住保护路径, 也拿不到额外可写根; write / edit 两者都生效.
  • Linux 落到 Landlock 时无法单独保护子路径, 命令按官方沙箱跑并告警一次; 额外可写根可以加 --rw, 本会话授权在这种 runner 下只作用于 write / edit.
  • 命令侧的授权与 bwrap 都要求路径真实存在: bwrap 挂不上还不存在的授权子树时会跳过并告警, 等目录出现后下一条命令生效 (write / edit 与 Seatbelt 不受这条限制).
  • 命令侧的枚举窗口仍然存在 (macOS 字面条目除外): Linux bwrap / Landlock 与 macOS 上的通配条目只能保护枚举出来的路径. 有监听时改动后的下一条命令就吃到新清单, 监听不可用时退回时间兜底 (最多 watchTtlMaxMs, 默认 30 秒).
  • 正则拒绝只覆盖字面条目, 且文本里只要出现 ! 取反就整体退回枚举 (纯 deny 表达不了 last-match-wins); 目录标记条目在正则通道上不区分文件与目录, 同名文件也会被一并挡住, 属于收紧.
  • 规则文件内容按 1 秒 TTL 重读, 改完最迟 1 秒后按新内容判定; write / edit 侧立刻按新条目判定, 命令侧要等下一次枚举才会进清单.
  • 授权是会话级内存态: 不跨会话, 也不写配置文件; 需要长期生效的额外可写根请写卡片配置或 patch.
  • broker 加固只在 macOS 生效, 且属于纵深加固: 它关掉的是 launchd / LaunchServices 这条代理通道, 沙箱内的进程仍可通过其他本地守护进程 (Docker socket, ssh-agent 一类) 让沙箱外的服务代劳, 这类问题本插件不处理.

Thanks

  • @readlotusx (PR #1): 异步 confine() 的适配 (含 Linux 上真实 bwrap 的内核级端到端用例) 与 write / edit 围栏按保护模式逐路径判定的实现, 具体效果见上面 Bug Fixes 与 Features 两节.

What's Changed

  • 适配dsh 0.1.6 alpha by @readlotusx in #1

New Contributors

  • @readlotusx made their first contribution in #1

Full Changelog: v0.1.1...v0.2.0

dsh-write-protect v0.1.1

Choose a tag to compare

@github-actions github-actions released this 11 Sep 19:08

dsh-write-protect v0.1.1

Date: 2026-09-12

这一版堵上两处能绕过只读保护的漏洞, 并修掉 Windows 上额外可写根的解析错误. macOS 用户升级后 broker 逃逸加固默认开启, 如果确实需要从沙箱内驱动宿主 GUI, 请先读 Upgrade Notes.

Highlights

  • macOS: 堵住 open 经 launchd 把命令挪到沙箱外执行的逃逸, deny file-write* 不再被整条绕开, read-only 下同样生效.
  • Windows: 额外可写根里的 \ 不再被当作转义符, C:\Users\me\caches 这类路径不会退化成落点不确定的盘符相对路径.
  • glob 展开去掉 5000 节点预算, 改为按目录剪枝, 并停止跟随目录符号链接, 超大工作区不再漏掉深层路径.
  • 设置页新增 "macOS broker 逃逸加固" 开关, 需要从沙箱内驱动宿主 GUI 时可以关掉.

Features

  • 设置页: 新增 macOS broker 逃逸加固开关, 对应 patch 的 hardenBroker, 缺省开启; 关掉后 provider 原样返回官方 argv.
  • 保护路径与额外可写根: 补上 Windows 写法说明, 保护路径的绝对条目写作 //C:/Users/me/secret, 额外可写根按字面反斜杠解析.
  • 市场展示: 仓库内声明截图并内嵌到 README 的设置页一节.

Bug Fixes

  • macOS: 官方 profile 的 (allow default) 让沙箱内一条 open x.app 经 launchd 派生出不继承 profile 的进程, 从而在沙箱外任意写入; 现在在 profile 末尾追加 (deny mach-lookup (global-name-prefix "com.apple.coreservices")), (deny appleevent-send) 与 (deny mach-priv-task-port). SBPL 按 last-match-wins 解释, 追加在末尾才能盖过 allow; 名称过滤器按 reverse-DNS 分段匹配, 收窄到子服务无效, 只能整段拒绝.
  • macOS: 加固不再只认 workspace-write, 因为 read-only 的官方 profile 形状相同, 同样是 (allow default).
  • Windows: 额外可写根解析改为平台感知, \ 只在 POSIX 上转义, Windows 上按分隔符原样保留; ~\caches 与 ~/caches 一样展开为家目录, 展开, 盘符判定与工作区包含比较统一走 win32 路径 API.
  • Windows: 盘符相对路径 (C:caches) 改为拒绝并告警, 它的落点取决于进程当前目录, 会静默给出调用方从未指定的可写根.
  • Windows: 保护路径的 glob 元字符检测不再跳过 \ 后面的字符.
  • glob 展开: 目录性检查改用 lstat, 指向目录的符号链接不再走进去, 避免 Applications -> /Applications 这类链接把大项目启动扫卡住.

Performance

  • glob 展开: 删除 EXPAND_NODE_BUDGET, 超大工作区不再因节点预算截断而漏掉深层路径; 已经会被保护的目录不再往里扫, 被 ! 放行的目录仍继续展开.

Known Limitations

  • Windows 上 bash / pwsh 既挡不住保护路径, 也拿不到额外可写根; write / edit 两者都生效.
  • Linux 落到 Landlock 时无法单独保护子路径, 命令按官方沙箱跑并告警一次; 额外可写根可以加 --rw.
  • danger-full-access 下 bash 不进沙箱; write / edit 仍然挡保护路径.
  • 通配只覆盖展开当时已存在的路径; 字面条目即使尚不存在, write / edit 也会拒绝.
  • 展开结果缓存 5 秒, 逐次 tool call 复用.
  • broker 加固是纵深加固: 它关掉的是 launchd / LaunchServices 这条代理通道, 沙箱内的进程仍可通过其他本地守护进程 (Docker socket, ssh-agent 一类) 让沙箱外的服务代劳.

Upgrade Notes

  1. 固定版本安装 dsh plugin --profile web add azazo1/dsh-write-protect#v0.1.1; 跟随最新版则去掉 #v0.1.1.
  2. macOS 上 broker 加固默认开启, 常规命令 (node, git, pnpm, python, curl, tar, rsync) 不受影响. 若流程确实需要从沙箱内 open 宿主 app, 在设置页关掉 "macOS broker 逃逸加固", 或在 patch 的 policy 行给出 hardenBroker: false; 关掉即恢复可以被 open 打穿的状态.
  3. Windows 用户检查额外可写根里的反斜杠路径: 它们现在按字面解析, 之前被吃掉分隔符而写成 C:Usermecaches 的行需要改回 C:\Users\me\caches. 盘符相对路径会被拒绝, 并出现在设置页预览的 "未生效" 列表里.
  4. 验证方式: 保护路径与额外可写根的行为不变, 用设置页的预览按钮确认生效路径; macOS 上可以让命令执行 open 一个宿主 app, 应当被拒绝.

Full Changelog: v0.1.0...v0.1.1

dsh-write-protect v0.1.0

Choose a tag to compare

@github-actions github-actions released this 08 Sep 08:17

dsh-write-protect v0.1.0

Date: 2026-09-08

首个发布版本. 为 DSH 沙箱补上工作区子路径级别的只读保护, 并支持在 workspace-write 下声明工作区外的额外可写根. 安装前请先阅读 Upgrade Notes.

Highlights

  • 为 DSH 沙箱补齐工作区子路径只读保护, 默认保护任意层级的 .git, 防止模型改动版本历史.
  • write / edit 工具在全平台挡住保护路径; Linux / macOS 上 bash 等命令同样生效, 并在 workspace-write 下把额外可写根并进 allow-list.
  • Web Settings 新增 gitignore 风格的 "写入保护" 页, 保存后实时生效; 预览按当前会话 cwd 展开, 不必先保存.
  • 额外可写根支持 ~, $NAME / ${NAME} 与相对 .. 路径, 不打穿 read-only.

Features

  • 沙箱 provider: Linux bwrap 叠加 ro-bind / --rw, macOS Seatbelt 追加 deny 与额外可写.
  • fs 围栏: write / edit 在官方模式围栏之前检查保护路径与额外根, danger-full-access 下保护路径仍然拒绝.
  • 保护路径: gitignore 语义, // 表示文件系统绝对路径, 通配与 ! 取反按 last-match-wins 解释.
  • 设置页预览: 把当前草稿交给 Host 展开, 列出生效路径与被忽略或拒绝的行.
  • systemPrompt: 告知模型哪些路径只读, 哪些额外根可写.

Known Limitations

  • Windows 上 bash / pwsh 既挡不住保护路径, 也拿不到额外可写根; write / edit 两者都生效.
  • Linux 落到 Landlock 时无法单独保护子路径, 命令按官方沙箱跑并告警一次; 额外可写根可以加 --rw.
  • danger-full-access 下 bash 不进沙箱; write / edit 仍然挡保护路径.
  • 通配只覆盖展开当时已存在的路径; 字面条目即使尚不存在, write / edit 也会拒绝.
  • 单次遍历最多 5000 个节点, 结果缓存 5 秒.

Upgrade Notes

  1. 执行 dsh plugin --profile web add azazo1/dsh-write-protect 安装, 固定版本使用 #v0.1.0.
  2. 预构建包见 GitHub Release 的 dsh-write-protect.tgz, 安装时跳过 allowBuilds.
  3. 安装后 patch 会替换 base bundle 的 sandbox-policy, fs-sandbox 与 Linux / macOS 上的 sandbox 行, 无需重启 dsh web.
  4. 验证方式: 在受保护工作区让模型写入 .git 内文件, 应被拒绝; 额外可写根默认为空.

Full Changelog: https://github.com/azazo1/dsh-write-protect/commits/v0.1.0