# 实验性讨论：Windows 安全拒绝、typed native argv 与显式路径转换

> **状态：实验性实现讨论 / Draft，Unreleased。**
> 本文介绍实验实现的设计、接口和工程取舍。它尚未完成生产验收，不是发布候选或完整安全认证。

关联：[验证矩阵与复现](<experimental-native-verification.md>)、[变更记录](<../CHANGELOG.md>)。

## 1. 要讨论的决定

本提案把三个容易混为一谈的问题拆开：

1. **没有可靠的 Windows Git Bash 围栏时怎么办？** 当前实验选择 fail-closed，不再“标注无围栏后继续”。
2. **必须精确保留 Windows 原生参数、明确解码原生输出时怎么办？** 新增独立 typed argv 入口，复用 Core 托管执行。
3. **原生 Node API 如何使用模型得到的 MSYS 路径？** 新增完整值运行时转换，不再把源码字符串猜测当作可靠机制。

另补 Agent 路由指引，让上述机制不是仅存在于 README 中的“需要用户手动教模型”的能力。

维护者已在 [PR #26 裁决](https://github.com/KannaKuron/dsh-gitbash-shell/pull/26#issuecomment-6146363513)接受 fail-closed 方向，要求默认开启且可实时关闭的行 Config 开关。正式验收门槛仍未完成。

## 2. 问题与非目标

### 2.1 MSYS 参数转换

在 Bash 启动原生程序时，MSYS 可能把 `/all`、`/c` 当作路径。单命令 EXCL、双斜杠等只适用于确定场景；EXCL 按前缀匹配，排除 `/c` 也可能影响真正的 `/c/...` 文件参数。

实验不全局设置 `MSYS2_ARG_CONV_EXCL=*`，不重写 Bash command，不猜某个字符串究竟是 option、路径还是程序源码。

### 2.2 输出编码

UTF-8 捕获不等于原生程序输出 UTF-8。locale 变量不保证改变其代码页；同一调用、甚至同一流可以混合编码。没有替换字符也不能证明转码正确。

实验只支持显式已知编码。**未知同流混编码不能普遍可靠解码；损坏后的字符串不能普遍还原原始字节。**

### 2.3 源码字面量猜测

legacy `codePaths` 无法识别 `/c/users` 是路径还是路由数据，也不能可靠处理所有动态表达式。它的默认值和既有兼容机制没有在本提案中移除。

显式 helper 是完整值接口，不是“更聪明的全程序重写器”。处理路径状业务数据时建议关闭 `codePaths`；普通工具路径参数翻译不因此关闭。

### 2.4 真正 Windows confinement

本提案**没有修复 OS 围栏**。匹配研究纠正了旧泛化：现代 MSYS cygheap 使用 VirtualAlloc，signal pipe 使用显式 SD；实际 Core 已补 TokenDefaultDacl。不能据此声称“再补默认 DACL 就解决”或“所有未来方案都无解”。

IPC/Token/AppContainer/VM 路线仍需隔离环境的真实正负权限验证；这些 OS 隔离机制不属于本提案的实现范围。

## 3. 原则与体验变化

| 项目 | 旧行为或既有原则 | 当前实验 |
|---|---|---|
| Windows 受限自有 Bash | 无围栏执行并标注 unconfined | RO/WW/缺失策略在执行前拒绝 |
| 默认 Shell | Git for Windows Bash | 保持；原生入口是显式补充，不是隐式 fallback |
| root PowerShell/provider | route B 不替换 | 保持 |
| 原生参数 | 通过 Bash/MSYS，可能转换 | typed native 入口只转换标明的路径 |
| Node 路径 | 字面量尽力重写 | 可显式转换运行时完整值；legacy 默认未改 |
| 编码 | 原有 UTF-8 捕获边界 | 新入口逐流显式编码，默认 utf8/replace |
| 设置/界面 | 现有 Config/UI | `experimentalNative` volatile 布尔，默认 true，同卡实时编辑 |
| 同装 dsh-ops 的受限会话 | 本插件 bash fail-closed；ops_bash 只发 full-access | 不是零命令工具：`ops_*` 在场时补位逻辑不遮蔽 pwsh（`inlineNeeds` 的 hidePwsh 短路，宿主 pwsh 故事保留），官方执行器带真围栏可直接跑命令；bash 要回来需切 danger-full-access 或关 experimentalNative（后者同时放开 ops_bash） |

**运行时开关：** 设置 → 插件 → dsh-gitbash-shell →「实验原生执行 / fail-closed」，权威值是 `gitbash-shell` 行 Config 的 `experimentalNative`，默认 true（包括旧配置缺键）。开启时受限/未确定 Windows Bash 拒绝，两个 companions 在能力可用且不冲突时注册。关闭时实时注销两个工具（旧 SDK 引用也拒绝），Bash 恢复旧版明确标注 `enforcement: 'unconfined'` 的受限执行并一次性日志告知；未确定策略保持旧版宿主委派。关闭不提供真实围栏，shell 文件写入限制不执行；官方文件工具策略始终独立。

变更作用于现有会话后续调用，无需重启，不终止已开始的后台 producer。native 在准备入口、异步准备后、执行入口及最终 spawn 重读开关；已关闭则不得开始新进程。重新开启恢复工具注册与拒绝机制。`posixPaths`、`codePaths`、`subagentDialect` 独立，不改变安全开关。拒绝错误说明设置关闭路径及用户在会话访问模式控件选择 `danger-full-access` 的路径，Agent 不得自行调整。

21 份词典均已补齐开关本地化，保留工具名、UNCONFINED 和 danger-full-access 的事实，维护者可继续审阅译文。

## 4. Agent 应如何选择

| 意图 | 选择 | 禁止的理解 |
|---|---|---|
| Shell 脚本、管道、重定向、globbing、POSIX utilities | `bash` | 不能把权限拒绝当作改用另一个执行入口的许可 |
| Windows `.exe` 精确 literal argv，或已知逐流编码 | 可见的 `gitbash_native` | 不是 shell source，也不是通用脚本包装器 |
| 完整 MSYS 路径交给原生 Node fs/path | 可见的 `gitbash_to_native_path` | 不转换路由、搜索数据、command/code；不授文件权限 |
| 真正 PowerShell cmdlet/服务/WMI | 可见且开关允许的 `pwsh` | 不调用隐藏名称、不把 PowerShell 嵌进 Bash 绕行 |
| 权限/访问拒绝 | 停止并说明 | 不自动换模式、解释器、文件工具或 Node I/O |
| 显示/解码失败 | 核对 producer exit 与输出事实 | 不盲目重复有副作用的命令 |

[路由生成](<../src/inline-tools.js>)在[提示组装](<../src/index.js>)按实际工具面生成，不只看开关意图。自有提示块可替换、幂等；重组后不残留失效建议。方言/子代理方言关闭不关闭安全选路指引。

PTC 中直调 schema 可只有 `run_code`：该描述解释如何选择已提供 SDK binding，具体 native/path binding 的描述进入 `tools:sdk`。没有把 SDK 声明误当成可直调工具，也不编造缺失名称。

**已验证提示送达，不等于已验证 LLM 必然正确决策。** 当前没有真实模型自主选工具评测。

## 5. 架构：不建立第二套进程系统

```text
agent-owned tool scope
  ├─ bash → private GitBashSandboxExecutor → existing Core lifecycle/provider
  ├─ gitbash_to_native_path → fresh shared translator → native_path（无I/O）
  └─ gitbash_native
       ├─ 每调用解析真实 standing policy / 严格请求校验
       ├─ prepared capability（WeakMap；不是可伪造的 policy DTO）
       ├─ bounded preparation / trusted artifact owner
       └─ per-operation scope（属于 agent，隔离 shell + subprocess）
            └─ actual LocalBashExecutor.executeArgv
                 └─ private guarded provider facade
                      └─ actual managed provider + per-stream output adapter
```

对应：[执行器](<../src/native-executor.js>)、[工具/Jobs/DTO](<../src/native-tool.js>)、[输出适配](<../src/native-output.js>)。

不改 root 服务条目，不另造 child_process runner/Jobs registry，不覆盖 private spawnSpec。新模块不静态导入宿主 peer；由既有 host resolution 通道注入真实模块。

## 6. 权限与拒绝

### 6.1 自有 Bash

[策略门](<../src/shell.js>)要求 Windows spec 明确为 `danger-full-access`；其他/缺失策略拒绝。工具层[预检查](<../src/bash-tool.js>)位于工作目录准备、环境收集和 Jobs admission/promotion之前。

实际 host SandboxUnavailableError 能解析时保留结构化 SANDBOX_UNAVAILABLE；直接/旧调用者缺导出也必须拒绝，不能因此运行。非 Windows 的受限路径仍继承父后端。

### 6.2 Native

不以部署 defaultMode 代替逐调用策略；准备、执行、目录 callback 前后、最终 spawn seam 重验真实 standing policy。Prepared request 只能由同一个 executor 产生，复制或跨 executor 不构成能力授权。

这只是本地 Windows danger-only 入口，**不是 native restricted-token adapter**；不支持模型传入 env、sandbox_permissions 或自动 escalation。

### 6.3 边界

官方文件工具有独立 policy；helper 转换不扩大权限。minimal 持久 PTY、侧栏交互终端和其他插件执行器不因本门控获得围栏。历史 unconfined 渲染仍兼容旧记录，不授权新调用降级。

## 7. 接口

### 7.1 `gitbash_to_native_path`

输入 `{ msys_path: string }`，成功值 `{ native_path: string }`。复用共享 translator 与 fresh mount facts；无 fs I/O、存在性判断或 env seeding。

Windows-native/cwd-relative 值声明后可 passthrough；未知 POSIX mount、缺映射、空值、NUL、URL明确拒绝。`msys_path`/`native_path`避开旧参数/结果的自动路径字段翻译，避免转换前被改写或成功值回流。

PTC 程序示例，假设 `inputPath` 是已经取得的完整路径值：

```typescript
const resolved = await tools.gitbash_to_native_path({ msys_path: inputPath });
const fs = await import('node:fs/promises');
return await fs.readFile(resolved.native_path, 'utf8');
```

这不是绕过文件拒绝的方式；已发生拒绝必须停止。

### 7.2 `gitbash_native`

| 字段 | 契约 |
|---|---|
| description | 必填、非空、NUL-free |
| executable | 必填 `{kind:'path',value}`，可解析绝对 `.exe`；无 bare command |
| argv | 必填 typed 数组；literal 原值，path 显式转换/规范化；允许空 literal |
| cwd | 可选绝对 typed path；默认 agent workspace |
| entrypoint | native 默认；已知解释器需显式 interpreter |
| stdoutEncoding/stderrEncoding | utf8 默认，或 gbk/utf16le/raw；不支持 auto |
| errors | replace 默认，strict opt-in |
| timeoutMs | 正有限数，默认120000ms，上限600000ms |
| run_in_background | 仅能力允许时声明；与显式 timeoutMs 同时给出会拒绝 |

Native typed path 不支持相对值、变量/home展开；需要时先通过显式 helper取得可用绝对值。Windows 启动字段/path argv规范化反斜杠；literal `/all`、`/c` 等不经过 MSYS。

拒绝未知字段、model env、path-list env、隐式脚本、`.cmd/.bat`。当前限制4096个argv及保守Windows command-line预算。已知解释器名称检查是防误门，不是二进制鉴定；重命名/custom exe可有自己的语言。

**Exact argv 指验证过的 Core/CRT/Node 语义，不承诺任意 cmd/非CRT parser 的 quoting。**

## 8. 编码与输出事实

只有已知 collect disposition `{maxBytes,spill?:{maxBytes}}` 转 raw pipe；未知/其他 disposition按原 provider契约委托。每 operation immutable config，stdout/stderr独立 decoder，只在 ingest解码。

- UTF8默认宽容替换，保留BOM；strict显式。
- Decoder能力在spawn前探测；unknown同流mixed选raw。
- 原字节产物与normalized UTF8产物分开；raw无可靠文本承诺。
- Reader为独立、非消费normalized UTF8 byte cursor；不是原编码byte游标，也不是Core Jobs全局游标。
- handle.done保留真实provider outcome/error身份；decode/storage问题只记facts，不伪造命令未执行。
- OutputCollector只使用公开push/snapshot/seal；不访问其私有spill字段。

| 字段 | 解释 |
|---|---|
| sourceComplete | 是否收到自然EOF；不能单独证明程序未被中止 |
| complete | 管线完整性；不承诺tail包含全文 |
| lossy/reliableText | 与尾缓存、解码、存储等损失合读，不是编码自动鉴定 |
| aborted | 取消/主动terminate/owner终止事实 |
| artifact.complete/capReached/error | 对该产物自身完整性、预算、存储错误判断 |
| rawBytes/rawCursor | 原流字节计数/游标 |
| decodedUtf8Bytes/decodedCursor | normalized UTF8计数/游标 |

Foreground DTO使用closed schemas；去undefined投影不放任unknown字段。无facts/投影失败是显式分支，不通过宽schema吞错。后台最终exit/facts进入现有Jobs文本source，不声称扩展Core Jobs结构化API。

## 9. 生命周期与预算

准备阶段workspace/scope/plugin mount/artifact callback受同一取消/有界期限控制，剩余预算交Core；后台/onExpiry:none只限制准备，已handoff producer不会被准备timer杀死。

operation-owned AbortController统一caller cancel、explicit kill、ownerdispose；先记录cause再terminate。正常settlement后释放scope不能反向污染已完成facts。

输出end/error/close/abort/provider failure分别收尾；默认250ms有界drain，不无限等后代持有管道。不保证所有输出在短drain窗口内到达，未完整必须如实标注。

默认两流raw+decoded总磁盘8MiB、自有payload16MiB；含保守tail/copy/scratch预留。**不是OS/RSS、provider buffer、caller副本或跨调用硬quota。**

trusted owner在真实policy/校验后创建自有临时目录，不从model cwd建产物目录。Adapter不创建目录、chmod、删除或授权；文件有限wx创建。完成产物保留供晚reader读取，无自动purge承诺。晚callback结果不续spawn，不武断删除可能仍被引用的目录；归trusted retention owner。

## 10. Jobs 与动态装配

复用registry.start/wait/read/kill/remove、processJob/processSources和owner/controller admission。Scope属于agent，不属于tool registration fiber；后台/promotion后卸载工具不提前销毁运行进程。

没有controller时明确拒绝，不以“admission失败后再运行前台”冒二次副作用风险。没有Jobs时为前台kill deadline；有Jobs时可timeout promotion。该差异需在正式用户体验验收中检查。

Windows own-bash agent scope挂两个companions，各自按 agent 创建注册模块。初始挂载、开关更新和 native Jobs 刷新共用注册入口：先注销自身注册，再检查当前 agent 的工具所有权；已有外部全局或同 agent 同名工具就让位。实际 Core 的 knownNames 包含隐藏工具，旧宿主回退 schemas 检查；检查失败也让位。外部所有者移除后，下次刷新可恢复。注册异常只记录日志，不阻断另一个 companion 的更新。两工具必须确认 apply 已执行；scope 就绪与实际注册成功分别报告，关闭或让位时不会报告注册成功。Bash/native 共用可选目录读取，有服务走官方 ensure（错误不降级），无服务走旧 policy.workspaceRoot ?? session.header.cwd 接缝。缺native executor/OutputCollector公开能力不撤bash/path。新工具不加入minimal持久终端通道，不改变root PowerShell、market patch或preset id。

## 11. 已发生的反证与修正

1. 初版ownerdispose/direct kill虽quiet且无LATE，却错报completed/aborted:false/artifact.complete:true。独立固定sentinel反证后，operation cause与adapter terminate共同修正；正常cleanup反向用例确保不污染成功结果。
2. trusted artifact callback初版可无限await；abort/deadline时0spawn但不返回。现准备阶段有界race，晚callback不恢复执行，保留资源责任明确。
3. 初版facts空宽schema现closed DTO；跨模块termination枚举/三参callback曾不同步，已对齐并真实Core负向校验。
4. 集成发现显式证据env未配置、路径夹具硬编码盘符/安装bootstrap；改test-only目录预载、动态盘符、宿主自身bootstrap，真实Node fs复跑通过。没有放宽产品断言掩盖失败。

详情见[验证文档](<experimental-native-verification.md>)。失败观察用例退出0只表示复现成功，不能算修复通过。

## 12. 合并讨论 / 未覆盖

- [x] 接受 fail-closed，新增默认 true 的 volatile 开关，关闭恢复旧行为。
- [x] Controller 缺失保持明确拒绝，不静默改前台重复执行。
- [x] Artifact ownership/quota/retention 由插件层自管；跨调用 quota/purge 尚未实现，不宣称已验收。
- [ ] **长期跟踪（不阻塞本 PR）**：Core 将来开放 native typed argv、逐流 encoding、closed DTO 等对应公开 API 时，遵循官方优先纪律整体切换并退役插件私有实现。
- [x] **同步上游**：前置 PR #24 已合并；实验已 rebase 到[上游 v0.39.4 的 main](https://github.com/KannaKuron/dsh-gitbash-shell/commit/52da331)（v0.39.4 起 `fastctxTools` 默认 OFF、新增 ops_* 互操作面），继承正式 v0.39.4 的兼容与激活验证；单装工具面在 `fastctxTools` OFF 下 companion 注册检测不受影响（`tests/runtime-preview.mjs`「companions mount on the single-install face with fastctxTools OFF」锁死设计文档 §10 语义）。
- [ ] **拆分评估**：建议两步 review：① Bash fail-closed + 行 Config/UI/拒绝引导/回退；② native/path、逐流输出与可见性路由。唯一开关在第一步建立，第二步复用同一字段。当前保留完整 Draft/RFC，尚未另开子 PR。
- [ ] **转正式的四项门槛**：真实 Agent 自主选路、生产 GUI（非 preview）、维护者 Windows 真机全量验证、非 CRT 解释器矩阵抽测。四项齐后才谈 ready for review → release。
- [ ] 真实Agent选工具评测、生产GUI/full apply、会话replay尚未验收。
- [ ] 真restricted-token/ACL/IPC/VM正负权限、PTY/SSH、任意非CRT/解释器quoting尚未验收。
- [ ] 真实disk exhaustion、吞吐/RSS峰值、跨任务DOS/自动purge尚未验收。
- [ ] 若未来Core提供受限native adapter，应如何探测启用而非偷换mode？

**当前结论：接口机制与确定性隔离用例可讨论；不能据此宣布普遍乱码解决、Windows沙箱恢复或正式发布就绪。**
