DSHarness 系统拆解 固定基线 47f943859b · 36 已复核 / 0 撰写中 / 36 章
English
工具系统·第 19 章

权限分析、审批与单调 Guard

谁提出风险、谁能放行、谁只能拒绝

已复核上游 47f943859b范围: 分析 pre-execute policy、approval waterfall、absence fail-closed、monotonic guards、preset 与每次调用授权。

结论:授权是分层的,只有最终否决具有单调性

DeepSeek Harness 并不存在一个中心化的“风险引擎”。在固定基线中,风险提议有两个入口:可扩展的 tools/pre-execute 监听器可以返回 ask;支持沙箱的 shell(Bash/PowerShell)或文件系统调用,也可以在自身已冻结的参数中请求严格更宽的模式。持久权限预设提供常驻的沙箱与询问策略;一次审批最多放行一次调用;同步 guard 则保留最终且不可逆的否决权。这些能力被有意设计成不同层次。

核心安全属性

注册表会在策略判断前对解析后的参数做脱离快照并深度冻结;pre、guard 与工具体共享这份不可变 graph,而审批通过 callId 关联更早的耐久 call record:Native tool/call 保留模型的 raw JSON,Code Mode tool/code-dispatch-start 保留 normalized JSON。pre 决策可以允许、拒绝或询问,却不能重写参数。询问获批不会绕过 guard:运行时按全局到 scope 链顺序检查,直到第一条拒绝或全部不表态;guard 没有允许结果。因此,策略判断与实际执行保持参数同一性,owner 的否决也不会被监听器顺序推翻。APPROVAL-EXEC-SNAPSHOTAPPROVAL-PRE-DECISIONAPPROVAL-PREPARE-ORDERAPPROVAL-GUARD-CONTRACT

1. 六个容易被相似产品文案混为一谈的概念

概念生命周期权限是否持久
工具可见性限制scope 在线组合期间移除继承 schema;它是能力呈现,不是执行授权
沙箱模式部署默认值或单个 Session 覆盖值通过真正执行约束的后端限制文件效果Session 覆盖:是
审批策略部署默认值或单个 Session 覆盖值ask 路由到回答器;never 拒绝所有询问Session 覆盖:是
权限预设Session 常驻选择把沙箱模式与审批策略打包
审批结果单次请求只有 allowed-once 放行;不会改变常驻策略审计事件对:是
单调 guard在线注册、逐调用判断最终同步、只拒绝的否决权否;产生的工具错误由调用方记录

最反直觉的是 approval/policy = never:它表示“永不弹出询问;凡需审批的动作一律拒绝”,而不是“不询问就批准”。全访问预设之所以无需询问,是因为沙箱本身已经开放,并非 never 构成授权。

2. 端到端决策图有两个审批入口

模型工具调用
  → durable tool/call 已存在
  → 参数脱离原引用并冻结
  → tools/pre-execute waterfall ── 拒绝 ─────────────→ 错误结果
              │
              ├─ ask → ApprovalService → allowed-once / 拒绝
              │
              └─ 允许
                   ↓
             全局 + scoped 单调 guard ───────────────→ 错误结果
                   ↓
             around wrapper → 工具体
                                  │
                                  └─ shell(bash/pwsh)或 fs sandbox_permissions
                                     → 严格扩权检查
                                     → ApprovalService
                                     → 仅为本次调用标记更宽模式
                                  ↓
                           有外部效果的后端执行
                                  ↓
                         post 策略 → 持久结果

通用入口在 guard 与工具体之前询问。沙箱入口位于 shell(bash/pwsh)或 fs 工具体内部,但仍发生在进程启动或文件变更之前。因此,通用 pre 监听器与工具特定的扩权请求可能对同一次调用各问一次;组合方应避免重复策略。反过来,guard 也可以在工具特定弹窗出现前,根据冻结后的扩权参数直接拒绝。

3. 风险由各层提出,并未被中心化评分

PreToolDecision 只有 allowdeny(reason)ask(reason?)。内建代码里没有风险等级、数值分数、通用命令分类器或带特权的“批准 token”。waterfall 末端默认允许,所以需要广泛风险分类的部署必须组合自己的监听器;内建沙箱工具采用的则是一对显式参数:sandbox_permissions 与非空 justification

模型可以提出什么

bash 指引把扩权约束为真实沙箱拒绝后的同一命令单次重试,要求选择够用的最窄更宽模式,并要求用户拒绝后不要继续尝试。这些指令有助于模型行为,但真正边界更严格:参数配对、运行时扩权表、审批结果与沙箱后端才负责执行。提示词不计作授权。

4. 为什么 tools/pre-execute 是可扩展策略,而不是不可绕过的闸门

pre 阶段是异步且按 scope 路由的 waterfall。监听器可以调用 next 继续下游、替换下游决策,或直接短路。因此注册顺序与 prepend 会影响结果。这个特性适合 hook、部署策略与机器审查器,但也意味着:若把“绝对拒绝”写成普通监听器,更靠前的监听器可以不调用下游、直接返回允许,从而绕过它。

独立 guard 注册表补上这一缺口

ctx.tools.guard() 注册同步且只拒绝的检查。运行时先判断全局层,再按从远到近的顺序判断所有适用 Agent scope 层;第一条拒绝原因胜出。任何 guard 都不能返回允许,而且 guard 之后不再运行 pre 监听器。APPROVAL-GUARD-REGISTRY

5. ApprovalService 是封闭且 fail-closed 的询问边界

结果含义工具后果
allowed-once某个回答器仅批准这次请求通用 gate 变为允许,或把请求的更宽模式仅标到本次调用
rejected明确拒绝,或 never 策略的确定性结果拒绝
cancelled请求被撤回拒绝/中止;迟到回答被丢弃
unavailable没有回答器、回答器抛错或返回非法值拒绝
能力缺失绝不升级权限

结果词表是封闭联合类型。服务会在分派前由自身检查 never 并返回 rejected,因此即使 prepended 回答器也无法覆盖它。处于 ask 时,回答器 waterfall 默认落到 unavailable;同步异常、异步拒绝与非法返回值都会被归一化为 unavailable。APPROVAL-OUTCOMESAPPROVAL-DECIDE-WATERFALL

在 ToolRuntime seam 上,缺少 ApprovalService 或调用没有 Agent 也会拒绝,因为此时不存在可审计的 Session 和路由身份。只有精确的 allowed-once 分支继续;rejected、cancelled 与 unavailable 保留不同错误文案,便于诊断。APPROVAL-ASK-MAPPING

6. 审计事件对可持久重放,但在线等待本身不可重放

开放 turn
  → append approval/asked { 新 id, toolName, callId?, reason? }
  → 等待回答器 waterfall
  → append approval/decided { 同一 id, 封闭结果 }
  → 返回结果

审批请求只能发生在开放 turn 内,因为 turn 是 Session 日志的提交与重放边界。请求刻意不重复携带参数:callId 将它链接到已经记录、已经呈现的工具调用,避免两份参数发生漂移。两个审计事件均为 log-only,不是模型 transcript block。模型通过 runtime context 得知常驻策略,并最终收到工具结果。APPROVAL-AUDIT-EVENTSAPPROVAL-REQUEST-SHAPEAPPROVAL-AUDIT-REQUEST

重放边界

重放日志能够恢复过去的询问、结果与最新常驻策略,但无法在 host 进程重启后复活一个尚未完成的 JavaScript Promise。invariant 允许崩溃尾部保留未匹配的 approval/asked,同时拒绝孤立 decision、重复开放 id、turn 外事件与未知词表。浏览器重连恢复是下文所述的另一套同进程 pending registry。APPROVAL-INVARIANT

7. 取消具有明确的竞态语义

若请求 signal 已中止,审批会在回答器分派前得到 cancelled。否则服务令回答器与 signal 竞速;取消立即结算,之后到达的回答成为无效操作。回答器计算会被隔离,但不会被强制杀死。

工具准备阶段会在策略前、审批后和分派前再次检查调用方取消。如果正是取消导致审批取消,结果是 ABORTED_BEFORE_DISPATCH;已经完成的策略拒绝可能作为信息更充分的结果保留。同进程工具体一旦开始,ToolRuntime 会等待它达到静止点,然后把原本成功的结果改为 aborted。工具作者必须把 signal 传给子进程或 provider。APPROVAL-CANCEL-DISPATCH

8. 预设是覆盖两个独立 knob 的持久常驻意图

预设表条目包含机器 key、沙箱模式、审批策略与可选呈现文本。custom 保留给派生状态:两个有效 knob 不匹配任何条目时才出现;它不能被配置或选择。若 shell 不声明约束能力、默认值无法解析,或组合默认值不匹配任何预设且又未显式指定默认值,服务会在加载期失败。APPROVAL-PRESET-CONFIG

事件职责是否直接控制执行
permission/preset记录用户选择意图,并区分相同 bundle 的不同预设名
sandbox/mode常驻沙箱覆盖值是,每次受约束调用都会 fold
approval/policy常驻询问行为是,每次 ask 都会检查

当前值由真实 knob 状态派生。如果上次记录的选择仍匹配,它会在多个同 bundle 名称中获胜;否则选择表中第一个匹配项,仍无匹配才是 custom。选择预设时先 append 意图事件,只为确实变化的 knob 写事件。APPROVAL-PRESET-DERIVEAPPROVAL-PRESET-WRITE

9. “未来 Session 默认值”与“当前 Session”是两种不同变更

真正新建的 Session 会读取最新用户设置,并在同步 session/created 发布期间固定全部三个权限事实;这早于创建返回、Agent 发布和首次 prompt,但不是早于 SessionStore 入表。带 seed、resume 或仅部分初始化的 Session 会保留自身有效权限,只补充缺失事实。因此,之后修改 Settings 只影响未来 Session,不会改变现有 Session。APPROVAL-PRESET-PIN

可选 permissions projection 暴露预设表选项与有效当前值;projection key 不存在意味着该能力未被组合,客户端应隐藏控制项。裸 /permission 命令报告当前值与可用值;/permission <key> 校验 key,并与 UI 共享同一个服务写入路径。APPROVAL-PRESET-PROJECTION

发布组合不等于库默认值

base bundle 发布了三行:read-only + askworkspace-write + askdanger-full-access + never。其环境变量派生默认值通常为 workspace-write + ask。权限服务独立 schema 默认值只有后两行,不能把这两个事实混为一谈。APPROVAL-SHIPPED-PRESETS

10. 单次沙箱授权必须严格更宽、明确提出且转瞬即逝

有效模式合法单次目标
read-onlyworkspace-writedanger-full-access
workspace-writedanger-full-access
danger-full-access

schema 公布封闭目标词表,但运行时会相对本次调用的有效常驻模式检查“严格更宽”。两个扩权字段必须成对出现,理由不能为空。同级或收窄请求会在弹窗前失败。APPROVAL-ESCALATION-LADDER

随后 resolver 要求 ApprovalService 与 Agent 都存在,把目标模式与理由记入审批 reason,并且只接受 allowed-once。返回模式只是单次调用的局部值;它不会 append sandbox/mode,也不会改变预设。APPROVAL-ESCALATION-FLOW

优先级

执行时,已批准的显式模式优先于 Session 最后一条 sandbox/mode,后者又优先于部署默认值。Session cwd 提供 workspace root。APPROVAL-SANDBOX-PRECEDENCE

11. Shell 与文件系统共享审批编排,再由不同后端真正执行约束

Bash 与 PowerShell 都先校验参数、解析常驻策略,在提出扩权时等待审批,把返回模式标到请求,然后才启动前台进程或提交后台 job starter。因为对象 schema 是开放的、呈现并不等于执行,未公布的扩权字段在当前 executor 不支持沙箱时仍会由工具体拒绝。APPROVAL-BASH-EXECUTIONAPPROVAL-PWSH-EXECUTION

文件系统变更工具使用共享 controller,执行同样的字段配对、常驻策略解析、审批与单次标记。provider 返回沙箱拒绝时,它会转换成结构化错误标记和“精确重试”提示;其他 provider 错误保持原样。APPROVAL-FS-ESCALATION

12. 被委派的 child 不继承 parent 的单次授权

创建 child 时,运行时同步捕获的只有 parent 的显式 Session 沙箱覆盖值——既不捕获部署默认值,也不捕获单次授权——并且只要审批能力存在,就把 child 的审批策略固定为 never。这些带 delegation source 的事件写在任何 fork seed 之后,因此旧 seed 无法重新开放 child。child 还会收到模型可见说明:需要审批的操作会被拒绝。APPROVAL-CHILD-PIN

权限后果

parent 可以在 child 已固定的沙箱范围内委派工作,但 child 模型无法通过询问自行扩权。之后,可信 runtime/用户仍可 append 更新的策略事件;所以这不是密码学意义上的永久锁。它防止的是通过正常审批路径发生的模型自主扩权,而不是阻止所有更高权限管理。

13. Web 审批是稳定的 server request,不是新的 unary RPC

host 发送带稳定 rpcIdapproval/requested,其中包含 Session id、approval id、工具名、可选 call id 与 reason。客户端响应复用相同 rpcId,并且只能提交 allowed-oncerejected;cancelled 与 unavailable 仍由 host 产生。APPROVAL-WIRE-CONTRACT

proxy 回答器会认领最新、匹配、未决定且未被其他 pending 项认领的 approval/asked 事件。如果找不到这条审计事实,它会交给下游,而不是凭空创建可回答请求。pending 项能跨浏览器断线保留;打开新 mux 时会用同一 id 重放。abort 与 proxy teardown 会将其结算为 cancelled。APPROVAL-WEB-REGISTRYAPPROVAL-WEB-REPLAY

响应先按 rpcId 路由,再经过解析,并且必须回显完全相同的 Session 与 approval id。第一个合法响应删除 pending 项;畸形、外来、过期与迟到响应都会被拒绝。APPROVAL-WEB-RESPOND

14. 审批面板是一次性的,但它展示的证据并不自包含

请求 pending 时,对话 UI 会用可滚动卡片替换 composer,显示 reason、可选命令、“拒绝”与“仅允许一次”。首次点击后按钮锁定;传输失败时重新启用;收到 resolved frame 后面板消失。只有当 root running tool call 的 JSON 参数包含字符串 command 时,界面才会解析并展示命令;参数畸形、嵌套、无法配对或非 shell 调用都不显示命令行。APPROVAL-UI-PANEL

知情同意的取舍

ApprovalRequest 不复制参数,避免了审计漂移,却也让已有工具调用呈现成为知情同意的一部分。对于文件系统或其他通用 ask,面板自身可能只有自由文本 reason 与工具名。更强的产品面可以从已记录调用渲染一份类型化、不可变摘要,而不创建第二份权威参数。

15. 权限选择器提供便利与风险确认,但不是执行边界

当前 Session 选择器过滤 custom 并提交 /permission <key>;可选命令弹窗执行同一路径;Settings 行用 expected revision 写入未来 Session 的 defaultPresetAPPROVAL-PERMISSION-SETTINGS-WRITE三者都消费服务端公布的选项,但其风险对话框只由一个硬编码机器 key 触发:danger-full-accessAPPROVAL-PERMISSION-COMPOSERAPPROVAL-PERMISSION-DECORATIONAPPROVAL-PERMISSION-SETTINGS

已确认的静态错配

服务端允许任意预设名,也允许任意沙箱/审批 bundle。因此,另一个名称映射到 danger-full-access + never 时不会触发确认;反过来,一个无害 bundle 若叫 danger-full-access 却会触发。直接命令或 Settings API 变更同样没有复选框。对于发布表,这个弹窗是有价值的 UX 警告,却不是通用授权检查。加固时应从公布的 bundle 或服务端所有的 risk 字段派生风险;真正权威仍应是服务端策略,而非 key 名称。APPROVAL-PRESET-CONFIG

16. 浏览器请求信任防御 origin 混淆,但源码明确说明它不是身份认证

每个 /api 请求都必须通过 Host 校验、显式 cross-site Fetch Metadata 拒绝,以及 Origin 存在时的同 authority 校验。这能阻止 DNS rebinding 与普通恶意网页的跨站请求。源码明确把网络可达性和身份认证留在范围外。APPROVAL-WEB-TRUST

部署边界

任何能够通过被接受 authority 合法访问本地 API 的客户端,都能看到 mux 并竞速回答 pending 审批;第一个 id 完全匹配的合法响应获胜。这符合其明确的本地单用户服务假设。若 carrier 要暴露给互不信任的用户,就必须在 Host/Origin 围栏之外增加“已认证主体到 Session”的授权。

17. ACP 是带精确所有权与单次选项的机器回答器

只有当 ACP bridge 拥有完全相同的 Agent,且请求携带 call id 时,它才会认领审批。它只呈现两个选项——仅允许一次与仅拒绝一次——并显式映射取消;所有未知的非允许选项都变为拒绝。它绝不会把响应升级成持久授权。APPROVAL-ACP-ANSWERER

这体现了回答器架构的意图:Web UI、ACP 或其他 provider 可以占据同一个 waterfall seam,而 ApprovalService 统一拥有词表、持久审计、取消与不可绕过的 never 策略。

18. 测试编码了负向路径,而不只是成功弹窗

测试组在固定基线中证明什么
ToolRuntime 策略deny/ask 映射、服务缺失、无 Agent 调用、非法结果、取消顺序,以及 pre 策略后的 guard
审批服务开放 turn 要求、审计事件对、首个回答器、异常/非法值、取消、新 id、prepended 监听器之前的 never,以及策略 context
权限预设fold、同 bundle 身份、custom、无变更写入、加载失败、新 Session 固定、seed/resume 保留与 HMR 补齐
沙箱扩权严格阶梯、字段配对、单次授权、非扩权拒绝、渠道缺失/无 Agent,以及全部结果
Web proxy往返、重连重放、畸形/外来/过期响应、abort、teardown、并行配对与无 call-id 请求隔离

APPROVAL-TOOLS-TESTSAPPROVAL-GUARD-TESTSAPPROVAL-SERVICE-TESTSAPPROVAL-PRESET-TESTSAPPROVAL-ESCALATION-TESTSAPPROVAL-WEB-TESTS

19. 两处 README 描述已经落后于生产源码

文档说法固定源码事实影响
权限 README 写成 permissionPresets/preset/permissionPresets运行时实际使用 permission/preset/permission运维或扩展作者可能搜索、集成错误标识符
API proxy README 声称 pending 表只处理 question,没有 approval 项proxy 已有完整审批 registry、重连重放、响应校验、abort 与 teardown文档中的 wire 能力和恢复模型已过时

APPROVAL-DOC-PRESET-DRIFTAPPROVAL-DOC-API-DRIFT

为何重要

权限代码对命名和生命周期误解尤其敏感。源码与测试内部一致;应修正公开 package README,让安全审阅从真实事件与命令路径开始。

20. 与 Codex 的窄范围比较:分层方式相似,策略丰富度不同

本节只使用 OpenAI 公开的 Agent approvals & securitySubagents 文档,读取日期为 2026-08-13。DeepSeek 一侧是固定 commit 的源码级证据;Codex 一侧是截至读取日期的文档级证据。本节不推断私有服务或未公开实现。

维度DeepSeek Harness 固定源码Codex 公开文档
核心分层沙箱模式与审批策略是两个独立持久 knob,由预设打包明确把沙箱模式与审批策略定义成两个独立控制项
典型平衡模式发布 workspace-write + askAuto 记录为 workspace-write + on-request
审批策略词表asknever记录了 untrustedon-requestnever 与 granular 类别
审查者可组合回答器 waterfall;发布 Web 与 ACP 回答器;未发现中心自动风险审查器记录了用户审查,以及通过独立 reviewer 完成的合格 auto_review
外部效果领域通用 pre 策略,加显式 bash/fs 扩权记录 shell/沙箱请求,以及带副作用的 app 与 MCP 工具审批
Fail-closed回答器缺失/抛错/非法、无 Agent 与 never 都会拒绝记录了无法获得必需审批时阻止动作;自动审查不会扩大沙箱
委派child 审批固定为 never,parent 单次授权不转移公开文档称 subagent 继承当前沙箱策略;无法呈现新审批时,需新批准的动作会失败
可迁移经验

两者都把技术 containment 与人/机器同意视作不同层。DeepSeek Harness 特别值得复用的设计是可扩展策略之后显式的 deny-only guard;Codex 文档体现的优势则是更丰富的审批策略词表与明确的自动审查角色。这个比较是架构对照而非功能打分:源码证据与产品文档的置信层级不同。

21. 设计评价、加固优先级与验证状态

发现评价优先级
冻结参数 + pre 不可重写强审计/执行同一性保持
可扩展审批之后的 guard能抵抗监听器顺序的强单调否决所有 owner 不变量都应采用
封闭结果与 fail-closed 缺失语义降级安全,且原因可诊断保持
预设与单次授权分离时间维度权限清楚;child 不继承临时扩权保持并补强文档
风险弹窗依赖预设名称可配置表下已确认的 UX/策略静态错配高优先级加固
非 shell 调用的审批面板不自包含同意质量依赖周边 call 呈现增加类型化摘要
可达但无身份认证只在明确的本地单用户信任模型下可接受多用户暴露前重审
README 标识符/恢复语义漂移实现能力强于文档描述尽快修正
验证范围

本章追踪了 ToolRuntime 准备与取消;审批类型、服务、事件与 invariant;权限配置、projection、命令、Settings 与发布 bundle;共享沙箱扩权及 shell/fs 消费者;child 委派;Web wire/registry/UI;ACP;以及聚焦的生产测试。本次交付还运行了覆盖审批、preset、扩权、文件、进程、Job 与 Terminal 的 15 个 targeted Vitest 文件:508 项通过,1 项跳过。静态发现均明确标注;测试通过不等于完成运行时渗透测试、OS 沙箱验证、已认证多用户行为验证,也不提供任何未公开 Codex 内部实现信息。

我的学习体会

内容仅自动保存到当前浏览器,不上传、不进入仓库。你可以导出 Markdown 自行归档。