OmX Issue 3257 Windows 命令可行性测试台(Phase-0 Harness):冻结契约、路径解析模型与 Only-Receipt 验证实践
【免费下载链接】oh-my-codexOmX - Oh My codeX: Your codex is not alone. Add hooks, agent teams, HUDs, and so much more.项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-codex
本篇文章围绕 OmX(Oh My codeX)仓库中docs/reports/issue-3257/windows-command-harness/目录的 Phase-0 可行性测试台展开。该测试台用于在 Windows 平台上验证 npm 全局安装流程中命令解析的安全性,严格遵循冻结的 Revision 8 命令契约,只产出“收据”(receipt)而不执行任何安装、生命周期脚本或外部命令。读完本文,你将掌握该 harness 的命令行用法、双模式(noop/disposable)与双来源(stable/dev)的收据形状、Windows 命令解析威胁模型(PATH 顺序 +.com/.exe/.bat/.cmd优先级)、嵌套调度器白名单机制,以及"确定性语料 + 所有者评审门"的验收边界。
背景:Issue 3257 要解决什么问题
OmX 作为 codex 的能力增强工具,其发布链路依赖 npm 全局安装(参见 package.json 中的prepack与postpack脚本)。在 Windows 上,npm 以npm.cmd批处理形式存在,安装时执行的生命周期脚本、嵌套的npm run边(edge)都可能被路径中预先存在的同名命令"遮蔽"(shadow),从而引发命令劫持风险。
Issue 3257 的目标是:在不执行任何命令的前提下,用确定性的方式评估"命令遮蔽"威胁是否可被建模与检测。为此,团队创建了这个Phase-0、仅收据(receipt-only)的 Node 测试台,用于冻结 Revision 8 命令契约,为后续真实 Windows 环境实证(Phase 1+)提供依据。
契约:冻结的 Revision 8 命令契约
测试台的核心约束记录在 README.md 中:
- 契约指纹:命令契约 SHA-256 为
4cacc4a13de4f6d53c54c9237aa4c1df6b9582cf824aa19d4911e12a57d447df,任何语料或策略与之不符即视为违反冻结策略。 - Windows 模型:只考虑启动前已存在的命令影子(command shadow),解析规则为 PATH 顺序优先,扩展名优先级为
.com、.exe、.bat、.cmd。 - 明确排除:启动后的替换(post-launch replacement)、TOCTOU 竞态、启动后 PATH 变更,以及任何产品代码执行。
- 嵌套调度器白名单:仅允许五条嵌套边:
npm run build、npm run verify:native-agents、npm run sync:plugin、npm run verify:plugin-bundle、npm run clean:native-package-assets。
这五条边与 package.json 中的真实脚本一一对应:build为编译主流程,verify:native-agents(node dist/scripts/verify-native-agents.js)、sync:plugin(node dist/scripts/sync-plugin-mirror.js)、verify:plugin-bundle(sync-plugin-mirror.js --check)与clean:native-package-assets(node dist/scripts/cleanup-explore-harness.js)共同构成 package.json 中prepack的发布前校验链。白名单冻结的不是任意命令,而是与发布流水线真实行为一致的最小边集。
分层防护:dispatcher.cmd 与 dispatcher.mjs
dispatcher.cmd只做一件事:委托给 Node 白名单检查器,自身不参与判断:
@echo off setlocal DisableDelayedExpansion node "%~dp0dispatcher.mjs" %* exit /b %ERRORLEVEL%其中的setlocal DisableDelayedExpansion是有意为之——批处理中若启用了延迟扩展,!字符可能被吞掉或展开,导致传入参数失真;禁用后保证%*原样传递给 Node。
dispatcher.mjs则是唯一裁决者(见 dispatcher.mjs):
const APPROVED_EDGES = new Set([ "npm run build", "npm run verify:native-agents", "npm run sync:plugin", "npm run verify:plugin-bundle", "npm run clean:native-package-assets", ]); const requestedEdge = process.argv.slice(2).join(" "); if (!APPROVED_EDGES.has(requestedEdge)) { process.stderr.write(`issue-3257 dispatcher rejected nested edge: ${requestedEdge || "<empty>"}\n`); process.exitCode = 64; } else { process.stdout.write(`${requestedEdge}\n`); }注意:白名单命中也只是打印该边(process.stdout.write),绝不会调用 npm 或任何外部命令——这正是"receipt-only"原则在调度器层的体现。
收据形状:noop / disposable × stable / dev
测试台入口 harness.mjs 的用法为:
node harness.mjs --mode noop|disposable --source stable|dev [--root <disposable-temp-root>]参数校验逻辑(harness.mjs):--mode与--source必填;选项不可重复;缺失值或未知选项都会输出 usage 并以退出码 64 结束;noop模式下传--root会被拒绝。
两条安装画像(install profile)
| 来源 | 画像 |
|---|---|
stable | install --global --ignore-scripts --no-audit --no-progress --prefix <FROZEN_PREFIX> oh-my-codex@latest |
dev | install --global --ignore-scripts --no-audit --no-progress --prefix <FROZEN_PREFIX> <VALIDATED_ABSOLUTE_CONTAINED_TARBALL> |
两个画像都显式携带--ignore-scripts(抑制生命周期脚本)、--no-audit(跳过审计)、--no-progress(关闭进度输出),并使用<FROZEN_PREFIX>占位符表示冻结的前缀路径;dev来源额外要求一个经过校验的绝对路径且被包含的 tarball。这两个画像与 issue-3257-update-owner-verification-manifest.json 中installProfiles的CONTROLLER_INSTALL_ENV_V1.windows/CONTROLLER_INSTALL_ENV_V1.posix一一对应。
fail-closed 的 disposable 根目录校验
disposable模式必须满足两个条件,否则一律 fail closed(见 harness.mjs):
- 解析后的根目录必须是系统临时目录(
os.tmpdir())的直接子目录(relative()结果不含..、不含/或\分隔符); - 目录名必须以
issue-3257-disposable-前缀开头。
这一设计杜绝了把任意目录当作"一次性根"传入的可能,确保测试台永远不会触碰仓库、用户目录或全局环境。
收据字段
无论哪种组合,收据(receiptType: ISSUE_3257_PHASE_0_FEASIBILITY_RECEIPT)都会原样报告:contractRevision: 8、契约 SHA-256、mode、source、disposableRoot、installProfile,以及一组恒为NOT_EXECUTED或SUPPRESSED的执行类字段:
installScripts: "SUPPRESSED"—— 安装脚本被--ignore-scripts抑制;productExecution、packageManagerMutation、globalOrUserMutation、externalLifecycleExecution、empiricalResult:全部NOT_EXECUTED;ownerReview: "REQUIRED"—— 收据必须交由所有者评审。
也就是说:收据是建议性的,永远不能自行关闭所有者门(owner gate)。
Windows 解析模型与确定性语料
解析算法
核心解析函数windowsResolve(harness.mjs)精确模拟 Windows 的cmd查找规则:按 PATH 目录顺序遍历,在每个目录内按.com、.exe、.bat、.cmd的扩展名顺序查找,命中即返回;全部未命中返回null。所有比较均做小写归一化,与 Windows 文件系统不区分大小写的语义一致。
判定分类
verifyCorpus(harness.mjs)先做三重"冻结策略校验"——契约版本必须为 8、契约哈希必须匹配、扩展名顺序必须是.com,.exe,.bat,.cmd、白名单边必须逐字一致——任何不一致直接抛错。随后对每个场景计算解析结果与处置(disposition):
| 处置 | 含义 |
|---|---|
ALLOW | 解析命中且不在\disposable\shadow\路径下 |
REJECT_SHADOW | 解析命中了\disposable\shadow\下的影子命令 |
REJECT_UNRESOLVED | 未解析到任何命令 |
语料中的四个场景
resolution-corpus.json 提供确定性 fixture:
- clean-path:
npm只存在于C:\Program Files\nodejs\npm.cmd,解析为该路径,ALLOW; - earlier-cmd-shadow:
C:\disposable\shadow位于 PATH 更前,npm.cmd影子优先命中,REJECT_SHADOW; - extension-precedence-shadow:同一目录下同时存在
npm.bat与npm.cmd,按扩展名优先级命中npm.bat,REJECT_SHADOW——验证了"先目录后扩展名、目录内扩展名有序"的双重优先级; - missing-command:目录为空,解析结果为
null,REJECT_UNRESOLVED。
语料的 schema 由 policy-schema.json 约束:command仅允许[A-Za-z0-9_-]+,expectedDisposition枚举严格限定为三种处置,且additionalProperties: false,从结构上杜绝语料漂移。
证据边界与限制(必须如实说明)
README 明确强调:该语料是确定性 fixture 数据,不是一次 Windows 运行的记录。测试台的能力边界是:
- 只覆盖启动前命令解析;
- 不能建立包管理器所有权(package-manager ownership)、生命周期安全性、Bun 行为、npm 行为,或对启动后竞态的保护;
- 收据输出是建议性的,不能关闭所有者门。
这一点在 issue-3257-update-owner-verification-manifest.json 中得到呼应:ownerGate.requiredBeforeClosure列出的关闭前提包括"所有者评审""实证执行证据""Bun 必须事务化或 fail closed 的证据""Bun dev 必须判定为不支持(fail unsupported)的证据",而residualWindowsThreatModel也再次列出被覆盖与排除的威胁面。
Phase-0 产物清单
本次 Phase-0 产出的文件全部位于docs/reports/issue-3257/windows-command-harness/,外加一份所有者验证清单:
- harness.mjs —— 主测试台,输出 JSON 收据;
- dispatcher.cmd —— 批处理委托入口;
- dispatcher.mjs —— 嵌套边白名单裁决器;
- resolution-corpus.json —— 确定性解析语料;
- policy-schema.json —— 策略 JSON Schema;
- README.md —— 本文所依据的契约文档;
- issue-3257-update-owner-verification-manifest.json —— 所有者验证清单(含 review 链的 SHA-256 审计痕迹)。
实践启示:如何将"仅收据"验证推广到你的发布流水线
从该 harness 可以提炼出三条可复用的工程原则:
- 验证与执行分离:在无法安全实证的环境(如跨平台 CI 矩阵、无 Windows runner 时),先做确定性建模验证(纯函数 + fixture),把"可能危险"的执行推迟到有实证条件的阶段,收据中显式标记
NOT_EXECUTED,绝不假装执行过。 - 冻结契约 + 双重校验:契约哈希、schema 约束、解析器内嵌断言三层校验相互印证(harness.mjs),任何一层漂移都会让验证失败而非静默通过。
- fail-closed 边界:无论是 disposable 根目录的前缀 + 直接子目录双重校验,还是白名单外边一律以退出码 64 拒绝,核心都是"无法证明安全即拒绝",这与 package.json 中
prepack串联五条校验边的发布纪律一脉相承。
总体而言,这个测试台是"安全验证不落地执行"理念的范本:它用确定性模型回答"Windows 命令遮蔽是否可检测",同时把结论的最终裁决权明确保留给所有者评审门。
【免费下载链接】oh-my-codexOmX - Oh My codeX: Your codex is not alone. Add hooks, agent teams, HUDs, and so much more.项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-codex
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考