Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,47 @@
> 倒序排列,新版本条目在最上面。条目格式:`## vX.Y.Z — YYYY-MM-DD` + 类型(feat / fix / docs / chore)+ 要点 + 相关链接。
> 纪律见 AGENTS.md「变更记录纪律」:发版前先更新本文件并随版本提交;事故复盘、复现与真机验证记录也记在这里。

## Unreleased — 2026-10-13

**类型**:fix(MSYS 参数/编码提示、源码路径边界与 unconfined 可观测性) / test / docs。

### PR #24:超时转后台的独立 sandbox 警示

- promoted 返回值显式携带可选 sandbox facts,输出 schema 保持闭合;defineTool 与 JSON-schema 回退共用此契约。
- `renderPromoted` 自行追加 sandbox notes,不再依赖 output 预先含警示;promotion 输出保留丢失/spill 提示,
避免重复追加 sandbox 警示。无 sandbox/full-access 的原反馈保持不变,denied/runnerFailed 分支继续保留。
- 纯回归改用不含警示的原始文本,覆盖空输出、full-access、拒绝与 runner failure。
新增实际 ToolRuntime→私有执行器→内存 provider 的 promotion 回归(Jobs wait/read 为受控桩),
验证 RO/WW 警示各出现一次、full 无警示、DTO 与最终模型文本分离、丢失/spill 提示及 root 服务不变。
- 验证:普通完整回归 **166/166**、实际安装Core完整preview **18/18**,均exit0,无skip/cancel;
语法与diff检查通过。不等同于生产GUI或真实Jobs时钟验收。

### MSYS 兼容边界尝试修复

- **范围**:从 [`6b26ded`](https://github.com/KouzakiUmi/dsh-gitbash-shell/commit/6b26ded6cc3a2a6113579149286047629b433c48)
开出 `fix/msys-compatibility-boundaries`;不改 root PowerShell、market patch、生产 Profile、
安全默认或版本号。原生参数转换/混合编码/Windows shell confinement 不能在这层安全地普遍修好,
因此修工具提示与可观测性,不全局 EXCL=*、不猜码、不将警示冒充沙箱修复。
- **源码路径**:run_code 的完整 Unicode 字母/组合字符/数字和含 `#` 文件名字面量走共享挂载表;
合法 EOF 行注释不再让整份重写弃权;tagged templates(含 String.raw、调用/索引 tag)保持原样,
避免 raw 设备路径被错误转义;of/await/yield 等上下文词可作 tag 时保守不改。
未闭合/不确定正则与除法仍整份 no-op,未扩展插值或动态表达式猜测。
词法层仍无法识别非路径数据的业务用途,提示明确建议直接 Node fs/path 用 native/cwd-relative 路径。
- **提示门控**:真实 prompt listener 读取 live codePaths;OFF 不再承诺程序字符串会被翻译,
ON 改为 best-effort 并指出模板、动态/非路径数据限制。关闭 codePaths 不改变工具路径字段翻译。
- **原生互操作**:登记的 bash 描述无条件说明 `/all`/`/c` 转换、双斜杠/单命令 EXCL 前缀及其
对 `/c/...` 的影响;检查预期效果而非只看 exit0。说明 UTF-8 捕获与 CP936/GBK 不匹配,
显式转码须在字节捕获前且保持退出状态;locale 与全流猜码不是普遍修复。
- **unconfined 可见性**:执行句柄在等待 result() 前带 sandbox facts;前台正文、后台 outcome/
增量嵌入与超时转后台显示警示。后台登记输出联合新增可选 sandbox facts,指出计划执行边界,
不把未启动的 job 宣称为已经执行。保留原请求 mode,不把 unconfined 当 denial/escalation,
并明确不得通过 shell 绕过文件工具策略拒绝。双语说明区分文件读能力与 write/edit 围栏,
删除“所有未来版本都无解”的过度结论。
- **验证**:新增9项兼容回归和1项后台登记回归,普通全量 **166/166**;新增3项实际 Core
ToolRuntime/执行句柄/run_code transport 回归,完整无头 preview **17/17**。旧护栏保留;打包 dry-run、语法和 diff
检查通过。新增 preview 使用实际宿主 API 与内存 subprocess,已有 preview 保留真实 Git Bash/
PowerShell 进程验收;不等同于生产 GUI、真实 PTC 引擎或新 confinement 方案验证。

## v0.39.2 — 2026-10-13

**类型**:fix(route B 工具/预设生命周期、动态配置与客户端回归) / test。
Expand Down
52 changes: 38 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,20 +118,19 @@ bundle patch(`cordis.patch.yml`)是**纯新增**(v0.39.0 起):只插入本插件
**Windows 宿主上的事实**:`read-only` / `workspace-write` 权限预设下的 **shell 命令**,
实际以**不受限(unconfined)方式执行**:

- **为什么**:MSYS2 运行时无法在 restricted-token 沙箱下启动——`msys-2.0.dll`
初始化时创建的 cygheap 映射与信号管道,DACL 只含用户 SID;而 WRITE_RESTRICTED
令牌的二次写检查要求 restricting-SID ACE → 初始化即死于 Win32 error 5 /
0xC0000142,**任何版本、任何参数都无解**(cmd/pwsh 走匿名管道不受影响)。
这是 OS 层冲突,插件内没有真正的修法,只有两个选择:**如实标注的绕过**(本插件,
v0.13.0 起)或**直接拒绝执行**。选了前者,是因为装这个插件的目的就是用 Git Bash——
静默换 pwsh 顶上、或让每条命令都失败,都不如把真相摆在结果里。
- **如何标注(绝不静默)**:每条此类结果的元数据携带
`sandbox: { mode: "<请求的模式>", denied: false, enforcement: "unconfined" }`,
并在每个进程首次受限调用时打印一行说明日志(`File sandbox enforcement is
partial on this host.`)。
- **文件工具不受影响**:read / write / edit / glob / grep 的沙箱是**另一层**
(fs-tool policy),照常拦截工作区外访问——也就是说 `workspace-write` 下
shell 能写出工作区、文件工具不能,**两者的边界并不一致**。
- **为什么**:已验证的 Git Bash/MSYS 组合在现有 restricted-token 后端下初始化失败,
历史复现为 `msys-2.0.dll` 的 cygheap/signal-pipe 对象权限与 restricting-SID
二次写检查不兼容(Win32 error 5 / 0xC0000142)。当前没有提供可用的受限 Git Bash
实现,不声称所有未来运行时或隔离方案都无解。本插件保留**如实标注的绕过**;
没有静默回退 pwsh/cmd/WSL,本次兼容修复也没有恢复 shell 写入围栏。
- **如何标注**:结果元数据保留
`sandbox: { mode: "<请求的模式>", denied: false, enforcement: "unconfined" }`;
工具描述、前台正文、后台登记及结果摘要、超时转后台反馈都明确警示 shell 写限制未生效。
后台登记标明的是计划采用的执行边界,不代表命令已经运行;实例首次调用另有日志。
- **文件工具是另一层**:官方 fs-tool policy 对 write/edit 保留策略检查:
`read-only` 拒绝修改,`workspace-write` 只允许声明的工作区和平台临时可写根。
read/glob/grep 等读取并非一律限制在工作区内。shell 进程不受这些工具检查约束,
**两者的边界并不一致**;绝不能用 shell 绕过文件工具已返回的策略拒绝。
- **可观察的对照**(相对 pwsh-sandbox 时代):会话进程的完整性级别不再被压低
(Low → Medium);`$env:TEMP` 不再被重定向到 per-session 隔离目录。

Expand Down Expand Up @@ -396,6 +395,31 @@ PTC/创造及用户自建模式一律覆盖。
都不再改写,文件工具收 Windows 路径。**bash 始终是 Git Bash,不受此开关影响**——它只决定
跨工具的路径方言。

## 原生 Windows 程序与 run_code 的兼容边界

工具描述始终警示下面的参数、编码和安全限制;这些限制不随路径方言关闭而消失。
插件**不会自动改写 command、全局关闭 MSYS 参数转换或猜测输出编码**。

- **斜杠开关**:MSYS 在启动原生 Windows 程序时可能把 `/all`、`/c` 当路径。
本机已验证 `ipconfig //all`、`cmd //c` 或单命令
`MSYS2_ARG_CONV_EXCL='/all' ipconfig /all` 可保留开关。
排除值是前缀:排除 `/c` 也会影响真正的 `/c/...` 路径,该次原生调用应明确用
Windows drive-letter 路径。不要全局设置 `MSYS2_ARG_CONV_EXCL='*'`。
`cmd /c` 被转换后可能 exit0 却没有执行所需命令,应检查预期效果而非只看退出码。
- **输出编码**:宿主按 UTF-8 解码;原生 Windows 程序可能输出 CP936/GBK 或其他代码页。
先使用程序自己的 UTF-8 输出选项,或在捕获前按已知编码显式转换字节,保持原退出状态。
`LANG/LC_ALL` 不保证改变原生程序的代码页。不要全流默认 GBK 或依替换字符数自动猜码:
同一 Bash 调用可能混合 UTF-8/GBK,已有乱码字符串也不能可靠恢复原字节。
- **run_code**:`codePaths` 默认开,与 `posixPaths`/委托 gate 同时生效时,完整、可判定的
普通路径字面量按共享挂载表尽力转换,包括中文/组合字符/空格/`#` 文件名。
EOF 行注释不再阻止转换;tagged template(包括 String.raw)保持原样,避免破坏 raw/tag 语义。
插值模板、转义路径、不确定的正则/除法语法仍可能不改;不确定时整份 no-op。
直接调用 Node fs/path 时优先明确的 Windows drive-letter 或 cwd-relative 路径;
工具 path/file_path 字段仍走工具参数翻译。词法扫描不能识别路由、搜索数据或 shell
command 的用途;此类字符串不要依赖自动重写,必要时关闭 `codePaths`(提示同步标为 OFF)。
- **沙箱**:参见上方安全章节。警示可见性得到修复,但 Windows shell 的 unconfined
取舍未改变,文件工具的策略拒绝绝不授权 shell 重试。

## 与 dsh-better-sidebar 联动

装了 [dsh-better-sidebar](https://github.com/omdsh-dev/DSH-better-sidebar)(v0.15.2+) 时,Windows 上本插件会通过**它的官方设置补丁口**(运行时 `terminalShell` 设置,对方文档明示"settings-page overrides win for terminals opened afterwards")把它打开的终端 shell 指向 Git Bash——**UI 终端标签与模型侧 `terminal_*` 工具统一生效**,不改对方一行代码、新开的终端即生效。此外,**本插件的 bundle patch 还会给对方的行补上 `config.shell`**(启动期解析,连 **tab 标题也会显示 bash**;对方未安装时该行无害跳过)。规则:
Expand Down
72 changes: 54 additions & 18 deletions README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,24 +93,26 @@ restricted tokens, workspace fencing, denial classification, identical to
**On Windows**, shell commands under the `read-only` / `workspace-write`
permission presets actually run **UNCONFINED**:

- **Why**: the MSYS2 runtime cannot start under the restricted-token sandbox —
`msys-2.0.dll` creates its cygheap mapping and signal pipe with DACLs naming
only the user SID, while a WRITE_RESTRICTED token's pass-2 write check
demands a restricting-SID ACE, so init dies with Win32 error 5 / 0xC0000142
before argv ever runs — **every version, no workaround** (cmd/pwsh use
anonymous pipes and are unaffected). This is an OS-level conflict with no
in-plugin cure; the two options are **a labelled bypass** (this plugin,
since v0.13.0) or **refusing to run at all**. The former was chosen because
installing this plugin means wanting Git Bash — silently falling back to
pwsh, or failing every command, is worse than putting the truth in the result.
- **How it is labelled (never silent)**: every such result carries
`sandbox: { mode: "<requested mode>", denied: false, enforcement: "unconfined" }`,
plus a one-time notice per process (`File sandbox enforcement is partial on
this host.`).
- **File tools are NOT affected**: the read / write / edit / glob / grep
sandbox is a **separate layer** (the fs-tool policy) and still fences
out-of-workspace access. Under `workspace-write` the shell can write outside
the workspace while file tools cannot — **the two boundaries differ**.
- **Why**: tested Git Bash/MSYS combinations failed to initialize under the
existing restricted-token backend. Historical reproduction found cygheap/
signal-pipe object permissions incompatible with the restricting-SID write
check (Win32 error 5 / 0xC0000142). No working confined Git Bash implementation
is provided here; this is not a claim that every future runtime or isolation
design is impossible. The plugin retains its **labelled bypass**, with no
silent fallback to pwsh/cmd/WSL. Compatibility fixes do not restore confinement.
- **How it is labelled**: results retain
`sandbox: { mode: "<requested mode>", denied: false, enforcement: "unconfined" }`.
The tool description, foreground text, initial background registration,
background outcome and timeout-promotion feedback warn that shell write
restrictions are not enforced. Initial registration describes the planned
boundary, not a claim that the command has already run; there is also a
one-time notice per executor instance.
- **Official file tools have a separate policy**: write/edit still reject
mutations under `read-only`; `workspace-write` checks the declared workspace
and platform writable temp roots. Reads such as read/glob/grep are not all
limited to the workspace. Shell processes are not governed by these tool
checks — **the two boundaries differ**. Never bypass a file-tool policy
denial through shell.
- **Observable differences** vs the pwsh-sandbox era: the session process's
integrity level is no longer lowered (Low → Medium), and `$env:TEMP` is no
longer redirected to a per-session directory.
Expand All @@ -125,6 +127,40 @@ official PowerShell executor was never touched by this plugin. The
> The full analysis (the 0.6.0–0.13.1 failure history and the community's two
> camps) is in the CHANGELOG under v0.13.2 and in the `src/shell.js` header.

## Native Windows and run_code compatibility boundaries

The tool description always warns about these argument, encoding and security
boundaries, independently of the path-dialect switch. The plugin does not
silently rewrite command source, globally disable argument conversion or guess
output encodings.

- **Slash options**: MSYS can treat native Windows `/all` and `/c` as paths.
Tested alternatives are `ipconfig //all`, `cmd //c`, or a per-command
`MSYS2_ARG_CONV_EXCL='/all' ipconfig /all`. Exclusions match prefixes: excluding
`/c` also excludes `/c/...` file paths, so explicitly use native drive-letter
paths for those arguments. Do not set `MSYS2_ARG_CONV_EXCL='*'` globally.
A converted `cmd /c` may exit0 without performing the intended action; verify
the expected effect, not only the exit code.
- **Output encodings**: the host decodes UTF-8. Native programs may emit CP936/GBK
or another code page. Prefer a program's UTF-8 option, or explicitly convert
known-encoding bytes before capture while preserving the original exit state.
Locale variables do not guarantee native UTF-8. Do not decode all Bash output
as GBK or infer encoding from replacement-character counts: one call can mix
UTF-8 and GBK, and an already-corrupted string cannot recover the original bytes.
- **run_code**: `codePaths` defaults ON and follows the total/delegate dialect
gates. Complete compatible plain path literals are rewritten best-effort with
the shared mount table, including Unicode letters/marks, spaces and `#` names.
Legal EOF line comments no longer prevent rewriting; tagged templates (such
as String.raw) retain their data and raw semantics. Interpolation, escapes,
uncertain regex/division syntax may remain unchanged; uncertainty abandons the
entire rewrite. Prefer native drive-letter or cwd-relative paths for direct
Node fs/path APIs; typed tool path arguments remain translated separately.
Lexical scanning cannot distinguish routes/search data/shell command strings
from paths. Do not rely on rewriting those values; disable `codePaths` when
needed (the model hint now accurately states OFF).
- **Security**: warnings are now visible, but Windows unconfined behavior is
unchanged. A file-tool policy denial never authorizes a shell retry.

## Config

The `gitbash-shell` row (its id has matched the settings namespace since v0.24.0; it was
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"icon.svg"
],
"scripts": {
"test": "node --test tests/smoke.mjs tests/private-executor.mjs tests/client-regressions.mjs tests/bridge-regressions.mjs tests/runtime-regressions.mjs tests/host-regressions.mjs",
"test": "node --test tests/smoke.mjs tests/private-executor.mjs tests/client-regressions.mjs tests/bridge-regressions.mjs tests/runtime-regressions.mjs tests/host-regressions.mjs tests/compatibility-regressions.mjs",
"test:preview": "node --import ./tests/host-loader.mjs --test tests/preview.mjs tests/bridge-preview.mjs tests/runtime-preview.mjs tests/host-preview.mjs",
"version": "node -e \"const fs=require('fs');const m=JSON.parse(fs.readFileSync('dsh.plugin.json','utf8'));const v=JSON.parse(fs.readFileSync('package.json','utf8')).version;m.version=v;fs.writeFileSync('dsh.plugin.json',JSON.stringify(m,null,2)+'\\n')\" && git add dsh.plugin.json"
},
Expand Down
Loading
Loading