oh-my-codex 0.20.2 → 0.20.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +7 -6
- package/Cargo.toml +1 -1
- package/README.md +26 -0
- package/crates/omx-runtime/Cargo.toml +1 -0
- package/crates/omx-runtime/src/main.rs +25 -2
- package/crates/omx-runtime/tests/execution.rs +219 -0
- package/crates/omx-runtime-core/src/dispatch.rs +96 -10
- package/crates/omx-runtime-core/src/engine.rs +839 -29
- package/crates/omx-runtime-core/src/lib.rs +8 -0
- package/dist/agents/__tests__/definitions.test.js +81 -0
- package/dist/agents/__tests__/definitions.test.js.map +1 -1
- package/dist/agents/__tests__/native-config.test.js +25 -0
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/native-config.d.ts +2 -1
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js.map +1 -1
- package/dist/auth/hotswap.d.ts +6 -0
- package/dist/auth/hotswap.d.ts.map +1 -1
- package/dist/auth/hotswap.js +85 -15
- package/dist/auth/hotswap.js.map +1 -1
- package/dist/autopilot/__tests__/ralplan-gate.test.js +1 -1
- package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
- package/dist/cli/__tests__/auth.test.js +60 -6
- package/dist/cli/__tests__/auth.test.js.map +1 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +80 -0
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +103 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/exec.test.js +136 -0
- package/dist/cli/__tests__/exec.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +258 -4
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +287 -1
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/mcp-parity.test.js +4 -1
- package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
- package/dist/cli/__tests__/native-hook-claim-journal.test.js +84 -6
- package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +3 -3
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
- package/dist/cli/__tests__/ralph.test.js +3 -3
- package/dist/cli/__tests__/ralph.test.js.map +1 -1
- package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +56 -171
- package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js.map +1 -1
- package/dist/cli/__tests__/ralplan.test.js +37 -111
- package/dist/cli/__tests__/ralplan.test.js.map +1 -1
- package/dist/cli/__tests__/resume.test.js +11 -10
- package/dist/cli/__tests__/resume.test.js.map +1 -1
- package/dist/cli/__tests__/session-search-help.test.js +25 -1
- package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +42 -4
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +105 -7
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +46 -0
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js +2 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
- package/dist/cli/doctor.d.ts +3 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +15 -2
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +4 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +138 -38
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp-parity.js +2 -2
- package/dist/cli/mcp-parity.js.map +1 -1
- package/dist/cli/native-hook-claim-journal.d.ts +16 -5
- package/dist/cli/native-hook-claim-journal.d.ts.map +1 -1
- package/dist/cli/native-hook-claim-journal.js +42 -29
- package/dist/cli/native-hook-claim-journal.js.map +1 -1
- package/dist/cli/ralph.js +2 -2
- package/dist/cli/ralph.js.map +1 -1
- package/dist/cli/ralplan.d.ts +4 -8
- package/dist/cli/ralplan.d.ts.map +1 -1
- package/dist/cli/ralplan.js +19 -112
- package/dist/cli/ralplan.js.map +1 -1
- package/dist/cli/session-search.d.ts +5 -0
- package/dist/cli/session-search.d.ts.map +1 -1
- package/dist/cli/session-search.js +87 -0
- package/dist/cli/session-search.js.map +1 -1
- package/dist/cli/setup.d.ts +5 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +80 -31
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/uninstall.d.ts +9 -0
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +61 -19
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/compat/__tests__/rust-runtime-compat.test.js +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +330 -1
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +2 -2
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/__tests__/models.test.js +67 -3
- package/dist/config/__tests__/models.test.js.map +1 -1
- package/dist/config/generator.d.ts +4 -20
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +499 -119
- package/dist/config/generator.js.map +1 -1
- package/dist/config/models.d.ts +12 -2
- package/dist/config/models.d.ts.map +1 -1
- package/dist/config/models.js +15 -1
- package/dist/config/models.js.map +1 -1
- package/dist/hooks/__tests__/agents-overlay.test.js +2 -2
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +4 -4
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +238 -543
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +38 -4
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +96 -9
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +531 -12
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +155 -23
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +482 -32
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +68 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +7 -1
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +43 -4
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/session.test.js +348 -2
- package/dist/hooks/__tests__/session.test.js.map +1 -1
- package/dist/hooks/agents-overlay.js +1 -1
- package/dist/hooks/session.d.ts +26 -2
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js +169 -13
- package/dist/hooks/session.js.map +1 -1
- package/dist/leader/__tests__/contract.test.js +57 -7
- package/dist/leader/__tests__/contract.test.js.map +1 -1
- package/dist/leader/contract.d.ts +16 -0
- package/dist/leader/contract.d.ts.map +1 -1
- package/dist/leader/contract.js +79 -4
- package/dist/leader/contract.js.map +1 -1
- package/dist/mcp/__tests__/code-intel-server.test.js +31 -3
- package/dist/mcp/__tests__/code-intel-server.test.js.map +1 -1
- package/dist/mcp/__tests__/wiki-server.test.js +18 -0
- package/dist/mcp/__tests__/wiki-server.test.js.map +1 -1
- package/dist/mcp/code-intel-server.d.ts.map +1 -1
- package/dist/mcp/code-intel-server.js +13 -3
- package/dist/mcp/code-intel-server.js.map +1 -1
- package/dist/mcp/wiki-server.d.ts +11 -0
- package/dist/mcp/wiki-server.d.ts.map +1 -1
- package/dist/mcp/wiki-server.js +9 -2
- package/dist/mcp/wiki-server.js.map +1 -1
- package/dist/modes/__tests__/base-tmux-pane.test.js +4 -4
- package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
- package/dist/notifications/temp-contract.d.ts.map +1 -1
- package/dist/notifications/temp-contract.js +4 -0
- package/dist/notifications/temp-contract.js.map +1 -1
- package/dist/pipeline/__tests__/orchestrator.test.js +4 -4
- package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
- package/dist/pipeline/__tests__/stages.test.js +8 -8
- package/dist/pipeline/__tests__/stages.test.js.map +1 -1
- package/dist/ralplan/__tests__/consensus-gate.test.js +134 -10
- package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
- package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts +2 -0
- package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts.map +1 -0
- package/dist/ralplan/__tests__/documented-leader-preflight.test.js +46 -0
- package/dist/ralplan/__tests__/documented-leader-preflight.test.js.map +1 -0
- package/dist/ralplan/__tests__/runtime.test.js +8 -4
- package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
- package/dist/ralplan/consensus-gate.d.ts.map +1 -1
- package/dist/ralplan/consensus-gate.js +58 -50
- package/dist/ralplan/consensus-gate.js.map +1 -1
- package/dist/ralplan/documented-leader-preflight.d.ts +34 -0
- package/dist/ralplan/documented-leader-preflight.d.ts.map +1 -0
- package/dist/ralplan/documented-leader-preflight.js +58 -0
- package/dist/ralplan/documented-leader-preflight.js.map +1 -0
- package/dist/ralplan/runtime.d.ts +1 -0
- package/dist/ralplan/runtime.d.ts.map +1 -1
- package/dist/ralplan/runtime.js +11 -7
- package/dist/ralplan/runtime.js.map +1 -1
- package/dist/runtime/__tests__/bridge.test.js +42 -1
- package/dist/runtime/__tests__/bridge.test.js.map +1 -1
- package/dist/runtime/bridge.d.ts +12 -0
- package/dist/runtime/bridge.d.ts.map +1 -1
- package/dist/runtime/bridge.js +52 -1
- package/dist/runtime/bridge.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +2620 -487
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts +2 -0
- package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/reasoning-artifact-contract.test.js +108 -0
- package/dist/scripts/__tests__/reasoning-artifact-contract.test.js.map +1 -0
- package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +46 -173
- package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js.map +1 -1
- package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js +39 -159
- package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -1
- package/dist/scripts/__tests__/smoke-packed-install.test.js +254 -2
- package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts +4 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +11013 -1153
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/notify-fallback-watcher.js +82 -108
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.js +74 -3
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.d.ts +9 -0
- package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.js +234 -79
- package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +39 -13
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts +12 -4
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.js +365 -44
- package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.js +19 -18
- package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker.d.ts +3 -0
- package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker.js +37 -28
- package/dist/scripts/notify-hook/team-worker.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +28 -2
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/notify-hook.js +1 -0
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/scripts/smoke-packed-install.d.ts +48 -3
- package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
- package/dist/scripts/smoke-packed-install.js +2796 -13
- package/dist/scripts/smoke-packed-install.js.map +1 -1
- package/dist/state/__tests__/mode-state-context.test.js +54 -0
- package/dist/state/__tests__/mode-state-context.test.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +2 -0
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/mode-state-context.d.ts +5 -0
- package/dist/state/mode-state-context.d.ts.map +1 -1
- package/dist/state/mode-state-context.js +59 -0
- package/dist/state/mode-state-context.js.map +1 -1
- package/dist/subagents/__tests__/leader-bootstrap-3181.test.js +24 -245
- package/dist/subagents/__tests__/leader-bootstrap-3181.test.js.map +1 -1
- package/dist/subagents/tracker.d.ts +1 -82
- package/dist/subagents/tracker.d.ts.map +1 -1
- package/dist/subagents/tracker.js +3 -275
- package/dist/subagents/tracker.js.map +1 -1
- package/dist/team/__tests__/api-interop.test.js +91 -1
- package/dist/team/__tests__/api-interop.test.js.map +1 -1
- package/dist/team/__tests__/delivery-e2e-smoke.test.js +52 -10
- package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
- package/dist/team/__tests__/idle-nudge.test.js +45 -15
- package/dist/team/__tests__/idle-nudge.test.js.map +1 -1
- package/dist/team/__tests__/mcp-comm.test.js +93 -27
- package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
- package/dist/team/__tests__/model-contract.test.js +110 -3
- package/dist/team/__tests__/model-contract.test.js.map +1 -1
- package/dist/team/__tests__/runtime-cli.test.js +9 -0
- package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +3204 -335
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +2359 -142
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/state.test.js +804 -36
- package/dist/team/__tests__/state.test.js.map +1 -1
- package/dist/team/__tests__/team-ops-contract.test.js +6 -0
- package/dist/team/__tests__/team-ops-contract.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +3049 -784
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-runtime-identity.test.js +38 -2
- package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
- package/dist/team/__tests__/worktree.test.js +6 -0
- package/dist/team/__tests__/worktree.test.js.map +1 -1
- package/dist/team/api-interop.d.ts.map +1 -1
- package/dist/team/api-interop.js +15 -9
- package/dist/team/api-interop.js.map +1 -1
- package/dist/team/exact-pane.d.ts +23 -0
- package/dist/team/exact-pane.d.ts.map +1 -0
- package/dist/team/exact-pane.js +133 -0
- package/dist/team/exact-pane.js.map +1 -0
- package/dist/team/idle-nudge.d.ts +12 -1
- package/dist/team/idle-nudge.d.ts.map +1 -1
- package/dist/team/idle-nudge.js +41 -14
- package/dist/team/idle-nudge.js.map +1 -1
- package/dist/team/model-contract.d.ts +2 -5
- package/dist/team/model-contract.d.ts.map +1 -1
- package/dist/team/model-contract.js +59 -17
- package/dist/team/model-contract.js.map +1 -1
- package/dist/team/runtime.d.ts +1 -0
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +1244 -180
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts +13 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +1430 -384
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state/dispatch.d.ts.map +1 -1
- package/dist/team/state/dispatch.js +26 -8
- package/dist/team/state/dispatch.js.map +1 -1
- package/dist/team/state/locks.d.ts.map +1 -1
- package/dist/team/state/locks.js +61 -13
- package/dist/team/state/locks.js.map +1 -1
- package/dist/team/state/tasks.d.ts.map +1 -1
- package/dist/team/state/tasks.js +4 -3
- package/dist/team/state/tasks.js.map +1 -1
- package/dist/team/state.d.ts +67 -1
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +636 -129
- package/dist/team/state.js.map +1 -1
- package/dist/team/team-ops.d.ts +5 -0
- package/dist/team/team-ops.d.ts.map +1 -1
- package/dist/team/team-ops.js +4 -0
- package/dist/team/team-ops.js.map +1 -1
- package/dist/team/tmux-session.d.ts +123 -23
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +1250 -291
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worktree.d.ts.map +1 -1
- package/dist/team/worktree.js +4 -0
- package/dist/team/worktree.js.map +1 -1
- package/dist/utils/__tests__/file-durability.test.d.ts +2 -0
- package/dist/utils/__tests__/file-durability.test.d.ts.map +1 -0
- package/dist/utils/__tests__/file-durability.test.js +57 -0
- package/dist/utils/__tests__/file-durability.test.js.map +1 -0
- package/dist/utils/file-durability.d.ts +15 -0
- package/dist/utils/file-durability.d.ts.map +1 -0
- package/dist/utils/file-durability.js +37 -0
- package/dist/utils/file-durability.js.map +1 -0
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +3 -0
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
- package/dist/wiki/lint.d.ts +4 -1
- package/dist/wiki/lint.d.ts.map +1 -1
- package/dist/wiki/lint.js +3 -2
- package/dist/wiki/lint.js.map +1 -1
- package/package.json +1 -1
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +24 -0
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +6 -4
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +3 -2
- package/plugins/oh-my-codex/skills/team/SKILL.md +13 -9
- package/skills/ralph/SKILL.md +6 -4
- package/skills/ralplan/SKILL.md +3 -2
- package/skills/team/SKILL.md +13 -9
- package/src/scripts/__tests__/codex-native-hook.test.ts +2826 -497
- package/src/scripts/__tests__/reasoning-artifact-contract.test.ts +129 -0
- package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +48 -185
- package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +44 -177
- package/src/scripts/__tests__/smoke-packed-install.test.ts +320 -2
- package/src/scripts/codex-native-hook.ts +10715 -1069
- package/src/scripts/notify-fallback-watcher.ts +84 -115
- package/src/scripts/notify-hook/auto-nudge.ts +71 -3
- package/src/scripts/notify-hook/team-dispatch.ts +242 -75
- package/src/scripts/notify-hook/team-leader-nudge.ts +40 -18
- package/src/scripts/notify-hook/team-tmux-guard.ts +366 -39
- package/src/scripts/notify-hook/team-worker-stop.ts +20 -16
- package/src/scripts/notify-hook/team-worker.ts +39 -26
- package/src/scripts/notify-hook/tmux-injection.ts +28 -2
- package/src/scripts/notify-hook.ts +1 -0
- package/src/scripts/smoke-packed-install.ts +3196 -14
- package/dist/subagents/__tests__/adapted-role-binding.test.d.ts +0 -2
- package/dist/subagents/__tests__/adapted-role-binding.test.d.ts.map +0 -1
- package/dist/subagents/__tests__/adapted-role-binding.test.js +0 -988
- package/dist/subagents/__tests__/adapted-role-binding.test.js.map +0 -1
- package/dist/subagents/adapted-role-binding.d.ts +0 -17
- package/dist/subagents/adapted-role-binding.d.ts.map +0 -1
- package/dist/subagents/adapted-role-binding.js +0 -75
- package/dist/subagents/adapted-role-binding.js.map +0 -1
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
1
2
|
import {
|
|
3
|
+
chmodSync,
|
|
2
4
|
existsSync,
|
|
5
|
+
linkSync,
|
|
3
6
|
lstatSync,
|
|
4
7
|
mkdirSync,
|
|
5
8
|
mkdtempSync,
|
|
@@ -8,24 +11,25 @@ import {
|
|
|
8
11
|
realpathSync,
|
|
9
12
|
rmSync,
|
|
10
13
|
statSync,
|
|
14
|
+
symlinkSync,
|
|
11
15
|
writeFileSync,
|
|
12
16
|
} from 'node:fs';
|
|
13
17
|
import { tmpdir } from 'node:os';
|
|
14
|
-
import { delimiter, join, resolve } from 'node:path';
|
|
18
|
+
import { delimiter, dirname, join, resolve } from 'node:path';
|
|
15
19
|
import { spawn, spawnSync, type ChildProcessWithoutNullStreams } from 'node:child_process';
|
|
16
20
|
import { pathToFileURL } from 'node:url';
|
|
17
21
|
import { TextDecoder } from 'node:util';
|
|
18
22
|
|
|
19
|
-
import TOML from '@iarna/toml';
|
|
23
|
+
import TOML, { parse as parseToml } from '@iarna/toml';
|
|
20
24
|
import { isManagedCodexHookCommand, planManagedCodexHooksRemoval } from '../config/codex-hooks.js';
|
|
21
25
|
import {
|
|
22
26
|
spawnPlatformCommand,
|
|
23
27
|
spawnPlatformCommandSync,
|
|
24
28
|
} from '../utils/platform-command.js';
|
|
25
|
-
|
|
26
29
|
import {
|
|
27
30
|
ensureReusableNodeModules,
|
|
28
31
|
} from '../utils/repo-deps.js';
|
|
32
|
+
import { escapeTomlString } from '../utils/toml.js';
|
|
29
33
|
|
|
30
34
|
|
|
31
35
|
export {
|
|
@@ -262,6 +266,16 @@ const CODEX_EVENT_LABELS: Readonly<Record<string, string>> = {
|
|
|
262
266
|
|
|
263
267
|
const PINNED_CODEX_VERSION = '0.142.5';
|
|
264
268
|
const PINNED_CODEX_VERSION_OUTPUT = `codex-cli ${PINNED_CODEX_VERSION}`;
|
|
269
|
+
|
|
270
|
+
/** Sanitized Codex 0.144.5 PreToolUse shape: documented fields only, with no pointer or tracker state. */
|
|
271
|
+
export const PACKED_CODEX_01445_NO_POINTER_NO_TRACKER_FIXTURE = Object.freeze({
|
|
272
|
+
hook_event_name: 'PreToolUse',
|
|
273
|
+
session_id: 'packed-01445-session',
|
|
274
|
+
turn_id: 'packed-01445-turn',
|
|
275
|
+
tool_name: 'Bash',
|
|
276
|
+
tool_use_id: 'packed-01445-tool',
|
|
277
|
+
tool_input: { command: 'omx ralplan role-intent write --role architect --parent-thread "$CODEX_THREAD_ID" --json' },
|
|
278
|
+
} as const);
|
|
265
279
|
const CODEX_APP_SERVER_MAX_STDOUT_FRAME_LENGTH = 1_048_576;
|
|
266
280
|
|
|
267
281
|
type JsonRecord = Record<string, unknown>;
|
|
@@ -446,8 +460,7 @@ function resolvePinnedCodexExecutable(
|
|
|
446
460
|
);
|
|
447
461
|
}
|
|
448
462
|
seenPathEntries.add(pathEntry);
|
|
449
|
-
|
|
450
|
-
if (remainingMs <= 0) throw versionProbeDeadlineError();
|
|
463
|
+
|
|
451
464
|
|
|
452
465
|
let pathEntryState = inspectCodexCandidate(pathEntry);
|
|
453
466
|
if (pathEntryState.kind === 'absent') {
|
|
@@ -486,6 +499,8 @@ function resolvePinnedCodexExecutable(
|
|
|
486
499
|
continue;
|
|
487
500
|
}
|
|
488
501
|
|
|
502
|
+
const remainingMs = deadline - Date.now();
|
|
503
|
+
if (remainingMs <= 0) throw versionProbeDeadlineError();
|
|
489
504
|
const { result } = spawnPlatformCommandSync(executable, ['--version'], {
|
|
490
505
|
cwd,
|
|
491
506
|
env,
|
|
@@ -502,6 +517,7 @@ function resolvePinnedCodexExecutable(
|
|
|
502
517
|
continue;
|
|
503
518
|
}
|
|
504
519
|
if (isNodeErrorWithCode(result.error, 'ETIMEDOUT')) {
|
|
520
|
+
if (remainingMs <= CODEX_APP_SERVER_TIMEOUTS.versionProbeMs) throw versionProbeDeadlineError();
|
|
505
521
|
observed.push(`${executable}: version probe timed out after ${CODEX_APP_SERVER_TIMEOUTS.versionProbeMs}ms and was force-terminated`);
|
|
506
522
|
continue;
|
|
507
523
|
}
|
|
@@ -1201,12 +1217,787 @@ export function probeCodexVersion(
|
|
|
1201
1217
|
): string {
|
|
1202
1218
|
return resolvePinnedCodexExecutable(cwd, env, seam).version;
|
|
1203
1219
|
}
|
|
1220
|
+
export const PACKED_INSTALL_PLUGIN_MCP_TARGETS = [
|
|
1221
|
+
['omx_state', 'state', 'omx-state'],
|
|
1222
|
+
['omx_memory', 'memory', 'omx-memory'],
|
|
1223
|
+
['omx_code_intel', 'code-intel', 'omx-code-intel'],
|
|
1224
|
+
['omx_trace', 'trace', 'omx-trace'],
|
|
1225
|
+
['omx_wiki', 'wiki', 'omx-wiki'],
|
|
1226
|
+
['omx_hermes', 'hermes', 'omx-hermes'],
|
|
1227
|
+
] as const;
|
|
1228
|
+
|
|
1229
|
+
const PACKED_INSTALL_OPERATIONAL_PROBE_TIMEOUT_MS = 5_000;
|
|
1230
|
+
|
|
1231
|
+
function buildPackedProbeEnv(overrides: NodeJS.ProcessEnv = {}): NodeJS.ProcessEnv {
|
|
1232
|
+
const env: NodeJS.ProcessEnv = { ...process.env };
|
|
1233
|
+
for (const key of Object.keys(env)) {
|
|
1234
|
+
const upper = key.toUpperCase();
|
|
1235
|
+
if (upper === 'NODE_OPTIONS' || upper.startsWith('OMX_') || upper.startsWith('CODEX_')) {
|
|
1236
|
+
delete env[key];
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
return { ...env, ...overrides };
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
export interface PackedInstallNpmFile {
|
|
1243
|
+
path: string;
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
export interface PackedInstallNpmPackResult {
|
|
1247
|
+
filename: string;
|
|
1248
|
+
files?: PackedInstallNpmFile[];
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
export interface PackedInstallCommandResult {
|
|
1252
|
+
status: number | null;
|
|
1253
|
+
stdout?: string | Buffer;
|
|
1254
|
+
stderr?: string | Buffer;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
export const PACKED_INSTALL_REQUIRED_ARTIFACT_PATHS = [
|
|
1258
|
+
'dist/config/models.js',
|
|
1259
|
+
'dist/config/models.d.ts',
|
|
1260
|
+
'dist/agents/definitions.js',
|
|
1261
|
+
'dist/agents/definitions.d.ts',
|
|
1262
|
+
'dist/agents/native-config.js',
|
|
1263
|
+
'dist/agents/native-config.d.ts',
|
|
1264
|
+
'dist/team/model-contract.js',
|
|
1265
|
+
'dist/team/model-contract.d.ts',
|
|
1266
|
+
'dist/cli/index.js',
|
|
1267
|
+
'dist/cli/index.d.ts',
|
|
1268
|
+
'dist/cli/omx.js',
|
|
1269
|
+
'dist/cli/omx.d.ts',
|
|
1270
|
+
'skills/team/SKILL.md',
|
|
1271
|
+
'plugins/oh-my-codex/skills/team/SKILL.md',
|
|
1272
|
+
'plugins/oh-my-codex/.codex-plugin/plugin.json',
|
|
1273
|
+
'plugins/oh-my-codex/.mcp.json',
|
|
1274
|
+
'plugins/oh-my-codex/.app.json',
|
|
1275
|
+
'plugins/oh-my-codex/hooks/hooks.json',
|
|
1276
|
+
'plugins/oh-my-codex/hooks/codex-native-hook.mjs',
|
|
1277
|
+
] as const;
|
|
1278
|
+
|
|
1279
|
+
export const PACKED_INSTALL_FORBIDDEN_ARTIFACT_PATHS = [
|
|
1280
|
+
'.gjc/',
|
|
1281
|
+
'docs/',
|
|
1282
|
+
'.omx/',
|
|
1283
|
+
] as const;
|
|
1284
|
+
|
|
1285
|
+
function normalizedPackPath(path: string): string {
|
|
1286
|
+
return path.replaceAll('\\', '/').replace(/^\.\//, '');
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
export function isForbiddenPackedInstallArtifact(path: string): boolean {
|
|
1290
|
+
const normalized = normalizedPackPath(path);
|
|
1291
|
+
return PACKED_INSTALL_FORBIDDEN_ARTIFACT_PATHS.some((prefix) => normalized.startsWith(prefix))
|
|
1292
|
+
|| normalized.endsWith('.tgz')
|
|
1293
|
+
|| normalized.endsWith('.tmp')
|
|
1294
|
+
|| normalized.endsWith('.temp')
|
|
1295
|
+
|| /(^|\/)(?:tmp|temp)\//.test(normalized);
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
export function assertPackedInstallFileMetadata(files: readonly PackedInstallNpmFile[]): void {
|
|
1299
|
+
const paths = new Set(files.map((file) => normalizedPackPath(file.path)));
|
|
1300
|
+
for (const requiredPath of PACKED_INSTALL_REQUIRED_ARTIFACT_PATHS) {
|
|
1301
|
+
if (!paths.has(requiredPath)) {
|
|
1302
|
+
throw new Error(`npm pack is missing required artifact: ${requiredPath}`);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
for (const path of paths) {
|
|
1307
|
+
if (isForbiddenPackedInstallArtifact(path)) {
|
|
1308
|
+
throw new Error(`npm pack includes forbidden workspace artifact: ${path}`);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
function assertTextIncludes(text: string, expected: string, surface: string): void {
|
|
1314
|
+
if (!text.includes(expected)) {
|
|
1315
|
+
throw new Error(`${surface} is missing required text: ${expected}`);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
function assertTextMatches(text: string, pattern: RegExp, surface: string): void {
|
|
1320
|
+
if (!pattern.test(text)) {
|
|
1321
|
+
throw new Error(`${surface} does not satisfy required pattern: ${pattern}`);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
export function assertInstalledRootReasoningHelp(help: string): void {
|
|
1326
|
+
assertTextIncludes(help, 'Usage: omx reasoning <low|medium|high|xhigh>', 'installed omx help');
|
|
1327
|
+
for (const unsupportedOption of ['--max', '--ultra']) {
|
|
1328
|
+
if (help.includes(unsupportedOption)) {
|
|
1329
|
+
throw new Error(`installed omx help must not advertise ${unsupportedOption}`);
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
export function assertInstalledRootReasoningRejection(
|
|
1335
|
+
mode: 'max' | 'ultra',
|
|
1336
|
+
result: PackedInstallCommandResult,
|
|
1337
|
+
configBefore: string | undefined,
|
|
1338
|
+
configAfter: string | undefined,
|
|
1339
|
+
): void {
|
|
1340
|
+
if (result.status !== 1) {
|
|
1341
|
+
throw new Error(`omx reasoning ${mode} must exit 1, received ${String(result.status)}`);
|
|
1342
|
+
}
|
|
1343
|
+
if (String(result.stdout ?? '').trim() !== '') {
|
|
1344
|
+
throw new Error(`omx reasoning ${mode} must not emit success stdout`);
|
|
1345
|
+
}
|
|
1346
|
+
if (configBefore !== configAfter) {
|
|
1347
|
+
throw new Error(`omx reasoning ${mode} must not create or mutate config.toml`);
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
const stderr = String(result.stderr ?? '');
|
|
1351
|
+
const fragments = mode === 'max'
|
|
1352
|
+
? [
|
|
1353
|
+
'Reasoning mode "max" is not supported by "omx reasoning".',
|
|
1354
|
+
'Per-agent "max" is configured with agentReasoning; direct -c model_reasoning_effort=... is passed to Codex and remains capability-dependent.',
|
|
1355
|
+
'Invalid reasoning mode "max". Expected one of: low, medium, high, xhigh.',
|
|
1356
|
+
'Usage: omx reasoning <low|medium|high|xhigh>',
|
|
1357
|
+
]
|
|
1358
|
+
: [
|
|
1359
|
+
'Reasoning mode "ultra" is not supported by OMX root or per-agent reasoning and is not an alias for "max".',
|
|
1360
|
+
'Direct -c model_reasoning_effort=... remains opaque Codex passthrough.',
|
|
1361
|
+
'Invalid reasoning mode "ultra". Expected one of: low, medium, high, xhigh.',
|
|
1362
|
+
'Usage: omx reasoning <low|medium|high|xhigh>',
|
|
1363
|
+
];
|
|
1364
|
+
for (const fragment of fragments) {
|
|
1365
|
+
assertTextIncludes(stderr, fragment, `omx reasoning ${mode} stderr`);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
export function assertInstalledReasoningRuntimeContract(models: Record<string, unknown>): void {
|
|
1370
|
+
const expected = ['low', 'medium', 'high', 'xhigh'];
|
|
1371
|
+
const equalTuple = (name: string, actual: unknown, values: string[]): void => {
|
|
1372
|
+
if (!Array.isArray(actual) || actual.length !== values.length || actual.some((value, index) => value !== values[index])) {
|
|
1373
|
+
throw new Error(`${name} must be exactly ${JSON.stringify(values)}`);
|
|
1374
|
+
}
|
|
1375
|
+
};
|
|
1376
|
+
|
|
1377
|
+
equalTuple('CANONICAL_REASONING_EFFORTS', models.CANONICAL_REASONING_EFFORTS, expected);
|
|
1378
|
+
equalTuple('AMBIGUOUS_UNSUPPORTED_REASONING_EFFORTS', models.AMBIGUOUS_UNSUPPORTED_REASONING_EFFORTS, ['max', 'ultra']);
|
|
1379
|
+
equalTuple('PER_AGENT_REASONING_EFFORTS', models.PER_AGENT_REASONING_EFFORTS, [...expected, 'max']);
|
|
1380
|
+
equalTuple('ROOT_REASONING_EFFORTS', models.ROOT_REASONING_EFFORTS, expected);
|
|
1381
|
+
equalTuple('ROOT_UNSUPPORTED_REASONING_EFFORTS', models.ROOT_UNSUPPORTED_REASONING_EFFORTS, ['max', 'ultra']);
|
|
1382
|
+
|
|
1383
|
+
const isLegacyUnsupported = models.isAmbiguousUnsupportedReasoningEffort;
|
|
1384
|
+
const isRootUnsupported = models.isUnsupportedRootReasoningEffort;
|
|
1385
|
+
const normalizeRootUnsupported = models.normalizeUnsupportedRootReasoningEffort;
|
|
1386
|
+
if (typeof isLegacyUnsupported !== 'function' || isLegacyUnsupported('MAX') !== true) {
|
|
1387
|
+
throw new Error('legacy unsupported reasoning helper must remain case-insensitive');
|
|
1388
|
+
}
|
|
1389
|
+
if (typeof isRootUnsupported !== 'function'
|
|
1390
|
+
|| isRootUnsupported('MAX') !== true
|
|
1391
|
+
|| isRootUnsupported('ultra') !== true
|
|
1392
|
+
|| isRootUnsupported('xhigh') !== false) {
|
|
1393
|
+
throw new Error('root unsupported reasoning helper must be a case-insensitive max/ultra classifier');
|
|
1394
|
+
}
|
|
1395
|
+
if (typeof normalizeRootUnsupported !== 'function'
|
|
1396
|
+
|| normalizeRootUnsupported(' MAX ') !== 'max'
|
|
1397
|
+
|| normalizeRootUnsupported('Ultra') !== 'ultra'
|
|
1398
|
+
|| normalizeRootUnsupported('xhigh') !== undefined) {
|
|
1399
|
+
throw new Error('root unsupported reasoning normalizer must canonicalize only max and ultra');
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
export function assertInstalledReasoningDeclarationContract(declarations: {
|
|
1404
|
+
models: string;
|
|
1405
|
+
definitions: string;
|
|
1406
|
+
nativeConfig: string;
|
|
1407
|
+
team: string;
|
|
1408
|
+
}): void {
|
|
1409
|
+
const { models, definitions, nativeConfig, team } = declarations;
|
|
1410
|
+
const tuple = (values: string[]): string => values.map((value) => `["']${value}["']`).join('\\s*,\\s*');
|
|
1411
|
+
assertTextMatches(models, new RegExp(`export\\s+declare\\s+const\\s+CANONICAL_REASONING_EFFORTS\\s*:\\s*readonly\\s*\\[\\s*${tuple(['low', 'medium', 'high', 'xhigh'])}\\s*\\]\\s*;`), 'models declaration');
|
|
1412
|
+
assertTextIncludes(models, 'export type ConfiguredAgentReasoningEffort = (typeof CANONICAL_REASONING_EFFORTS)[number];', 'models declaration');
|
|
1413
|
+
assertTextMatches(models, new RegExp(`export\\s+declare\\s+const\\s+AMBIGUOUS_UNSUPPORTED_REASONING_EFFORTS\\s*:\\s*readonly\\s*\\[\\s*${tuple(['max', 'ultra'])}\\s*\\]\\s*;`), 'models declaration');
|
|
1414
|
+
assertTextMatches(models, new RegExp(`export\\s+declare\\s+const\\s+PER_AGENT_REASONING_EFFORTS\\s*:\\s*readonly\\s*\\[\\s*${tuple(['low', 'medium', 'high', 'xhigh', 'max'])}\\s*\\]\\s*;`), 'models declaration');
|
|
1415
|
+
assertTextIncludes(models, 'export type PerAgentReasoningEffort = (typeof PER_AGENT_REASONING_EFFORTS)[number];', 'models declaration');
|
|
1416
|
+
assertTextMatches(models, new RegExp(`export\\s+declare\\s+const\\s+ROOT_REASONING_EFFORTS\\s*:\\s*readonly\\s*\\[\\s*${tuple(['low', 'medium', 'high', 'xhigh'])}\\s*\\]\\s*;`), 'models declaration');
|
|
1417
|
+
assertTextIncludes(models, 'export type RootReasoningEffort = (typeof ROOT_REASONING_EFFORTS)[number];', 'models declaration');
|
|
1418
|
+
assertTextMatches(models, new RegExp(`export\\s+declare\\s+const\\s+ROOT_UNSUPPORTED_REASONING_EFFORTS\\s*:\\s*readonly\\s*\\[\\s*${tuple(['max', 'ultra'])}\\s*\\]\\s*;`), 'models declaration');
|
|
1419
|
+
if (/isUnsupportedRootReasoningEffort\(value: string\): value is/.test(models)) {
|
|
1420
|
+
throw new Error('isUnsupportedRootReasoningEffort must be declared as a plain boolean, not a type predicate');
|
|
1421
|
+
}
|
|
1422
|
+
assertTextIncludes(models, 'export declare function isUnsupportedRootReasoningEffort(value: string): boolean;', 'models declaration');
|
|
1423
|
+
assertTextIncludes(models, 'export declare function normalizeUnsupportedRootReasoningEffort(value: string): RootUnsupportedReasoningEffort | undefined;', 'models declaration');
|
|
1424
|
+
|
|
1425
|
+
const definitionMatch = /export interface AgentDefinition\s*\{([\s\S]*?)\n\}/.exec(definitions);
|
|
1426
|
+
if (!definitionMatch) throw new Error('AgentDefinition declaration is missing');
|
|
1427
|
+
const reasoningField = /\breasoningEffort(\?)?:\s*([^;]+);/.exec(definitionMatch[1]);
|
|
1428
|
+
if (!reasoningField || reasoningField[1] === '?') {
|
|
1429
|
+
throw new Error('AgentDefinition.reasoningEffort must remain required');
|
|
1430
|
+
}
|
|
1431
|
+
if (reasoningField[2] !== "'low' | 'medium' | 'high' | 'xhigh'") {
|
|
1432
|
+
throw new Error(`AgentDefinition.reasoningEffort widened unexpectedly: ${reasoningField[2] ?? 'missing'}`);
|
|
1433
|
+
}
|
|
1434
|
+
if (/\b(?:undefined|max|ultra)\b/.test(reasoningField[2])) {
|
|
1435
|
+
throw new Error('AgentDefinition.reasoningEffort must not allow undefined, max, or ultra');
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
assertTextIncludes(nativeConfig, 'reasoningEffort?: PerAgentReasoningEffort;', 'native config declaration');
|
|
1439
|
+
assertTextIncludes(team, 'export type TeamReasoningEffort = PerAgentReasoningEffort;', 'Team declaration');
|
|
1440
|
+
if (/\bultra\b/.test(team)) {
|
|
1441
|
+
throw new Error('Team declaration must not recognize ultra');
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
export function assertInstalledTeamSkillContract(canonical: Buffer, pluginMirror: Buffer): void {
|
|
1446
|
+
if (!canonical.equals(pluginMirror)) {
|
|
1447
|
+
throw new Error('canonical and plugin Team skills must be byte-identical');
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
const skill = canonical.toString('utf-8');
|
|
1451
|
+
assertTextMatches(skill, /agentReasoning[\s\S]{0,240}`low`, `medium`, `high`, `xhigh`,(?: and)? `max`/, 'Team skill');
|
|
1452
|
+
assertTextMatches(skill, /`max`[\s\S]{0,240}passed[\s\S]{0,80}unchanged/i, 'Team skill');
|
|
1453
|
+
assertTextMatches(skill, /`max`[\s\S]{0,240}capability-dependent/i, 'Team skill');
|
|
1454
|
+
assertTextMatches(skill, /`ultra`[\s\S]{0,180}unsupported[\s\S]{0,180}not an alias for[\s\S]{0,100}`max`/i, 'Team skill');
|
|
1455
|
+
assertTextMatches(skill, /invalid configured values[\s\S]{0,180}built-in role-default fallback/i, 'Team skill');
|
|
1456
|
+
assertTextMatches(skill, /explicit raw `-c model_reasoning_effort=\.\.\.`[\s\S]{0,240}opaque[\s\S]{0,240}wins/i, 'Team skill');
|
|
1457
|
+
assertTextMatches(skill, /both sources[\s\S]{0,160}explicit raw reasoning[\s\S]{0,160}inherited Team reasoning[\s\S]{0,160}environment reasoning/i, 'Team skill');
|
|
1458
|
+
assertTextMatches(skill, /do not downgrade or retry `max` as `xhigh`[\s\S]{0,160}built-in role defaults remain unchanged/i, 'Team skill');
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
function isJsonRecord(value: unknown): value is Record<string, unknown> {
|
|
1462
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
function readInstalledPluginManifest(packageRoot: string, relativePath: string): Record<string, unknown> {
|
|
1466
|
+
const path = join(packageRoot, relativePath);
|
|
1467
|
+
if (!existsSync(path)) throw new Error(`installed plugin manifest is missing: ${relativePath}`);
|
|
1468
|
+
|
|
1469
|
+
let manifest: unknown;
|
|
1470
|
+
try {
|
|
1471
|
+
manifest = JSON.parse(readFileSync(path, 'utf-8'));
|
|
1472
|
+
} catch (error) {
|
|
1473
|
+
throw new Error(`installed plugin manifest is not parseable JSON: ${relativePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1474
|
+
}
|
|
1475
|
+
if (!isJsonRecord(manifest)) {
|
|
1476
|
+
throw new Error(`installed plugin manifest must be a JSON object: ${relativePath}`);
|
|
1477
|
+
}
|
|
1478
|
+
return manifest;
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
function assertInstalledPluginString(value: unknown, path: string, expected?: string): void {
|
|
1482
|
+
if (typeof value !== 'string' || value.trim() === '' || (expected !== undefined && value !== expected)) {
|
|
1483
|
+
throw new Error(`installed plugin manifest field is invalid: ${path}`);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
function assertInstalledPluginHookLauncherContract(hookSource: string): void {
|
|
1488
|
+
const requiredShape: Array<[RegExp, string]> = [
|
|
1489
|
+
[
|
|
1490
|
+
/['"]omx-command\.json['"]/,
|
|
1491
|
+
'read its pinned omx-command.json delegate configuration',
|
|
1492
|
+
],
|
|
1493
|
+
[
|
|
1494
|
+
/\bspawn\s*\(\s*command\s*,\s*\[\s*\.\.\.\s*argsPrefix\s*,\s*['"]codex-native-hook['"]\s*\]/s,
|
|
1495
|
+
'spawn the configured delegate with the codex-native-hook argument',
|
|
1496
|
+
],
|
|
1497
|
+
[
|
|
1498
|
+
/\.stdin\s*\.end\s*\(\s*input\s*\)/,
|
|
1499
|
+
'forward the hook input to the delegate stdin',
|
|
1500
|
+
],
|
|
1501
|
+
];
|
|
1502
|
+
for (const [pattern, requirement] of requiredShape) {
|
|
1503
|
+
if (!pattern.test(hookSource)) {
|
|
1504
|
+
throw new Error(`installed plugin native hook launcher must ${requirement}`);
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
export function assertInstalledPluginSurface(packageRoot: string): void {
|
|
1510
|
+
const pluginManifest = readInstalledPluginManifest(packageRoot, 'plugins/oh-my-codex/.codex-plugin/plugin.json');
|
|
1511
|
+
assertInstalledPluginString(pluginManifest.name, 'plugin.json.name', 'oh-my-codex');
|
|
1512
|
+
assertInstalledPluginString(pluginManifest.version, 'plugin.json.version');
|
|
1513
|
+
assertInstalledPluginString(pluginManifest.skills, 'plugin.json.skills', './skills/');
|
|
1514
|
+
assertInstalledPluginString(pluginManifest.mcpServers, 'plugin.json.mcpServers', './.mcp.json');
|
|
1515
|
+
assertInstalledPluginString(pluginManifest.apps, 'plugin.json.apps', './.app.json');
|
|
1516
|
+
assertInstalledPluginString(pluginManifest.hooks, 'plugin.json.hooks', './hooks/hooks.json');
|
|
1517
|
+
|
|
1518
|
+
if (!isJsonRecord(pluginManifest.interface)) {
|
|
1519
|
+
throw new Error('installed plugin manifest field is invalid: plugin.json.interface');
|
|
1520
|
+
}
|
|
1521
|
+
for (const key of ['displayName', 'shortDescription', 'longDescription', 'developerName', 'category']) {
|
|
1522
|
+
assertInstalledPluginString(pluginManifest.interface[key], `plugin.json.interface.${key}`);
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
const mcpManifest = readInstalledPluginManifest(packageRoot, 'plugins/oh-my-codex/.mcp.json');
|
|
1526
|
+
if (!isJsonRecord(mcpManifest.mcpServers)) {
|
|
1527
|
+
throw new Error('installed plugin manifest field is invalid: .mcp.json.mcpServers');
|
|
1528
|
+
}
|
|
1529
|
+
const mcpServers = mcpManifest.mcpServers;
|
|
1530
|
+
for (const [serverName, server] of Object.entries(mcpServers)) {
|
|
1531
|
+
if (!isJsonRecord(server)
|
|
1532
|
+
|| typeof server.command !== 'string'
|
|
1533
|
+
|| server.command.trim() === ''
|
|
1534
|
+
|| !Array.isArray(server.args)
|
|
1535
|
+
|| server.args.some((arg) => typeof arg !== 'string')
|
|
1536
|
+
|| typeof server.enabled !== 'boolean') {
|
|
1537
|
+
throw new Error(`installed MCP server is invalid: ${serverName}`);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
for (const [serverName, service] of PACKED_INSTALL_PLUGIN_MCP_TARGETS) {
|
|
1541
|
+
const server = mcpServers[serverName];
|
|
1542
|
+
if (!isJsonRecord(server)
|
|
1543
|
+
|| server.command !== 'omx'
|
|
1544
|
+
|| !Array.isArray(server.args)
|
|
1545
|
+
|| server.args.length !== 2
|
|
1546
|
+
|| server.args[0] !== 'mcp-serve'
|
|
1547
|
+
|| server.args[1] !== service
|
|
1548
|
+
|| server.enabled !== false) {
|
|
1549
|
+
throw new Error(`installed MCP server does not match the packaged contract: ${serverName}`);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
const appManifest = readInstalledPluginManifest(packageRoot, 'plugins/oh-my-codex/.app.json');
|
|
1554
|
+
if (!isJsonRecord(appManifest.apps)) {
|
|
1555
|
+
throw new Error('installed plugin manifest field is invalid: .app.json.apps');
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
const hooksManifest = readInstalledPluginManifest(packageRoot, 'plugins/oh-my-codex/hooks/hooks.json');
|
|
1559
|
+
if (!isJsonRecord(hooksManifest.hooks)) {
|
|
1560
|
+
throw new Error('installed plugin manifest field is invalid: hooks.json.hooks');
|
|
1561
|
+
}
|
|
1562
|
+
const expectedHookCommand = 'node "${PLUGIN_ROOT}/hooks/codex-native-hook.mjs"';
|
|
1563
|
+
for (const eventName of PACKED_INSTALL_NATIVE_HOOK_SMOKE_EVENTS) {
|
|
1564
|
+
const entries = hooksManifest.hooks[eventName];
|
|
1565
|
+
if (!Array.isArray(entries) || entries.length === 0) {
|
|
1566
|
+
throw new Error(`installed plugin hook event is missing: ${eventName}`);
|
|
1567
|
+
}
|
|
1568
|
+
for (const entry of entries) {
|
|
1569
|
+
if (!isJsonRecord(entry) || !Array.isArray(entry.hooks) || entry.hooks.length === 0) {
|
|
1570
|
+
throw new Error(`installed plugin hook entry is invalid: ${eventName}`);
|
|
1571
|
+
}
|
|
1572
|
+
for (const hook of entry.hooks) {
|
|
1573
|
+
if (!isJsonRecord(hook) || hook.type !== 'command' || hook.command !== expectedHookCommand) {
|
|
1574
|
+
throw new Error(`installed plugin hook command must target the native launcher: ${eventName}`);
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
const hookPath = join(packageRoot, 'plugins/oh-my-codex/hooks/codex-native-hook.mjs');
|
|
1581
|
+
if (!existsSync(hookPath)) {
|
|
1582
|
+
throw new Error('installed plugin native hook launcher is missing');
|
|
1583
|
+
}
|
|
1584
|
+
const hookSource = readFileSync(hookPath, 'utf-8');
|
|
1585
|
+
if (!hookSource.startsWith('#!/usr/bin/env node')) {
|
|
1586
|
+
throw new Error('installed plugin native hook launcher must start with a Node shebang');
|
|
1587
|
+
}
|
|
1588
|
+
if (!/\bconst\s+OMX_PLUGIN_HOOK_LAUNCHER_CONTRACT_MARKER\s*=\s*['"]omx-plugin-hook-launcher:v1['"]/.test(hookSource)) {
|
|
1589
|
+
throw new Error('installed plugin native hook launcher is missing its stable contract marker');
|
|
1590
|
+
}
|
|
1591
|
+
assertInstalledPluginHookLauncherContract(hookSource);
|
|
1592
|
+
const syntaxCheck = spawnSync(process.execPath, ['--check', hookPath], { encoding: 'utf-8' });
|
|
1593
|
+
if (syntaxCheck.error || syntaxCheck.status !== 0) {
|
|
1594
|
+
const detail = String(syntaxCheck.stderr || syntaxCheck.error?.message || 'unknown syntax error').trim();
|
|
1595
|
+
throw new Error(`installed plugin native hook launcher fails Node syntax check: ${detail}`);
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
function assertInstalledRequiredArtifacts(packageRoot: string): void {
|
|
1600
|
+
for (const relativePath of PACKED_INSTALL_REQUIRED_ARTIFACT_PATHS) {
|
|
1601
|
+
if (!existsSync(join(packageRoot, relativePath))) {
|
|
1602
|
+
throw new Error(`installed package is missing required artifact: ${relativePath}`);
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
async function assertInstalledReasoningArtifacts(packageRoot: string): Promise<void> {
|
|
1608
|
+
const models = await import(pathToFileURL(join(packageRoot, 'dist/config/models.js')).href) as Record<string, unknown>;
|
|
1609
|
+
const nativeConfig = await import(pathToFileURL(join(packageRoot, 'dist/agents/native-config.js')).href) as Record<string, unknown>;
|
|
1610
|
+
const definitions = await import(pathToFileURL(join(packageRoot, 'dist/agents/definitions.js')).href) as Record<string, unknown>;
|
|
1611
|
+
const team = await import(pathToFileURL(join(packageRoot, 'dist/team/model-contract.js')).href) as Record<string, unknown>;
|
|
1612
|
+
|
|
1613
|
+
assertInstalledReasoningRuntimeContract(models);
|
|
1614
|
+
assertInstalledReasoningDeclarationContract({
|
|
1615
|
+
models: readFileSync(join(packageRoot, 'dist/config/models.d.ts'), 'utf-8'),
|
|
1616
|
+
definitions: readFileSync(join(packageRoot, 'dist/agents/definitions.d.ts'), 'utf-8'),
|
|
1617
|
+
nativeConfig: readFileSync(join(packageRoot, 'dist/agents/native-config.d.ts'), 'utf-8'),
|
|
1618
|
+
team: readFileSync(join(packageRoot, 'dist/team/model-contract.d.ts'), 'utf-8'),
|
|
1619
|
+
});
|
|
1620
|
+
|
|
1621
|
+
const codexHome = mkdtempSync(join(tmpdir(), 'omx-packed-max-native-'));
|
|
1622
|
+
try {
|
|
1623
|
+
writeFileSync(join(codexHome, '.omx-config.json'), JSON.stringify({
|
|
1624
|
+
agentReasoning: { architect: ' MAX ' },
|
|
1625
|
+
}));
|
|
1626
|
+
const generateAgentToml = nativeConfig.generateAgentToml as (
|
|
1627
|
+
agent: unknown,
|
|
1628
|
+
prompt: string,
|
|
1629
|
+
options: { codexHomeOverride: string },
|
|
1630
|
+
) => string;
|
|
1631
|
+
const agentDefinitions = definitions.AGENT_DEFINITIONS as Record<string, unknown>;
|
|
1632
|
+
const maxToml = generateAgentToml(agentDefinitions.architect, 'installed native max contract', { codexHomeOverride: codexHome });
|
|
1633
|
+
assertTextIncludes(maxToml, 'model_reasoning_effort = "max"', 'installed native TOML');
|
|
1634
|
+
if (maxToml.includes('model_reasoning_effort = "xhigh"')) {
|
|
1635
|
+
throw new Error('installed native TOML must not downgrade max to xhigh');
|
|
1636
|
+
}
|
|
1637
|
+
if ((parseToml(maxToml) as { model_reasoning_effort?: unknown }).model_reasoning_effort !== 'max') {
|
|
1638
|
+
throw new Error('installed native TOML must parse back to exact max');
|
|
1639
|
+
}
|
|
1640
|
+
} finally {
|
|
1641
|
+
rmSync(codexHome, { recursive: true, force: true });
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
const resolveTeamWorkerLaunchArgs = team.resolveTeamWorkerLaunchArgs as (options: {
|
|
1645
|
+
existingRaw?: string;
|
|
1646
|
+
preferredReasoning?: string;
|
|
1647
|
+
}) => string[];
|
|
1648
|
+
const resolveTeamWorkerLaunchDiagnostics = team.resolveTeamWorkerLaunchDiagnostics as (options: {
|
|
1649
|
+
existingRaw?: string;
|
|
1650
|
+
preferredReasoning?: string;
|
|
1651
|
+
}) => { actualReasoning?: string; reasoningSource?: string };
|
|
1652
|
+
const maxArgs = resolveTeamWorkerLaunchArgs({ preferredReasoning: 'max' });
|
|
1653
|
+
if (maxArgs.join('\u0000') !== ['-c', 'model_reasoning_effort="max"'].join('\u0000')) {
|
|
1654
|
+
throw new Error(`Team must transport configured max exactly, received ${JSON.stringify(maxArgs)}`);
|
|
1655
|
+
}
|
|
1656
|
+
if (maxArgs.includes('model_reasoning_effort="xhigh"')) {
|
|
1657
|
+
throw new Error('Team must not downgrade max to xhigh');
|
|
1658
|
+
}
|
|
1659
|
+
const opaqueUltra = resolveTeamWorkerLaunchDiagnostics({
|
|
1660
|
+
existingRaw: '-c model_reasoning_effort="ultra"',
|
|
1661
|
+
preferredReasoning: 'max',
|
|
1662
|
+
});
|
|
1663
|
+
if (opaqueUltra.reasoningSource !== 'explicit' || opaqueUltra.actualReasoning !== undefined) {
|
|
1664
|
+
throw new Error('Team must preserve opaque explicit ultra without recognizing it');
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
assertInstalledTeamSkillContract(
|
|
1668
|
+
readFileSync(join(packageRoot, 'skills/team/SKILL.md')),
|
|
1669
|
+
readFileSync(join(packageRoot, 'plugins/oh-my-codex/skills/team/SKILL.md')),
|
|
1670
|
+
);
|
|
1671
|
+
assertInstalledPluginSurface(packageRoot);
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
function smokeInstalledRootReasoningRejections(omxPath: string, cwd: string): void {
|
|
1675
|
+
const codexHome = mkdtempSync(join(tmpdir(), 'omx-packed-root-reasoning-'));
|
|
1676
|
+
try {
|
|
1677
|
+
const env = buildPackedProbeEnv({ CODEX_HOME: codexHome });
|
|
1678
|
+
const configPath = join(codexHome, 'config.toml');
|
|
1679
|
+
const usageResult = spawnSync(omxPath, ['reasoning'], { cwd, encoding: 'utf-8', env });
|
|
1680
|
+
if (usageResult.status !== 0) {
|
|
1681
|
+
throw new Error(`omx reasoning usage must exit 0, received ${String(usageResult.status)}`);
|
|
1682
|
+
}
|
|
1683
|
+
assertInstalledRootReasoningHelp(String(usageResult.stdout ?? ''));
|
|
1684
|
+
const maxResult = spawnSync(omxPath, ['reasoning', 'max'], { cwd, encoding: 'utf-8', env });
|
|
1685
|
+
assertInstalledRootReasoningRejection('max', maxResult, undefined, existsSync(configPath) ? readFileSync(configPath, 'utf-8') : undefined);
|
|
1686
|
+
|
|
1687
|
+
const originalConfig = 'model = "unchanged-root-model"\n';
|
|
1688
|
+
writeFileSync(configPath, originalConfig);
|
|
1689
|
+
const ultraResult = spawnSync(omxPath, ['reasoning', 'ultra'], { cwd, encoding: 'utf-8', env });
|
|
1690
|
+
assertInstalledRootReasoningRejection('ultra', ultraResult, originalConfig, readFileSync(configPath, 'utf-8'));
|
|
1691
|
+
} finally {
|
|
1692
|
+
rmSync(codexHome, { recursive: true, force: true });
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
export interface FakeCodexLaunch {
|
|
1697
|
+
argv: string[];
|
|
1698
|
+
cwd: string;
|
|
1699
|
+
hasResumeSqlite: boolean;
|
|
1700
|
+
OMX_NOTIFY_TEMP_CONTRACT: string | null;
|
|
1701
|
+
OMX_TEAM_WORKER_LAUNCH_ARGS: string | null;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
export function parseFakeCodexLaunches(capture: string): FakeCodexLaunch[] {
|
|
1705
|
+
const trimmed = capture.trim();
|
|
1706
|
+
if (!trimmed) return [];
|
|
1707
|
+
return trimmed.split('\n').map((line) => {
|
|
1708
|
+
const launch: unknown = JSON.parse(line);
|
|
1709
|
+
if (!isJsonRecord(launch)
|
|
1710
|
+
|| !Array.isArray(launch.argv)
|
|
1711
|
+
|| launch.argv.some((arg) => typeof arg !== 'string')
|
|
1712
|
+
|| typeof launch.cwd !== 'string'
|
|
1713
|
+
|| typeof launch.hasResumeSqlite !== 'boolean'
|
|
1714
|
+
|| (launch.OMX_NOTIFY_TEMP_CONTRACT !== null && typeof launch.OMX_NOTIFY_TEMP_CONTRACT !== 'string')
|
|
1715
|
+
|| (launch.OMX_TEAM_WORKER_LAUNCH_ARGS !== null && typeof launch.OMX_TEAM_WORKER_LAUNCH_ARGS !== 'string')) {
|
|
1716
|
+
throw new Error('fake Codex capture did not contain a valid launch record');
|
|
1717
|
+
}
|
|
1718
|
+
return {
|
|
1719
|
+
argv: launch.argv,
|
|
1720
|
+
cwd: launch.cwd,
|
|
1721
|
+
hasResumeSqlite: launch.hasResumeSqlite,
|
|
1722
|
+
OMX_NOTIFY_TEMP_CONTRACT: launch.OMX_NOTIFY_TEMP_CONTRACT,
|
|
1723
|
+
OMX_TEAM_WORKER_LAUNCH_ARGS: launch.OMX_TEAM_WORKER_LAUNCH_ARGS,
|
|
1724
|
+
};
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
function readFakeCodexLaunches(capturePath: string): FakeCodexLaunch[] {
|
|
1729
|
+
if (!existsSync(capturePath)) return [];
|
|
1730
|
+
return parseFakeCodexLaunches(readFileSync(capturePath, 'utf-8'));
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
function smokeInstalledLaunchArgumentBoundary(omxPath: string): void {
|
|
1734
|
+
const smokeRoot = mkdtempSync(join(tmpdir(), 'omx-packed-launch-boundary-'));
|
|
1735
|
+
const modelInstructionsPath = join(smokeRoot, 'model-instructions.md');
|
|
1736
|
+
try {
|
|
1737
|
+
const fakeBinDir = join(smokeRoot, 'bin');
|
|
1738
|
+
const launchCwd = join(smokeRoot, 'cwd');
|
|
1739
|
+
const codexHome = join(smokeRoot, 'codex-home');
|
|
1740
|
+
const isolatedHome = join(smokeRoot, 'home');
|
|
1741
|
+
const capturePath = join(smokeRoot, 'fake-codex-argv.jsonl');
|
|
1742
|
+
mkdirSync(fakeBinDir, { recursive: true });
|
|
1743
|
+
mkdirSync(launchCwd, { recursive: true });
|
|
1744
|
+
mkdirSync(codexHome, { recursive: true });
|
|
1745
|
+
mkdirSync(isolatedHome, { recursive: true });
|
|
1746
|
+
|
|
1747
|
+
const fakeCodexSource = [
|
|
1748
|
+
'const { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } = require("node:fs");',
|
|
1749
|
+
'const capturePath = process.env.OMX_PACKED_FAKE_CODEX_CAPTURE_PATH;',
|
|
1750
|
+
'if (!capturePath) process.exit(2);',
|
|
1751
|
+
'const priorLaunchCount = existsSync(capturePath) ? readFileSync(capturePath, "utf8").trim().split("\\n").filter(Boolean).length : 0;',
|
|
1752
|
+
'appendFileSync(capturePath, `${JSON.stringify({',
|
|
1753
|
+
' argv: process.argv.slice(2),',
|
|
1754
|
+
' cwd: process.cwd(),',
|
|
1755
|
+
' OMX_NOTIFY_TEMP_CONTRACT: process.env.OMX_NOTIFY_TEMP_CONTRACT ?? null,',
|
|
1756
|
+
' hasResumeSqlite: existsSync(require("node:path").join(process.env.CODEX_HOME ?? "", "state_5.sqlite")),',
|
|
1757
|
+
' OMX_TEAM_WORKER_LAUNCH_ARGS: process.env.OMX_TEAM_WORKER_LAUNCH_ARGS ?? null,',
|
|
1758
|
+
'})}\\n`);',
|
|
1759
|
+
'if (process.env.OMX_PACKED_FAKE_CODEX_QUOTA_ON_FIRST === "1" && priorLaunchCount === 0) {',
|
|
1760
|
+
' const sessionDir = require("node:path").join(process.env.CODEX_HOME ?? "", "sessions", "2026", "07", "13");',
|
|
1761
|
+
' mkdirSync(sessionDir, { recursive: true });',
|
|
1762
|
+
' writeFileSync(require("node:path").join(sessionDir, "rollout-packed-session-123.jsonl"), "{}\\n");',
|
|
1763
|
+
' process.stderr.write("HTTP 429 quota exceeded\\n");',
|
|
1764
|
+
' process.exit(1);',
|
|
1765
|
+
'}',
|
|
1766
|
+
].join('\n');
|
|
1767
|
+
if (process.platform === 'win32') {
|
|
1768
|
+
const fakeCodexScript = join(fakeBinDir, 'codex.cjs');
|
|
1769
|
+
writeFileSync(fakeCodexScript, fakeCodexSource);
|
|
1770
|
+
writeFileSync(join(fakeBinDir, 'codex.cmd'), [
|
|
1771
|
+
'@echo off',
|
|
1772
|
+
`"${process.execPath}" "${fakeCodexScript}" %*`,
|
|
1773
|
+
].join('\r\n'));
|
|
1774
|
+
} else {
|
|
1775
|
+
const fakeCodexPath = join(fakeBinDir, 'codex');
|
|
1776
|
+
writeFileSync(fakeCodexPath, `#!/usr/bin/env node\n${fakeCodexSource}\n`);
|
|
1777
|
+
chmodSync(fakeCodexPath, 0o755);
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
const pathKey = Object.keys(process.env).find((key) => key.toUpperCase() === 'PATH') ?? 'PATH';
|
|
1781
|
+
const env = buildPackedProbeEnv({
|
|
1782
|
+
[pathKey]: `${fakeBinDir}${delimiter}${process.env[pathKey] ?? ''}`,
|
|
1783
|
+
CODEX_HOME: codexHome,
|
|
1784
|
+
HOME: isolatedHome,
|
|
1785
|
+
USERPROFILE: isolatedHome,
|
|
1786
|
+
OMX_BYPASS_DEFAULT_SYSTEM_PROMPT: '1',
|
|
1787
|
+
OMX_MODEL_INSTRUCTIONS_FILE: modelInstructionsPath,
|
|
1788
|
+
OMX_LAUNCH_POLICY: 'direct',
|
|
1789
|
+
OMX_PACKED_FAKE_CODEX_CAPTURE_PATH: capturePath,
|
|
1790
|
+
});
|
|
1791
|
+
delete env.OMX_NOTIFY_TEMP_CONTRACT;
|
|
1792
|
+
delete env.OMX_TEAM_WORKER_LAUNCH_ARGS;
|
|
1793
|
+
writeFileSync(modelInstructionsPath, '# Packed launch boundary instructions\n');
|
|
1794
|
+
|
|
1795
|
+
for (const shorthand of ['--max', '--ultra'] as const) {
|
|
1796
|
+
const result = spawnSync(omxPath, [shorthand], { cwd: launchCwd, encoding: 'utf-8', env });
|
|
1797
|
+
if (result.status !== 1) {
|
|
1798
|
+
throw new Error(`omx ${shorthand} must reject before launching Codex, received ${String(result.status)}`);
|
|
1799
|
+
}
|
|
1800
|
+
assertTextIncludes(String(result.stderr ?? ''), `Unsupported OMX launch shorthand "${shorthand}".`, `omx ${shorthand} stderr`);
|
|
1801
|
+
if (readFakeCodexLaunches(capturePath).length !== 0) {
|
|
1802
|
+
throw new Error(`omx ${shorthand} must not launch Codex before the end-of-options marker`);
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
for (const expectedArgs of [
|
|
1807
|
+
['--', '--max'],
|
|
1808
|
+
['--', '--ultra'],
|
|
1809
|
+
['--', '-c', 'model_reasoning_effort=opaque-packed-smoke'],
|
|
1810
|
+
['--', '--worktree', 'post-marker-branch'],
|
|
1811
|
+
['--', '--notify-temp'],
|
|
1812
|
+
['--', '--discord'],
|
|
1813
|
+
['--', '--spark'],
|
|
1814
|
+
['--', '--hotswap'],
|
|
1815
|
+
['--', 'resume', '--project', '--codex-home', 'literal-codex-home', '--version'],
|
|
1816
|
+
]) {
|
|
1817
|
+
writeFileSync(capturePath, '');
|
|
1818
|
+
const result = spawnSync(omxPath, expectedArgs, {
|
|
1819
|
+
cwd: launchCwd,
|
|
1820
|
+
encoding: 'utf-8',
|
|
1821
|
+
env,
|
|
1822
|
+
timeout: PACKED_INSTALL_OPERATIONAL_PROBE_TIMEOUT_MS,
|
|
1823
|
+
killSignal: 'SIGKILL',
|
|
1824
|
+
});
|
|
1825
|
+
if (result.status !== 0) {
|
|
1826
|
+
throw new Error(formatCommandFailure(omxPath, expectedArgs, result));
|
|
1827
|
+
}
|
|
1828
|
+
const launches = readFakeCodexLaunches(capturePath);
|
|
1829
|
+
const launch = launches[0];
|
|
1830
|
+
const expectedCodexArgs = [
|
|
1831
|
+
'-c',
|
|
1832
|
+
`model_instructions_file="${escapeTomlString(modelInstructionsPath)}"`,
|
|
1833
|
+
...expectedArgs,
|
|
1834
|
+
];
|
|
1835
|
+
if (launches.length !== 1 || !launch) {
|
|
1836
|
+
throw new Error(`omx must launch exactly one fake Codex process: ${JSON.stringify(launches)}`);
|
|
1837
|
+
}
|
|
1838
|
+
assert.deepStrictEqual(
|
|
1839
|
+
launch.argv,
|
|
1840
|
+
expectedCodexArgs,
|
|
1841
|
+
'omx must inject model instructions before -- and preserve exact post-marker argument boundaries',
|
|
1842
|
+
);
|
|
1843
|
+
if (launch.cwd !== launchCwd) {
|
|
1844
|
+
throw new Error(`omx must not change cwd for post-marker arguments: expected ${launchCwd}, received ${launch.cwd}`);
|
|
1845
|
+
}
|
|
1846
|
+
if (launch.OMX_NOTIFY_TEMP_CONTRACT !== null) {
|
|
1847
|
+
throw new Error(`omx must not activate temporary notification routing for post-marker arguments: ${JSON.stringify(launch)}`);
|
|
1848
|
+
}
|
|
1849
|
+
if (launch.OMX_TEAM_WORKER_LAUNCH_ARGS !== null) {
|
|
1850
|
+
throw new Error(`omx must not enable spark worker routing for post-marker arguments: ${JSON.stringify(launch)}`);
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
const projectOmxDir = join(launchCwd, '.omx');
|
|
1855
|
+
const projectCodexHome = join(launchCwd, '.codex');
|
|
1856
|
+
mkdirSync(projectOmxDir, { recursive: true });
|
|
1857
|
+
mkdirSync(projectCodexHome, { recursive: true });
|
|
1858
|
+
writeFileSync(join(projectOmxDir, 'setup-scope.json'), JSON.stringify({ scope: 'project' }));
|
|
1859
|
+
writeFileSync(join(projectCodexHome, 'state_5.sqlite'), 'resume-only-sentinel');
|
|
1860
|
+
const imageResumeEnv = { ...env };
|
|
1861
|
+
delete imageResumeEnv.CODEX_HOME;
|
|
1862
|
+
const imageResumeCases: Array<{ args: string[]; resumes: boolean }> = [
|
|
1863
|
+
{ args: ['-i', 'resume'], resumes: false },
|
|
1864
|
+
{ args: ['--image', 'resume'], resumes: false },
|
|
1865
|
+
{ args: ['--image=resume'], resumes: false },
|
|
1866
|
+
{ args: ['-iresume'], resumes: false },
|
|
1867
|
+
{ args: ['-i=resume'], resumes: false },
|
|
1868
|
+
{ args: ['-i', 'screenshot.png', 'resume'], resumes: false },
|
|
1869
|
+
{ args: ['--image', 'screenshot.png', 'resume'], resumes: false },
|
|
1870
|
+
{ args: ['--image=screenshot.png', 'resume'], resumes: true },
|
|
1871
|
+
{ args: ['-iscreenshot.png', 'resume'], resumes: true },
|
|
1872
|
+
{ args: ['-i=screenshot.png', 'resume'], resumes: true },
|
|
1873
|
+
{ args: ['-i', 'one.png', '-i', 'two.png', 'resume'], resumes: false },
|
|
1874
|
+
{ args: ['--image', 'one.png', '--image', 'two.png', 'resume'], resumes: false },
|
|
1875
|
+
{ args: ['-i', 'one.png,two.png', 'resume'], resumes: false },
|
|
1876
|
+
{ args: ['--image', 'one.png,two.png', 'resume'], resumes: false },
|
|
1877
|
+
{ args: ['--image', 'one.png', '--model', 'gpt-review', 'resume'], resumes: true },
|
|
1878
|
+
{ args: ['--image=one.png', '--model=gpt-review', 'resume'], resumes: true },
|
|
1879
|
+
];
|
|
1880
|
+
for (const testCase of imageResumeCases) {
|
|
1881
|
+
writeFileSync(capturePath, '');
|
|
1882
|
+
const result = spawnSync(omxPath, ['--direct', ...testCase.args], {
|
|
1883
|
+
cwd: launchCwd,
|
|
1884
|
+
encoding: 'utf-8',
|
|
1885
|
+
env: imageResumeEnv,
|
|
1886
|
+
timeout: PACKED_INSTALL_OPERATIONAL_PROBE_TIMEOUT_MS,
|
|
1887
|
+
killSignal: 'SIGKILL',
|
|
1888
|
+
});
|
|
1889
|
+
assertBoundedProbeExit(`installed image resume arity ${JSON.stringify(testCase.args)}`, result, 0);
|
|
1890
|
+
const launches = readFakeCodexLaunches(capturePath);
|
|
1891
|
+
const launch = launches[0];
|
|
1892
|
+
if (launches.length !== 1 || !launch) {
|
|
1893
|
+
throw new Error(`installed image resume arity must invoke fake Codex exactly once: ${JSON.stringify(launches)}`);
|
|
1894
|
+
}
|
|
1895
|
+
const modelInstructionsArgs = ['-c', `model_instructions_file="${escapeTomlString(modelInstructionsPath)}"`];
|
|
1896
|
+
assert.deepStrictEqual(
|
|
1897
|
+
launch.argv,
|
|
1898
|
+
[...testCase.args, ...modelInstructionsArgs],
|
|
1899
|
+
'installed image resume arity must preserve exact Codex argv',
|
|
1900
|
+
);
|
|
1901
|
+
if (launch.hasResumeSqlite !== testCase.resumes) {
|
|
1902
|
+
throw new Error(`installed image resume arity mismatch for ${JSON.stringify(testCase.args)}: ${JSON.stringify(launch)}`);
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
const packedAuthDir = join(isolatedHome, '.omx', 'auth');
|
|
1907
|
+
mkdirSync(packedAuthDir, { recursive: true });
|
|
1908
|
+
writeFileSync(join(packedAuthDir, 'first.json'), '{"access_token":"first-secret"}\n');
|
|
1909
|
+
writeFileSync(join(packedAuthDir, 'second.json'), '{"access_token":"second-secret"}\n');
|
|
1910
|
+
writeFileSync(join(packedAuthDir, 'slots.json'), JSON.stringify({
|
|
1911
|
+
version: 1,
|
|
1912
|
+
currentSlot: 'first',
|
|
1913
|
+
slots: [
|
|
1914
|
+
{ slot: 'first', createdAt: 'now', updatedAt: 'now' },
|
|
1915
|
+
{ slot: 'second', createdAt: 'now', updatedAt: 'now' },
|
|
1916
|
+
],
|
|
1917
|
+
}, null, 2));
|
|
1918
|
+
writeFileSync(capturePath, '');
|
|
1919
|
+
const quotaSelectors = ['--last', '--all', '--include-non-interactive'];
|
|
1920
|
+
const quotaSuffix = ['--', ...quotaSelectors, '--model', 'opaque-model', 'opaque suffix'];
|
|
1921
|
+
const quotaResult = spawnSync(omxPath, [
|
|
1922
|
+
'--hotswap', '--direct', 'resume', ...quotaSelectors,
|
|
1923
|
+
'--model', 'gpt-review', '--remote', 'ws://127.0.0.1:4500', ...quotaSuffix,
|
|
1924
|
+
], {
|
|
1925
|
+
cwd: launchCwd,
|
|
1926
|
+
encoding: 'utf-8',
|
|
1927
|
+
env: { ...env, OMX_PACKED_FAKE_CODEX_QUOTA_ON_FIRST: '1' },
|
|
1928
|
+
timeout: PACKED_INSTALL_OPERATIONAL_PROBE_TIMEOUT_MS,
|
|
1929
|
+
killSignal: 'SIGKILL',
|
|
1930
|
+
});
|
|
1931
|
+
assertBoundedProbeExit('installed quota hotswap explicit-session retry', quotaResult, 0);
|
|
1932
|
+
const quotaLaunches = readFakeCodexLaunches(capturePath);
|
|
1933
|
+
const generatedInstructions = ['-c', `model_instructions_file="${escapeTomlString(modelInstructionsPath)}"`];
|
|
1934
|
+
assert.deepStrictEqual(quotaLaunches.map((launch) => launch.argv), [
|
|
1935
|
+
[
|
|
1936
|
+
'resume', ...quotaSelectors,
|
|
1937
|
+
'--model', 'gpt-review', '--remote', 'ws://127.0.0.1:4500',
|
|
1938
|
+
...generatedInstructions, ...quotaSuffix,
|
|
1939
|
+
],
|
|
1940
|
+
[
|
|
1941
|
+
'resume', 'packed-session-123',
|
|
1942
|
+
'--model', 'gpt-review', '--remote', 'ws://127.0.0.1:4500',
|
|
1943
|
+
...generatedInstructions, ...quotaSuffix,
|
|
1944
|
+
],
|
|
1945
|
+
], 'installed quota retry must remove resume selectors only before the marker');
|
|
1946
|
+
|
|
1947
|
+
const twoMarkerArgs = [
|
|
1948
|
+
'--direct',
|
|
1949
|
+
'--',
|
|
1950
|
+
'--notify-temp',
|
|
1951
|
+
'--spark',
|
|
1952
|
+
'--worktree',
|
|
1953
|
+
'post-marker-worktree',
|
|
1954
|
+
'--hotswap',
|
|
1955
|
+
'--xhigh',
|
|
1956
|
+
'--',
|
|
1957
|
+
'--madmax',
|
|
1958
|
+
'--notify-temp',
|
|
1959
|
+
'--spark',
|
|
1960
|
+
'trailing argument with spaces',
|
|
1961
|
+
'',
|
|
1962
|
+
];
|
|
1963
|
+
writeFileSync(capturePath, '');
|
|
1964
|
+
const twoMarkerResult = spawnSync(omxPath, twoMarkerArgs, {
|
|
1965
|
+
cwd: launchCwd,
|
|
1966
|
+
encoding: 'utf-8',
|
|
1967
|
+
env,
|
|
1968
|
+
timeout: PACKED_INSTALL_OPERATIONAL_PROBE_TIMEOUT_MS,
|
|
1969
|
+
killSignal: 'SIGKILL',
|
|
1970
|
+
});
|
|
1971
|
+
assertBoundedProbeExit('installed first-marker boundary launch', twoMarkerResult, 0);
|
|
1972
|
+
const twoMarkerLaunches = readFakeCodexLaunches(capturePath);
|
|
1973
|
+
if (twoMarkerLaunches.length !== 1 || !twoMarkerLaunches[0]) {
|
|
1974
|
+
throw new Error(`installed first-marker boundary launch must invoke fake Codex exactly once: ${JSON.stringify(twoMarkerLaunches)}`);
|
|
1975
|
+
}
|
|
1976
|
+
const twoMarkerLaunch = twoMarkerLaunches[0];
|
|
1977
|
+
assert.deepStrictEqual(twoMarkerLaunch.argv, [
|
|
1978
|
+
'-c',
|
|
1979
|
+
`model_instructions_file="${escapeTomlString(modelInstructionsPath)}"`,
|
|
1980
|
+
...twoMarkerArgs.slice(1),
|
|
1981
|
+
], 'the first literal -- must terminate OMX parsing and preserve every later argv element');
|
|
1982
|
+
if (twoMarkerLaunch.cwd !== launchCwd) {
|
|
1983
|
+
throw new Error(`the first literal -- must preserve launch cwd: expected ${launchCwd}, received ${twoMarkerLaunch.cwd}`);
|
|
1984
|
+
}
|
|
1985
|
+
if (twoMarkerLaunch.OMX_NOTIFY_TEMP_CONTRACT !== null) {
|
|
1986
|
+
throw new Error(`the first literal -- must not activate notification routing: ${JSON.stringify(twoMarkerLaunch)}`);
|
|
1987
|
+
}
|
|
1988
|
+
if (twoMarkerLaunch.OMX_TEAM_WORKER_LAUNCH_ARGS !== null) {
|
|
1989
|
+
throw new Error(`the first literal -- must not activate Team worker routing: ${JSON.stringify(twoMarkerLaunch)}`);
|
|
1990
|
+
}
|
|
1991
|
+
} finally {
|
|
1992
|
+
rmSync(smokeRoot, { recursive: true, force: true });
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1204
1995
|
function usage(): string {
|
|
1205
1996
|
return [
|
|
1206
1997
|
'Usage: node scripts/smoke-packed-install.mjs',
|
|
1207
1998
|
'',
|
|
1208
1999
|
'Creates an npm tarball, installs it into an isolated prefix, and smoke tests the installed omx CLI.',
|
|
1209
|
-
'Release smoke validates installed CLI boot, native-hook dispatch, and the isolated setup/rerun/uninstall lifecycle; Codex trust checks run when the pinned CLI is present.',
|
|
2000
|
+
'Release smoke validates installed CLI boot, native-hook dispatch, packaged artifacts, installed reasoning boundaries, and the isolated setup/rerun/uninstall lifecycle; Codex trust checks run when the pinned CLI is present.',
|
|
1210
2001
|
].join('\n');
|
|
1211
2002
|
}
|
|
1212
2003
|
|
|
@@ -1363,6 +2154,30 @@ export function buildNativeHookSmokePayload(
|
|
|
1363
2154
|
}
|
|
1364
2155
|
}
|
|
1365
2156
|
|
|
2157
|
+
function parseNativeHookSmokeOutput(probe: string, stdout: string): Record<string, unknown> {
|
|
2158
|
+
validateHookStdout(probe, stdout);
|
|
2159
|
+
if (!stdout.trim()) throw new Error(`native hook ${probe} emitted no JSON stdout`);
|
|
2160
|
+
const parsed = JSON.parse(stdout) as unknown;
|
|
2161
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
2162
|
+
throw new Error(`native hook ${probe} emitted a non-object JSON result`);
|
|
2163
|
+
}
|
|
2164
|
+
return parsed as Record<string, unknown>;
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
function requireNativeHookPermissionDeny(probe: string, output: Record<string, unknown>, reason: RegExp): void {
|
|
2168
|
+
const hookSpecificOutput = output.hookSpecificOutput;
|
|
2169
|
+
if (!hookSpecificOutput || typeof hookSpecificOutput !== 'object' || Array.isArray(hookSpecificOutput)) {
|
|
2170
|
+
throw new Error(`native hook ${probe} did not emit hookSpecificOutput`);
|
|
2171
|
+
}
|
|
2172
|
+
const hookOutput = hookSpecificOutput as Record<string, unknown>;
|
|
2173
|
+
if (hookOutput.permissionDecision !== 'deny') {
|
|
2174
|
+
throw new Error(`native hook ${probe} did not deny permission`);
|
|
2175
|
+
}
|
|
2176
|
+
if (!reason.test(String(hookOutput.permissionDecisionReason ?? ''))) {
|
|
2177
|
+
throw new Error(`native hook ${probe} denial did not match ${reason.source}`);
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
|
|
1366
2181
|
function smokeInstalledNativeHookDist(prefixDir: string): void {
|
|
1367
2182
|
function runPackedTransportRegressions(hookScript: string, smokeCwd: string): void {
|
|
1368
2183
|
const invoke = (cwd: string, environment: NodeJS.ProcessEnv, payload: Record<string, unknown>) => run(process.execPath, [realpathSync(hookScript)], {
|
|
@@ -1437,6 +2252,15 @@ function runPackedTransportRegressions(hookScript: string, smokeCwd: string): vo
|
|
|
1437
2252
|
for (const [index, file] of g2bFiles.entries()) if (Buffer.compare(readFileSync(file), g2bBefore[index]!) !== 0) throw new Error(`packed G2b negated prompt mutated terminal state ${file}`);
|
|
1438
2253
|
if (stopDecision(g2bCwd, g2bEnv, g2bPayload) === 'block') throw new Error('packed G2b negated prompt blocked Stop');
|
|
1439
2254
|
for (const [index, file] of g2bFiles.entries()) if (Buffer.compare(readFileSync(file), g2bBefore[index]!) !== 0) throw new Error(`packed G2b negated prompt mutated terminal state after Stop ${file}`);
|
|
2255
|
+
|
|
2256
|
+
const g2cCwd = join(smokeCwd, 'g2c-01445');
|
|
2257
|
+
mkdirSync(g2cCwd, { recursive: true });
|
|
2258
|
+
const g2cPayload = { ...PACKED_CODEX_01445_NO_POINTER_NO_TRACKER_FIXTURE, cwd: g2cCwd };
|
|
2259
|
+
const g2cResult = invoke(g2cCwd, buildPackedRegressionEnvironment({ name: 'g2c-01445' }), g2cPayload);
|
|
2260
|
+
const g2cStdout = String(g2cResult.stdout || '');
|
|
2261
|
+
const g2cExpected = '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"unsupported_documented_leader_proof: Codex 0.144.5 hooks do not expose documented root identity required for adapted Ralplan."}}\n';
|
|
2262
|
+
if (g2cStdout !== g2cExpected) throw new Error('packed Codex 0.144.5 fixture returned unexpected PreToolUse bytes');
|
|
2263
|
+
if (existsSync(join(g2cCwd, '.omx', 'state'))) throw new Error('packed Codex 0.144.5 fixture created pointer or tracker state');
|
|
1440
2264
|
}
|
|
1441
2265
|
|
|
1442
2266
|
const globalNodeModules = resolveGlobalNodeModules(prefixDir);
|
|
@@ -1448,18 +2272,2099 @@ function runPackedTransportRegressions(hookScript: string, smokeCwd: string): vo
|
|
|
1448
2272
|
const payload = buildNativeHookSmokePayload(eventName, smokeCwd);
|
|
1449
2273
|
const result = run(process.execPath, [realpathSync(hookScript)], {
|
|
1450
2274
|
cwd: smokeCwd,
|
|
1451
|
-
env: {
|
|
1452
|
-
...process.env,
|
|
2275
|
+
env: buildPackedProbeEnv({
|
|
1453
2276
|
OMX_NATIVE_HOOK_DOCTOR_SMOKE: '1',
|
|
1454
2277
|
OMX_ROOT: join(smokeCwd, '.omx-packed-hook-root'),
|
|
1455
2278
|
OMX_SESSION_ID: `packed-install-smoke-${eventName}`,
|
|
1456
2279
|
OMX_SOURCE_CWD: smokeCwd,
|
|
1457
2280
|
OMX_STARTUP_CWD: smokeCwd,
|
|
1458
|
-
},
|
|
2281
|
+
}),
|
|
1459
2282
|
input: JSON.stringify(payload),
|
|
1460
2283
|
});
|
|
1461
2284
|
validateHookStdout(eventName, result.stdout as string);
|
|
1462
2285
|
}
|
|
2286
|
+
const pluginHookScript = join(packageRoot, 'plugins', 'oh-my-codex', 'hooks', 'codex-native-hook.mjs');
|
|
2287
|
+
const pluginChildScript = join(smokeCwd, 'packed-plugin-oversized-child.mjs');
|
|
2288
|
+
const pluginLauncher = join(smokeCwd, process.platform === 'win32' ? 'packed-plugin-oversized.cmd' : 'packed-plugin-oversized.sh');
|
|
2289
|
+
writeFileSync(pluginChildScript, `import { writeFileSync } from 'node:fs';\nwriteFileSync(process.env.OMX_PLUGIN_SENTINEL, 'delegated');\nprocess.stdout.write('{}\\n');\n`);
|
|
2290
|
+
if (process.platform === 'win32') {
|
|
2291
|
+
writeFileSync(pluginLauncher, `@echo off\r\n"${process.execPath}" "${pluginChildScript}" %*\r\n`);
|
|
2292
|
+
} else {
|
|
2293
|
+
writeFileSync(pluginLauncher, `#!/bin/sh\nexec "${process.execPath}" "${pluginChildScript}" "$@"\n`, { mode: 0o755 });
|
|
2294
|
+
}
|
|
2295
|
+
for (const eventName of ['PreToolUse', 'PostToolUse'] as const) {
|
|
2296
|
+
const launchId = eventName === 'PreToolUse' ? 'packed-plugin-oversized-pre' : 'packed-plugin-oversized-post';
|
|
2297
|
+
const sessionId = `${launchId}-session`;
|
|
2298
|
+
const sentinel = join(smokeCwd, `${launchId}.sentinel`);
|
|
2299
|
+
const base = JSON.stringify({ hook_event_name: eventName, session_id: sessionId, unicode: '😀é', padding: '' });
|
|
2300
|
+
const input = JSON.stringify({ hook_event_name: eventName, session_id: sessionId, unicode: '😀é', padding: 'x'.repeat(1_048_577 - Buffer.byteLength(base, 'utf8')) });
|
|
2301
|
+
if (Buffer.byteLength(input, 'utf8') !== 1_048_577 || input.length === Buffer.byteLength(input, 'utf8')) {
|
|
2302
|
+
throw new Error(`packed plugin ${eventName} oversized fixture did not prove UTF-8 byte sizing`);
|
|
2303
|
+
}
|
|
2304
|
+
const result = run(process.execPath, [realpathSync(pluginHookScript)], {
|
|
2305
|
+
cwd: smokeCwd,
|
|
2306
|
+
env: {
|
|
2307
|
+
...process.env,
|
|
2308
|
+
OMX_ENTRY_PATH: join(packageRoot, 'dist', 'cli', 'omx.js'),
|
|
2309
|
+
OMX_CODEX_LAUNCH_ID: launchId,
|
|
2310
|
+
OMX_NATIVE_HOOK_COMMAND: pluginLauncher,
|
|
2311
|
+
OMX_PLUGIN_SENTINEL: sentinel,
|
|
2312
|
+
},
|
|
2313
|
+
input,
|
|
2314
|
+
});
|
|
2315
|
+
const systemMessage = 'OMX native hook rejected oversized stdin JSON before parsing; maxBytes=1048576.';
|
|
2316
|
+
const expected = eventName === 'PreToolUse'
|
|
2317
|
+
? { systemMessage, hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: systemMessage } }
|
|
2318
|
+
: { continue: false, stopReason: 'native_hook_stdin_oversized', systemMessage };
|
|
2319
|
+
if (result.status !== 0 || String(result.stderr) !== '' || String(result.stdout) !== `${JSON.stringify(expected)}\n` || existsSync(sentinel)) {
|
|
2320
|
+
throw new Error(`packed plugin ${eventName} oversized stdin did not return the native-equivalent local response without delegation`);
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
for (const input of [
|
|
2324
|
+
'{"hook_event_name":"PreToolUse",',
|
|
2325
|
+
`{"hook_event_name":"PreToolUse","transcript":"${'x'.repeat(1_048_577)}"}`,
|
|
2326
|
+
]) {
|
|
2327
|
+
const result = run(process.execPath, [realpathSync(hookScript)], {
|
|
2328
|
+
cwd: smokeCwd,
|
|
2329
|
+
env: { ...process.env, OMX_ROOT: join(smokeCwd, '.omx-packed-hook-root') },
|
|
2330
|
+
input,
|
|
2331
|
+
});
|
|
2332
|
+
const output = JSON.parse(String(result.stdout)) as { hookSpecificOutput?: Record<string, unknown> };
|
|
2333
|
+
if (output.hookSpecificOutput?.permissionDecision !== 'deny' || output.hookSpecificOutput?.hookEventName !== 'PreToolUse') {
|
|
2334
|
+
throw new Error('packed malformed or oversized PreToolUse stdin must emit a canonical deny envelope');
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
const hookRoot = smokeCwd;
|
|
2339
|
+
const stateDir = join(hookRoot, '.omx', 'state');
|
|
2340
|
+
const sessionId = 'packed-install-option-c';
|
|
2341
|
+
const leaderAgentId = 'agent-packed-install-leader';
|
|
2342
|
+
const teamName = 'packed-option-c';
|
|
2343
|
+
mkdirSync(join(stateDir, 'sessions', sessionId), { recursive: true });
|
|
2344
|
+
mkdirSync(join(stateDir, 'team', teamName, 'workers', 'worker-1'), { recursive: true });
|
|
2345
|
+
mkdirSync(join(smokeCwd, 'src', 'shared'), { recursive: true });
|
|
2346
|
+
mkdirSync(join(smokeCwd, 'src', 'state'), { recursive: true });
|
|
2347
|
+
mkdirSync(join(smokeCwd, '.omx', 'state'), { recursive: true });
|
|
2348
|
+
mkdirSync(join(smokeCwd, '.omx', 'state', 'tmp'), { recursive: true });
|
|
2349
|
+
writeFileSync(join(smokeCwd, '.omx', 'state', 'conductor-ledger.json'), '{}\n');
|
|
2350
|
+
writeFileSync(join(smokeCwd, '.omx', 'state', 'reference-copy'), 'metadata reference target\n');
|
|
2351
|
+
writeFileSync(join(smokeCwd, 'README.md'), 'read-only source\n');
|
|
2352
|
+
mkdirSync(join(smokeCwd, '.omx', 'state', 'inbox'), { recursive: true });
|
|
2353
|
+
writeFileSync(join(smokeCwd, '.omx', 'state', 'payload'), 'metadata payload\n');
|
|
2354
|
+
writeFileSync(join(smokeCwd, 'src', 'runtime.ts'), 'export {};\n');
|
|
2355
|
+
writeFileSync(join(smokeCwd, 'a'), 'finite metadata source\n');
|
|
2356
|
+
writeFileSync(join(smokeCwd, 'src', 'large.bin'), Buffer.alloc(16 * 1024 * 1024 + 1));
|
|
2357
|
+
mkdirSync(join(smokeCwd, '.omx', 'state', 'zsh-home'), { recursive: true });
|
|
2358
|
+
writeFileSync(join(smokeCwd, '.omx', 'state', 'zsh-home', '.zshenv'), 'touch src/zsh-owned.ts\n');
|
|
2359
|
+
writeFileSync(join(smokeCwd, '.omx', 'state', 'inbox', 'time'), '#!/bin/sh\ntouch src/time-wrapper-owned.ts\n');
|
|
2360
|
+
chmodSync(join(smokeCwd, '.omx', 'state', 'inbox', 'time'), 0o755);
|
|
2361
|
+
writeFileSync(join(smokeCwd, '.omx', 'state', 'inbox', 'node'), '#!/bin/sh\ntouch src/shebang-owned.ts\n');
|
|
2362
|
+
chmodSync(join(smokeCwd, '.omx', 'state', 'inbox', 'node'), 0o755);
|
|
2363
|
+
symlinkSync(join(smokeCwd, 'src', 'runtime.ts'), join(smokeCwd, '.omx', 'state', 'inbox', 'payload'));
|
|
2364
|
+
symlinkSync(join(smokeCwd, 'src', 'runtime.ts'), join(smokeCwd, '.omx', 'state', 'curl-glob-1.log'));
|
|
2365
|
+
mkdirSync(join(smokeCwd, 'src', 'subdir'), { recursive: true });
|
|
2366
|
+
mkdirSync(join(smokeCwd, '.omx', 'state', 'bash-home'), { recursive: true });
|
|
2367
|
+
writeFileSync(join(smokeCwd, '.omx', 'state', 'bash-home', '.bashrc'), 'touch src/interactive-owned.ts\n');
|
|
2368
|
+
symlinkSync(join(smokeCwd, 'src', 'subdir'), join(smokeCwd, '.omx', 'state', 'link'));
|
|
2369
|
+
symlinkSync(join(smokeCwd, 'src'), join(smokeCwd, '.omx', 'state', 'inbox', 'product-dir'));
|
|
2370
|
+
symlinkSync(join(smokeCwd, 'src', 'dangling-target.ts'), join(smokeCwd, '.omx', 'state', 'inbox', 'dangling'));
|
|
2371
|
+
writeFileSync(
|
|
2372
|
+
join(smokeCwd, '.omx', 'state', 'session.json'),
|
|
2373
|
+
JSON.stringify({ session_id: sessionId, native_session_id: leaderAgentId, cwd: smokeCwd }),
|
|
2374
|
+
);
|
|
2375
|
+
writeFileSync(
|
|
2376
|
+
join(stateDir, 'session.json'),
|
|
2377
|
+
JSON.stringify({ session_id: sessionId, native_session_id: leaderAgentId, cwd: smokeCwd }),
|
|
2378
|
+
);
|
|
2379
|
+
writeFileSync(
|
|
2380
|
+
join(stateDir, 'subagent-tracking.json'),
|
|
2381
|
+
JSON.stringify({
|
|
2382
|
+
schemaVersion: 1,
|
|
2383
|
+
sessions: {
|
|
2384
|
+
[sessionId]: {
|
|
2385
|
+
session_id: sessionId,
|
|
2386
|
+
leader_thread_id: leaderAgentId,
|
|
2387
|
+
threads: { [leaderAgentId]: { thread_id: leaderAgentId, kind: 'leader' } },
|
|
2388
|
+
},
|
|
2389
|
+
},
|
|
2390
|
+
}),
|
|
2391
|
+
);
|
|
2392
|
+
writeFileSync(
|
|
2393
|
+
join(stateDir, 'sessions', sessionId, 'skill-active-state.json'),
|
|
2394
|
+
JSON.stringify({
|
|
2395
|
+
active: true,
|
|
2396
|
+
skill: 'ultragoal',
|
|
2397
|
+
phase: 'executing',
|
|
2398
|
+
session_id: sessionId,
|
|
2399
|
+
active_skills: [{ skill: 'ultragoal', phase: 'executing', active: true, session_id: sessionId }],
|
|
2400
|
+
}),
|
|
2401
|
+
);
|
|
2402
|
+
writeFileSync(
|
|
2403
|
+
join(stateDir, 'sessions', sessionId, 'ultragoal-state.json'),
|
|
2404
|
+
JSON.stringify({ active: true, mode: 'ultragoal', current_phase: 'executing', session_id: sessionId }),
|
|
2405
|
+
);
|
|
2406
|
+
const packedTeamAuthority = {
|
|
2407
|
+
name: teamName,
|
|
2408
|
+
leader_pane_id: '%packed-leader',
|
|
2409
|
+
leader_cwd: smokeCwd,
|
|
2410
|
+
team_state_root: stateDir,
|
|
2411
|
+
workers: [{
|
|
2412
|
+
name: 'worker-1',
|
|
2413
|
+
pane_id: '%packed-worker',
|
|
2414
|
+
working_dir: smokeCwd,
|
|
2415
|
+
worktree_path: smokeCwd,
|
|
2416
|
+
team_state_root: stateDir,
|
|
2417
|
+
}],
|
|
2418
|
+
};
|
|
2419
|
+
for (const fileName of ['config.json', 'manifest.v2.json']) {
|
|
2420
|
+
writeFileSync(join(stateDir, 'team', teamName, fileName), JSON.stringify(packedTeamAuthority));
|
|
2421
|
+
}
|
|
2422
|
+
writeFileSync(
|
|
2423
|
+
join(stateDir, 'team', teamName, 'workers', 'worker-1', 'identity.json'),
|
|
2424
|
+
JSON.stringify({
|
|
2425
|
+
name: 'worker-1',
|
|
2426
|
+
pane_id: '%packed-worker',
|
|
2427
|
+
team_state_root: stateDir,
|
|
2428
|
+
worktree_path: smokeCwd,
|
|
2429
|
+
}),
|
|
2430
|
+
);
|
|
2431
|
+
|
|
2432
|
+
const invokeAuthorizationProbe = (payload: Record<string, unknown>, env: NodeJS.ProcessEnv) => run(
|
|
2433
|
+
process.execPath,
|
|
2434
|
+
[realpathSync(hookScript)],
|
|
2435
|
+
{ cwd: smokeCwd, env, input: JSON.stringify(payload) },
|
|
2436
|
+
);
|
|
2437
|
+
const hookEnv = {
|
|
2438
|
+
...process.env,
|
|
2439
|
+
OMX_NATIVE_HOOK_DOCTOR_SMOKE: '1',
|
|
2440
|
+
OMX_ROOT: hookRoot,
|
|
2441
|
+
OMX_SOURCE_CWD: smokeCwd,
|
|
2442
|
+
OMX_STARTUP_CWD: smokeCwd,
|
|
2443
|
+
};
|
|
2444
|
+
const officialTeamRootPayload = {
|
|
2445
|
+
hook_event_name: 'PreToolUse',
|
|
2446
|
+
cwd: smokeCwd,
|
|
2447
|
+
session_id: sessionId,
|
|
2448
|
+
tool_name: 'Edit',
|
|
2449
|
+
tool_use_id: 'packed-install-team-root',
|
|
2450
|
+
tool_input: { file_path: 'src/packed-team-worker.ts', old_string: 'a', new_string: 'b' },
|
|
2451
|
+
};
|
|
2452
|
+
const teamEnv: NodeJS.ProcessEnv = {
|
|
2453
|
+
...hookEnv,
|
|
2454
|
+
OMX_TEAM_WORKER: `packed-display/worker-1`,
|
|
2455
|
+
OMX_TEAM_STATE_ROOT: stateDir,
|
|
2456
|
+
OMX_TEAM_INTERNAL_WORKER: `${teamName}/worker-1`,
|
|
2457
|
+
OMX_TEAM_LEADER_CWD: smokeCwd,
|
|
2458
|
+
TMUX_PANE: '%packed-worker',
|
|
2459
|
+
};
|
|
2460
|
+
const teamOutput = parseNativeHookSmokeOutput(
|
|
2461
|
+
'PreToolUse official Team root',
|
|
2462
|
+
String(invokeAuthorizationProbe(officialTeamRootPayload, teamEnv).stdout),
|
|
2463
|
+
);
|
|
2464
|
+
if (Object.keys(teamOutput).length !== 0) {
|
|
2465
|
+
throw new Error('native hook official Team root did not preserve the validated Team-worker exemption');
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
const leaderOutput = parseNativeHookSmokeOutput(
|
|
2469
|
+
'PreToolUse leader with Team environment',
|
|
2470
|
+
String(invokeAuthorizationProbe({
|
|
2471
|
+
...officialTeamRootPayload,
|
|
2472
|
+
session_id: leaderAgentId,
|
|
2473
|
+
tool_use_id: 'packed-install-team-leader',
|
|
2474
|
+
agent_id: leaderAgentId,
|
|
2475
|
+
thread_id: leaderAgentId,
|
|
2476
|
+
}, teamEnv).stdout),
|
|
2477
|
+
);
|
|
2478
|
+
requireNativeHookPermissionDeny('PreToolUse leader with Team environment', leaderOutput, /Main-root Conductor mode is active/);
|
|
2479
|
+
|
|
2480
|
+
const childEnv = {
|
|
2481
|
+
...hookEnv,
|
|
2482
|
+
OMX_TEAM_WORKER: '',
|
|
2483
|
+
OMX_TEAM_INTERNAL_WORKER: '',
|
|
2484
|
+
OMX_TEAM_STATE_ROOT: '',
|
|
2485
|
+
};
|
|
2486
|
+
const runActorProbeResult = (
|
|
2487
|
+
actor: 'main-root' | 'native-child',
|
|
2488
|
+
probe: string,
|
|
2489
|
+
toolName: string,
|
|
2490
|
+
toolInput: Record<string, unknown>,
|
|
2491
|
+
inheritedEnv: Record<string, string> = {},
|
|
2492
|
+
): { output: Record<string, unknown>; stdout: string } => {
|
|
2493
|
+
const result = invokeAuthorizationProbe({
|
|
2494
|
+
hook_event_name: 'PreToolUse',
|
|
2495
|
+
cwd: smokeCwd,
|
|
2496
|
+
session_id: actor === 'main-root' ? leaderAgentId : sessionId,
|
|
2497
|
+
...(actor === 'main-root'
|
|
2498
|
+
? { agent_id: leaderAgentId, thread_id: leaderAgentId }
|
|
2499
|
+
: { agent_id: 'agent-packed-install-child' }),
|
|
2500
|
+
tool_name: toolName,
|
|
2501
|
+
tool_use_id: `packed-install-${actor}-${probe.replace(/\W+/g, '-')}`,
|
|
2502
|
+
tool_input: toolInput,
|
|
2503
|
+
}, { ...childEnv, ...inheritedEnv });
|
|
2504
|
+
const stdout = String(result.stdout);
|
|
2505
|
+
return { output: parseNativeHookSmokeOutput(`PreToolUse ${actor} ${probe}`, stdout), stdout };
|
|
2506
|
+
};
|
|
2507
|
+
const runActorProbe = (
|
|
2508
|
+
actor: 'main-root' | 'native-child',
|
|
2509
|
+
probe: string,
|
|
2510
|
+
toolName: string,
|
|
2511
|
+
toolInput: Record<string, unknown>,
|
|
2512
|
+
inheritedEnv: Record<string, string> = {},
|
|
2513
|
+
): Record<string, unknown> => runActorProbeResult(actor, probe, toolName, toolInput, inheritedEnv).output;
|
|
2514
|
+
const requireActorDeny = (
|
|
2515
|
+
actor: 'main-root' | 'native-child',
|
|
2516
|
+
probe: string,
|
|
2517
|
+
output: Record<string, unknown>,
|
|
2518
|
+
expectedReason = actor === 'native-child' ? /OWNER_CONFIRMATION_REQUIRED/ : /Main-root Conductor mode is active/,
|
|
2519
|
+
): void => requireNativeHookPermissionDeny(
|
|
2520
|
+
`PreToolUse ${actor} ${probe}`,
|
|
2521
|
+
output,
|
|
2522
|
+
expectedReason,
|
|
2523
|
+
);
|
|
2524
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
2525
|
+
for (const [probe, command] of [
|
|
2526
|
+
['finite cp directory leaf symlink', 'cp .omx/state/payload .omx/state/inbox'],
|
|
2527
|
+
['finite curl remote-name leaf symlink', 'curl -q --output-dir .omx/state/inbox -O https://example.test/payload'],
|
|
2528
|
+
['finite wget prefix leaf symlink', 'wget --no-config -P .omx/state/inbox https://example.test/payload'],
|
|
2529
|
+
['finite curl remote-name scheme-less extra operand leaf symlink', 'curl -q --output-dir .omx/state/inbox -O https://example.test/payload ftp.example.test/payload'],
|
|
2530
|
+
['finite wget prefix scheme-less extra operand leaf symlink', 'wget --no-config --no-hsts -P .omx/state/inbox https://example.test/payload ftp.example.test/payload'],
|
|
2531
|
+
['finite curl template leaf symlink', "curl -q -o .omx/state/curl-glob-#1.log 'https://example.test/file[1-2]'"],
|
|
2532
|
+
['dangling symlink touch', 'touch .omx/state/inbox/dangling'],
|
|
2533
|
+
['dangling symlink Wget output', 'wget --no-config --no-hsts -P .omx/state/inbox https://example.test/dangling'],
|
|
2534
|
+
['wait -p POSIX writer', 'export POSIXLY_CORRECT; sleep 0 & wait -n -p POSIXLY_CORRECT; wget --no-config --no-hsts -O src/wait-owned.ts https://example.test/file -O -'],
|
|
2535
|
+
['clustered wait -p POSIX writer', 'export POSIXLY_CORRECT; sleep 0 & wait -np POSIXLY_CORRECT; wget --no-config --no-hsts -O src/wait-cluster-owned.ts https://example.test/file -O -'],
|
|
2536
|
+
['fd redirect source sink', 'printf pwn >& src/native-child-owned.ts'],
|
|
2537
|
+
['fd redirect protected state sink', `printf pwn >& .omx/state/sessions/${sessionId}/ultragoal-state.json`],
|
|
2538
|
+
['function return cwd flow', 'f(){ cd src; return; cd ..; }; f; touch .omx/state/inbox/return-owned'],
|
|
2539
|
+
['protected state ancestor removal', `rm -rf .omx/state/sessions/${sessionId}`],
|
|
2540
|
+
['ordered metadata append overflow', 'truncate --size=16777216 .omx/state/inbox/oversized; printf x >> .omx/state/inbox/oversized'],
|
|
2541
|
+
['rsync aliased source and log', 'rsync --log-file=.omx/state/conductor-ledger.json .omx/state/conductor-ledger.json .omx/state/inbox/rsync-alias-copy'],
|
|
2542
|
+
['exec login shell startup', 'HOME=.omx/state/bash-home exec -l /bin/bash -c ":"'],
|
|
2543
|
+
['env argv0 login shell startup', 'HOME=.omx/state/bash-home env --argv0=-bash /bin/bash -c ":"'],
|
|
2544
|
+
['function keyword command-not-found handler', 'function command_not_found_handle { /usr/bin/touch src/path-keyword-owned.ts; }; PATH=/definitely/not-present; cat README.md'],
|
|
2545
|
+
['shadowed printf redirect producer', 'printf(){ /usr/bin/head -c 16777217 /dev/zero; }; printf safe > .omx/state/inbox/oversized'],
|
|
2546
|
+
['shadowed cat heredoc producer', "cat(){ /usr/bin/head -c 16777217 /dev/zero; }; cat <<'EOF' > .omx/state/inbox/oversized-heredoc\nsafe\nEOF"],
|
|
2547
|
+
['glob redirect producer', 'echo .omx/state/* > .omx/state/inbox/glob-producer'],
|
|
2548
|
+
['brace redirect producer', 'echo {a,b} > .omx/state/inbox/brace-producer'],
|
|
2549
|
+
['tilde redirect producer', 'echo ~/metadata > .omx/state/inbox/tilde-producer'],
|
|
2550
|
+
['oversized truncate', 'truncate --size=16777217 .omx/state/inbox/oversized'],
|
|
2551
|
+
['attached oversized truncate', 'truncate -s16777217 .omx/state/inbox/oversized-attached'],
|
|
2552
|
+
['relative truncate size', 'truncate --size=+1 .omx/state/inbox/relative'],
|
|
2553
|
+
['Wget uncapped file sink', 'wget --no-config --no-hsts -O .omx/state/inbox/stream https://example.test/file'],
|
|
2554
|
+
['Wget non-HTTP file sink', 'wget --no-config --no-hsts -O .omx/state/inbox/ftp ftp://example.test/file'],
|
|
2555
|
+
['Wget multiple file sink URLs', 'wget --no-config --no-hsts -O .omx/state/inbox/multiple https://example.test/one https://example.test/two'],
|
|
2556
|
+
['curl uncapped file sink', 'curl -q --max-time 1 -o .omx/state/inbox/stream https://example.test/file'],
|
|
2557
|
+
['trusted script environment interpreter shadow', 'PATH=.omx/state/inbox:/usr/bin:/bin /usr/bin/npm --version'],
|
|
2558
|
+
] as const) {
|
|
2559
|
+
requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }));
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
for (const fileName of [
|
|
2563
|
+
'autopilot-state.json',
|
|
2564
|
+
'autoresearch-state.json',
|
|
2565
|
+
'deep-interview-state.json',
|
|
2566
|
+
'ralplan-state.json',
|
|
2567
|
+
'ralph-state.json',
|
|
2568
|
+
'ultrawork-state.json',
|
|
2569
|
+
'team-state.json',
|
|
2570
|
+
'ultraqa-state.json',
|
|
2571
|
+
'ultragoal-state.json',
|
|
2572
|
+
'skill-active-state.json',
|
|
2573
|
+
'release-readiness-state.json',
|
|
2574
|
+
'run-state.json',
|
|
2575
|
+
'session.json',
|
|
2576
|
+
'subagent-tracking.json',
|
|
2577
|
+
'native-stop-state.json',
|
|
2578
|
+
] as const) {
|
|
2579
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
2580
|
+
requireActorDeny(
|
|
2581
|
+
actor,
|
|
2582
|
+
`raw gate state write ${fileName}`,
|
|
2583
|
+
runActorProbe(actor, `raw gate state write ${fileName}`, 'Write', {
|
|
2584
|
+
file_path: `.omx/state/sessions/${sessionId}/${fileName}`,
|
|
2585
|
+
content: JSON.stringify({ active: false, mode: fileName.slice(0, -'-state.json'.length), current_phase: 'complete', session_id: sessionId }),
|
|
2586
|
+
}),
|
|
2587
|
+
);
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
for (const filePath of [
|
|
2591
|
+
`.omx/state/team/${teamName}/phase.json`,
|
|
2592
|
+
`.omx/state/team/${teamName}/manifest.v2.json`,
|
|
2593
|
+
`.omx/state/team/${teamName}/config.json`,
|
|
2594
|
+
`.omx/state/team/${teamName}/workers/worker-1/identity.json`,
|
|
2595
|
+
]) {
|
|
2596
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
2597
|
+
requireActorDeny(
|
|
2598
|
+
actor,
|
|
2599
|
+
`raw Team authority write ${filePath}`,
|
|
2600
|
+
runActorProbe(actor, `raw Team authority write ${filePath}`, 'Write', { file_path: filePath, content: '{}' }),
|
|
2601
|
+
);
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
for (const filePath of [
|
|
2605
|
+
`.omx/state/sessions/${sessionId}/ULTRAGOAL-STATE.JSON`,
|
|
2606
|
+
`.omx/state/sessions/${sessionId}/ultragoal-state.json. `,
|
|
2607
|
+
]) {
|
|
2608
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
2609
|
+
requireActorDeny(
|
|
2610
|
+
actor,
|
|
2611
|
+
`protected gate-state filesystem alias ${filePath}`,
|
|
2612
|
+
runActorProbe(actor, `protected gate-state filesystem alias ${filePath}`, 'Write', { file_path: filePath, content: '{}' }),
|
|
2613
|
+
);
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
for (const [probe, command] of [
|
|
2617
|
+
['gate state redirect', `printf x > .omx/state/sessions/${sessionId}/ultragoal-state.json`],
|
|
2618
|
+
['gate state editor', `sed -i 's/executing/complete/' .omx/state/sessions/${sessionId}/ultragoal-state.json`],
|
|
2619
|
+
['gate state interpreter', `node -e "require('fs').writeFileSync('.omx/state/sessions/${sessionId}/ultragoal-state.json','{}')"`],
|
|
2620
|
+
] as const) {
|
|
2621
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
2622
|
+
requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }));
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
requireNativeHookPermissionDeny(
|
|
2626
|
+
'identityless remote mutation',
|
|
2627
|
+
parseNativeHookSmokeOutput(
|
|
2628
|
+
'PreToolUse identityless remote mutation',
|
|
2629
|
+
String(invokeAuthorizationProbe({
|
|
2630
|
+
hook_event_name: 'PreToolUse',
|
|
2631
|
+
cwd: smokeCwd,
|
|
2632
|
+
session_id: sessionId,
|
|
2633
|
+
tool_name: 'Bash',
|
|
2634
|
+
tool_use_id: 'packed-install-identityless-remote-mutation',
|
|
2635
|
+
tool_input: { command: 'PATH=/usr/bin:/bin gh issue create --title x --body y' },
|
|
2636
|
+
}, childEnv).stdout),
|
|
2637
|
+
),
|
|
2638
|
+
/OWNER_CONFIRMATION_REQUIRED/,
|
|
2639
|
+
);
|
|
2640
|
+
for (const [probe, identity] of [
|
|
2641
|
+
['conflicting owner and current thread aliases', { owner_codex_thread_id: leaderAgentId, thread_id: 'agent-packed-install-child' }],
|
|
2642
|
+
['conflicting session aliases', { session_id: sessionId, sessionId: 'foreign-session', agent_id: leaderAgentId, thread_id: leaderAgentId }],
|
|
2643
|
+
['reversed conflicting session aliases', { session_id: 'foreign-session', sessionId, agent_id: leaderAgentId, thread_id: leaderAgentId }],
|
|
2644
|
+
['conflicting owner and agent aliases', { owner_codex_thread_id: 'foreign-owner', agent_id: leaderAgentId }],
|
|
2645
|
+
] as const) {
|
|
2646
|
+
requireNativeHookPermissionDeny(
|
|
2647
|
+
probe,
|
|
2648
|
+
parseNativeHookSmokeOutput(
|
|
2649
|
+
probe,
|
|
2650
|
+
String(invokeAuthorizationProbe({
|
|
2651
|
+
hook_event_name: 'PreToolUse',
|
|
2652
|
+
cwd: smokeCwd,
|
|
2653
|
+
session_id: leaderAgentId,
|
|
2654
|
+
...identity,
|
|
2655
|
+
tool_name: 'Bash',
|
|
2656
|
+
tool_use_id: `packed-install-${probe.replace(/\W+/g, '-')}`,
|
|
2657
|
+
tool_input: { command: 'PATH=/usr/bin:/bin gh issue create --title x --body y' },
|
|
2658
|
+
}, childEnv).stdout),
|
|
2659
|
+
),
|
|
2660
|
+
/PROVENANCE_DENIED/,
|
|
2661
|
+
);
|
|
2662
|
+
}
|
|
2663
|
+
for (const [probe, identity] of [
|
|
2664
|
+
['foreign active session', { session_id: 'foreign-session', agent_id: leaderAgentId, thread_id: leaderAgentId }],
|
|
2665
|
+
['absent active session', { agent_id: leaderAgentId, thread_id: leaderAgentId }],
|
|
2666
|
+
] as const) {
|
|
2667
|
+
requireNativeHookPermissionDeny(
|
|
2668
|
+
probe,
|
|
2669
|
+
parseNativeHookSmokeOutput(
|
|
2670
|
+
probe,
|
|
2671
|
+
String(invokeAuthorizationProbe({
|
|
2672
|
+
hook_event_name: 'PreToolUse',
|
|
2673
|
+
cwd: smokeCwd,
|
|
2674
|
+
...identity,
|
|
2675
|
+
tool_name: 'Write',
|
|
2676
|
+
tool_use_id: `packed-install-${probe.replace(/\W+/g, '-')}`,
|
|
2677
|
+
tool_input: { file_path: 'src/session-bypass.ts', content: 'owned\n' },
|
|
2678
|
+
}, childEnv).stdout),
|
|
2679
|
+
),
|
|
2680
|
+
/PROVENANCE_DENIED/,
|
|
2681
|
+
);
|
|
2682
|
+
}
|
|
2683
|
+
for (const [order, sessionAliases] of [
|
|
2684
|
+
['canonical-first', { session_id: sessionId, sessionId: 'foreign-session' }],
|
|
2685
|
+
['foreign-first', { session_id: 'foreign-session', sessionId }],
|
|
2686
|
+
] as const) {
|
|
2687
|
+
for (const [transport, toolName, toolInput] of [
|
|
2688
|
+
['path', 'Write', { file_path: 'src/alias-bypass.ts', content: 'owned\n' }],
|
|
2689
|
+
['bash', 'Bash', { command: 'printf pwn >& src/alias-bypass.ts' }],
|
|
2690
|
+
['state', 'mcp__omx_state__state_write', { mode: 'ultragoal', active: true }],
|
|
2691
|
+
] as const) {
|
|
2692
|
+
requireNativeHookPermissionDeny(
|
|
2693
|
+
`session alias ${order}/${transport}`,
|
|
2694
|
+
parseNativeHookSmokeOutput(
|
|
2695
|
+
`PreToolUse session alias ${order}/${transport}`,
|
|
2696
|
+
String(invokeAuthorizationProbe({
|
|
2697
|
+
hook_event_name: 'PreToolUse',
|
|
2698
|
+
cwd: smokeCwd,
|
|
2699
|
+
...sessionAliases,
|
|
2700
|
+
agent_id: leaderAgentId,
|
|
2701
|
+
thread_id: leaderAgentId,
|
|
2702
|
+
tool_name: toolName,
|
|
2703
|
+
tool_use_id: `packed-install-session-alias-${order}-${transport}`,
|
|
2704
|
+
tool_input: toolInput,
|
|
2705
|
+
}, childEnv).stdout),
|
|
2706
|
+
),
|
|
2707
|
+
/PROVENANCE_DENIED/,
|
|
2708
|
+
);
|
|
2709
|
+
}
|
|
2710
|
+
}
|
|
2711
|
+
requireNativeHookPermissionDeny(
|
|
2712
|
+
'noncanonical cwd metadata target',
|
|
2713
|
+
parseNativeHookSmokeOutput(
|
|
2714
|
+
'PreToolUse noncanonical cwd metadata target',
|
|
2715
|
+
String(invokeAuthorizationProbe({
|
|
2716
|
+
hook_event_name: 'PreToolUse',
|
|
2717
|
+
cwd: join(smokeCwd, 'src'),
|
|
2718
|
+
session_id: sessionId,
|
|
2719
|
+
agent_id: leaderAgentId,
|
|
2720
|
+
thread_id: leaderAgentId,
|
|
2721
|
+
tool_name: 'Write',
|
|
2722
|
+
tool_use_id: 'packed-install-noncanonical-cwd-metadata-target',
|
|
2723
|
+
tool_input: { file_path: '.omx/state/inbox/cwd-bypass', content: 'owned\n' },
|
|
2724
|
+
}, childEnv).stdout),
|
|
2725
|
+
),
|
|
2726
|
+
/Main-root Conductor mode is active/,
|
|
2727
|
+
);
|
|
2728
|
+
requireNativeHookPermissionDeny(
|
|
2729
|
+
'identityless native-session remote mutation',
|
|
2730
|
+
parseNativeHookSmokeOutput(
|
|
2731
|
+
'PreToolUse identityless native-session remote mutation',
|
|
2732
|
+
String(invokeAuthorizationProbe({
|
|
2733
|
+
hook_event_name: 'PreToolUse',
|
|
2734
|
+
cwd: smokeCwd,
|
|
2735
|
+
session_id: leaderAgentId,
|
|
2736
|
+
tool_name: 'Bash',
|
|
2737
|
+
tool_use_id: 'packed-install-identityless-native-session-remote-mutation',
|
|
2738
|
+
tool_input: { command: 'PATH=/usr/bin:/bin gh issue create --title x --body y' },
|
|
2739
|
+
}, childEnv).stdout),
|
|
2740
|
+
),
|
|
2741
|
+
/Main-root Conductor mode is active/,
|
|
2742
|
+
);
|
|
2743
|
+
if (Object.keys(runActorProbe('main-root', 'finite cp metadata leaf control', 'Bash', {
|
|
2744
|
+
command: 'cp .omx/state/payload .omx/handoffs/run-1/payload',
|
|
2745
|
+
})).length !== 0) {
|
|
2746
|
+
throw new Error('packed main-root finite cp metadata leaf control should be allowed');
|
|
2747
|
+
}
|
|
2748
|
+
if (Object.keys(runActorProbe('main-root', 'bounded dd metadata copy control', 'Bash', {
|
|
2749
|
+
command: 'dd if=a of=.omx/state/inbox/dd-copy bs=1 count=1',
|
|
2750
|
+
})).length !== 0) {
|
|
2751
|
+
throw new Error('packed main-root bounded dd metadata copy control should be allowed');
|
|
2752
|
+
}
|
|
2753
|
+
if (Object.keys(runActorProbe('main-root', 'bounded truncate metadata control', 'Bash', {
|
|
2754
|
+
command: 'truncate --size=16777216 .omx/state/inbox/truncate',
|
|
2755
|
+
})).length !== 0) {
|
|
2756
|
+
throw new Error('packed main-root bounded truncate metadata control should be allowed');
|
|
2757
|
+
}
|
|
2758
|
+
if (Object.keys(runActorProbe('main-root', 'bounded quoted heredoc metadata control', 'Bash', {
|
|
2759
|
+
command: "cat <<'EOF' > .omx/state/conductor-heredoc.json\n{}\nEOF",
|
|
2760
|
+
})).length !== 0) {
|
|
2761
|
+
throw new Error('packed main-root bounded quoted heredoc metadata control should be allowed');
|
|
2762
|
+
}
|
|
2763
|
+
if (Object.keys(runActorProbe('main-root', 'zsh fast startup control', 'Bash', {
|
|
2764
|
+
command: `zsh -f -c ':'`,
|
|
2765
|
+
})).length !== 0) {
|
|
2766
|
+
throw new Error('packed main-root zsh fast startup control should be allowed');
|
|
2767
|
+
}
|
|
2768
|
+
const boxedPlanningRoot = join(smokeCwd, 'boxed-planning-root');
|
|
2769
|
+
const boxedPlanningStateDir = join(boxedPlanningRoot, '.omx', 'state');
|
|
2770
|
+
const boxedPlanningStatePath = join(boxedPlanningStateDir, 'sessions', leaderAgentId, 'ralplan-state.json');
|
|
2771
|
+
writeFileSync(join(smokeCwd, 'src', 'boxed-planning-runtime.ts'), JSON.stringify({
|
|
2772
|
+
active: true,
|
|
2773
|
+
mode: 'ralplan',
|
|
2774
|
+
current_phase: 'planning',
|
|
2775
|
+
session_id: leaderAgentId,
|
|
2776
|
+
}));
|
|
2777
|
+
mkdirSync(join(boxedPlanningStateDir, 'sessions', leaderAgentId), { recursive: true });
|
|
2778
|
+
writeFileSync(join(boxedPlanningStateDir, 'session.json'), JSON.stringify({ session_id: leaderAgentId, cwd: smokeCwd }));
|
|
2779
|
+
writeFileSync(join(boxedPlanningStateDir, 'sessions', leaderAgentId, 'skill-active-state.json'), JSON.stringify({
|
|
2780
|
+
active: true,
|
|
2781
|
+
skill: 'ralplan',
|
|
2782
|
+
phase: 'planning',
|
|
2783
|
+
session_id: leaderAgentId,
|
|
2784
|
+
active_skills: [{ skill: 'ralplan', phase: 'planning', active: true, session_id: leaderAgentId }],
|
|
2785
|
+
}));
|
|
2786
|
+
symlinkSync(join(smokeCwd, 'src', 'boxed-planning-runtime.ts'), boxedPlanningStatePath);
|
|
2787
|
+
const boxedPlanningHardLinkPath = join(boxedPlanningStateDir, 'sessions', leaderAgentId, 'deep-interview-state.json');
|
|
2788
|
+
linkSync(join(smokeCwd, 'src', 'boxed-planning-runtime.ts'), boxedPlanningHardLinkPath);
|
|
2789
|
+
const boxedPlanningSiblingSessionId = `${leaderAgentId}-sibling`;
|
|
2790
|
+
const boxedPlanningSiblingPath = join(boxedPlanningStateDir, 'sessions', boxedPlanningSiblingSessionId, 'ralplan-state.json');
|
|
2791
|
+
mkdirSync(dirname(boxedPlanningSiblingPath), { recursive: true });
|
|
2792
|
+
writeFileSync(boxedPlanningSiblingPath, JSON.stringify({
|
|
2793
|
+
active: true,
|
|
2794
|
+
mode: 'ralplan',
|
|
2795
|
+
current_phase: 'planning',
|
|
2796
|
+
session_id: boxedPlanningSiblingSessionId,
|
|
2797
|
+
}));
|
|
2798
|
+
requireNativeHookPermissionDeny(
|
|
2799
|
+
'main-root boxed planning state symlink',
|
|
2800
|
+
runActorProbe('main-root', 'boxed planning state symlink', 'Write', { file_path: boxedPlanningStatePath, content: 'owned\n' }, {
|
|
2801
|
+
OMX_ROOT: boxedPlanningRoot,
|
|
2802
|
+
OMX_TEAM_STATE_ROOT: '',
|
|
2803
|
+
}),
|
|
2804
|
+
/(?:Ralplan is active|Main-root Conductor mode is active)/,
|
|
2805
|
+
);
|
|
2806
|
+
requireNativeHookPermissionDeny(
|
|
2807
|
+
'main-root boxed planning state hardlink',
|
|
2808
|
+
runActorProbe('main-root', 'boxed planning state hardlink', 'Write', { file_path: boxedPlanningHardLinkPath, content: 'owned\n' }, {
|
|
2809
|
+
OMX_ROOT: boxedPlanningRoot,
|
|
2810
|
+
OMX_TEAM_STATE_ROOT: '',
|
|
2811
|
+
}),
|
|
2812
|
+
/(?:Ralplan is active|Main-root Conductor mode is active)/,
|
|
2813
|
+
);
|
|
2814
|
+
requireNativeHookPermissionDeny(
|
|
2815
|
+
'main-root boxed planning sibling session',
|
|
2816
|
+
runActorProbe('main-root', 'boxed planning sibling session', 'Write', { file_path: boxedPlanningSiblingPath, content: 'owned\n' }, {
|
|
2817
|
+
OMX_ROOT: boxedPlanningRoot,
|
|
2818
|
+
OMX_TEAM_STATE_ROOT: '',
|
|
2819
|
+
}),
|
|
2820
|
+
/(?:Ralplan is active|Main-root Conductor mode is active)/,
|
|
2821
|
+
);
|
|
2822
|
+
if (Object.keys(runActorProbe('main-root', 'hardlink metadata source control', 'Bash', {
|
|
2823
|
+
command: 'ln .omx/state/conductor-ledger.json .omx/handoffs/run-1/ledger-link',
|
|
2824
|
+
})).length !== 0) {
|
|
2825
|
+
throw new Error('packed main-root hardlink metadata source control should be allowed');
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2828
|
+
writeFileSync(join(stateDir, 'session.json'), JSON.stringify({ session_id: sessionId }));
|
|
2829
|
+
requireActorDeny('native-child', 'anchorless state write', runActorProbe('native-child', 'anchorless state write', 'mcp__omx_state__state_write', { mode: 'ultragoal', active: true }));
|
|
2830
|
+
writeFileSync(join(stateDir, 'session.json'), JSON.stringify({ session_id: sessionId, native_session_id: leaderAgentId, cwd: smokeCwd }));
|
|
2831
|
+
|
|
2832
|
+
for (const [probe, toolName, toolInput] of [
|
|
2833
|
+
['filesystem write', 'mcp__filesystem__write_file', { path: 'src/packed-mcp-write.ts', content: 'escaped' }],
|
|
2834
|
+
['state write', 'mcp__omx_state__state_write', { mode: 'ultragoal', active: true }],
|
|
2835
|
+
] as const) {
|
|
2836
|
+
requireActorDeny('native-child', probe, runActorProbe('native-child', probe, toolName, toolInput));
|
|
2837
|
+
}
|
|
2838
|
+
for (const [probe, toolInput] of [
|
|
2839
|
+
['state write foreign routing', { mode: 'ultragoal', workingDirectory: 'src', session_id: 'foreign', active: false }],
|
|
2840
|
+
['state write unknown payload key', { mode: 'ultragoal', active: true, child_marker: 'forbidden' }],
|
|
2841
|
+
] as const) {
|
|
2842
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
2843
|
+
requireActorDeny(actor, probe, runActorProbe(actor, probe, 'mcp__omx_state__state_write', toolInput));
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
2847
|
+
requireActorDeny(
|
|
2848
|
+
actor,
|
|
2849
|
+
'state clear active workflow',
|
|
2850
|
+
runActorProbe(actor, 'state clear active workflow', 'mcp__omx_state__state_clear', { mode: 'ultragoal' }),
|
|
2851
|
+
);
|
|
2852
|
+
}
|
|
2853
|
+
for (const identity of [
|
|
2854
|
+
{ agent_id: leaderAgentId, thread_id: 'thread-packed-install-foreign' },
|
|
2855
|
+
{ agent_id: 'agent-packed-install-foreign', thread_id: leaderAgentId },
|
|
2856
|
+
]) {
|
|
2857
|
+
const output = parseNativeHookSmokeOutput(
|
|
2858
|
+
'PreToolUse conflicting leader identity',
|
|
2859
|
+
String(invokeAuthorizationProbe({
|
|
2860
|
+
hook_event_name: 'PreToolUse',
|
|
2861
|
+
cwd: smokeCwd,
|
|
2862
|
+
session_id: sessionId,
|
|
2863
|
+
...identity,
|
|
2864
|
+
tool_name: 'Write',
|
|
2865
|
+
tool_input: { file_path: 'src/packed-conflict.ts', content: 'escaped' },
|
|
2866
|
+
}, childEnv).stdout),
|
|
2867
|
+
);
|
|
2868
|
+
requireNativeHookPermissionDeny('PreToolUse conflicting leader identity', output, /PROVENANCE_DENIED/);
|
|
2869
|
+
}
|
|
2870
|
+
const consistentLeaderOutput = parseNativeHookSmokeOutput(
|
|
2871
|
+
'PreToolUse consistent leader identity',
|
|
2872
|
+
String(invokeAuthorizationProbe({
|
|
2873
|
+
hook_event_name: 'PreToolUse',
|
|
2874
|
+
cwd: smokeCwd,
|
|
2875
|
+
session_id: sessionId,
|
|
2876
|
+
agent_id: leaderAgentId,
|
|
2877
|
+
thread_id: leaderAgentId,
|
|
2878
|
+
tool_name: 'Write',
|
|
2879
|
+
tool_input: { file_path: 'src/packed-consistent-leader.ts', content: 'escaped' },
|
|
2880
|
+
}, childEnv).stdout),
|
|
2881
|
+
);
|
|
2882
|
+
requireNativeHookPermissionDeny('PreToolUse consistent leader identity', consistentLeaderOutput, /Main-root Conductor mode is active/);
|
|
2883
|
+
const ownerSessionClaimOutput = parseNativeHookSmokeOutput(
|
|
2884
|
+
'PreToolUse owner-session identity claim',
|
|
2885
|
+
String(invokeAuthorizationProbe({
|
|
2886
|
+
hook_event_name: 'PreToolUse',
|
|
2887
|
+
cwd: smokeCwd,
|
|
2888
|
+
session_id: sessionId,
|
|
2889
|
+
owner_codex_session_id: leaderAgentId,
|
|
2890
|
+
owner_omx_session_id: sessionId,
|
|
2891
|
+
tool_name: 'Write',
|
|
2892
|
+
tool_input: { file_path: 'src/packed-session-claim.ts', content: 'escaped' },
|
|
2893
|
+
}, childEnv).stdout),
|
|
2894
|
+
);
|
|
2895
|
+
requireNativeHookPermissionDeny('PreToolUse owner-session identity claim', ownerSessionClaimOutput, /OWNER_CONFIRMATION_REQUIRED/);
|
|
2896
|
+
|
|
2897
|
+
const wgetReviewMutationCommands = [
|
|
2898
|
+
['bare wget download', 'wget https://example.test/file'],
|
|
2899
|
+
['wget short output log', 'wget -o .omx/state/wget.log https://example.invalid/native-child-write'],
|
|
2900
|
+
['wget long output log', 'wget --output-file=.omx/state/wget.log https://example.invalid/native-child-write'],
|
|
2901
|
+
['wget short append log', 'wget -a .omx/state/wget.log https://example.invalid/native-child-write'],
|
|
2902
|
+
['wget long append log', 'wget --append-output=.omx/state/wget.log https://example.invalid/native-child-write'],
|
|
2903
|
+
['wget dot-slash dash file target', 'wget --no-config --no-hsts -O ./- https://example.test/file'],
|
|
2904
|
+
['xargs short arg file', 'xargs -a .omx/state/urls wget'],
|
|
2905
|
+
['xargs long arg file', 'xargs --arg-file .omx/state/urls wget'],
|
|
2906
|
+
['xargs short delimiter', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs -d , wget`],
|
|
2907
|
+
['xargs long delimiter', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --delimiter , wget`],
|
|
2908
|
+
['xargs short eof', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs -E STOP wget`],
|
|
2909
|
+
['xargs long eof', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --eof=STOP wget`],
|
|
2910
|
+
['xargs short replace', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs -I X wget X`],
|
|
2911
|
+
['xargs long replace', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --replace=X wget X`],
|
|
2912
|
+
['xargs bsd replace', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs -J X wget X`],
|
|
2913
|
+
['xargs short max lines', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs -L 1 wget`],
|
|
2914
|
+
['xargs long max lines', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --max-lines=1 wget`],
|
|
2915
|
+
['xargs short max args', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs -n 1 wget`],
|
|
2916
|
+
['xargs long max args', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --max-args 1 wget`],
|
|
2917
|
+
['xargs short max procs', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs -P 1 wget`],
|
|
2918
|
+
['xargs long max procs', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --max-procs 1 wget`],
|
|
2919
|
+
['xargs short max chars', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs -s 4096 wget`],
|
|
2920
|
+
['xargs long max chars', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --max-chars 4096 wget`],
|
|
2921
|
+
['xargs process slot var', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --process-slot-var SLOT wget`],
|
|
2922
|
+
['xargs abbreviated process slot var', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --process-slot-v SLOT wget`],
|
|
2923
|
+
['xargs ambiguous long option', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --max SLOT wget`],
|
|
2924
|
+
['xargs unknown long option', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --future-option SLOT wget`],
|
|
2925
|
+
['wget end-of-options spider operand', 'wget -- --spider https://example.test/file'],
|
|
2926
|
+
['wget short option argument smuggling', 'wget -U --spider https://example.test/file'],
|
|
2927
|
+
['wget long option argument smuggling', 'wget --user-agent --spider https://example.test/file'],
|
|
2928
|
+
['xargs eof no-value mutator', `printf '%s\n' src/victim.ts | xargs --eof rm`],
|
|
2929
|
+
['xargs replace no-value mutator', `printf '%s\n' src/victim.ts | xargs --replace rm {}`],
|
|
2930
|
+
['xargs max-lines no-value wget', `printf '%s\n' 'https://example.test/file' | xargs --max-lines wget true`],
|
|
2931
|
+
['xargs single quoted empty eof mutator', `printf '%s\n' src/xargs-owned.ts | xargs -E '' touch true`],
|
|
2932
|
+
['xargs double quoted empty eof mutator', `printf '%s\n' src/xargs-owned.ts | xargs -E "" touch true`],
|
|
2933
|
+
['xargs wget short stdout body override', `printf '%s\n' '--output-document=src/xargs-wget-owned.ts' 'https://example.test/file' | xargs wget -O -`],
|
|
2934
|
+
['xargs wget long stdout body override', `printf '%s\n' '--output-document=src/xargs-wget-owned.ts' 'https://example.test/file' | xargs wget --output-document=-`],
|
|
2935
|
+
['xargs wget stdout log injection', `printf '%s\n' '--output-file=src/xargs-wget.log' 'https://example.test/file' | xargs wget -O -`],
|
|
2936
|
+
['dynamic wget stdout body override', `WGET_OPT=--output-document=src/dynamic-wget-owned.ts; wget -O - $WGET_OPT https://example.test/file`],
|
|
2937
|
+
['dynamic wget stdout log injection', `WGET_OPT=--output-file=src/dynamic-wget.log; wget -O - $WGET_OPT https://example.test/file`],
|
|
2938
|
+
['posix wget option ordering', `POSIXLY_CORRECT=1 wget -O src/posix-wget-owned.ts https://example.test/file -O -`],
|
|
2939
|
+
['posix wget append assignment', `POSIXLY_CORRECT+=1 wget --no-config -O src/plus-prefix-owned.ts https://example.test/file -O -`],
|
|
2940
|
+
['export posix wget append assignment', `export POSIXLY_CORRECT+=1; wget --no-config -O src/plus-export-owned.ts https://example.test/file -O -`],
|
|
2941
|
+
['declare posix wget append assignment', `declare -x POSIXLY_CORRECT+=1; wget --no-config -O src/plus-declare-owned.ts https://example.test/file -O -`],
|
|
2942
|
+
['function posix wget append assignment', `f(){ export POSIXLY_CORRECT+=1; }; f; wget --no-config -O src/plus-function-owned.ts https://example.test/file -O -`],
|
|
2943
|
+
['wrapper posix wget append assignment', `command env POSIXLY_CORRECT+=1 wget --no-config -O src/plus-wrapper-owned.ts https://example.test/file -O -`],
|
|
2944
|
+
['quoted posix wget append assignment', `export "POSIXLY_CORRECT+=1"; wget --no-config -O src/plus-quoted-owned.ts https://example.test/file -O -`],
|
|
2945
|
+
['function local posix wget append assignment', `f(){ local -x POSIXLY_CORRECT+=1; wget --no-config -O src/plus-local-owned.ts https://example.test/file -O -; }; f`],
|
|
2946
|
+
['unset posix then append wget', `export POSIXLY_CORRECT=1; unset POSIXLY_CORRECT; export POSIXLY_CORRECT+=1; wget --no-config -O src/unset-append-owned.ts https://example.test/file -O -`],
|
|
2947
|
+
['braced wget stdout body override', `WGET_OPT=--output-document=src/braced-wget-owned.ts; wget -O - \${WGET_OPT} https://example.test/file`],
|
|
2948
|
+
['command substitution wget log injection', `wget -O - $(printf '%s' '--output-file=src/substitution-wget.log') https://example.test/file`],
|
|
2949
|
+
['exported posix wget ordering', `export POSIXLY_CORRECT=1; wget -O src/exported-posix-owned.ts https://example.test/file -O -`],
|
|
2950
|
+
['nested posix wget ordering', `POSIXLY_CORRECT=1 sh -c 'wget -O src/nested-posix-owned.ts https://example.test/file -O -'`],
|
|
2951
|
+
['quote concatenated posix wget ordering', `export POSIXLY_''CORRECT=1; wget -O src/quoted-posix-owned.ts https://example.test/file -O -`],
|
|
2952
|
+
['escaped posix wget ordering', `export POSIXLY_\\CORRECT=1; wget -O src/escaped-posix-owned.ts https://example.test/file -O -`],
|
|
2953
|
+
['ansi c posix wget ordering', `export $'POSIXLY_CORRECT'=1; wget -O src/ansi-posix-owned.ts https://example.test/file -O -`],
|
|
2954
|
+
['dynamic name posix wget ordering', `POSIX_NAME=POSIXLY_CORRECT; export "$POSIX_NAME=1"; wget -O src/dynamic-posix-owned.ts https://example.test/file -O -`],
|
|
2955
|
+
['split dynamic name posix wget ordering', `A=POSIXLY; B=_CORRECT; export "$A$B=1"; wget -O src/split-dynamic-posix-owned.ts https://example.test/file -O -`],
|
|
2956
|
+
['command export posix wget ordering', `command export POSIXLY_CORRECT=1; wget -O src/command-export-posix-owned.ts https://example.test/file -O -`],
|
|
2957
|
+
['command env posix wget ordering', `command env POSIXLY_CORRECT=1 wget -O src/command-env-posix-owned.ts https://example.test/file -O -`],
|
|
2958
|
+
['command double dash export posix wget ordering', `command -- export POSIXLY_CORRECT=1; wget -O src/command-dash-posix-owned.ts https://example.test/file -O -`],
|
|
2959
|
+
['compound dynamic export posix wget ordering', `N=POSIXLY_CORRECT; if export "$N=1"; then wget -O src/compound-posix-owned.ts https://example.test/file -O -; fi`],
|
|
2960
|
+
['time env posix wget ordering', `time env POSIXLY_CORRECT=1 wget -O src/time-posix-owned.ts https://example.test/file -O -`],
|
|
2961
|
+
['function export posix wget ordering', `f(){ export POSIXLY_CORRECT=1; }; f; wget -O src/function-posix-owned.ts https://example.test/file -O -`],
|
|
2962
|
+
['function dynamic export posix wget ordering', `f(){ N=POSIXLY_CORRECT; export "$N=1"; }; f; wget -O src/function-dynamic-posix-owned.ts https://example.test/file -O -`],
|
|
2963
|
+
['function local export posix wget ordering', `f(){ local -x POSIXLY_CORRECT=1; wget -O src/local-posix-owned.ts https://example.test/file -O -; }; f`],
|
|
2964
|
+
['function local dynamic export posix wget ordering', `f(){ N=POSIXLY_CORRECT; local -x "$N=1"; wget -O src/local-dynamic-posix-owned.ts https://example.test/file -O -; }; f`],
|
|
2965
|
+
['case arm function wget', `f(){ wget https://example.test/file; }; case true in true) f;; esac`],
|
|
2966
|
+
['multi case arm function wget', `f(){ wget https://example.test/file; }; case x in a) true;; x) f;; esac`],
|
|
2967
|
+
['direct case arm wget log', `case true in true) wget --no-config -o src/case-wget.log -O - http://127.0.0.1:1/;; esac`],
|
|
2968
|
+
['direct case arm mutator', `case true in true) touch src/case-mutator.ts;; esac`],
|
|
2969
|
+
['pre redefinition posix wget', `f(){ export POSIXLY_CORRECT=1; }; f; f(){ :; }; wget --no-config -O src/redefined-posix-owned.ts https://example.test/file -O -`],
|
|
2970
|
+
['nested shadowed builtin posix wget', `echo(){ export POSIXLY_CORRECT=1; }; f(){ echo; }; f; wget --no-config -O src/nested-posix-owned.ts https://example.test/file -O -`],
|
|
2971
|
+
['deep nested shadowed builtin posix wget', `echo(){ export POSIXLY_CORRECT=1; }; f(){ g(){ echo; }; g; }; f; wget --no-config -O src/deep-nested-posix-owned.ts https://example.test/file -O -`],
|
|
2972
|
+
['case pattern alternation posix wget', `f(){ export POSIXLY_CORRECT=1; }; case true in true|false) f;; esac; wget --no-config -O src/case-posix-owned.ts https://example.test/file -O -`],
|
|
2973
|
+
['env chdir wget log', `env -C src wget --no-config -o .omx/state/env-c.log -O - http://127.0.0.1:1/`],
|
|
2974
|
+
['wgetrc logfile write', `printf '%s\n' 'logfile = src/wgetrc.log' | WGETRC=/dev/stdin wget -O - http://127.0.0.1:1/`],
|
|
2975
|
+
['wgetrc output document write', `printf '%s\n' 'output_document = src/wgetrc-output.ts' | WGETRC=/dev/stdin wget -O - http://127.0.0.1:1/`],
|
|
2976
|
+
['wgetrc directory prefix write', `printf '%s\n' 'dir_prefix = src/wgetrc-directory' | WGETRC=/dev/stdin wget -O - http://127.0.0.1:1/`],
|
|
2977
|
+
['command wrapper function shadow', `touch(){ :; }; command touch src/wrapped-shadow-owned.ts`],
|
|
2978
|
+
['short circuit posix unset', `export POSIXLY_CORRECT=1; false && unset POSIXLY_CORRECT; wget --no-config -O src/short-circuit-owned.ts http://127.0.0.1:1/ -O -`],
|
|
2979
|
+
['conditional function rebind', `f(){ export POSIXLY_CORRECT=1; }; if false; then f(){ :; }; fi; f; wget --no-config -O src/conditional-rebind-owned.ts http://127.0.0.1:1/ -O -`],
|
|
2980
|
+
['readonly posix unset', `export POSIXLY_CORRECT=1; readonly POSIXLY_CORRECT; unset POSIXLY_CORRECT; wget --no-config -O src/readonly-unset-owned.ts http://127.0.0.1:1/ -O -`],
|
|
2981
|
+
['prefix posix function', `f(){ wget --no-config -O src/prefix-function-owned.ts http://127.0.0.1:1/ -O -; }; POSIXLY_CORRECT=1 f`],
|
|
2982
|
+
['prefix posix append function', `f(){ wget --no-config -O src/prefix-append-function-owned.ts http://127.0.0.1:1/ -O -; }; POSIXLY_CORRECT+=1 f`],
|
|
2983
|
+
['declare global posix', `f(){ declare -gx POSIXLY_CORRECT=1; }; f; wget --no-config -O src/declare-global-owned.ts http://127.0.0.1:1/ -O -`],
|
|
2984
|
+
['export before local posix', `f(){ export POSIXLY_CORRECT=1; local POSIXLY_CORRECT=1; }; f; wget --no-config -O src/export-before-local-owned.ts http://127.0.0.1:1/ -O -`],
|
|
2985
|
+
['process substitution current shell', `f(){ export POSIXLY_CORRECT=1; }; f <(true); wget --no-config -O src/process-substitution-owned.ts http://127.0.0.1:1/ -O -`],
|
|
2986
|
+
['coproc ordinary argument', `f(){ export POSIXLY_CORRECT=1; }; f coproc; wget --no-config -O src/coproc-argument-owned.ts http://127.0.0.1:1/ -O -`],
|
|
2987
|
+
['subshell function rebind', `g(){ export POSIXLY_CORRECT=1; }; f(){ g(){ :; }; }; ( f ); g; wget --no-config -O src/subshell-rebind-owned.ts http://127.0.0.1:1/ -O -`],
|
|
2988
|
+
['background function rebind', `g(){ export POSIXLY_CORRECT=1; }; f(){ g(){ :; }; }; f & wait; g; wget --no-config -O src/background-rebind-owned.ts http://127.0.0.1:1/ -O -`],
|
|
2989
|
+
['pipeline function rebind', `g(){ export POSIXLY_CORRECT=1; }; f(){ g(){ :; }; }; f | cat; g; wget --no-config -O src/pipeline-rebind-owned.ts http://127.0.0.1:1/ -O -`],
|
|
2990
|
+
['coproc function rebind', `g(){ export POSIXLY_CORRECT=1; }; f(){ g(){ :; }; }; coproc f; wait; g; wget --no-config -O src/coproc-rebind-owned.ts http://127.0.0.1:1/ -O -`],
|
|
2991
|
+
['later case pattern posix', `f(){ export POSIXLY_CORRECT=1; }; case true in false) :;; true|false) f;; esac; wget --no-config -o src/later-case.log http://127.0.0.1:1/ -o .omx/state/final.log -O -`],
|
|
2992
|
+
['optional case pattern posix', `f(){ export POSIXLY_CORRECT=1; }; case true in (true) f;; esac; wget --no-config -O src/optional-case-owned.ts http://127.0.0.1:1/ -O -`],
|
|
2993
|
+
['nested case pattern posix', `f(){ export POSIXLY_CORRECT=1; }; case true in false) case true in true) :;; esac;; true|false) f;; esac; wget --no-config -O src/nested-case-owned.ts http://127.0.0.1:1/ -O -`],
|
|
2994
|
+
['env chdir curl', `env -C src curl -q -o .omx/state/curl-owned.log http://127.0.0.1:1/`],
|
|
2995
|
+
['env chdir mutator', `env -C src mkdir -p .omx/state/cwd-owned`],
|
|
2996
|
+
['env long chdir mutator', `env --chdir=src touch .omx/state/env-touch-owned`],
|
|
2997
|
+
['persistent wgetrc config', `export WGETRC=/dev/stdin; printf '%s\n' 'logfile = src/persistent-wgetrc.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
|
|
2998
|
+
['nested posix shell environment', `POSIXLY_CORRECT=1 sh -c 'wget --no-config -o src/nested-posix.log http://127.0.0.1:1/ -o .omx/state/final.log -O -'`],
|
|
2999
|
+
['lastpipe posix', `bash -O lastpipe -c 'f(){ export POSIXLY_CORRECT=1; }; true | f; wget --no-config -o src/lastpipe.log http://127.0.0.1:1/ -o .omx/state/final.log -O -'`],
|
|
3000
|
+
['subshell definition scope', `f(){ export POSIXLY_CORRECT=1; }; ( :; f(){ :; }; ); f; wget --no-config -o src/redefinition-scope.log http://127.0.0.1:1/ -o .omx/state/final.log -O -`],
|
|
3001
|
+
['shadowed env function', `env(){ export POSIXLY_CORRECT=1; }; env; wget --no-config -o src/shadow-env.log http://127.0.0.1:1/ -o .omx/state/final.log -O -`],
|
|
3002
|
+
['command suppressed shadow', `export POSIXLY_CORRECT=1; echo(){ unset POSIXLY_CORRECT; }; command echo safe; wget --no-config -o src/command-shadow.log http://127.0.0.1:1/ -o .omx/state/final.log -O -`],
|
|
3003
|
+
['assignment shaped wget operand', `POSIXLY_CORRECT=1 wget --no-config -o src/assignment-operand.log NAME=value -o .omx/state/final.log -O -`],
|
|
3004
|
+
['wrapped xargs wget', `printf '%s\n' '--output-file=src/wrapped-xargs.log' 'http://127.0.0.1:1/' | env xargs wget --no-config -O -`],
|
|
3005
|
+
['wrapped xargs empty eof mutator', `printf '%s\n' src/wrapped-xargs-owned.ts | env xargs -E '' touch .omx/state/anchor`],
|
|
3006
|
+
['nested chdir shell', `env --chdir=src sh -c 'touch .omx/state/env-nested-owned'`],
|
|
3007
|
+
['case branch posix unset', `case true in true) export POSIXLY_CORRECT=1;; false) unset POSIXLY_CORRECT;; esac; wget --no-config -O src/case-branch-owned.ts http://127.0.0.1:1/ -O -`],
|
|
3008
|
+
['subshell direct posix unset', `export POSIXLY_CORRECT=1; ( unset POSIXLY_CORRECT ); wget --no-config -o src/subshell-unset.log http://127.0.0.1:1/ -o .omx/state/final.log -O -`],
|
|
3009
|
+
['env long chdir wget', `env --chdir=src wget --no-config -o .omx/state/env-long-wget.log -O - http://127.0.0.1:1/`],
|
|
3010
|
+
['env long chdir curl', `env --chdir=src curl -q -o .omx/state/env-long-curl.log http://127.0.0.1:1/`],
|
|
3011
|
+
['posix no config after operand', `POSIXLY_CORRECT=1 wget --spider https://example.test/file --no-config`],
|
|
3012
|
+
['posix no hsts after operand', `HOME=src POSIXLY_CORRECT=1 wget --no-config --spider https://example.test/file --no-hsts`],
|
|
3013
|
+
['bare bash login shell', `bash -lc "printf safe"`],
|
|
3014
|
+
['subshell sequence posix wget', `( export POSIXLY_CORRECT=1; wget --no-config -O src/subshell-sequence-owned.ts http://127.0.0.1:1/ -O - )`],
|
|
3015
|
+
['conditional unbound touch', `if false; then :; touch(){ :; }; fi; touch src/conditional-binding-owned.ts`],
|
|
3016
|
+
['reassigned exported wgetrc', `export WGETRC=/dev/null; WGETRC=/dev/stdin; printf '%s\n' 'logfile = src/reassigned-wgetrc.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
|
|
3017
|
+
['function local wgetrc restoration', `export WGETRC=/dev/stdin; f(){ local -x WGETRC=/dev/null; export POSIXLY_CORRECT=1; }; f; printf '%s\n' 'logfile = src/function-scope-wgetrc.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
|
|
3018
|
+
['short circuit wgetrc', `export WGETRC=/dev/stdin; false && export WGETRC=/dev/null; printf '%s\n' 'logfile = src/short-circuit-wgetrc.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
|
|
3019
|
+
['short circuit function rebind', `f(){ export POSIXLY_CORRECT=1; }; false && f(){ :; }; f; wget --no-config -O src/short-rebind-owned.ts http://127.0.0.1:1/ -O -`],
|
|
3020
|
+
['case arm pipeline touch', `case x in x) : | touch src/case-pipeline-owned.ts;; esac`],
|
|
3021
|
+
['case pipeline function rebind', `g(){ export POSIXLY_CORRECT=1; }; f(){ g(){ :; }; }; case true in true) f | cat;; esac; g; wget --no-config -O src/case-pipeline-rebind-owned.ts http://127.0.0.1:1/ -O -`],
|
|
3022
|
+
['process substitution wgetrc state', `export WGETRC=/dev/stdin; : <(export WGETRC=/dev/null); printf '%s\n' 'logfile = src/process-sub-state.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
|
|
3023
|
+
['background group wgetrc state', `export WGETRC=/dev/stdin; { export WGETRC=/dev/null; } & wait; printf '%s\n' 'logfile = src/background-group.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
|
|
3024
|
+
['readonly wgetrc unset', `export WGETRC=/dev/stdin; readonly WGETRC; unset WGETRC; printf '%s\n' 'logfile = src/readonly-wgetrc.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
|
|
3025
|
+
['unset function mode wgetrc', `export WGETRC=/dev/stdin; unset -f WGETRC; printf '%s\n' 'logfile = src/unset-mode-wgetrc.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
|
|
3026
|
+
['quoted empty wget operand', `env -u POSIXLY_CORRECT wget --no-config -O - '' -o src/quoted-empty.log http://127.0.0.1:1/`],
|
|
3027
|
+
['assignment shaped mutator argv', `env -C src touch ../.omx/state/final.log OWNED=value`],
|
|
3028
|
+
['comment function binding', `true # ; touch(){ :; }\ntouch src/comment-binding-owned.ts`],
|
|
3029
|
+
['skipped if wgetrc', `export WGETRC=/dev/stdin; if false; then export WGETRC=/dev/null; fi; printf '%s\n' 'logfile = src/skipped-if-wgetrc.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
|
|
3030
|
+
['mutually exclusive case home', `export HOME=/dev/stdin; case true in true) export HOME=/dev/null;; false) :;; esac; wget -O - http://127.0.0.1:1/`],
|
|
3031
|
+
['synchronous brace posix', `f(){ export POSIXLY_CORRECT=1; }; { f; }; WGETRC=/dev/null wget -O src/synchronous-brace-owned.ts https://example.test/file -O -`],
|
|
3032
|
+
['nested isolation parent state', `( export POSIXLY_CORRECT=1; ( wget --no-config -O src/nested-region-owned.ts http://127.0.0.1:1/ -O - ) )`],
|
|
3033
|
+
['function alternative state join', `f(){ export WGETRC=/dev/stdin; }; if false; then f(){ export WGETRC=/dev/null; }; fi; f; printf '%s\n' 'logfile = src/function-alt-join.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
|
|
3034
|
+
['function binding alternative join', `g(){ :; }; f(){ g(){ export POSIXLY_CORRECT=1; }; }; if false; then f(){ :; }; fi; f; g; wget --no-config -O src/function-binding-join-owned.ts http://127.0.0.1:1/ -O -`],
|
|
3035
|
+
['conditional wrapper unbound', `if false; then :; env(){ :; }; fi; env touch src/conditional-env-owned.ts`],
|
|
3036
|
+
['shadowed false status', `false(){ :; }; false && export POSIXLY_CORRECT=1; wget --no-config -O src/shadow-false-owned.ts http://127.0.0.1:1/ -O -`],
|
|
3037
|
+
['path conditional readonly', `export WGETRC=/dev/null; if false; then readonly WGETRC; fi; WGETRC=/dev/stdin; printf '%s\n' 'logfile = src/maybe-readonly-wgetrc.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
|
|
3038
|
+
['readonly function mode', `export WGETRC=/dev/null; readonly -f WGETRC; WGETRC=/dev/stdin; printf '%s\n' 'logfile = src/readonly-function-mode.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
|
|
3039
|
+
['local inherit wgetrc', `export WGETRC=/dev/stdin; f(){ local -I WGETRC; printf '%s\n' 'logfile = src/local-inherit-wgetrc.log' | HOME=/dev/null wget -O - http://127.0.0.1/; }; f`],
|
|
3040
|
+
['case fallthrough touch', `case x in x) : ;& true) touch src/case-fallthrough-owned.ts;; esac`],
|
|
3041
|
+
['case reserved word touch', `case case in case) touch src/case-reserved-word-owned.ts;; esac`],
|
|
3042
|
+
['parameter length comment touch', `: \${#x}; touch src/parameter-length-comment-owned.ts`],
|
|
3043
|
+
['touch mode arity', `touch .omx/state/final.log -m src/touch-option-owned.ts`],
|
|
3044
|
+
['nested export posix', `export POSIXLY_CORRECT=1; sh -c 'wget --no-config -O src/nested-export-owned.ts http://127.0.0.1:1/ -O -'`],
|
|
3045
|
+
['process substitution inherited posix', `export POSIXLY_CORRECT=1; cat <(wget --no-config -O src/process-inherited-owned.ts http://127.0.0.1:1/ -O -)`],
|
|
3046
|
+
['bashopts lastpipe', `env BASHOPTS=lastpipe bash -c 'f(){ export POSIXLY_CORRECT=1; }; true | f; wget --no-config -O src/bashopts-lastpipe-owned.ts http://127.0.0.1:1/ -O -'`],
|
|
3047
|
+
['skipped function call state', `export WGETRC=/dev/stdin; f(){ export WGETRC=/dev/null; }; if false; then f; fi; printf '%s\n' 'logfile = src/skipped-function.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
|
|
3048
|
+
['nested isolation wgetrc', `export WGETRC=/dev/null; ( export WGETRC=/dev/stdin; ( printf '%s\n' 'logfile = src/nested-isolation.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/ ) )`],
|
|
3049
|
+
['declare function local', `export WGETRC=/dev/stdin; f(){ declare -x WGETRC=/dev/null; }; f; printf '%s\n' 'logfile = src/declare-local.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
|
|
3050
|
+
['readonly branch join', `export WGETRC=/dev/null; if true; then :; else readonly WGETRC; fi; WGETRC=/dev/stdin; printf '%s\n' 'logfile = src/readonly-join.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
|
|
3051
|
+
['nested background isolation parent state', `( export POSIXLY_CORRECT=1; ( wget --no-config -O src/nested-background-owned.ts http://127.0.0.1:1/ -O - ) ) & wait`],
|
|
3052
|
+
['case retest fallthrough touch', `case x in x) : ;;& x) touch src/case-retest-owned.ts;; esac`],
|
|
3053
|
+
['recursive nested cwd root', `env --chdir=src sh -c "sh -c 'mkdir -p .omx/state/nested-cwd-owned'"`],
|
|
3054
|
+
['process substitution lexical cwd', `cd src; cat <(mkdir -p .omx/state/process-cwd-owned)`],
|
|
3055
|
+
['nameref posix wget', `f(){ declare -n ref=POSIXLY_CORRECT; export ref=1; }; f; wget --no-config -O src/nameref-posix-owned.ts https://example.test/file -O -`],
|
|
3056
|
+
['global nameref posix wget', `f(){ declare -gn ref=POSIXLY_CORRECT; }; f; export ref=1; wget --no-config -O src/global-nameref-posix-owned.ts https://example.test/file -O -`],
|
|
3057
|
+
['arithmetic expansion posix wget', `export POSIXLY_CORRECT; : $((POSIXLY_CORRECT=1)); wget --no-config https://example.test/file -O src/arithmetic-posix-owned.ts`],
|
|
3058
|
+
['curl header sink', `curl -q --dump-header src/curl-header-owned.txt https://example.test/file`],
|
|
3059
|
+
['rsync remote sink', `rsync .omx/state/conductor-ledger.json example.test:state/`],
|
|
3060
|
+
['rsync remote log sink', `rsync --log-file=.omx/state/rsync.log .omx/state/conductor-ledger.json example.test:state/`],
|
|
3061
|
+
['function cwd state', `f(){ cd src; }; f; mkdir -p .omx/state/function-cwd-owned`],
|
|
3062
|
+
['function caller local state', `export WGETRC=/dev/null; g(){ WGETRC=/dev/stdin; }; f(){ local -x WGETRC=/dev/null; g; printf '%s\n' 'logfile = src/function-caller-local-owned.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/; }; f`],
|
|
3063
|
+
['process substitution before inline unset', `export POSIXLY_CORRECT=1; unset POSIXLY_CORRECT <(wget --no-config -O src/process-inline-unset-owned.ts http://127.0.0.1:1/ -O -)`],
|
|
3064
|
+
['process substitution before function', `export POSIXLY_CORRECT=1; f(){ unset POSIXLY_CORRECT; }; f <(wget --no-config -O src/process-before-function-owned.ts http://127.0.0.1:1/ -O -)`],
|
|
3065
|
+
['nested process substitution touch', `cat <(cat <(touch src/nested-process-owned.ts))`],
|
|
3066
|
+
['conditional job control lastpipe', `export POSIXLY_CORRECT=1; shopt -s lastpipe; if true; then set -m; fi; f(){ unset POSIXLY_CORRECT; }; true | f; wget --no-config -O src/jobcontrol-owned.ts http://127.0.0.1:1/ -O -`],
|
|
3067
|
+
['unsupported shopt option', `shopt -s extglob; touch .omx/state/final.log`],
|
|
3068
|
+
['dynamic shopt lastpipe', `mode=lastpipe; shopt -s "$mode"; touch .omx/state/final.log`],
|
|
3069
|
+
['loop function definition', `while false; do touch(){ :; }; done; touch src/loop-definition-owned.ts`],
|
|
3070
|
+
['background function definition', `{ touch(){ :; }; } & wait; touch src/background-definition-owned.ts`],
|
|
3071
|
+
['pipeline function definition', `{ touch(){ :; }; } | cat; touch src/pipeline-definition-owned.ts`],
|
|
3072
|
+
['coproc function definition', `coproc { touch(){ :; }; }; wait; touch src/coproc-definition-owned.ts`],
|
|
3073
|
+
['coproc external touch mutation', `coproc touch src/coproc-external-owned.ts; wait`],
|
|
3074
|
+
['coproc function runtime does not leak', `coproc { local_read(){ :; }; local_read; }; wait; local_read`],
|
|
3075
|
+
['subshell function definition', `( touch(){ :; }; ); touch src/subshell-definition-owned.ts`],
|
|
3076
|
+
['conditional readonly function', `f(){ :; }; if false; then readonly -f f; fi; f(){ export POSIXLY_CORRECT=1; }; f; wget --no-config -O src/conditional-readonly-function-owned.ts http://127.0.0.1:1/ -O -`],
|
|
3077
|
+
['readonly unbound function', `readonly -f touch; touch src/readonly-unbound-owned.ts`],
|
|
3078
|
+
['unexported child function', `touch(){ :; }; bash -c 'touch src/unexported-child-function-owned.ts'`],
|
|
3079
|
+
['export nf removes child function', `f(){ :; }; export -f f; export -nf f; bash -c 'f; wget --no-config -O - https://example.test/file'`],
|
|
3080
|
+
['exported child function uninspected body', `f(){ source src/exported-child-uninspected.sh; }; export -f f; bash -c 'f'`],
|
|
3081
|
+
['env child unset', `export POSIXLY_CORRECT=1; env unset POSIXLY_CORRECT; wget --no-config -O src/env-child-unset-owned.ts http://127.0.0.1:1/ -O -`],
|
|
3082
|
+
['global attribute under local', `WGETRC=/dev/stdin; f(){ local WGETRC=/dev/null; declare -gx WGETRC; }; f; printf '%s\n' 'logfile = src/global-attribute-owned.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
|
|
3083
|
+
['nameref unset referent', `export HOME=src; export WGETRC=/dev/null; declare -n ref=WGETRC; unset ref; wget -O - http://127.0.0.1:1/`],
|
|
3084
|
+
['arithmetic compound posix', `unset POSIXLY_CORRECT; export POSIXLY_CORRECT; : x$((POSIXLY_CORRECT+=1)); wget --no-config -O src/arithmetic-compound-owned.ts http://127.0.0.1:1/ -O -`],
|
|
3085
|
+
['parameter nameref posix', `unset POSIXLY_CORRECT; export POSIXLY_CORRECT; declare -n ref=POSIXLY_CORRECT; : x\${ref:=1}; wget --no-config -O src/parameter-nameref-owned.ts https://example.test/file -O -`],
|
|
3086
|
+
['case escaped esac', `case x in \\esac|x) touch src/case-escaped-esac-owned.ts;; esac`],
|
|
3087
|
+
['reserved filename touch', `touch .omx/state/final.log case`],
|
|
3088
|
+
['reserved filename copy', `cp .omx/state/conductor-ledger.json case`],
|
|
3089
|
+
['chmod reference product', `chmod --reference=.omx/state/session.json src/chmod-reference-owned.ts .omx/state/session.json`],
|
|
3090
|
+
['chown reference product', `chown --reference=.omx/state/session.json src/chown-reference-owned.ts .omx/state/session.json`],
|
|
3091
|
+
['chgrp reference product', `chgrp --reference=.omx/state/session.json src/chgrp-reference-owned.ts .omx/state/session.json`],
|
|
3092
|
+
['chmod reference separated product', `chmod --reference .omx/state/session.json src/chmod-reference-separated-owned.ts`],
|
|
3093
|
+
['chown reference separated product', `chown --reference .omx/state/session.json src/chown-reference-separated-owned.ts`],
|
|
3094
|
+
['chgrp reference separated product', `chgrp --reference .omx/state/session.json src/chgrp-reference-separated-owned.ts`],
|
|
3095
|
+
['curl cluster header sink', `curl -q -sD src/curl-cluster-owned.txt -o - http://127.0.0.1:1/`],
|
|
3096
|
+
['curl config sink', `curl -q -K src/curl-config-owned.txt http://127.0.0.1:1/`],
|
|
3097
|
+
['rsync log sink', `rsync --log-file=src/rsync-owned.log .omx/state/conductor-ledger.json .omx/state/rsync-copy`],
|
|
3098
|
+
['rsync partial sink', `rsync --partial-dir=src/rsync-partial .omx/state/conductor-ledger.json .omx/state/rsync-copy`],
|
|
3099
|
+
['rsync write batch sink', `rsync --write-batch=src/rsync-owned.batch .omx/state/conductor-ledger.json .omx/state/rsync-copy`],
|
|
3100
|
+
['failed cd rsync metadata control', `cd .omx/missing; rsync --log-file=.omx/state/failed-cd-rsync.log .omx/state/conductor-ledger.json .omx/state/failed-cd-rsync-copy`],
|
|
3101
|
+
['failed pushd rsync metadata control', `pushd .omx/missing; rsync --log-file=.omx/state/failed-pushd-rsync.log .omx/state/conductor-ledger.json .omx/state/failed-pushd-rsync-copy`],
|
|
3102
|
+
['cleared exported child function', `touch(){ :; }; export -f touch; env -u BASH_FUNC_touch%% bash -c 'touch src/cleared-exported-child-owned.ts'`],
|
|
3103
|
+
['cdpath relative cwd', `CDPATH=src; cd team; mkdir -p ../.omx/state/cdpath-owned`],
|
|
3104
|
+
['cdpath absolute cwd product', `CDPATH=src; cd /tmp; touch src/cdpath-absolute-owned.ts`],
|
|
3105
|
+
['cdpath balanced stack product', `CDPATH=src; pushd ./src; popd; touch src/cdpath-balanced-stack-owned.ts`],
|
|
3106
|
+
['unbalanced popd product', `popd; touch src/unbalanced-popd-owned.ts`],
|
|
3107
|
+
['reference without target', `chmod --reference=.omx/state/session.json`],
|
|
3108
|
+
['chown reference without target', `chown --reference=.omx/state/session.json`],
|
|
3109
|
+
['chgrp reference without target', `chgrp --reference=.omx/state/session.json`],
|
|
3110
|
+
['chmod reference separated without target', `chmod --reference .omx/state/session.json`],
|
|
3111
|
+
['chown reference separated without target', `chown --reference .omx/state/session.json`],
|
|
3112
|
+
['chgrp reference separated without target', `chgrp --reference .omx/state/session.json`],
|
|
3113
|
+
['exported bashopts lastpipe', `shopt -s lastpipe; export BASHOPTS; bash -c 'f(){ export POSIXLY_CORRECT=1; }; true | f; wget --no-config -O src/exported-bashopts-owned.ts http://127.0.0.1:1/ -O -'`],
|
|
3114
|
+
['depth process substitution prefix', `f4(){ export POSIXLY_CORRECT=1; cat <(true); cat <(wget --no-config -O src/depth-prefix-owned.ts http://127.0.0.1:1/ -O -); }; f3(){ f4; }; f2(){ f3; }; f1(){ f2; }; f1`],
|
|
3115
|
+
|
|
3116
|
+
|
|
3117
|
+
['wget stdout short directory prefix', `wget -O - -P src https://example.test/file`],
|
|
3118
|
+
['wget stdout long directory prefix', `wget --output-document=- --directory-prefix=src https://example.test/file`],
|
|
3119
|
+
['wget stdout short log target', `wget -O - -o src/wget-stdout.log https://example.test/file`],
|
|
3120
|
+
['wget stdout long log target', `wget --output-document=- --output-file=src/wget-stdout.log https://example.test/file`],
|
|
3121
|
+
['wget repeated output last file', `wget -O - https://example.test/file -O src/wget-last-output.ts`],
|
|
3122
|
+
['wget repeated log last file', `wget -o .omx/state/ignored.log -o src/wget-final.log -O - https://example.test/file`],
|
|
3123
|
+
['wget repeated directory last file', `wget -P .omx/state -P src https://example.test/file`],
|
|
3124
|
+
['wget repeated long directory last file', `wget --directory-prefix=.omx/state --directory-prefix=src https://example.test/file`],
|
|
3125
|
+
|
|
3126
|
+
|
|
3127
|
+
|
|
3128
|
+
|
|
3129
|
+
['wget short execute output sink', `wget --no-config -e 'output_document=src/wget-execute-owned.ts' https://example.test/file`],
|
|
3130
|
+
['wget long execute log sink', `wget --no-config --execute='logfile=src/wget-execute.log' -O - https://example.test/file`],
|
|
3131
|
+
['wget execute directory prefix sink', `wget --no-config --execute='dir_prefix=src/wget-execute-directory' https://example.test/file`],
|
|
3132
|
+
['wget execute unknown directive', `wget --no-config --execute='future_directive=src/wget-unknown.ts' -O - https://example.test/file`],
|
|
3133
|
+
['stdbuf i retains environment', `export POSIXLY_CORRECT=1; stdbuf -i 0 sh -c 'wget --no-config -O src/stdbuf-posix-owned.ts https://example.test/file -O -'`],
|
|
3134
|
+
['env i reapplies prefix environment', `export POSIXLY_CORRECT=1; env -i POSIXLY_CORRECT=1 sh -c 'wget --no-config -O src/env-i-posix-owned.ts https://example.test/file -O -'`],
|
|
3135
|
+
['curl startup config unresolved', `curl https://example.test/file`],
|
|
3136
|
+
['curl write out output sink', `curl -q --write-out '%output{src/curl-write-out-owned.log}' https://example.test/file`],
|
|
3137
|
+
['curl short write out output sink', `curl -q -w '%output{src/curl-short-write-out-owned.log}' https://example.test/file`],
|
|
3138
|
+
['curl write out append sink', `curl -q --write-out '%output{>>src/curl-write-out-append.log}' https://example.test/file`],
|
|
3139
|
+
['time short output sink', `time -o src/time-output-owned.log printf safe`],
|
|
3140
|
+
['time long output sink', `time --output=src/time-long-output-owned.log printf safe`],
|
|
3141
|
+
['command substitution later cwd', `cd src; printf '%s' "$(mkdir -p .omx/state/command-substitution-cwd-owned)"; cd ..`],
|
|
3142
|
+
['backtick substitution later cwd', "cd src; printf '%s' \`mkdir -p .omx/state/backtick-substitution-cwd-owned\`; cd .."],
|
|
3143
|
+
['command substitution later posix unset', `export POSIXLY_CORRECT=1; printf '%s' "$(wget --no-config -O src/command-substitution-posix-owned.ts http://127.0.0.1:1/ -O -)"; unset POSIXLY_CORRECT`],
|
|
3144
|
+
['command substitution later function unset', `export POSIXLY_CORRECT=1; printf '%s' "$(wget --no-config -O src/command-substitution-function-owned.ts http://127.0.0.1:1/ -O -)"; f(){ unset POSIXLY_CORRECT; }; f`],
|
|
3145
|
+
['grouped command substitution cwd', `( cd src; printf '%s' "$(mkdir -p .omx/state/group-command-substitution-owned)" )`],
|
|
3146
|
+
['grouped process substitution cwd', `( cd src; cat <(mkdir -p .omx/state/group-process-substitution-owned) )`],
|
|
3147
|
+
['printf v cdpath writer', `printf -v CDPATH '%s' src; cd shared; mkdir -p .omx/state/printf-v-cdpath-owned`],
|
|
3148
|
+
['read posix writer', `export POSIXLY_CORRECT; read POSIXLY_CORRECT <<< 1; wget --no-config -O src/read-posix-writer-owned.ts http://127.0.0.1:1/ -O -`],
|
|
3149
|
+
['printf v wgetrc writer', `printf -v WGETRC '%s' src/wgetrc; export WGETRC; wget -O - https://example.test/file`],
|
|
3150
|
+
['getopts wgetrc writer', `export WGETRC; getopts a WGETRC; wget -O - https://example.test/file`],
|
|
3151
|
+
['for cdpath writer', `for CDPATH in src; do :; done; cd shared; mkdir -p .omx/state/for-cdpath-owned`],
|
|
3152
|
+
['select cdpath writer', `select CDPATH in src; do break; done; cd shared; mkdir -p .omx/state/select-cdpath-owned`],
|
|
3153
|
+
['curl dynamic output', `OUT=src/curl-dynamic-output.log; curl -q -o "$OUT" https://example.test/file`],
|
|
3154
|
+
['curl dynamic write out', `FMT='%output{src/curl-dynamic-write-out.log}'; curl -q -w "$FMT" https://example.test/file`],
|
|
3155
|
+
['curl write out at file', `curl -q --write-out @.omx/state/curl-format https://example.test/file`],
|
|
3156
|
+
['curl dynamic argv injection', `CURL_OPTIONS='--upload-file=.omx/state/conductor-ledger.json'; curl -q $CURL_OPTIONS https://example.test/file`],
|
|
3157
|
+
['wget dynamic argv injection', `WGET_OPTIONS='--post-data=mode=write'; wget --no-config $WGET_OPTIONS -O - https://example.test/file`],
|
|
3158
|
+
['curl post data', `curl -q --data 'mode=write' https://example.test/file`],
|
|
3159
|
+
['curl request post', `curl -q --request POST https://example.test/file`],
|
|
3160
|
+
['curl upload file', `curl -q --upload-file .omx/state/conductor-ledger.json https://example.test/file`],
|
|
3161
|
+
['wget post data', `wget --no-config --post-data='mode=write' -O - https://example.test/file`],
|
|
3162
|
+
['wget method post', `wget --no-config --method=POST -O - https://example.test/file`],
|
|
3163
|
+
['wget background', `wget --no-config --background -O - https://example.test/file`],
|
|
3164
|
+
['rsync dynamic option', `OPTS='--log-file=src/rsync-dynamic.log'; rsync $OPTS .omx/state/conductor-ledger.json .omx/state/rsync-copy`],
|
|
3165
|
+
['rsync short cluster temp dir', `rsync -aTsrc/rsync-cluster-temp .omx/state/conductor-ledger.json .omx/state/rsync-copy`],
|
|
3166
|
+
['rsync short cluster temp dir next argv', `rsync -aT src/rsync-cluster-temp .omx/state/conductor-ledger.json .omx/state/rsync-copy`],
|
|
3167
|
+
['rsync short cluster unknown', `rsync -aZ .omx/state/conductor-ledger.json .omx/state/rsync-copy`],
|
|
3168
|
+
['time abbreviated output sink', `time --out=src/time-abbreviated-output.log printf safe`],
|
|
3169
|
+
['time exact separate output sink', `time --output src/time-exact-separate-output.log printf safe`],
|
|
3170
|
+
['time separate abbreviated output sink', `time --out src/time-separate-output.log printf safe`],
|
|
3171
|
+
['time attached output sink', `time -osrc/time-attached-output.log printf safe`],
|
|
3172
|
+
['time wrapper attached output sink', `env time -osrc/time-wrapper-attached-output.log printf safe`],
|
|
3173
|
+
['time append attached output sink', `time -aosrc/time-append-attached-output.log printf safe`],
|
|
3174
|
+
['time dynamic output sink', `OUT=src/time-dynamic-output.log; time -o "$OUT" printf safe`],
|
|
3175
|
+
['time malformed output sink', `time --output= printf safe`],
|
|
3176
|
+
['path repository shadow', `cp -p /usr/bin/touch .omx/state/cat; PATH=.omx/state; cat src/conductor-owned.ts`],
|
|
3177
|
+
['path prefix repository shadow', `cp -p /usr/bin/touch .omx/state/cat; PATH=.omx/state cat src/conductor-owned.ts`],
|
|
3178
|
+
['path env repository shadow', `cp -p /usr/bin/touch .omx/state/cat; env PATH=.omx/state cat src/conductor-owned.ts`],
|
|
3179
|
+
['cdpath global dirty function', `CDPATH=.omx; f(){ local CDPATH=.omx; declare -g CDPATH=src; }; f; cd shared; mkdir -p .omx/state/cdpath-global-dirty-owned`],
|
|
3180
|
+
['cp hardlink then reference', `cp -l .omx/state/conductor-ledger.json .omx/state/linked-ledger; chmod --reference=.omx/state/conductor-ledger.json .omx/state/linked-ledger`],
|
|
3181
|
+
['curl method override header', `curl -q -H 'X-HTTP-Method-Override: POST' -o .omx/state/curl-method-override.log https://example.test/file`],
|
|
3182
|
+
['curl next transfer product output', `curl -q --output-dir src -o first.log https://example.test/first --next --output-dir .omx/state -o second.log https://example.test/second`],
|
|
3183
|
+
['wget warc cdx standalone sink', `wget --no-config --warc-cdx .omx/state/wget-cdx.log https://example.test/file`],
|
|
3184
|
+
['wget no proxy standalone sink', `wget --no-config --no-proxy .omx/state/wget-proxy.log https://example.test/file`],
|
|
3185
|
+
['wget short x standalone sink', `wget --no-config -x .omx/state/wget-x.log https://example.test/file`],
|
|
3186
|
+
['rsync rsync-path helper execution', `rsync --rsync-path=.omx/state/helper .omx/state/conductor-ledger.json .omx/state/rsync-copy`],
|
|
3187
|
+
['rmdir invalidates static cwd proof', `rmdir .omx/state/tmp; cd .omx/state/tmp; touch .omx/state/final.log`],
|
|
3188
|
+
['posix special builtin prefix persistence', `set -o posix; POSIXLY_CORRECT=1 export POSIXLY_CORRECT; wget --no-config -O src/posix-special-owned.ts https://example.test/file -O -`],
|
|
3189
|
+
['wget late no config', `wget -q --no-config -O - https://example.test/file`],
|
|
3190
|
+
['curl output template traversal', `curl -q -o .omx/state/curl-#1.log https://example.test/file[..]`],
|
|
3191
|
+
['native child dead reference marker', `f(){ chmod --reference=.omx/state/session.json .omx/state/dead-reference.log; }; touch src/live-after-dead-marker.log`],
|
|
3192
|
+
['reference looking post end of options', `chmod 600 -- --reference=.omx/state/session.json`],
|
|
3193
|
+
['curl quote request control', `curl -q --quote 'DELE state' https://example.test/file`],
|
|
3194
|
+
['curl short quote request control', `curl -q -Q 'DELE state' https://example.test/file`],
|
|
3195
|
+
['curl postquote request control', `curl -q --postquote 'DELE state' https://example.test/file`],
|
|
3196
|
+
['curl dynamic prequote request control', `OP='NOOP'; curl -q --prequote "$OP" https://example.test/file`],
|
|
3197
|
+
['curl unknown request control', `curl -q --request-target=/state https://example.test/file`],
|
|
3198
|
+
['wget header method override', `wget --no-config --header='X-HTTP-Method-Override: POST' -O - https://example.test/file`],
|
|
3199
|
+
['wget dynamic header method override', `HEADER='X-HTTP-Method-Override: POST'; wget --no-config --header="$HEADER" -O - https://example.test/file`],
|
|
3200
|
+
['curl brace template traversal', `curl -q -o .omx/state/curl-#1.log 'https://example.test/{safe,../escape}'`],
|
|
3201
|
+
['rsync archive then reference', `rsync -a --log-file=.omx/state/rsync-alias.log .omx/state/conductor-ledger.json .omx/state/rsync-alias-copy; chmod --reference=.omx/state/session.json .omx/state/rsync-alias-copy`],
|
|
3202
|
+
['cp archive then reference', `cp -a .omx/state/conductor-ledger.json .omx/state/archive-ledger; chmod --reference=.omx/state/session.json .omx/state/archive-ledger`],
|
|
3203
|
+
['nested rmdir invalidates cwd proof', `rmdir .omx/state/tmp; sh -c 'cd .omx/state/tmp; touch .omx/state/final.log'`],
|
|
3204
|
+
['nested chmod invalidates cwd proof', `chmod --reference=.omx/state/session.json .omx/state/tmp; sh -c 'cd .omx/state/tmp; touch .omx/state/final.log'`],
|
|
3205
|
+
['relative slash command', `./src/conductor-owned.ts`],
|
|
3206
|
+
['unresolved arithmetic expansion', `touch .omx/state/unresolved-arithmetic.log $((UNFINISHED)`],
|
|
3207
|
+
['elif mutation reachability', `if false; then :; elif true; then touch src/elif-reachable-owned.ts; fi`],
|
|
3208
|
+
['dynamic loader preload wrapper', `LD_PRELOAD=.omx/state/mutator.so cat src/conductor-owned.ts`],
|
|
3209
|
+
['dynamic loader audit env wrapper', `env LD_AUDIT=.omx/state/audit.so cat src/conductor-owned.ts`],
|
|
3210
|
+
['curl clustered form mutation', `curl -q -sF field=value https://example.test/file`],
|
|
3211
|
+
['curl clustered quote mutation', `curl -q -sQ 'DELE state' https://example.test/file`],
|
|
3212
|
+
['curl proxy header method override', `curl -q --proxy-header 'X-HTTP-Method-Override: POST' https://example.test/file`],
|
|
3213
|
+
['rsync product destination', `rsync README.md src/readme.md`],
|
|
3214
|
+
['recursive arithmetic substitution', `arith='slot[$(touch src/arithmetic-owned.ts)0]'; : $((arith))`],
|
|
3215
|
+
['arithmetic array subscript', `slot=1; : $((slot[$(touch src/arithmetic-array-owned.ts)0]))`],
|
|
3216
|
+
['numeric binding arithmetic', `N=1; : $((N << 2))`],
|
|
3217
|
+
['conditional numeric binding arithmetic', `arith='slot[$(touch src/arithmetic-conditional-owned.ts)0]'; if false; then arith=1; fi; : $((arith))`],
|
|
3218
|
+
['printf v numeric binding arithmetic', `N=1; printf -v N '%s' 'slot[$(touch src/arithmetic-printf-owned.ts)0]'; : $((N))`],
|
|
3219
|
+
['loader conditional clear', `declare -n loader=LD_PRELOAD; export loader; loader=.omx/state/mutator.so; false && unset loader; /usr/bin/cat /dev/null`],
|
|
3220
|
+
['loader function clear', `declare -n loader=LD_PRELOAD; export loader; loader=.omx/state/mutator.so; clear_loader(){ unset loader; }; false && clear_loader; /usr/bin/cat /dev/null`],
|
|
3221
|
+
['node persistent v8 coverage output', `export NODE_V8_COVERAGE=src; node -e "console.log('ok')"`],
|
|
3222
|
+
['node function compile cache output', `configure_node(){ export NODE_COMPILE_CACHE=src; }; configure_node; node -e "console.log('ok')"`],
|
|
3223
|
+
['cp parents path shaping', `cp --parents src/runtime.ts .omx/state`],
|
|
3224
|
+
['cp hardlink cross boundary', `cp --link src/runtime.ts .omx/state/runtime-link.ts`],
|
|
3225
|
+
['curl template unbounded capture', `curl -q -o .omx/state/curl-glob-#1.log https://example.test/file[1-999]`],
|
|
3226
|
+
['gh dynamic web helper', `OPT=--web; GH_BROWSER=.omx/state/mutator PATH=/usr/bin:/bin gh issue create --title x --body y $OPT`],
|
|
3227
|
+
['gh command prefix pager helper', `GH_PAGER=cat PATH=/usr/bin:/bin gh issue create --title x --body y`],
|
|
3228
|
+
['loader nameref preload', `declare -n loader=LD_PRELOAD; export loader; loader=.omx/state/mutator.so; cat src/conductor-owned.ts`],
|
|
3229
|
+
['loader debug output', `LD_DEBUG=libs LD_DEBUG_OUTPUT=src/ld-debug cat src/conductor-owned.ts`],
|
|
3230
|
+
['loader profile output', `LD_PROFILE=cat LD_PROFILE_OUTPUT=src/ld-profile cat src/conductor-owned.ts`],
|
|
3231
|
+
['node cpu profile output', `node --cpu-prof --cpu-prof-dir=src -e "console.log('ok')"`],
|
|
3232
|
+
['node v8 coverage output', `NODE_V8_COVERAGE=src node -e "console.log('ok')"`],
|
|
3233
|
+
['node compile cache output', `NODE_COMPILE_CACHE=src node -e "console.log('ok')"`],
|
|
3234
|
+
['curl expand data request', `curl -q --expand-data 'mode=write' https://example.test/state`],
|
|
3235
|
+
['curl expand output sink', `curl -q --variable OUT=src/curl-expand-owned.txt --expand-output '{{OUT}}' file:///etc/hosts`],
|
|
3236
|
+
['curl unknown long option', `curl -q --future-option https://example.test/file`],
|
|
3237
|
+
['gh repo create interactive', `PATH=/usr/bin:/bin gh repo create`],
|
|
3238
|
+
['gh repo fork local remote', `PATH=/usr/bin:/bin gh repo fork OWNER/REPO`],
|
|
3239
|
+
['curl header file source', `curl -q --header @.omx/state/headers -o .omx/state/header.log https://example.test/file`],
|
|
3240
|
+
['wget warc tempdir sink', `wget --no-config --warc-tempdir=src -O - https://example.test/file`],
|
|
3241
|
+
['install strip program execution', `install --strip-program=.omx/state/mutator package.json .omx/state/install-copy`],
|
|
3242
|
+
['python unsafe x option', `python3 -I -X importtime -c "print('ok')"`],
|
|
3243
|
+
['perl debugger option', `perl -d -e 'print'`],
|
|
3244
|
+
['rg config file', `rg --config .omx/state/rg-config pattern`],
|
|
3245
|
+
['git trace output', `GIT_TRACE=src/git-trace git diff`],
|
|
3246
|
+
['python pycache prefix', `PYTHONPYCACHEPREFIX=src python3 -I -c "print('ok')"`],
|
|
3247
|
+
['allexport node output', `set -a; NODE_V8_COVERAGE=src; node -e "console.log('ok')"`],
|
|
3248
|
+
['loader command wrapper', `command env LD_PRELOAD=.omx/state/mutator.so cat src/conductor-owned.ts`],
|
|
3249
|
+
['loader before env clear', `LD_PRELOAD=.omx/state/mutator.so /usr/bin/env -i /usr/bin/cat src/conductor-owned.ts`],
|
|
3250
|
+
['loader in background pipeline', `LD_PRELOAD=.omx/state/mutator.so /usr/bin/cat src/conductor-owned.ts | cat &`],
|
|
3251
|
+
['loader in later pipeline member', `printf x | LD_DEBUG=libs LD_DEBUG_OUTPUT=src/pipe-loader /usr/bin/cat /dev/null`],
|
|
3252
|
+
['dynamic set option', `PS4='$(touch src/dynamic-set-owned.ts)'; OPTION=-x; set $OPTION; :`],
|
|
3253
|
+
['prompt parameter transform', `PS4='$(touch src/prompt-transform-owned.ts)'; : \${PS4@P}`],
|
|
3254
|
+
['dynamic declaration attribute', `ATTRIBUTE=i; declare -$ATTRIBUTE 'slot[$(touch src/dynamic-declare-owned.ts)0]=1'`],
|
|
3255
|
+
['perl regex assertion in place', `perl -pi -e 's/(?!safe)/x/' .omx/state/conductor-ledger.json`],
|
|
3256
|
+
['uniq second output operand', `uniq src/conductor-owned.ts src/uniq-owned.ts`],
|
|
3257
|
+
['rg hostname helper', `rg --hostname-bin=.omx/state/inbox/helper needle src`],
|
|
3258
|
+
['escaped time path shadow', `PATH=.omx/state/inbox:/usr/bin:/bin \\time printf safe`],
|
|
3259
|
+
['quoted time path shadow', `PATH=.omx/state/inbox:/usr/bin:/bin 'time' printf safe`],
|
|
3260
|
+
['ansi-c time path shadow', `PATH=.omx/state/inbox:/usr/bin:/bin $'time' printf safe`],
|
|
3261
|
+
['concatenated time path shadow', `PATH=.omx/state/inbox:/usr/bin:/bin "ti"me printf safe`],
|
|
3262
|
+
['coproc path shadow', `PATH=.omx/state/inbox:/usr/bin:/bin 'coproc' printf safe`],
|
|
3263
|
+
['env chdir product mutation', `env --chdir=src touch ../.omx/state/final.log`],
|
|
3264
|
+
['rsync permissions alias', `rsync -p .omx/state/conductor-ledger.json .omx/state/rsync-copy`],
|
|
3265
|
+
['reference directory target', `chmod --reference=.omx/state/session.json .omx/state`],
|
|
3266
|
+
['reference glob target', `chmod --reference=.omx/state/session.json .omx/state/*`],
|
|
3267
|
+
['wget startup options after end', `WGETRC=/dev/stdin wget -O - http://127.0.0.1:1/ -- --no-config --no-hsts`],
|
|
3268
|
+
['wget recursive metadata transfer', `wget --no-config --no-hsts --recursive --page-requisites --directory-prefix=.omx/state https://example.test/index.html`],
|
|
3269
|
+
['wget SSL key log output', `SSLKEYLOGFILE=src/wget-tls.keys wget --no-config --no-hsts -O - https://example.test/file`],
|
|
3270
|
+
['curl SSL key log output', `SSLKEYLOGFILE=src/curl-tls.keys curl -q -o - https://example.test/file`],
|
|
3271
|
+
['curl SSL key log persistent output', `export SSLKEYLOGFILE=src/curl-tls.keys; curl -q -o - https://example.test/file`],
|
|
3272
|
+
['wget SSL key log persistent output', `export SSLKEYLOGFILE=src/wget-tls.keys; wget --no-config --no-hsts -O - https://example.test/file`],
|
|
3273
|
+
['curl SSL key log dynamic output', `SSLKEYLOGFILE="$TLS_KEY_LOG" curl -q -o - https://example.test/file`],
|
|
3274
|
+
['rsync partial environment output', `RSYNC_PARTIAL_DIR=src/rsync-partials rsync .omx/state/conductor-ledger.json .omx/state/inbox/rsync-copy`],
|
|
3275
|
+
['rsync exported runtime output', `export RSYNC_PARTIAL_DIR=src/rsync-partials; rsync .omx/state/conductor-ledger.json .omx/state/inbox/rsync-copy`],
|
|
3276
|
+
['shellopts allexport posix ordering', `env SHELLOPTS=allexport bash -c 'POSIXLY_CORRECT=1; wget --no-config --no-hsts -O src/shellopts-owned.ts https://example.test/file -O -'`],
|
|
3277
|
+
['bash posix PATH persistence', `bash -o posix -c 'PATH=.omx/state export PATH; cat src/conductor-owned.ts'`],
|
|
3278
|
+
['bash long posix PATH persistence', `bash --posix -c 'PATH=.omx/state export PATH; cat src/conductor-owned.ts'`],
|
|
3279
|
+
['bashopts allexport posix ordering', `env BASHOPTS=allexport bash -c 'POSIXLY_CORRECT=1; wget --no-config --no-hsts -O src/bashopts-owned.ts https://example.test/file -O -'`],
|
|
3280
|
+
['unresolved PATH command not found', `command_not_found_handle(){ /usr/bin/touch src/path-owned.ts; }; PATH=/definitely/not-present; cat README.md`],
|
|
3281
|
+
['curl command protocol local listener', `printf 'set state\n' | curl -q -m 1 telnet://127.0.0.1:9`],
|
|
3282
|
+
['curl command protocol gopher', `curl -q -m 1 gopher://127.0.0.1:9/1/state`],
|
|
3283
|
+
['curl command protocol smtp', `curl -q -m 1 smtp://127.0.0.1:9`],
|
|
3284
|
+
['curl command protocol unknown', `curl -q -m 1 mutator://127.0.0.1/state`],
|
|
3285
|
+
['curl remote header name cluster', `curl -q -OJ https://example.test/payload`],
|
|
3286
|
+
['curl remote header name long', `curl -q --remote-header-name -O https://example.test/payload`],
|
|
3287
|
+
['wget execute recursive directive', `wget --no-config --no-hsts -e 'recursive=on' -O - https://example.test/file`],
|
|
3288
|
+
['mixed scheme wget directory prefix', `wget --no-config --no-hsts --directory-prefix=.omx/state https://example.test/file file:///etc/hosts`],
|
|
3289
|
+
['mixed scheme curl remote name', `curl -q --output-dir=.omx/state -O https://example.test/file file:///etc/hosts`],
|
|
3290
|
+
['unsupported bashopts localvar inherit', `CDPATH=src env BASHOPTS=localvar_inherit bash --noprofile --norc -c 'f(){ local CDPATH; cd state; touch ../.omx/state/owned; }; f'`],
|
|
3291
|
+
['curl schemeless extra operand remote name', `curl -q --output-dir=.omx/state/inbox -O https://example.test/payload ftp.example.test/payload`],
|
|
3292
|
+
['wget schemeless extra operand directory prefix', `wget --no-config --no-hsts --directory-prefix=.omx/state/inbox https://example.test/payload ftp.example.test/payload`],
|
|
3293
|
+
['curl multiple static URLs remote name', `curl -q --output-dir=.omx/state/inbox -O https://example.test/first https://example.test/second`],
|
|
3294
|
+
['wget multiple static URLs directory prefix', `wget --no-config --no-hsts --directory-prefix=.omx/state/inbox https://example.test/first https://example.test/second`],
|
|
3295
|
+
['wget short input file decoy URL', `wget --no-config --no-hsts -i urls.txt -O - https://example.test/file`],
|
|
3296
|
+
['wget attached input file decoy URL', `wget --no-config --no-hsts -iurls.txt -O - https://example.test/file`],
|
|
3297
|
+
['wget clustered input file decoy URL', `wget --no-config --no-hsts -qiurls.txt -O - https://example.test/file`],
|
|
3298
|
+
['wget long input file decoy URL', `wget --no-config --no-hsts --input-file=urls.txt -O - https://example.test/file`],
|
|
3299
|
+
['wget execute tries zero', `wget --no-config --no-hsts -e 'tries=0' -O - https://example.test/file`],
|
|
3300
|
+
['wget execute timeout zero', `wget --no-config --no-hsts -e 'timeout=0' -O - https://example.test/file`],
|
|
3301
|
+
['wget direct tries zero', `wget --no-config --no-hsts --tries=0 -O - https://example.test/file`],
|
|
3302
|
+
['wget short tries zero', `wget --no-config --no-hsts -t0 -O - https://example.test/file`],
|
|
3303
|
+
['wget direct timeout zero', `wget --no-config --no-hsts --timeout=0 -O - https://example.test/file`],
|
|
3304
|
+
['wget direct wait excessive', `wget --no-config --no-hsts --wait=61 -O - https://example.test/file`],
|
|
3305
|
+
['wget execute waitretry infinite', `wget --no-config --no-hsts -e 'waitretry=infinite' -O - https://example.test/file`],
|
|
3306
|
+
['wget short timeout zero', `wget --no-config --no-hsts -T0 -O - https://example.test/file`],
|
|
3307
|
+
['wget short wait zero', `wget --no-config --no-hsts -w0 -O - https://example.test/file`],
|
|
3308
|
+
['wget direct waitretry zero', `wget --no-config --no-hsts --waitretry=0 -O - https://example.test/file`],
|
|
3309
|
+
['wget malformed wait', `wget --no-config --no-hsts --wait=one -O - https://example.test/file`],
|
|
3310
|
+
['wget dynamic timeout', `wget --no-config --no-hsts --timeout="$WGET_TIMEOUT" -O - https://example.test/file`],
|
|
3311
|
+
['wget connect timeout zero', `wget --no-config --no-hsts --connect-timeout=0 -O - https://example.test/file`],
|
|
3312
|
+
['wget DNS timeout excessive', `wget --no-config --no-hsts --dns-timeout=61 -O - https://example.test/file`],
|
|
3313
|
+
['wget read timeout dynamic', `wget --no-config --no-hsts --read-timeout="$WGET_TIMEOUT" -O - https://example.test/file`],
|
|
3314
|
+
['wget execute connect timeout zero', `wget --no-config --no-hsts -e 'connecttimeout=0' -O - https://example.test/file`],
|
|
3315
|
+
['wget late no-config WGETRC product log', `WGETRC=/dev/stdin; printf 'output = src/late-no-config-wgetrc.log\n' | wget -O - https://example.test/file --no-config --no-hsts`],
|
|
3316
|
+
['curl glob cumulative cardinality', `curl -q -o .omx/state/curl-explosive.log 'https://example.test/{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p}{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p}{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p}{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p}'`],
|
|
3317
|
+
['select conditional touch', `select choice in safe; do touch src/select-owned.ts; done </dev/null`],
|
|
3318
|
+
['allexport POSIX Wget', `set -a; POSIXLY_CORRECT=1; wget --no-config --no-hsts -O src/allexport-posix-owned.ts https://example.test/file -O -`],
|
|
3319
|
+
['allexport SSL keylog curl', `set -o allexport; SSLKEYLOGFILE=src/allexport-curl.keys; curl -q -o - https://example.test/file`],
|
|
3320
|
+
['nested allexport SSL keylog curl', `bash -a -c 'SSLKEYLOGFILE=src/nested-allexport-curl.keys; curl -q -o - https://example.test/file'`],
|
|
3321
|
+
['composed SSL keylog export', `NAME=SSLKEYLOGFILE; export "$NAME"; SSLKEYLOGFILE=src/composed-curl.keys; curl -q -o - https://example.test/file`],
|
|
3322
|
+
['dynamic export name', `export "$UNRESOLVED_NAME"; cat src/conductor-owned.ts`],
|
|
3323
|
+
['curl unquoted glob output sink', `curl -q -o .omx/state/*.log https://example.test/file`],
|
|
3324
|
+
['curl unquoted brace URL', `curl -q -o .omx/state/curl-brace-#1.log https://example.test/{one,two}`],
|
|
3325
|
+
['nested bash shopt localvar inherit cdpath', `CDPATH=src bash -O localvar_inherit -c 'f(){ local CDPATH; cd state; touch ../.omx/state/owned; }; f'`],
|
|
3326
|
+
['nested bash shopt localvar inherit posix', `POSIXLY_CORRECT=1 bash -Olocalvar_inherit -c 'f(){ local POSIXLY_CORRECT; wget --no-config --no-hsts -O - https://example.test/file; }; f'`],
|
|
3327
|
+
['nested bash shopt plus localvar inherit cdpath', `CDPATH=src bash +O localvar_inherit -c 'f(){ local CDPATH; cd state; touch ../.omx/state/owned; }; f'`],
|
|
3328
|
+
['nested bash shopt plus attached localvar inherit posix', `POSIXLY_CORRECT=1 bash +Olocalvar_inherit -c 'f(){ local POSIXLY_CORRECT; wget --no-config --no-hsts -O - https://example.test/file; }; f'`],
|
|
3329
|
+
['git cat-file filters', `git cat-file --filters HEAD:filtered.txt`],
|
|
3330
|
+
['xtrace PS4 command substitution', `PS4='$(touch src/ps4-owned.ts)'; set -x; :`],
|
|
3331
|
+
['keyword late environment assignment', `set -o keyword; wget --no-config --no-hsts -U POSIXLY_CORRECT=1 -O src/keyword-owned.ts https://example.test/file -O -`],
|
|
3332
|
+
['interactive Bash startup', `HOME=.omx/state/bash-home bash -ic ':'`],
|
|
3333
|
+
['clustered allexport POSIX ordering', `set -ae; POSIXLY_CORRECT=1; wget --no-config --no-hsts -O src/cluster-owned.ts https://example.test/file -O -`],
|
|
3334
|
+
['nested clustered allexport POSIX ordering', `bash -ae -c 'POSIXLY_CORRECT=1; wget --no-config --no-hsts -O src/nested-cluster-owned.ts https://example.test/file -O -'`],
|
|
3335
|
+
['local shell option snapshot', `set -a; f(){ local -; set +a; }; f; POSIXLY_CORRECT=1; wget --no-config --no-hsts -O src/local-option-owned.ts https://example.test/file -O -`],
|
|
3336
|
+
['monitor disables lastpipe', `export POSIXLY_CORRECT=1; shopt -s lastpipe; set -o monitor; f(){ unset POSIXLY_CORRECT; }; true | f; wget --no-config --no-hsts -O src/monitor-owned.ts https://example.test/file -O -`],
|
|
3337
|
+
['generic glob mutation target', `truncate --size=0 .omx/state/*.log`],
|
|
3338
|
+
['generic brace mutation target', `touch .omx/state/{conductor,ledger}.log`],
|
|
3339
|
+
['generic glob directory destination', `mv .omx/state/conductor-ledger.json .omx/state/inbox/*`],
|
|
3340
|
+
['physical cd symlink escape', `cd -P .omx/state/link; cd ..; touch physical-owned.ts`],
|
|
3341
|
+
['physical shell mode symlink escape', `set -P; cd .omx/state/link; cd ..; touch physical-mode-owned.ts`],
|
|
3342
|
+
['Python metadata copy eval alias', `python3 -I - <<'PY'
|
|
3343
|
+
import shutil
|
|
3344
|
+
shutil.copyfile('a', '.omx/state/inbox/py-control')
|
|
3345
|
+
print=eval
|
|
3346
|
+
print("'Pa' + 'th'('src/python-alias-owned.ts').write_text('x')")
|
|
3347
|
+
PY`],
|
|
3348
|
+
['literal quote metadata path', "mkdir -p \\'.omx/handoffs/run-1"],
|
|
3349
|
+
['external dispatcher time wrapper', `PATH=.omx/state/inbox:/usr/bin:/bin env time printf safe`],
|
|
3350
|
+
['Python interactive short cluster stdin', `python3 -Ii -c "print('safe')" <<'PY'
|
|
3351
|
+
__import__('os').system('touch src/python-interactive-owned.ts')
|
|
3352
|
+
PY`],
|
|
3353
|
+
['Perl trailing substitution statement', `perl -pi -e 's/a/b/;system("touch src/perl-owned.ts") #/' .omx/state/conductor.log`],
|
|
3354
|
+
['multiple Perl substitution programs', `perl -pi -e 's/a/b/' -e 's/b/c/' .omx/state/conductor.log`],
|
|
3355
|
+
['multiple sed substitution programs', `sed -i -e 's/a/b/' -e 's/b/c/' .omx/state/conductor.log`],
|
|
3356
|
+
['integer declaration secondary expansion', `declare -a slot; declare -i sink='slot[$(touch src/declare-integer-owned.ts)0]'`],
|
|
3357
|
+
['zsh HOME startup', `HOME=.omx/state/zsh-home zsh -c ':'`],
|
|
3358
|
+
['interactive Bash no-rc startup', `PS0='$(touch src/ps0-owned.ts)' bash --noprofile --norc -ic ':'`],
|
|
3359
|
+
['background lastpipe pipeline', `export POSIXLY_CORRECT=1; shopt -s lastpipe; f(){ unset POSIXLY_CORRECT; }; true | f & wait; wget --no-config --no-hsts -o src/async-lastpipe.log http://127.0.0.1:1/ -o - -O -`],
|
|
3360
|
+
['sed in-place backup sink', `printf x > .omx/state/runtime.ts; cd .omx/state; command sed --in-place='../../src/*' -e 's/x/y/' runtime.ts`],
|
|
3361
|
+
['unbounded Python metadata copy', `python3 -I - <<'PY'
|
|
3362
|
+
import shutil
|
|
3363
|
+
shutil.copyfile('/dev/zero', '.omx/state/inbox/unbounded')
|
|
3364
|
+
PY`],
|
|
3365
|
+
['unbounded dd metadata copy', `dd if=/dev/zero of=.omx/state/inbox/unbounded`],
|
|
3366
|
+
] as const;
|
|
3367
|
+
|
|
3368
|
+
for (const [probe, command] of [
|
|
3369
|
+
['node fs.rmSync', `node -e "require('fs').rmSync('src/victim.ts')"`],
|
|
3370
|
+
['node create read stream write', `node -e "require('fs').createReadStream('src/victim.ts',{flags:'w'})"`],
|
|
3371
|
+
['node bound create read stream append', `node -e "const {createReadStream: stream}=require('fs');stream('src/victim.ts',{flags:'a'})"`],
|
|
3372
|
+
['posix uniq output after operand', `cd src; POSIXLY_CORRECT=1 uniq runtime.ts --count`],
|
|
3373
|
+
['posix touch reference after operand', `POSIXLY_CORRECT=1 touch .omx/state/inbox/marker --reference src/runtime.ts`],
|
|
3374
|
+
['nested sh posix special builtin', `sh -c 'POSIXLY_CORRECT=1 export POSIXLY_CORRECT; wget --no-config --no-hsts -O src/sh-posix-owned.ts https://example.test/file -O -'`],
|
|
3375
|
+
['nested exec argv0 sh posix', `exec -a sh bash -c 'POSIXLY_CORRECT=1 export POSIXLY_CORRECT; wget --no-config --no-hsts -O src/exec-argv0-posix-owned.ts https://example.test/file -O -'`],
|
|
3376
|
+
['nested env argv0 sh posix', `env --argv0 sh bash -c 'POSIXLY_CORRECT=1 export POSIXLY_CORRECT; wget --no-config --no-hsts -O src/env-argv0-posix-owned.ts https://example.test/file -O -'`],
|
|
3377
|
+
['bare arithmetic command secondary expansion', `true='slot[$(touch src/arithmetic-command-owned.ts)0]'; ((true))`],
|
|
3378
|
+
['arithmetic for secondary expansion', `for ((i=$(touch src/arithmetic-for-owned.ts); i<1; i++)); do :; done`],
|
|
3379
|
+
['let secondary expansion', `let 'slot[$(touch src/let-owned.ts)0]=1'`],
|
|
3380
|
+
['oversized metadata redirect', `head -c 16777217 /dev/zero > .omx/state/inbox/oversized`],
|
|
3381
|
+
['unbounded metadata redirect', `cat /dev/zero > .omx/state/inbox/unbounded`],
|
|
3382
|
+
['unbounded metadata redirect with unrelated heredoc', `cat /dev/zero > .omx/state/inbox/unbounded-with-heredoc; cat <<'EOF'
|
|
3383
|
+
bounded
|
|
3384
|
+
EOF`],
|
|
3385
|
+
['oversized rsync metadata source', `rsync src/large.bin .omx/state/inbox/large-copy`],
|
|
3386
|
+
['node fs.renameSync', `node -e "require('fs').renameSync('src/a.ts','src/b.ts')"`],
|
|
3387
|
+
['node template interpolation', "node -e '" + '`${require("fs").rmSync("src/template.ts")}`' + "'"],
|
|
3388
|
+
['node computed mutation', `node -e "const fs=require('fs');const op='rmSync';fs[op]('src/computed.ts')"`],
|
|
3389
|
+
['node ESM rename', `node --input-type=module -e "import fs from 'node:fs';fs.renameSync('src/esm-a.ts','src/esm-b.ts')"`],
|
|
3390
|
+
['nodejs fs.rmSync', `nodejs -e "require('fs').rmSync('src/nodejs.ts')"`],
|
|
3391
|
+
['node.exe fs.rmSync', `node.exe -e "require('fs').rmSync('src/node-exe.ts')"`],
|
|
3392
|
+
['node getBuiltinModule', `node -e "process.getBuiltinModule('fs').rmSync('src/builtin.ts')"`],
|
|
3393
|
+
['node optional mutation', `node -e "const fs=require('fs');fs?.rmSync('src/optional.ts')"`],
|
|
3394
|
+
['node aliased fs object', `node -e "const fs=require('fs');const alias=fs;alias.rmSync('src/alias.ts')"`],
|
|
3395
|
+
['node dynamic eval source', `PAYLOAD="require('fs').rmSync('src/env.ts')"; node -e "$PAYLOAD"`],
|
|
3396
|
+
['node concatenated eval source', `A="require('fs')."; B="rmSync('src/concat.ts')"; node -e "$A$B"`],
|
|
3397
|
+
['node backtick eval source', `node -e "\`cat payload.js\`"`],
|
|
3398
|
+
['node command-substitution eval source', `node -e "$(cat payload.js)"`],
|
|
3399
|
+
['node combined print eval', `node -pe "require('fs').rmSync('src/combined.ts')"`],
|
|
3400
|
+
['node repeated eval mutation', `node -e "0" -e "require('fs').writeFileSync('src/repeated-eval.ts','x')"`],
|
|
3401
|
+
['node aliased require', `node -e "const req=require;const fs=req('fs');fs.rmSync('src/aliased-require.ts')"`],
|
|
3402
|
+
['node object-escaped fs', `node -e "const h={fs:require('fs')};h.fs.rmSync('src/object-escape.ts')"`],
|
|
3403
|
+
['node computed require alias', `node -e "const req=module['require'];const fs=req('fs');fs.rmSync('src/computed-require.ts')"`],
|
|
3404
|
+
['node computed builtin loader', `node -e "const fs=process['getBuiltinModule']('fs');fs.rmSync('src/computed-builtin.ts')"`],
|
|
3405
|
+
['node postfix division mutation', `node -e "let x=1;x++ / require('fs').rmSync('src/postfix-division.ts') / 1"`],
|
|
3406
|
+
['node string division mutation', `node -e "'value' / require('fs').rmSync('src/string-division.ts') / 1"`],
|
|
3407
|
+
['node unicode-escaped loader', `node -e 'requ\\u0069re("fs").rmSync("src/unicode-escape.ts")'`],
|
|
3408
|
+
['node parenthesized eval', `node -e "(eval)('require(\\"fs\\").rmSync(\\"src/eval-bypass.ts\\")')"`],
|
|
3409
|
+
['node concatenated computed loader', `node -e "const fs=module['requ'+'ire']('fs');fs.rmSync('src/computed-loader.ts')"`],
|
|
3410
|
+
['node attached short eval', `node -e"require('fs').rmSync('src/attached-eval.ts')"`],
|
|
3411
|
+
['node xargs wrapper mutation', `printf x | xargs node -e "require('fs').rmSync('src/xargs-bypass.ts')"`],
|
|
3412
|
+
['node xargs wrapper read', `printf x | xargs node -e "require('fs').readFileSync('src/victim.ts','utf8')"`],
|
|
3413
|
+
['node child-process mutation', `node -e "require('child_process').execFileSync('rm',['-f','src/child-process-bypass.ts'])"`],
|
|
3414
|
+
['node internal module loader', `node -e "module.constructor._load('fs').rmSync('src/internal-loader-bypass.ts')"`],
|
|
3415
|
+
['node prototype module loader', `node -e "module.__proto__.constructor._load('fs').rmSync('src/prototype-loader-bypass.ts')"`],
|
|
3416
|
+
['node computed prototype loader', `node -e "Object['getPrototypeOf'](module)['constructor']['_load']('fs')['rmSync']('src/prototype-computed-bypass.ts')"`],
|
|
3417
|
+
['node optional computed prototype loader', `node -e "Object?.['getPrototypeOf'](module)?.['constructor']?.['_load']('fs')?.['rmSync']('src/optional-prototype-bypass.ts',{force:true})"`],
|
|
3418
|
+
['node function constructor', `node -e '(()=>{}).constructor("return process.getBuiltinModule(\\"fs\\").rmSync(\\"src/function-dot.ts\\")")()'`],
|
|
3419
|
+
['node method function constructor', `node -e '({}).toString.constructor("return process.getBuiltinModule(\\"fs\\").rmSync(\\"src/function-method.ts\\")")()'`],
|
|
3420
|
+
['node descriptor builtin', `node -e "Object.getOwnPropertyDescriptor(process,'getBuiltinModule').value('fs').rmSync('src/descriptor.ts')"`],
|
|
3421
|
+
['node descriptor builtin call', `node -e "Object.getOwnPropertyDescriptor(process,'getBuiltinModule').value.call(process,'fs').rmSync('src/descriptor-call.ts')"`],
|
|
3422
|
+
['node destructured comma call', `node -e "const {rmSync}=require('fs');(0,rmSync)('src/destructured-comma.ts')"`],
|
|
3423
|
+
['node destructured method call', `node -e "const {rmSync}=require('fs');rmSync.call(null,'src/destructured-call.ts')"`],
|
|
3424
|
+
['node destructured Reflect.apply', `node -e "const {rmSync}=require('fs');Reflect.apply(rmSync,null,['src/destructured-reflect.ts'])"`],
|
|
3425
|
+
['node ANSI-C eval flag', `node $'-e' "require('fs').rmSync('src/ansi-c.ts')"`],
|
|
3426
|
+
['node nine env wrappers', `${Array.from({ length: 9 }, () => 'env').join(' ')} node -e "require('fs').rmSync('src/env-nine.ts')"`],
|
|
3427
|
+
['node stdin pipe', `printf "require('fs').rmSync('src/stdin-pipe.ts')" | node`],
|
|
3428
|
+
['node global Function computed', `node -e "globalThis['Fun'+'ction'](\\"return require('fs').rmSync('src/global-function.ts')\\")()"`],
|
|
3429
|
+
['node Reflect global Function', `node -e "Reflect.get(globalThis,'Fun'+'ction')(\\"return require('fs').rmSync('src/reflect-function.ts')\\")()"`],
|
|
3430
|
+
['node parenthesized constructor', `node -e "(console.log.constructor)(\\"return require('fs').rmSync('src/parenthesized-constructor.ts')\\")()"`],
|
|
3431
|
+
['node side-effect import', `node --input-type=module -e "import './mutator.mjs'"`],
|
|
3432
|
+
['node require preload', `node --require ./.omx/state/mutator.cjs -e "console.log('ok')"`],
|
|
3433
|
+
['node options require preload', `NODE_OPTIONS='--require ./.omx/state/mutator.cjs' node -e "console.log('ok')"`],
|
|
3434
|
+
['node exported options preload', `export NODE_OPTIONS='--require ./.omx/state/mutator.cjs'; node -e "console.log('ok')"`],
|
|
3435
|
+
['node indirect exported options preload', `P='--require ./.omx/state/mutator.cjs'; export NODE_OPTIONS="$P"; node -e "console.log('ok')"`],
|
|
3436
|
+
['node command exported options preload', `command export NODE_OPTIONS='--require ./.omx/state/mutator.cjs'; node -e "console.log('ok')"`],
|
|
3437
|
+
['node declare exported options preload', `declare -x NODE_OPTIONS='--require ./.omx/state/mutator.cjs'; node -e "console.log('ok')"`],
|
|
3438
|
+
['node function exported options preload', `f(){ export NODE_OPTIONS='--require ./.omx/state/mutator.cjs'; }; f; node -e "console.log('ok')"`],
|
|
3439
|
+
['node dynamic export preload', `N=NODE_OPTIONS; export "$N=--require ./.omx/state/mutator.cjs"; node -e "console.log('ok')"`],
|
|
3440
|
+
['node deep command exported options preload', `command command command command command command export NODE_OPTIONS='--require ./.omx/state/mutator.cjs'; node -e "console.log('ok')"`],
|
|
3441
|
+
['node vm runInThisContext', `node -e "require('node:vm').runInThisContext(\\"require('fs').rmSync('src/vm.ts')\\")"`],
|
|
3442
|
+
['node process alias builtin loader', `node -e "const p=process;p.getBuiltinModule('fs').rmSync('src/process-alias.ts')"`],
|
|
3443
|
+
['node Reflect module require', `node -e "Reflect.apply(Reflect.get(module,'require'),module,['fs']).rmSync('src/reflect-require.ts')"`],
|
|
3444
|
+
['node global process loader', `node -e "global['process'].getBuiltinModule('fs').rmSync('src/global-process.ts')"`],
|
|
3445
|
+
['node ANSI-C command name', `$'\\u006e\\u006f\\u0064\\u0065' -e "require('fs').rmSync('src/ansi-command.ts')"`],
|
|
3446
|
+
['node ANSI-C hex command name', `$'\\x6e\\x6f\\x64\\x65' -e "require('fs').rmSync('src/ansi-hex-command.ts')"`],
|
|
3447
|
+
['node ANSI-C octal command name', `$'\\156\\157\\144\\145' -e "require('fs').rmSync('src/ansi-octal-command.ts')"`],
|
|
3448
|
+
['node ANSI-C wide command name', `$'\\U0000006e\\U0000006f\\U00000064\\U00000065' -e "require('fs').rmSync('src/ansi-wide-command.ts')"`],
|
|
3449
|
+
['python os.remove', `python3 -c "import os;os.remove('src/python-remove.ts')"`],
|
|
3450
|
+
['python modeled write piggyback', `python3 -c "from pathlib import Path;import subprocess;Path('.omx/state/probe').write_text('x');subprocess.run(['rm','-f','src/python-piggyback.ts'])"`],
|
|
3451
|
+
['python path sitecustomize preload', `PYTHONPATH=./.omx/state python3 -c "print('ok')"`],
|
|
3452
|
+
['python dynamic open mode', `python3 -c "m='w';open('src/python-dynamic-open.ts',m)"`],
|
|
3453
|
+
['python warnings module preload', `PYTHONWARNINGS='ignore::Mutator.Warning' python3 -c "print('ok')"`],
|
|
3454
|
+
['python non-isolation read-only', `python3 -c "print('ok')"`],
|
|
3455
|
+
['python non-isolation modeled metadata copy', "python3 - <<'PY'\nimport shutil\nshutil.copyfile('a', '.omx/state/foo')\nPY"],
|
|
3456
|
+
['ruby uninspected runtime', `ruby -e "File.delete('src/ruby-delete.ts')"`],
|
|
3457
|
+
['python f-string side effect', `python3 -c "import subprocess;f'{subprocess.run([\\"touch\\",\\"src/python-fstring.ts\\"])}'"`],
|
|
3458
|
+
['python isolated script', `python3 -I .omx/tmp/session/run.py`],
|
|
3459
|
+
['perl eval substitution', `perl -pi -e 's/^/system("rm -f src\/perl-eval.ts")/e' .omx/state/conductor.log`],
|
|
3460
|
+
['perl startup module preload', `PERL5LIB=./.omx/state PERL5OPT=-MMutator perl -e 'print;'`],
|
|
3461
|
+
['git add unmodeled mutation', `git add src/runtime.ts`],
|
|
3462
|
+
['sort output mutation', `sort -o src/sort-output.ts package.json`],
|
|
3463
|
+
['sed write command', `sed -n 'w src/sed-output.ts' package.json`],
|
|
3464
|
+
['sed addressed write command', `sed -n '1w src/sed-addressed-output.ts' package.json`],
|
|
3465
|
+
['git diff output mutation', `git diff --output=src/git-output.ts --no-index /dev/null package.json`],
|
|
3466
|
+
['sort compress program execution', `sort --compress-program=./.omx/state/mutator package.json`],
|
|
3467
|
+
['rg pre helper', `rg --pre ./.omx/state/mutator pattern .`],
|
|
3468
|
+
['gh release download write', `gh release download --dir src`],
|
|
3469
|
+
['gh global repo release download', `gh -R owner/repo release download --dir src`],
|
|
3470
|
+
['git external diff env', `GIT_EXTERNAL_DIFF=./.omx/state/mutator git diff`],
|
|
3471
|
+
['git config external diff', `git -c diff.external=./.omx/state/mutator diff`],
|
|
3472
|
+
['git config env helper', `HELPER=/usr/bin/touch git --config-env=diff.external=HELPER diff --no-index README.md src/scripts/codex-native-hook.ts`],
|
|
3473
|
+
['rg search zip helper', `rg -z never .omx/state/input.gz`],
|
|
3474
|
+
['sensitive printf v runtime writer', `printf -v NODE_V8_COVERAGE '%s' src; export NODE_V8_COVERAGE; node -e "console.log('ok')"`],
|
|
3475
|
+
['sensitive read runtime writer', `set -a; read GIT_TRACE <<< src/git-trace.log; git status`],
|
|
3476
|
+
['bash login startup', `HOME=.omx/state/login-home bash -lc 'printf safe'`],
|
|
3477
|
+
['wget prefix home hsts', `WGETRC=/dev/null HOME=src wget --no-config -O - https://example.test/file`],
|
|
3478
|
+
['reference precedes reference option', `chmod src/victim.ts --reference=.omx/state/session.json .omx/state/reference-copy`],
|
|
3479
|
+
['awk uninspected runtime', `awk 'BEGIN { print "x" > "src/awk-write.ts" }'`],
|
|
3480
|
+
['npm restart script', 'npm restart'],
|
|
3481
|
+
['npm run build', 'npm run build'],
|
|
3482
|
+
['bash env stdin preload', `printf 'touch src/bash-env-preload.ts\\n' | BASH_ENV=/dev/stdin bash -c 'printf safe\\n'`],
|
|
3483
|
+
['zsh startup preload', `ZDOTDIR=./.omx/state zsh -c "printf safe"`],
|
|
3484
|
+
['python heredoc owner mismatch', `cat <<'SAFE' >/dev/null\nprint('safe')\nSAFE\npython3 <<'PY'\nfrom pathlib import Path\nimport subprocess\nPath('.omx/state/probe').write_text('x')\nsubprocess.run(['touch','src/python-heredoc-bypass.ts'])\nPY`],
|
|
3485
|
+
['python pipeline heredoc mismatch', `cat <<'SAFE' >/dev/null | python3 <<'PY'\nprint('safe')\nSAFE\nfrom pathlib import Path\nimport subprocess\nPath('.omx/state/probe').write_text('x')\nsubprocess.run(['touch','src/python-pipeline-bypass.ts'])\nPY`],
|
|
3486
|
+
['python indented heredoc terminator bypass', `python3 <<'true'
|
|
3487
|
+
true=None
|
|
3488
|
+
if True:
|
|
3489
|
+
true
|
|
3490
|
+
echo=__import__('os').system
|
|
3491
|
+
echo ('touch src/heredoc-bypass.ts')
|
|
3492
|
+
true`],
|
|
3493
|
+
['ANSI-C heredoc delimiter bypass', `cat <<$'EOF'
|
|
3494
|
+
safe
|
|
3495
|
+
EOF
|
|
3496
|
+
touch src/ansi-heredoc-bypass.ts`],
|
|
3497
|
+
['comment heredoc opener bypass', `true # <<'EOF'
|
|
3498
|
+
touch src/comment-heredoc-bypass.ts
|
|
3499
|
+
EOF`],
|
|
3500
|
+
['arithmetic heredoc opener bypass', `: $((1 << 2))
|
|
3501
|
+
touch src/arithmetic-heredoc-bypass.ts`],
|
|
3502
|
+
['legacy arithmetic heredoc opener bypass', `: $[1 << 2]
|
|
3503
|
+
touch src/legacy-arith-bypass.ts`],
|
|
3504
|
+
['parameter expansion heredoc opener bypass', `: ${"${x#<<EOF}"}
|
|
3505
|
+
touch src/parameter-expansion-bypass.ts`],
|
|
3506
|
+
['ANSI CR heredoc terminator bypass', `cat <<$'EOF\\r'
|
|
3507
|
+
safe
|
|
3508
|
+
EOF\r
|
|
3509
|
+
touch src/ansi-cr-heredoc-bypass.ts`],
|
|
3510
|
+
['piped shell function bypass', `mutate(){ touch src/piped-function-bypass.ts; }; true | mutate`],
|
|
3511
|
+
['transformed heredoc runtime bypass', `cat <<'PY' | tr a-z A-Z | python3
|
|
3512
|
+
from pathlib import Path
|
|
3513
|
+
Path('.omx/state/probe').write_text('x')
|
|
3514
|
+
PY`],
|
|
3515
|
+
['path qualified runtime shadow', `./.omx/state/python3 -c "print('ok')"`],
|
|
3516
|
+
['PATH environment runtime shadow', `env PATH=.omx/state:/usr/bin:/bin python3 -c "print('ok')"`],
|
|
3517
|
+
['python escaped path bypass', `python3 -c "from pathlib import Path;Path('.omx/state/\\x2e\\x2e/\\x2e\\x2e/src/python-escape.ts').write_text('x')"`],
|
|
3518
|
+
['clobber redirect bypass', `true >| src/clobber-bypass.ts`],
|
|
3519
|
+
['cross boundary hardlink bypass', `ln src/source.ts .omx/state/source-link.ts`],
|
|
3520
|
+
['target directory cross boundary hardlink bypass', `ln src/source.ts -t .omx/handoffs/run-1`],
|
|
3521
|
+
['symbolic link metadata bypass', `ln -s .omx/state/conductor-ledger.json .omx/state/ledger-symlink`],
|
|
3522
|
+
['target directory value missing', 'cp package.json --target-directory'],
|
|
3523
|
+
['target directory attached empty', 'install --target-directory= .omx/state/conductor-ledger.json'],
|
|
3524
|
+
['target directory ordering missing value', 'mv src/a.ts --target-directory'],
|
|
3525
|
+
['target directory terminator missing value', 'ln .omx/state/conductor-ledger.json -t --'],
|
|
3526
|
+
['node env file preload bypass', `node --env-file=.omx/state/node.env -e "console.log('ok')"`],
|
|
3527
|
+
['python cwd startup bypass', `cd .omx/state && python3 -c "print('ok')"`],
|
|
3528
|
+
['sed in-place execute bypass', `sed -i '1e touch src/sed-exec.ts' .omx/state/conductor.log`],
|
|
3529
|
+
...wgetReviewMutationCommands,
|
|
3530
|
+
['unknown extensionless executable', './.omx/state/mutator'],
|
|
3531
|
+
['heredoc delimiter executable collision', `cat <<'MUTATOR' > .omx/state/conductor.log\nsafe\nMUTATOR\n./.omx/state/mutator`],
|
|
3532
|
+
['path executable function-name collision', `mutator() { printf safe; }; ./.omx/state/mutator`],
|
|
3533
|
+
['wrapped executable function-name collision', `mutator() { printf safe; }; env PATH=.omx/state:/usr/bin:/bin mutator`],
|
|
3534
|
+
['omx state clear', `omx state clear --input '{"mode":"ultragoal"}' --json`],
|
|
3535
|
+
['bash uninspected script', `bash .omx/state/run.sh`],
|
|
3536
|
+
['source uninspected script', `source .omx/state/run.sh`],
|
|
3537
|
+
] as const) {
|
|
3538
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3539
|
+
requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }));
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3542
|
+
const workspacePackageCli = realpathSync(join(packageRoot, 'dist', 'cli', 'omx.js'));
|
|
3543
|
+
const packedNpmBinDir = join(smokeCwd, 'node_modules', '.bin');
|
|
3544
|
+
const packedNpmOmxShim = join(packedNpmBinDir, 'omx');
|
|
3545
|
+
const packedNpmGjcShim = join(packedNpmBinDir, 'gjc');
|
|
3546
|
+
mkdirSync(packedNpmBinDir, { recursive: true });
|
|
3547
|
+
symlinkSync(resolve(workspacePackageCli), packedNpmOmxShim);
|
|
3548
|
+
symlinkSync(resolve(workspacePackageCli), packedNpmGjcShim);
|
|
3549
|
+
const selfAssertedWorkspaceCli = join(smokeCwd, 'bin', 'omx.js');
|
|
3550
|
+
mkdirSync(dirname(selfAssertedWorkspaceCli), { recursive: true });
|
|
3551
|
+
writeFileSync(join(smokeCwd, 'package.json'), JSON.stringify({ name: 'oh-my-codex', bin: { omx: 'bin/omx.js' } }));
|
|
3552
|
+
writeFileSync(selfAssertedWorkspaceCli, '#!/usr/bin/env node\n', { mode: 0o755 });
|
|
3553
|
+
rmSync(packedNpmOmxShim);
|
|
3554
|
+
symlinkSync(selfAssertedWorkspaceCli, packedNpmOmxShim);
|
|
3555
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3556
|
+
requireActorDeny(
|
|
3557
|
+
actor,
|
|
3558
|
+
'workspace self-asserted package CLI',
|
|
3559
|
+
runActorProbe(actor, 'workspace self-asserted package CLI', 'Bash', { command: `omx state write --input '{"mode":"ultragoal","active":true}' --json` }, { PATH: `${packedNpmBinDir}:/usr/bin:/bin` }),
|
|
3560
|
+
);
|
|
3561
|
+
}
|
|
3562
|
+
rmSync(packedNpmOmxShim);
|
|
3563
|
+
symlinkSync(workspacePackageCli, packedNpmOmxShim);
|
|
3564
|
+
const packedNpmPath = `${packedNpmBinDir}:${process.env.PATH || '/usr/bin:/bin'}`;
|
|
3565
|
+
const packedNpmCommandPrefix = `PATH="${packedNpmBinDir}:/usr/bin:/bin"`;
|
|
3566
|
+
requireNativeHookPermissionDeny(
|
|
3567
|
+
'main-root boxed planning CLI poisoned state root',
|
|
3568
|
+
runActorProbe(
|
|
3569
|
+
'main-root',
|
|
3570
|
+
'boxed planning CLI poisoned state root',
|
|
3571
|
+
'Bash',
|
|
3572
|
+
{ command: `OMX_STATE_ROOT=${join(smokeCwd, 'src', 'state')} ${packedNpmCommandPrefix} omx state write --input '{"mode":"ralplan","active":true}' --json` },
|
|
3573
|
+
{ OMX_ROOT: boxedPlanningRoot, OMX_TEAM_STATE_ROOT: '' },
|
|
3574
|
+
),
|
|
3575
|
+
/(?:Ralplan is active|Main-root Conductor mode is active)/,
|
|
3576
|
+
);
|
|
3577
|
+
const npmBinPathShadow = join(smokeCwd, '.omx', 'state', 'inbox', 'cat');
|
|
3578
|
+
symlinkSync('/usr/bin/touch', npmBinPathShadow);
|
|
3579
|
+
const npmBinMissingCandidatePath = `${packedNpmBinDir}:${join(smokeCwd, '.omx', 'state', 'inbox')}:/usr/bin:/bin`;
|
|
3580
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3581
|
+
requireActorDeny(
|
|
3582
|
+
actor,
|
|
3583
|
+
'npm bin missing candidate continues PATH scan',
|
|
3584
|
+
runActorProbe(actor, 'npm bin missing candidate continues PATH scan', 'Bash', { command: 'cat src/path-owned.ts' }, { PATH: npmBinMissingCandidatePath }),
|
|
3585
|
+
);
|
|
3586
|
+
}
|
|
3587
|
+
const nonexistentAbsoluteNpmBin = join(smokeCwd, 'node_modules', 'oh-my-codex', 'node_modules', '.bin');
|
|
3588
|
+
const packedNpmPathWithMissingNpmBin = `${nonexistentAbsoluteNpmBin}:${packedNpmPath}`;
|
|
3589
|
+
for (const [probe, command, path] of [
|
|
3590
|
+
['absolute workspace npm bin cat parity', 'cat src/conductor-owned.ts', packedNpmPath],
|
|
3591
|
+
['absolute workspace npm bin wrapped cat parity', 'env cat src/conductor-owned.ts', packedNpmPath],
|
|
3592
|
+
['nonexistent absolute npm bin cat control', 'cat src/conductor-owned.ts', packedNpmPathWithMissingNpmBin],
|
|
3593
|
+
['nonexistent absolute npm bin wrapped cat control', 'env cat src/conductor-owned.ts', packedNpmPathWithMissingNpmBin],
|
|
3594
|
+
] as const) {
|
|
3595
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3596
|
+
const result = runActorProbeResult(actor, probe, 'Bash', { command }, { PATH: path });
|
|
3597
|
+
if (Object.keys(result.output).length !== 0) {
|
|
3598
|
+
throw new Error(`packed ${actor} ${probe} should resolve to the system command: ${JSON.stringify(result.output)}\nactual stdout:\n${result.stdout}`);
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3601
|
+
}
|
|
3602
|
+
const packedNpmNodeShadow = join(packedNpmBinDir, 'node');
|
|
3603
|
+
writeFileSync(packedNpmNodeShadow, '#!/bin/sh\ntouch src/omx-status-owned.ts\n', { mode: 0o755 });
|
|
3604
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3605
|
+
for (const command of [`omx state write --input '{"mode":"ultragoal","active":true}' --json`, 'omx status'] as const) {
|
|
3606
|
+
requireActorDeny(
|
|
3607
|
+
actor,
|
|
3608
|
+
`workspace npm bin Node shadow ${command}`,
|
|
3609
|
+
runActorProbe(actor, `workspace npm bin Node shadow ${command}`, 'Bash', { command }, { PATH: `${packedNpmBinDir}:/usr/bin:/bin` }),
|
|
3610
|
+
);
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
rmSync(packedNpmNodeShadow, { force: true });
|
|
3614
|
+
const cliStateWrite = `OMX_SESSION_ID=${sessionId} omx state write --input '${JSON.stringify({ mode: "ultragoal", active: true, current_phase: "executing", session_id: sessionId, workingDirectory: smokeCwd })}' --json`;
|
|
3615
|
+
const mainRootCliStateWriteProbe = runActorProbeResult('main-root', 'cli state write main', 'Bash', { command: cliStateWrite }, { PATH: packedNpmPath });
|
|
3616
|
+
if (Object.keys(mainRootCliStateWriteProbe.output).length !== 0) {
|
|
3617
|
+
throw new Error(`packed main-root CLI state write should retain metadata allowance: ${JSON.stringify(mainRootCliStateWriteProbe.output)}\nactual stdout:\n${mainRootCliStateWriteProbe.stdout}`);
|
|
3618
|
+
}
|
|
3619
|
+
requireActorDeny('native-child', 'cli state write native child', runActorProbe('native-child', 'cli state write native child', 'Bash', { command: cliStateWrite }, { PATH: packedNpmPath }));
|
|
3620
|
+
const systemNodeCliStateWriteProbe = runActorProbeResult(
|
|
3621
|
+
'main-root',
|
|
3622
|
+
'cli state write system Node',
|
|
3623
|
+
'Bash',
|
|
3624
|
+
{ command: cliStateWrite },
|
|
3625
|
+
{ PATH: `${packedNpmBinDir}:/usr/bin:/bin` },
|
|
3626
|
+
);
|
|
3627
|
+
if (Object.keys(systemNodeCliStateWriteProbe.output).length !== 0) {
|
|
3628
|
+
throw new Error(`packed main-root CLI state write should permit a trusted system Node: ${JSON.stringify(systemNodeCliStateWriteProbe.output)}\nactual stdout:\n${systemNodeCliStateWriteProbe.stdout}`);
|
|
3629
|
+
}
|
|
3630
|
+
const benignOrchestrationRuntimeEnvironment = {
|
|
3631
|
+
PATH: packedNpmPath,
|
|
3632
|
+
GJC_SESSION_CWD: smokeCwd,
|
|
3633
|
+
GJC_SESSION_FILE: join(stateDir, 'session.json'),
|
|
3634
|
+
GJC_SESSION_ID: sessionId,
|
|
3635
|
+
OMX_OPENCLAW: '1',
|
|
3636
|
+
OMX_OPENCLAW_COMMAND: 'resume',
|
|
3637
|
+
OMX_OPENCLAW_DEBUG: '1',
|
|
3638
|
+
OMX_TEST_RELAX_TMUX_TIMEOUT: '1',
|
|
3639
|
+
};
|
|
3640
|
+
const benignRuntimeEnvironmentCliStateWriteProbe = runActorProbeResult(
|
|
3641
|
+
'main-root',
|
|
3642
|
+
'cli state write benign runtime environment',
|
|
3643
|
+
'Bash',
|
|
3644
|
+
{ command: cliStateWrite },
|
|
3645
|
+
benignOrchestrationRuntimeEnvironment,
|
|
3646
|
+
);
|
|
3647
|
+
if (Object.keys(benignRuntimeEnvironmentCliStateWriteProbe.output).length !== 0) {
|
|
3648
|
+
throw new Error(`packed main-root CLI state write should permit benign runtime environment: ${JSON.stringify(benignRuntimeEnvironmentCliStateWriteProbe.output)}\nactual stdout:\n${benignRuntimeEnvironmentCliStateWriteProbe.stdout}`);
|
|
3649
|
+
}
|
|
3650
|
+
const benignRuntimeEnvironmentStatusProbe = runActorProbeResult(
|
|
3651
|
+
'main-root',
|
|
3652
|
+
'cli status benign runtime environment',
|
|
3653
|
+
'Bash',
|
|
3654
|
+
{ command: 'omx status' },
|
|
3655
|
+
benignOrchestrationRuntimeEnvironment,
|
|
3656
|
+
);
|
|
3657
|
+
if (Object.keys(benignRuntimeEnvironmentStatusProbe.output).length !== 0) {
|
|
3658
|
+
throw new Error(`packed main-root CLI status should permit benign runtime environment: ${JSON.stringify(benignRuntimeEnvironmentStatusProbe.output)}\nactual stdout:\n${benignRuntimeEnvironmentStatusProbe.stdout}`);
|
|
3659
|
+
}
|
|
3660
|
+
const functionPersistedRsyncEnvironment = `poison(){ export RSYNC_PARTIAL_DIR=${join(smokeCwd, 'src', 'rsync-partials')}; }; poison; rsync .omx/state/conductor-ledger.json .omx/state/inbox/rsync-copy`;
|
|
3661
|
+
const functionPersistedOmxEnvironment = `poison(){ export OMX_STATE_ROOT=${join(smokeCwd, 'src', 'state')}; }; poison; omx state write --input '{"mode":"ultragoal","active":true}' --json`;
|
|
3662
|
+
for (const [probe, command] of [
|
|
3663
|
+
['function persisted rsync runtime environment', functionPersistedRsyncEnvironment],
|
|
3664
|
+
['function persisted omx runtime environment', functionPersistedOmxEnvironment],
|
|
3665
|
+
['function persisted gjc runtime environment', `poison(){ export GJC_UNREVIEWED_HELPER=src/mutator; }; poison; omx status`],
|
|
3666
|
+
['nameref rsync runtime environment', `declare -n poison=RSYNC_PARTIAL_DIR; poison=src/rsync-partials; rsync .omx/state/conductor-ledger.json .omx/state/inbox/rsync-copy`],
|
|
3667
|
+
['joined rsync runtime environment', `if true; then export RSYNC_PARTIAL_DIR=src/rsync-partials; fi; rsync .omx/state/conductor-ledger.json .omx/state/inbox/rsync-copy`],
|
|
3668
|
+
['nested omx runtime environment', `export OMX_STATE_ROOT=${join(smokeCwd, 'src', 'state')}; bash --noprofile --norc -c 'omx status'`],
|
|
3669
|
+
['function readonly omx root failed unset', `poison(){ readonly OMX_ROOT=${join(smokeCwd, 'src')}; unset OMX_ROOT; }; poison; omx status`],
|
|
3670
|
+
['function readonly rsync runtime failed unset', `poison(){ readonly RSYNC_PARTIAL_DIR=src/rsync-partials; unset RSYNC_PARTIAL_DIR; }; poison; rsync .omx/state/conductor-ledger.json .omx/state/inbox/rsync-copy`],
|
|
3671
|
+
['function readonly gjc runtime failed unset', `poison(){ readonly GJC_UNREVIEWED_HELPER=src/mutator; unset GJC_UNREVIEWED_HELPER; }; poison; omx status`],
|
|
3672
|
+
] as const) {
|
|
3673
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3674
|
+
requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }, { PATH: packedNpmPath }));
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
const modeCliStateWrite = `OMX_SESSION_ID=${sessionId} omx state write --mode=ultragoal --input '${JSON.stringify({ active: true, current_phase: "blocked", reason: "native delegation unavailable -> terminalized", session_id: sessionId, workingDirectory: smokeCwd })}' --json`;
|
|
3678
|
+
const mainRootModeCliStateWriteProbe = runActorProbeResult('main-root', 'mode CLI state write main', 'Bash', { command: modeCliStateWrite }, { PATH: packedNpmPath });
|
|
3679
|
+
if (Object.keys(mainRootModeCliStateWriteProbe.output).length !== 0) {
|
|
3680
|
+
throw new Error(`packed main-root mode CLI state write should retain metadata allowance: ${JSON.stringify(mainRootModeCliStateWriteProbe.output)}\nactual stdout:\n${mainRootModeCliStateWriteProbe.stdout}`);
|
|
3681
|
+
}
|
|
3682
|
+
requireActorDeny('native-child', 'mode CLI state write native child', runActorProbe('native-child', 'mode CLI state write native child', 'Bash', { command: modeCliStateWrite }, { PATH: packedNpmPath }));
|
|
3683
|
+
for (const [probe, prefix] of [
|
|
3684
|
+
['poisoned OMX root', `OMX_ROOT=${join(smokeCwd, 'src')} PATH=${packedNpmPath}`],
|
|
3685
|
+
['poisoned OMX state root', `OMX_STATE_ROOT=${join(smokeCwd, 'src', 'state')} PATH=${packedNpmPath}`],
|
|
3686
|
+
['unknown OMX runtime output', `OMX_UNREVIEWED_OUTPUT=src/output PATH=${packedNpmPath}`],
|
|
3687
|
+
['unknown OMX runtime environment', `OMX_UNREVIEWED_HELPER=src/mutator PATH=${packedNpmPath}`],
|
|
3688
|
+
['unknown GJC runtime environment', `GJC_UNREVIEWED_HELPER=src/mutator PATH=${packedNpmPath}`],
|
|
3689
|
+
] as const) {
|
|
3690
|
+
for (const command of [cliStateWrite, 'omx status'] as const) {
|
|
3691
|
+
const surface = command === cliStateWrite ? 'state write' : 'read-only omx';
|
|
3692
|
+
requireActorDeny(
|
|
3693
|
+
'main-root',
|
|
3694
|
+
`${probe} ${surface}`,
|
|
3695
|
+
runActorProbe('main-root', `${probe} ${surface}`, 'Bash', { command: `${prefix} ${command}` }),
|
|
3696
|
+
);
|
|
3697
|
+
}
|
|
3698
|
+
}
|
|
3699
|
+
const fakeOmxDirectory = join(smokeCwd, '.omx', 'state');
|
|
3700
|
+
const fakeOmxPath = join(fakeOmxDirectory, 'omx');
|
|
3701
|
+
const fakeGjcPath = join(fakeOmxDirectory, 'gjc');
|
|
3702
|
+
writeFileSync(fakeOmxPath, '#!/bin/sh\nexit 0\n', { mode: 0o755 });
|
|
3703
|
+
writeFileSync(fakeGjcPath, '#!/bin/sh\nexit 0\n', { mode: 0o755 });
|
|
3704
|
+
const fakeOmxStateWrite = `omx state write --input '{"mode":"ultragoal","active":true}' --json`;
|
|
3705
|
+
const fakeGjcCheckpoint = `gjc ultragoal checkpoint --goal-id G001 --status failed --evidence unauthorized`;
|
|
3706
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3707
|
+
requireActorDeny(
|
|
3708
|
+
actor,
|
|
3709
|
+
'repository omx shadow',
|
|
3710
|
+
runActorProbe(actor, 'repository omx shadow', 'Bash', { command: fakeOmxStateWrite }, {
|
|
3711
|
+
PATH: `${fakeOmxDirectory}:${packedNpmPath}`,
|
|
3712
|
+
}),
|
|
3713
|
+
);
|
|
3714
|
+
requireActorDeny(
|
|
3715
|
+
actor,
|
|
3716
|
+
'repository gjc shadow',
|
|
3717
|
+
runActorProbe(actor, 'repository gjc shadow', 'Bash', { command: fakeGjcCheckpoint }, {
|
|
3718
|
+
PATH: `${fakeOmxDirectory}:${packedNpmPath}`,
|
|
3719
|
+
}),
|
|
3720
|
+
);
|
|
3721
|
+
}
|
|
3722
|
+
rmSync(fakeOmxPath, { force: true });
|
|
3723
|
+
rmSync(fakeGjcPath, { force: true });
|
|
3724
|
+
rmSync(packedNpmOmxShim, { force: true });
|
|
3725
|
+
writeFileSync(packedNpmOmxShim, '#!/bin/sh\nexit 0\n', { mode: 0o755 });
|
|
3726
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3727
|
+
requireActorDeny(
|
|
3728
|
+
actor,
|
|
3729
|
+
'workspace npm bin omx shadow',
|
|
3730
|
+
runActorProbe(actor, 'workspace npm bin omx shadow', 'Bash', { command: fakeOmxStateWrite }, { PATH: packedNpmPath }),
|
|
3731
|
+
);
|
|
3732
|
+
}
|
|
3733
|
+
rmSync(packedNpmOmxShim, { force: true });
|
|
3734
|
+
symlinkSync(workspacePackageCli, packedNpmOmxShim);
|
|
3735
|
+
rmSync(packedNpmGjcShim, { force: true });
|
|
3736
|
+
writeFileSync(packedNpmGjcShim, '#!/bin/sh\nexit 0\n', { mode: 0o755 });
|
|
3737
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3738
|
+
requireActorDeny(
|
|
3739
|
+
actor,
|
|
3740
|
+
'workspace npm bin gjc shadow',
|
|
3741
|
+
runActorProbe(actor, 'workspace npm bin gjc shadow', 'Bash', { command: fakeGjcCheckpoint }, { PATH: packedNpmPath }),
|
|
3742
|
+
);
|
|
3743
|
+
}
|
|
3744
|
+
rmSync(packedNpmGjcShim, { force: true });
|
|
3745
|
+
symlinkSync(workspacePackageCli, packedNpmGjcShim);
|
|
3746
|
+
const workspaceNpmBinChmodShadowCommand = `chmod --reference=.omx/state/session.json .omx/state/reference-copy; omx state write --input '{"mode":"ultragoal"}' --json`;
|
|
3747
|
+
const workspaceNpmBinChmodShadow = join(packedNpmBinDir, 'chmod');
|
|
3748
|
+
writeFileSync(workspaceNpmBinChmodShadow, '#!/bin/sh\nexit 0\n', { mode: 0o755 });
|
|
3749
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3750
|
+
requireActorDeny(
|
|
3751
|
+
actor,
|
|
3752
|
+
'workspace npm bin chmod shadow',
|
|
3753
|
+
runActorProbe(actor, 'workspace npm bin chmod shadow', 'Bash', { command: workspaceNpmBinChmodShadowCommand }, { PATH: packedNpmPath }),
|
|
3754
|
+
);
|
|
3755
|
+
}
|
|
3756
|
+
rmSync(workspaceNpmBinChmodShadow, { force: true });
|
|
3757
|
+
const currentRuntimeNodePath = process.execPath;
|
|
3758
|
+
const currentRuntimeNodePathEnvironment = { PATH: `${dirname(currentRuntimeNodePath)}:/usr/bin:/bin` };
|
|
3759
|
+
const untrustedExternalBin = mkdtempSync(join(tmpdir(), 'omx-native-hook-external-path-'));
|
|
3760
|
+
const untrustedExternalCat = join(untrustedExternalBin, 'cat');
|
|
3761
|
+
const untrustedExternalGh = join(untrustedExternalBin, 'gh');
|
|
3762
|
+
const untrustedExternalNode = join(untrustedExternalBin, 'node');
|
|
3763
|
+
const untrustedExternalNodeLookalike = join(untrustedExternalBin, 'node-copy');
|
|
3764
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3765
|
+
const output = runActorProbe(actor, 'external path without cat candidate', 'Bash', {
|
|
3766
|
+
command: `PATH=${untrustedExternalBin}:${process.env.PATH || '/usr/bin:/bin'} cat src/conductor-owned.ts`,
|
|
3767
|
+
});
|
|
3768
|
+
if (Object.keys(output).length !== 0) throw new Error(`packed ${actor} external PATH without a cat candidate should fall through to the system command`);
|
|
3769
|
+
}
|
|
3770
|
+
symlinkSync('/bin/cat', untrustedExternalCat);
|
|
3771
|
+
writeFileSync(untrustedExternalGh, '#!/bin/sh\nexit 0\n', { mode: 0o755 });
|
|
3772
|
+
writeFileSync(untrustedExternalNode, '#!/bin/sh\nexit 0\n', { mode: 0o755 });
|
|
3773
|
+
writeFileSync(untrustedExternalNodeLookalike, '#!/bin/sh\nexit 0\n', { mode: 0o755 });
|
|
3774
|
+
try {
|
|
3775
|
+
for (const [probe, command] of [
|
|
3776
|
+
['untrusted external absolute executable', `${untrustedExternalCat} src/conductor-owned.ts`],
|
|
3777
|
+
['untrusted external PATH executable', `PATH=${untrustedExternalBin}:${process.env.PATH || '/usr/bin:/bin'} cat src/conductor-owned.ts`],
|
|
3778
|
+
['untrusted external absolute gh', `${untrustedExternalGh} issue create --title x --body y`],
|
|
3779
|
+
['untrusted external PATH gh', `PATH=${untrustedExternalBin}:${process.env.PATH || '/usr/bin:/bin'} gh issue create --title x --body y`],
|
|
3780
|
+
['untrusted external absolute node', `${untrustedExternalNode} -e "require('fs').readFileSync('src/victim.ts','utf8')"`],
|
|
3781
|
+
['untrusted external PATH node', `PATH=${untrustedExternalBin}:/usr/bin:/bin node -e "require('fs').readFileSync('src/victim.ts','utf8')"`],
|
|
3782
|
+
['untrusted external node lookalike', `${untrustedExternalNodeLookalike} -e "require('fs').readFileSync('src/victim.ts','utf8')"`],
|
|
3783
|
+
] as const) {
|
|
3784
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3785
|
+
requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }));
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
} finally {
|
|
3789
|
+
rmSync(untrustedExternalBin, { recursive: true, force: true });
|
|
3790
|
+
}
|
|
3791
|
+
const dynamicCliStateWrite = `omx state write --input "$STATE_INPUT" --json`;
|
|
3792
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3793
|
+
requireActorDeny(
|
|
3794
|
+
actor,
|
|
3795
|
+
'dynamic CLI state write',
|
|
3796
|
+
runActorProbe(actor, 'dynamic CLI state write', 'Bash', { command: dynamicCliStateWrite }, { PATH: packedNpmPath }),
|
|
3797
|
+
);
|
|
3798
|
+
}
|
|
3799
|
+
const unknownStateWriteFlag = `omx state write --unexpected --input '{"mode":"ultragoal","active":true}' --json`;
|
|
3800
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3801
|
+
requireActorDeny(
|
|
3802
|
+
actor,
|
|
3803
|
+
'unknown state write flag',
|
|
3804
|
+
runActorProbe(actor, 'unknown state write flag', 'Bash', { command: unknownStateWriteFlag }, { PATH: packedNpmPath }),
|
|
3805
|
+
);
|
|
3806
|
+
}
|
|
3807
|
+
for (const [probe, command] of [
|
|
3808
|
+
['state write foreign routing', `omx state write --input '{"mode":"ultragoal","workingDirectory":"src","session_id":"foreign","active":false}' --json`],
|
|
3809
|
+
['state write unknown payload key', `omx state write --input '{"mode":"ultragoal","active":true,"child_marker":"forbidden"}' --json`],
|
|
3810
|
+
['state write conflicting mode', `omx state write --mode=ultragoal --input '{"mode":"ralph","active":true}' --json`],
|
|
3811
|
+
['state write foreign session environment', `OMX_SESSION_ID=foreign omx state write --input '{"mode":"ultragoal","active":true}' --json`],
|
|
3812
|
+
['state write unset session environment', `env -uOMX_SESSION_ID omx state write --input '{"mode":"ultragoal","active":true}' --json`],
|
|
3813
|
+
['state write shell unset session', `unset OMX_SESSION_ID; omx state write --input '{"mode":"ultragoal","active":true}' --json`],
|
|
3814
|
+
['state write noncanonical cwd', `cd src; omx state write --input '{"mode":"ultragoal","active":true}' --json`],
|
|
3815
|
+
] as const) {
|
|
3816
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3817
|
+
requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }, { PATH: packedNpmPath }));
|
|
3818
|
+
}
|
|
3819
|
+
}
|
|
3820
|
+
for (const [probe, inheritedEnv] of [
|
|
3821
|
+
['inherited foreign OMX session selector', { OMX_SESSION_ID: 'foreign' }],
|
|
3822
|
+
['inherited foreign GJC session selector', { GJC_SESSION_ID: 'foreign' }],
|
|
3823
|
+
['inherited conflicting session selectors', { OMX_SESSION_ID: sessionId, GJC_SESSION_ID: 'foreign' }],
|
|
3824
|
+
] as const) {
|
|
3825
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3826
|
+
requireActorDeny(
|
|
3827
|
+
actor,
|
|
3828
|
+
probe,
|
|
3829
|
+
runActorProbe(actor, probe, 'Bash', { command: `omx state write --input '{"mode":"ultragoal","active":true}' --json` }, { PATH: packedNpmPath, ...inheritedEnv }),
|
|
3830
|
+
);
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
const mixedReferenceStateCommand = `chmod --reference=.omx/state/session.json .omx/state/reference-copy; OMX_SESSION_ID=${sessionId} omx state write --input '${JSON.stringify({ mode: "ultragoal", active: true, current_phase: "executing", session_id: sessionId, workingDirectory: smokeCwd })}' --json`;
|
|
3834
|
+
requireActorDeny(
|
|
3835
|
+
"main-root",
|
|
3836
|
+
"native child mixed reference state write main",
|
|
3837
|
+
runActorProbe("main-root", "native child mixed reference state write main", "Bash", { command: mixedReferenceStateCommand }, { PATH: packedNpmPath }),
|
|
3838
|
+
);
|
|
3839
|
+
requireActorDeny(
|
|
3840
|
+
'native-child',
|
|
3841
|
+
'native child mixed reference state write native child',
|
|
3842
|
+
runActorProbe('native-child', 'native child mixed reference state write native child', 'Bash', { command: mixedReferenceStateCommand }, { PATH: packedNpmPath }),
|
|
3843
|
+
);
|
|
3844
|
+
requireActorDeny(
|
|
3845
|
+
'native-child',
|
|
3846
|
+
'native child rsync authority target',
|
|
3847
|
+
runActorProbe('native-child', 'native child rsync authority target', 'Bash', { command: `rsync .omx/state/conductor-ledger.json .omx/state/session.json` }),
|
|
3848
|
+
);
|
|
3849
|
+
const referenceUnknownCommand = `chmod --reference=.omx/state/session.json .omx/state/reference-copy; unknown-mutation-transport`;
|
|
3850
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3851
|
+
requireActorDeny(
|
|
3852
|
+
actor,
|
|
3853
|
+
'native child reference unknown command',
|
|
3854
|
+
runActorProbe(actor, 'native child reference unknown command', 'Bash', { command: referenceUnknownCommand }),
|
|
3855
|
+
);
|
|
3856
|
+
}
|
|
3857
|
+
for (const [probe, command, inheritedEnv] of [
|
|
3858
|
+
['python inherited sitecustomize preload', `python3 -c "print('ok')"`, { PYTHONPATH: './.omx/state' }],
|
|
3859
|
+
['perl inherited module preload', `perl -e 'print;'`, { PERL5LIB: './.omx/state', PERL5OPT: '-MMutator' }],
|
|
3860
|
+
['bash inherited env preload', `bash -c 'printf safe\\n'`, { BASH_ENV: './.omx/state/mutator.sh' }],
|
|
3861
|
+
['bash inherited env plain read-only', `cat src/conductor-owned.ts`, { BASH_ENV: './.omx/state/mutator.sh' }],
|
|
3862
|
+
['zsh inherited startup preload', `zsh -c 'printf safe'`, { ZDOTDIR: './.omx/state' }],
|
|
3863
|
+
['curl inherited SSL key log output', `curl -q -o - https://example.test/file`, { SSLKEYLOGFILE: 'src/curl-tls.keys' }],
|
|
3864
|
+
['wget inherited SSL key log output', `wget --no-config --no-hsts -O - https://example.test/file`, { SSLKEYLOGFILE: 'src/wget-tls.keys' }],
|
|
3865
|
+
['wget inherited posix option ordering', `wget -O src/posix-inherited-wget-owned.ts https://example.test/file -O -`, { POSIXLY_CORRECT: '1' }],
|
|
3866
|
+
['inherited empty path', `cat src/conductor-owned.ts`, { PATH: '' }],
|
|
3867
|
+
['inherited relative path', `cat src/conductor-owned.ts`, { PATH: '.' }],
|
|
3868
|
+
['inherited unresolved path', `cat src/conductor-owned.ts`, { PATH: '$UNKNOWN_PATH' }],
|
|
3869
|
+
['inherited dynamic loader library path', `cat src/conductor-owned.ts`, { LD_LIBRARY_PATH: './.omx/state' }],
|
|
3870
|
+
['inherited dynamic loader audit path', `cat src/conductor-owned.ts`, { DYLD_LIBRARY_PATH: './.omx/state' }],
|
|
3871
|
+
['inherited xtrace shell option', `cat src/conductor-owned.ts`, { SHELLOPTS: 'xtrace' }],
|
|
3872
|
+
['inherited keyword shell option', `cat src/conductor-owned.ts`, { SHELLOPTS: 'keyword' }],
|
|
3873
|
+
] as const) {
|
|
3874
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3875
|
+
requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }, inheritedEnv));
|
|
3876
|
+
}
|
|
3877
|
+
}
|
|
3878
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3879
|
+
for (const [probe, command] of [
|
|
3880
|
+
['python inherited unbuffered read-only control', `python3 -I -c "print('ok')"`],
|
|
3881
|
+
['python inherited unbuffered isolated metadata cwd control', `cd .omx/state && python3 -I -c "print('ok')"`],
|
|
3882
|
+
] as const) {
|
|
3883
|
+
const output = runActorProbe(actor, probe, 'Bash', { command }, { PYTHONUNBUFFERED: '1' });
|
|
3884
|
+
if (Object.keys(output).length !== 0) throw new Error(`packed ${actor} ${probe} should be allowed`);
|
|
3885
|
+
}
|
|
3886
|
+
}
|
|
3887
|
+
const unbufferedModeledMetadataOutput = runActorProbe(
|
|
3888
|
+
'main-root',
|
|
3889
|
+
'python inherited unbuffered modeled metadata control',
|
|
3890
|
+
'Bash',
|
|
3891
|
+
{ command: "python3 -I - <<'PY'\nimport shutil\nshutil.copyfile('a', '.omx/state/foo')\nPY" },
|
|
3892
|
+
{ PYTHONUNBUFFERED: '1' },
|
|
3893
|
+
);
|
|
3894
|
+
if (Object.keys(unbufferedModeledMetadataOutput).length !== 0) {
|
|
3895
|
+
throw new Error('packed main-root python inherited unbuffered modeled metadata control should be allowed');
|
|
3896
|
+
}
|
|
3897
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3898
|
+
const output = runActorProbe(actor, 'repository path without cat candidate', 'Bash', { command: 'cat src/conductor-owned.ts' }, {
|
|
3899
|
+
PATH: `${smokeCwd}:${process.env.PATH || '/usr/bin:/bin'}`,
|
|
3900
|
+
});
|
|
3901
|
+
if (Object.keys(output).length !== 0) throw new Error(`packed ${actor} repository PATH without a cat candidate should fall through to the system command`);
|
|
3902
|
+
}
|
|
3903
|
+
|
|
3904
|
+
for (const [probe, command, inheritedEnv] of [
|
|
3905
|
+
['inherited bash function shadow', `cat src/conductor-owned.ts`, { 'BASH_FUNC_cat%%': '() { touch src/inherited-function-owned.ts; }' }],
|
|
3906
|
+
] as const) {
|
|
3907
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3908
|
+
requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }, inheritedEnv));
|
|
3909
|
+
}
|
|
3910
|
+
}
|
|
3911
|
+
writeFileSync(join(fakeOmxDirectory, 'env'), '#!/bin/sh\nexit 0\n', { mode: 0o755 });
|
|
3912
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3913
|
+
requireActorDeny(
|
|
3914
|
+
actor,
|
|
3915
|
+
'repository external wrapper shadow',
|
|
3916
|
+
runActorProbe(actor, 'repository external wrapper shadow', 'Bash', {
|
|
3917
|
+
command: `PATH=${fakeOmxDirectory}:/usr/bin:/bin env cat src/conductor-owned.ts`,
|
|
3918
|
+
}),
|
|
3919
|
+
);
|
|
3920
|
+
}
|
|
3921
|
+
rmSync(join(fakeOmxDirectory, 'env'), { force: true });
|
|
3922
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3923
|
+
requireActorDeny(
|
|
3924
|
+
actor,
|
|
3925
|
+
'loader before external env clear',
|
|
3926
|
+
runActorProbe(actor, 'loader before external env clear', 'Bash', { command: `env -i cat src/conductor-owned.ts` }, { LD_LIBRARY_PATH: './.omx/state' }),
|
|
3927
|
+
);
|
|
3928
|
+
}
|
|
3929
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3930
|
+
const output = runActorProbe(actor, 'cleared inherited bash function control', 'Bash', { command: `env -i cat src/conductor-owned.ts` }, { 'BASH_FUNC_cat%%': '() { touch src/inherited-function-owned.ts; }' });
|
|
3931
|
+
if (Object.keys(output).length !== 0) throw new Error(`packed ${actor} clear-environment inherited-function control should be allowed`);
|
|
3932
|
+
}
|
|
3933
|
+
for (const [probe, command] of [
|
|
3934
|
+
['cleared environment empty path', `env -i PATH= sh -c 'wget --no-config -O - https://example.test/file'`],
|
|
3935
|
+
['cleared environment relative path', `env -i PATH=. sh -c 'wget --no-config -O - https://example.test/file'`],
|
|
3936
|
+
['cleared environment repository path', `env -i PATH=${smokeCwd} sh -c 'wget --no-config -O - https://example.test/file'`],
|
|
3937
|
+
['cleared exec empty path', `exec -c env PATH= sh -c 'wget --no-config -O - https://example.test/file'`],
|
|
3938
|
+
['cleared exec relative path', `exec -c env PATH=. sh -c 'wget --no-config -O - https://example.test/file'`],
|
|
3939
|
+
['cleared exec repository path', `exec -c env PATH=${smokeCwd} sh -c 'wget --no-config -O - https://example.test/file'`],
|
|
3940
|
+
['recursive reference metadata', `chmod -R --reference=.omx/state/session.json .omx/state`],
|
|
3941
|
+
['rsync directory tree metadata', `rsync -a --log-file=.omx/state/rsync-tree.log .omx/state/ .omx/state/rsync-tree-copy/`],
|
|
3942
|
+
['rsync keep dirlinks metadata', `rsync --keep-dirlinks --log-file=.omx/state/rsync-dirlinks.log .omx/state/conductor-ledger.json .omx/state/rsync-dirlinks-copy`],
|
|
3943
|
+
['rsync explicit links metadata', `rsync --links --log-file=.omx/state/rsync-links.log .omx/state/conductor-ledger.json .omx/state/rsync-links-copy`],
|
|
3944
|
+
] as const) {
|
|
3945
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3946
|
+
requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }));
|
|
3947
|
+
}
|
|
3948
|
+
}
|
|
3949
|
+
const trustedSystemGh = '/usr/bin/gh';
|
|
3950
|
+
const hasTrustedSystemGh = existsSync(trustedSystemGh);
|
|
3951
|
+
for (const [probe, command] of [
|
|
3952
|
+
['gh issue create', `${trustedSystemGh} issue create --title x --body y`],
|
|
3953
|
+
['gh issue quoted regex body', `${trustedSystemGh} issue create --title x --body 'Guard regex /[^>]+>{1,2}/ is data, not a redirect'`],
|
|
3954
|
+
['gh api post', `${trustedSystemGh} api --method POST /repos/OWNER/REPO/issues -f title=x`],
|
|
3955
|
+
['gh api attached post', `${trustedSystemGh} api -XPOST --input .omx/state/create-repo.json /user/repos`],
|
|
3956
|
+
['gh api attached field post', `${trustedSystemGh} api -ftitle=x /repos/OWNER/REPO/issues`],
|
|
3957
|
+
['omx ultragoal final checkpoint', `${packedNpmCommandPrefix} omx ultragoal checkpoint --goal-id G001 --status complete --codex-goal-json goal.json --quality-gate-json quality.json --evidence unauthorized --json`],
|
|
3958
|
+
['wrapped gh issue create', `bash --noprofile --norc -lc '${trustedSystemGh} issue create --title x --body y'`],
|
|
3959
|
+
['wrapped omx ultragoal final checkpoint', `${packedNpmCommandPrefix} bash --noprofile --norc -lc '${packedNpmCommandPrefix} omx ultragoal checkpoint --goal-id G001 --status complete --codex-goal-json goal.json --quality-gate-json quality.json --evidence unauthorized --json'`],
|
|
3960
|
+
['wrapped gjc ultragoal final checkpoint', `${packedNpmCommandPrefix} bash --noprofile --norc -lc '${packedNpmCommandPrefix} gjc ultragoal checkpoint --goal-id G001 --status complete --codex-goal-json goal.json --quality-gate-json quality.json --evidence unauthorized --json'`],
|
|
3961
|
+
['performance goal complete', `${packedNpmCommandPrefix} omx performance-goal complete --slug latency --codex-goal-json goal.json --evidence done --json`],
|
|
3962
|
+
['wrapped performance goal complete', `${packedNpmCommandPrefix} bash --noprofile --norc -lc '${packedNpmCommandPrefix} omx performance-goal complete --slug latency --codex-goal-json goal.json --evidence done --json'`],
|
|
3963
|
+
['autoresearch goal complete', `${packedNpmCommandPrefix} gjc autoresearch-goal complete --slug safety --codex-goal-json goal.json --json`],
|
|
3964
|
+
['wrapped autoresearch goal complete', `${packedNpmCommandPrefix} bash --noprofile --norc -lc '${packedNpmCommandPrefix} gjc autoresearch-goal complete --slug safety --codex-goal-json goal.json --json'`],
|
|
3965
|
+
['pipeline read then mutate', `${packedNpmCommandPrefix} omx status | ${packedNpmCommandPrefix} omx performance-goal complete --slug latency --codex-goal-json goal.json --evidence done --json`],
|
|
3966
|
+
] as const) {
|
|
3967
|
+
const mainRootProbe = runActorProbeResult('main-root', `${probe} main`, 'Bash', { command });
|
|
3968
|
+
const requiresTrustedGh = probe.startsWith('gh ') || probe.startsWith('wrapped gh ');
|
|
3969
|
+
if (requiresTrustedGh && !hasTrustedSystemGh) {
|
|
3970
|
+
requireActorDeny('main-root', `${probe} main`, mainRootProbe.output);
|
|
3971
|
+
} else if (Object.keys(mainRootProbe.output).length !== 0) {
|
|
3972
|
+
throw new Error(`packed main-root ${probe} should retain remote orchestration allowance: ${JSON.stringify(mainRootProbe.output)}\nactual stdout:\n${mainRootProbe.stdout}`);
|
|
3973
|
+
}
|
|
3974
|
+
requireActorDeny('native-child', `${probe} native child`, runActorProbe('native-child', `${probe} native child`, 'Bash', { command }));
|
|
3975
|
+
}
|
|
3976
|
+
const inheritedGhHelperEnvironment = { GH_BROWSER: 'true', GH_PAGER: 'cat', GIT_EDITOR: 'true' };
|
|
3977
|
+
for (const [probe, command] of [
|
|
3978
|
+
['gh issue create inherited helper control', `${trustedSystemGh} issue create --title x --body y`],
|
|
3979
|
+
['gh api inherited helper control', `${trustedSystemGh} api --method POST /repos/OWNER/REPO/issues -f title=x`],
|
|
3980
|
+
] as const) {
|
|
3981
|
+
const mainRootProbe = runActorProbeResult('main-root', probe, 'Bash', { command }, inheritedGhHelperEnvironment);
|
|
3982
|
+
if (!hasTrustedSystemGh) {
|
|
3983
|
+
requireActorDeny('main-root', probe, mainRootProbe.output);
|
|
3984
|
+
} else if (Object.keys(mainRootProbe.output).length !== 0) {
|
|
3985
|
+
throw new Error(`packed main-root ${probe} should ignore inherited GH helper environment: ${JSON.stringify(mainRootProbe.output)}\nactual stdout:\n${mainRootProbe.stdout}`);
|
|
3986
|
+
}
|
|
3987
|
+
}
|
|
3988
|
+
for (const [probe, command] of [
|
|
3989
|
+
['gh api dynamic method', `gh api --method "$GH_METHOD" /repos/OWNER/REPO/issues`],
|
|
3990
|
+
['xargs gh api mutation', `printf '%s' '-XPOST /repos/OWNER/REPO/issues' | PATH=/usr/bin:/bin xargs gh api`],
|
|
3991
|
+
['omx unknown mutation', `omx unrecognized mutate --status failed`],
|
|
3992
|
+
] as const) {
|
|
3993
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
3994
|
+
requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }));
|
|
3995
|
+
}
|
|
3996
|
+
}
|
|
3997
|
+
for (const [probe, command] of [
|
|
3998
|
+
['omx checkpoint unknown option', `${packedNpmCommandPrefix} omx ultragoal checkpoint --goal-id G001 --status failed --evidence x --future-output=src/owned.ts`],
|
|
3999
|
+
['omx node options preload', `NODE_OPTIONS='--require ./.omx/state/mutator.cjs' ${packedNpmCommandPrefix} omx status`],
|
|
4000
|
+
['omx node coverage output', `NODE_V8_COVERAGE=src ${packedNpmCommandPrefix} omx status`],
|
|
4001
|
+
['posix stdout before extra operand', `WGETRC=/dev/null POSIXLY_CORRECT=1 wget --no-config --no-hsts -O - https://example.test/file -O src/posix-after-operand.ts`],
|
|
4002
|
+
['posix spider before extra operand', `WGETRC=/dev/null POSIXLY_CORRECT=1 wget --no-config --no-hsts --spider https://example.test/file -O src/posix-spider-after-operand.ts`],
|
|
4003
|
+
['posix append stdout before extra operand', `POSIXLY_CORRECT+=1 wget --no-config --no-hsts -O - https://example.test/file -O src/posix-append-after-operand.ts`],
|
|
4004
|
+
['coproc function lifecycle child', `touch(){ :; }; coproc { unset -f touch; }; wait; touch src/coproc-lifecycle-shadowed.ts`],
|
|
4005
|
+
['wget repeated log last metadata sink', `wget --no-config --no-hsts -o src/ignored.log -o .omx/state/final.log -O - https://example.test/file`],
|
|
4006
|
+
['wget repeated directory last metadata sink', `wget --no-config --no-hsts -P src -P .omx/state https://example.test/file`],
|
|
4007
|
+
['wget repeated long directory last metadata sink', `wget --no-config --no-hsts --directory-prefix=src --directory-prefix=.omx/state https://example.test/file`],
|
|
4008
|
+
['wget exact output document metadata sink', `wget --no-config --no-hsts --output-document=.omx/state/download.ts https://example.test/file`],
|
|
4009
|
+
['wget execute metadata log sink', `wget --no-config --no-hsts --execute='logfile=.omx/state/wget-execute.log' -O - https://example.test/file`],
|
|
4010
|
+
['curl literal hash output metadata sink', `curl -q -o .omx/state/curl-literal#hash.log https://example.test/file`],
|
|
4011
|
+
['curl write out metadata sink', `curl -q --write-out '%output{.omx/state/curl-write-out.log}' https://example.test/file`],
|
|
4012
|
+
['curl inline write out metadata sink', `curl -q --write-out='%output{.omx/state/curl-inline-write-out.log}' https://example.test/file`],
|
|
4013
|
+
] as const) {
|
|
4014
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
4015
|
+
requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }));
|
|
4016
|
+
}
|
|
4017
|
+
}
|
|
4018
|
+
|
|
4019
|
+
for (const [probe, command] of [
|
|
4020
|
+
['current runtime node absolute read', `${currentRuntimeNodePath} -e "require('fs').readFileSync('src/victim.ts','utf8')"`],
|
|
4021
|
+
['node fs readFileSync', `node -e "require('fs').readFileSync('src/victim.ts','utf8')"`],
|
|
4022
|
+
['node write mutation text', `node -e 'console.log("require(\\"fs\\").writeFileSync(\\"src/victim.ts\\", \\"x\\")")'`],
|
|
4023
|
+
['node ESM fs readFileSync', `node --input-type=module -e "import fs from 'node:fs';fs.readFileSync('src/victim.ts','utf8')"`],
|
|
4024
|
+
['node fs openSync read-only', `node -e "require('fs').openSync('src/victim.ts','r')"`],
|
|
4025
|
+
['node regex mutation text', `node -e 'console.log(/require\\("fs"\\)\\.rmSync\\("src\\/victim.ts"\\)/.test("x"))'`],
|
|
4026
|
+
['node static unrelated computed member', `node -e "console.log(module['filename'])"`],
|
|
4027
|
+
['node attached short read', `node -e"require('fs').readFileSync('src/victim.ts','utf8')"`],
|
|
4028
|
+
['node read-only path module', `node -e "console.log(require('path').join('src','victim.ts'))"`],
|
|
4029
|
+
['node array index', `node -e "const a=[1];console.log(a[0])"`],
|
|
4030
|
+
['node dynamic object read', `node -e "const o={x:1};const k='x';console.log(o[k])"`],
|
|
4031
|
+
['node Object.getPrototypeOf', `node -e "console.log(Object.getPrototypeOf({}))"`],
|
|
4032
|
+
['node Object computed getPrototypeOf', `node -e "Object['getPrototypeOf']({x:1})"`],
|
|
4033
|
+
['node object computed constructor', `node -e "const o={constructor:7};console.log(o['constructor'])"`],
|
|
4034
|
+
['node Reflect.get', `node -e "console.log(Reflect.get({x:1},'x'))"`],
|
|
4035
|
+
['python isolated read-only', `python3 -I -c "print('ok')"`],
|
|
4036
|
+
['wget spider no-body', 'wget --no-config --no-hsts --spider https://example.test/file'],
|
|
4037
|
+
['wget stdout short output document', 'wget --no-config --no-hsts -O - https://example.test/file'],
|
|
4038
|
+
['wget stdout long output document', 'wget --no-config --no-hsts --output-document=- https://example.test/file'],
|
|
4039
|
+
['wget stdout attached short output document', 'wget --no-config --no-hsts -O- https://example.test/file'],
|
|
4040
|
+
['wget repeated output last stdout', 'wget --no-config --no-hsts -O src/wget-first-output.ts https://example.test/file -O -'],
|
|
4041
|
+
['posix wget literal data', `printf '%s\n' 'POSIXLY_CORRECT wget -O src/not-executed.ts -O -'`],
|
|
4042
|
+
['posix url data before stdout wget', `printf '%s\n' 'https://example.test/POSIXLY_CORRECT'; wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4043
|
+
['function after wget no posix taint', `f(){ export POSIXLY_CORRECT=1; }; wget --no-config --no-hsts -O src/function-after.ts https://example.test/file -O -; f`],
|
|
4044
|
+
['subshell function no posix taint', `f(){ export POSIXLY_CORRECT=1; }; ( f ); wget --no-config --no-hsts -O src/function-subshell.ts https://example.test/file -O -`],
|
|
4045
|
+
['returned local posix control', `f(){ local -x POSIXLY_CORRECT=1; }; f; wget --no-config --no-hsts -O src/local-scope-control.ts https://example.test/file -O -`],
|
|
4046
|
+
['background posix control', `f(){ export POSIXLY_CORRECT=1; }; f & wait; wget --no-config --no-hsts -O src/background-control.ts https://example.test/file -O -`],
|
|
4047
|
+
['pipeline posix control', `f(){ export POSIXLY_CORRECT=1; }; f | cat; wget --no-config --no-hsts -O src/pipeline-control.ts https://example.test/file -O -`],
|
|
4048
|
+
['coproc posix control', `f(){ export POSIXLY_CORRECT=1; }; coproc f; wait; WGETRC=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4049
|
+
['nested subshell posix control', `f(){ export POSIXLY_CORRECT=1; }; if ( f ); then :; fi; wget --no-config --no-hsts -O src/nested-subshell-control.ts https://example.test/file -O -`],
|
|
4050
|
+
['wrapper only posix control', `env POSIXLY_CORRECT=1 true; wget --no-config --no-hsts -O src/wrapper-control.ts https://example.test/file -O -`],
|
|
4051
|
+
['wgetrc null control', `WGETRC=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4052
|
+
['home null control', `HOME=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4053
|
+
['case arm stdout control', `case true in true) wget --no-config --no-hsts -O - https://example.test/file;; esac`],
|
|
4054
|
+
['xargs empty eof read-only control', `printf '%s\n' safe | xargs -E '' printf`],
|
|
4055
|
+
['env chdir stdout control', `env -C src wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4056
|
+
['nonexported declare posix control', `declare POSIXLY_CORRECT=1; WGETRC=/dev/null wget --no-config --no-hsts -O src/declare-control.ts https://example.test/file -O -`],
|
|
4057
|
+
['persistent wgetrc null control', `export WGETRC=/dev/null; wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4058
|
+
['restored wgetrc control', `export WGETRC=/dev/stdin; unset WGETRC; HOME=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4059
|
+
['dynamic case subject stdout control', `mode=true; case "$mode" in true) wget --no-config --no-hsts -O - https://example.test/file;; esac`],
|
|
4060
|
+
['case arm pipeline read only', `case x in x) : | true;; esac`],
|
|
4061
|
+
['later optional case pattern stdout', `case x in a) :;; (x) wget --no-config --no-hsts -O - https://example.test/file;; esac`],
|
|
4062
|
+
['successful posix unset stdout', `export POSIXLY_CORRECT=1; unset POSIXLY_CORRECT; WGETRC=/dev/null wget --no-config --no-hsts -O src/unset-posix-first.ts https://example.test/file -O -`],
|
|
4063
|
+
['nested posix stdout', `POSIXLY_CORRECT=1 sh -c 'wget --no-config --no-hsts -O - https://example.test/file'`],
|
|
4064
|
+
['nested chdir stdout', `env --chdir=src sh -c 'wget --no-config --no-hsts -O - https://example.test/file'`],
|
|
4065
|
+
['nested lastpipe stdout', `bash -O lastpipe -c 'wget --no-config --no-hsts -O - https://example.test/file'`],
|
|
4066
|
+
['nested lastpipe pipeline stdout', `bash -O lastpipe -c 'printf safe | cat; wget --no-config --no-hsts -O - https://example.test/file'`],
|
|
4067
|
+
['process substitution static read only', `cat <(printf safe)`],
|
|
4068
|
+
['process substitution wget stdout', `cat <(wget --no-config --no-hsts -O - https://example.test/file)`],
|
|
4069
|
+
['background brace posix control', `f(){ export POSIXLY_CORRECT=1; }; { f; } & wait; WGETRC=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4070
|
+
['background function definition child control', `{ touch(){ :; }; touch src/background-child-shadowed.ts; } & wait`],
|
|
4071
|
+
['pipeline function definition child control', `{ touch(){ :; }; touch src/pipeline-child-shadowed.ts; } | cat`],
|
|
4072
|
+
['coproc function definition child control', `coproc { touch(){ :; }; touch src/coproc-child-shadowed.ts; }; wait`],
|
|
4073
|
+
['coproc inherited function runtime child control', `inherited_read(){ :; }; coproc { inherited_read; }; wait`],
|
|
4074
|
+
['subshell function definition child control', `( touch(){ :; }; touch src/subshell-child-shadowed.ts )`],
|
|
4075
|
+
['background function lifecycle child control', `touch(){ :; }; { unset -f touch; } & wait; touch src/background-lifecycle-shadowed.ts`],
|
|
4076
|
+
['pipeline function lifecycle child control', `touch(){ :; }; { unset -f touch; } | cat; touch src/pipeline-lifecycle-shadowed.ts`],
|
|
4077
|
+
['subshell function lifecycle child control', `touch(){ :; }; ( unset -f touch; ); touch src/subshell-lifecycle-shadowed.ts`],
|
|
4078
|
+
['nested isolation stdout control', `( export POSIXLY_CORRECT=1; ( wget --no-config --no-hsts -O - https://example.test/file ) )`],
|
|
4079
|
+
['function alternative null control', `f(){ export WGETRC=/dev/null; }; if false; then f(){ export WGETRC=/dev/null; }; fi; f; wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4080
|
+
['readonly function mode control', `export WGETRC=/dev/null; readonly -f WGETRC; HOME=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4081
|
+
['case fallthrough read only', `case x in x) : ;& true) printf safe;; esac`],
|
|
4082
|
+
['export n wgetrc control', `export WGETRC=/dev/stdin; export -n WGETRC; HOME=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4083
|
+
['nested export posix stdout', `export POSIXLY_CORRECT=1; sh -c 'wget --no-config --no-hsts -O - https://example.test/file'`],
|
|
4084
|
+
['bashopts lastpipe stdout', `env BASHOPTS=lastpipe bash -c 'true | printf safe; wget --no-config --no-hsts -O - https://example.test/file'`],
|
|
4085
|
+
['wget quiet cluster stdout', `wget --no-config --no-hsts -qO- https://example.test/file`],
|
|
4086
|
+
['case retest fallthrough read only', `case x in x) : ;;& x) printf safe;; esac`],
|
|
4087
|
+
['declare plus x wgetrc control', `export WGETRC=/dev/stdin; declare +x WGETRC; HOME=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4088
|
+
['declare function local stdout', `f(){ declare -x WGETRC=/dev/null; wget --no-config --no-hsts -O - https://example.test/file; }; f`],
|
|
4089
|
+
['parameter length expansion stdout control', `: \${#x}; wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4090
|
+
['parameter expansion stdout control', `: \${x:-safe}; wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4091
|
+
['nested clear environment stdout', `export POSIXLY_CORRECT=1; env -i sh -c 'wget --no-config --no-hsts -O - https://example.test/file'`],
|
|
4092
|
+
['parameter assignment expansion stdout', `export POSIXLY_CORRECT; : \${POSIXLY_CORRECT:=1}; wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4093
|
+
['nested exec clear environment stdout', `export POSIXLY_CORRECT=1; exec -c sh -c 'wget --no-config --no-hsts -O - https://example.test/file'`],
|
|
4094
|
+
['curl header stdout control', `curl -q --dump-header - https://example.test/file`],
|
|
4095
|
+
['unset wgetrc then append null control', `export WGETRC=/dev/stdin; unset WGETRC; WGETRC+=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4096
|
+
['process substitution nested read only', `cat <(cat <(printf safe))`],
|
|
4097
|
+
['sed read-only control', `sed -n '1,20p' src/runtime.ts`],
|
|
4098
|
+
['exported child function read only', `f(){ :; }; export -f f; bash -c 'f; wget --no-config --no-hsts -O - https://example.test/file'`],
|
|
4099
|
+
['cleared child function read only', `f(){ :; }; export -f f; env -i bash -c 'wget --no-config --no-hsts -O - https://example.test/file'`],
|
|
4100
|
+
['wgetrc unset control', `export WGETRC=/dev/null; unset WGETRC; HOME=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4101
|
+
['cdpath explicit relative control', `CDPATH=src; cd ./src; wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4102
|
+
['cdpath pushd popd control', `CDPATH=src; pushd ./src; popd; wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4103
|
+
['python isolated metadata cwd control', `cd .omx/state && python3 -I -c "print('ok')"`],
|
|
4104
|
+
['curl cluster stdout control', `curl -q -sD - -o - https://example.test/file`],
|
|
4105
|
+
['wget version control', `wget --no-config --no-hsts -V`],
|
|
4106
|
+
['wget value option stdout control', `wget --no-config --no-hsts -t 1 -O - https://example.test/file`],
|
|
4107
|
+
['wget help control', `wget --no-config --no-hsts --help`],
|
|
4108
|
+
['cdpath absolute control', `CDPATH=src; cd /tmp; wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4109
|
+
['function lastpipe option control', `export POSIXLY_CORRECT=1; f(){ shopt -s lastpipe; }; f; g(){ unset POSIXLY_CORRECT; }; true | g; wget --no-config --no-hsts -O src/function-lastpipe-first.ts https://example.test/file -O -`],
|
|
4110
|
+
|
|
4111
|
+
|
|
4112
|
+
['wget execute static nonsink stdout', `wget --no-config --no-hsts -e 'timeout=1' -O - https://example.test/file`],
|
|
4113
|
+
['wget bounded connect DNS read timeout control', `wget --no-config --no-hsts --connect-timeout=1 --dns-timeout=1 --read-timeout=1 -O - https://example.test/file`],
|
|
4114
|
+
['nested bash shopt lastpipe control', `bash -O lastpipe -c 'true | printf safe'`],
|
|
4115
|
+
['curl disabled startup stdout', `curl -q --write-out '%output{-}' https://example.test/file`],
|
|
4116
|
+
['curl short write out stdout', `curl -q -w '%output{-}' https://example.test/file`],
|
|
4117
|
+
['curl disable startup stdout', `curl --disable --write-out '%output{-}' https://example.test/file`],
|
|
4118
|
+
['curl inline write out stdout', `curl -q --write-out='%output{-}' https://example.test/file`],
|
|
4119
|
+
['static benign export control', `export GJC_SESSION_ID; cat src/conductor-owned.ts`],
|
|
4120
|
+
['curl SSL key log unset control', `SSLKEYLOGFILE=src/curl-tls.keys env -u SSLKEYLOGFILE curl -q -o - https://example.test/file`],
|
|
4121
|
+
['wget SSL key log unset control', `SSLKEYLOGFILE=src/wget-tls.keys env -u SSLKEYLOGFILE wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4122
|
+
['exported readonly child function control', `f(){ :; }; declare -frx f; bash -c 'f; wget --no-config --no-hsts -O - https://example.test/file'`],
|
|
4123
|
+
['exported redefined child function control', `f(){ :; }; export -f f; f(){ :; }; bash -c 'f; wget --no-config --no-hsts -O - https://example.test/file'`],
|
|
4124
|
+
['curl static head stdout', `curl -q --request HEAD https://example.test/file`],
|
|
4125
|
+
['wget no proxy standalone stdout control', `wget --no-config --no-hsts --no-proxy -O - https://example.test/file`],
|
|
4126
|
+
['wget quiet standalone stdout control', `wget --no-config --no-hsts -q -O - https://example.test/file`],
|
|
4127
|
+
['wget short quiet standalone stdout control', `wget --no-config --no-hsts -qO- https://example.test/file`],
|
|
4128
|
+
['wget static head stdout', `wget --no-config --no-hsts --method=HEAD -O - https://example.test/file`],
|
|
4129
|
+
['node output clear environment control', `NODE_V8_COVERAGE=src env -i node -e "console.log('ok')"`],
|
|
4130
|
+
['node persistent output clear environment control', `export NODE_V8_COVERAGE=src; env -i node -e "console.log('ok')"`],
|
|
4131
|
+
['path inherited system control', `cat src/conductor-owned.ts`],
|
|
4132
|
+
['absolute system path control', `/usr/bin/cat src/conductor-owned.ts`],
|
|
4133
|
+
['loader clear environment control', `env -i /usr/bin/cat src/conductor-owned.ts`],
|
|
4134
|
+
['loader unrelated declaration control', `export WGETRC=/dev/stdin; declare +x WGETRC; HOME=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
|
|
4135
|
+
['sort numeric read-only', `sort -n README.md`],
|
|
4136
|
+
['isolated bash login read-only', `bash --noprofile --norc -lc "printf safe"`],
|
|
4137
|
+
] as const) {
|
|
4138
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
4139
|
+
const output = runActorProbe(
|
|
4140
|
+
actor,
|
|
4141
|
+
probe,
|
|
4142
|
+
'Bash',
|
|
4143
|
+
{ command },
|
|
4144
|
+
);
|
|
4145
|
+
if (Object.keys(output).length !== 0) {
|
|
4146
|
+
throw new Error(`native hook blocked semantic Node read-only operation: ${actor} ${probe}: ${JSON.stringify(output)}`);
|
|
4147
|
+
}
|
|
4148
|
+
}
|
|
4149
|
+
}
|
|
4150
|
+
for (const [probe, command] of [
|
|
4151
|
+
['chmod reference separated metadata mutation', `chmod --reference .omx/state/reference-copy .omx/state/reference-copy`],
|
|
4152
|
+
['chown reference metadata mutation', `chown --reference=.omx/state/reference-copy .omx/state/reference-copy`],
|
|
4153
|
+
['chgrp reference metadata mutation', `chgrp --reference=.omx/state/reference-copy .omx/state/reference-copy`],
|
|
4154
|
+
['rsync metadata mutation', `rsync .omx/state/conductor-ledger.json .omx/state/inbox/rsync-copy`],
|
|
4155
|
+
['rsync product source metadata mutation', `rsync README.md .omx/state/inbox/readme.md`],
|
|
4156
|
+
['cdpath rsync metadata mutation', `CDPATH=.omx; cd state; rsync --log-file=rsync-cdpath.log conductor-ledger.json inbox/rsync-cdpath-copy`],
|
|
4157
|
+
['pushd cdpath rsync metadata mutation', `CDPATH=.omx; pushd state; rsync --log-file=rsync-pushd.log conductor-ledger.json inbox/rsync-pushd-copy`],
|
|
4158
|
+
['cdpath prefix cd metadata mutation', `CDPATH=src cd state; rsync --log-file=../../.omx/state/rsync-prefix-cdpath.log ../../.omx/state/conductor-ledger.json ../../.omx/state/inbox/rsync-prefix-cdpath-copy`],
|
|
4159
|
+
['cdpath local function rsync mutation', `CDPATH=.omx; f(){ local CDPATH=src; cd shared; cd ../..; }; f; cd state; rsync --log-file=rsync-local-cdpath.log conductor-ledger.json inbox/rsync-local-cdpath-copy`],
|
|
4160
|
+
['cdpath nested local function rsync mutation', `CDPATH=.omx; f(){ local CDPATH=src; g(){ local CDPATH=.omx; cd state; cd ../..; }; g; cd shared; cd ../..; }; f; cd state; rsync --log-file=rsync-nested-local-cdpath.log conductor-ledger.json inbox/rsync-nested-local-cdpath-copy`],
|
|
4161
|
+
] as const) {
|
|
4162
|
+
const mainRootOutput = runActorProbe('main-root', probe, 'Bash', { command });
|
|
4163
|
+
if (Object.keys(mainRootOutput).length !== 0) {
|
|
4164
|
+
throw new Error(`packed main-root metadata mutation should remain allowed: ${probe}: ${JSON.stringify(mainRootOutput)}`);
|
|
4165
|
+
}
|
|
4166
|
+
requireActorDeny('native-child', probe, runActorProbe('native-child', probe, 'Bash', { command }));
|
|
4167
|
+
}
|
|
4168
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
4169
|
+
const output = runActorProbe(
|
|
4170
|
+
actor,
|
|
4171
|
+
'current runtime node bare read',
|
|
4172
|
+
'Bash',
|
|
4173
|
+
{ command: `node -e "require('fs').readFileSync('src/victim.ts','utf8')"` },
|
|
4174
|
+
currentRuntimeNodePathEnvironment,
|
|
4175
|
+
);
|
|
4176
|
+
if (Object.keys(output).length !== 0) {
|
|
4177
|
+
throw new Error(`packed ${actor} current runtime bare node should retain read-only allowance`);
|
|
4178
|
+
}
|
|
4179
|
+
}
|
|
4180
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
4181
|
+
requireActorDeny(
|
|
4182
|
+
actor,
|
|
4183
|
+
'current runtime node loader',
|
|
4184
|
+
runActorProbe(
|
|
4185
|
+
actor,
|
|
4186
|
+
'current runtime node loader',
|
|
4187
|
+
'Bash',
|
|
4188
|
+
{ command: `LD_PRELOAD=.omx/state/mutator.so ${currentRuntimeNodePath} -e "require('fs').readFileSync('src/victim.ts','utf8')"` },
|
|
4189
|
+
),
|
|
4190
|
+
);
|
|
4191
|
+
}
|
|
4192
|
+
for (const [probe, command] of [
|
|
4193
|
+
['nested chdir metadata', `env --chdir=src sh -c 'touch ../.omx/state/final.log'`],
|
|
4194
|
+
['touch mode metadata', `touch .omx/state/final.log -m`],
|
|
4195
|
+
['time output metadata', `time --output=.omx/state/time-output.log printf safe`],
|
|
4196
|
+
] as const) {
|
|
4197
|
+
if (Object.keys(runActorProbe('main-root', `${probe} main`, 'Bash', { command })).length !== 0) {
|
|
4198
|
+
throw new Error(`packed main-root ${probe} should retain metadata allowance`);
|
|
4199
|
+
}
|
|
4200
|
+
requireActorDeny('native-child', `${probe} native child`, runActorProbe('native-child', `${probe} native child`, 'Bash', { command }));
|
|
4201
|
+
}
|
|
4202
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
4203
|
+
requireActorDeny(actor, 'curl static glob output template', runActorProbe(actor, 'curl static glob output template', 'Bash', {
|
|
4204
|
+
command: `curl -q -o .omx/state/curl-glob-#1.log 'https://example.test/file[1-2]'`,
|
|
4205
|
+
}));
|
|
4206
|
+
}
|
|
4207
|
+
const curlBraceTemplateCommand = `curl -q -o .omx/state/curl-brace-#2.log 'https://example.test/{one,two}[1-2]'`;
|
|
4208
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
4209
|
+
requireActorDeny(actor, 'curl brace glob output template', runActorProbe(actor, 'curl brace glob output template', 'Bash', { command: curlBraceTemplateCommand }));
|
|
4210
|
+
}
|
|
4211
|
+
|
|
4212
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
4213
|
+
requireActorDeny(actor, 'unknown transport', runActorProbe(actor, 'unknown transport', 'mcp__example__future_mutation', {
|
|
4214
|
+
target: 'src/packed-unknown.ts',
|
|
4215
|
+
}));
|
|
4216
|
+
}
|
|
4217
|
+
for (const [probe, toolName] of [
|
|
4218
|
+
['wiki ingest', 'mcp__omx_wiki__wiki_ingest'],
|
|
4219
|
+
['project memory write', 'mcp__omx_memory__project_memory_write'],
|
|
4220
|
+
] as const) {
|
|
4221
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
4222
|
+
requireActorDeny(actor, probe, runActorProbe(actor, probe, toolName, { content: 'mutation' }));
|
|
4223
|
+
}
|
|
4224
|
+
}
|
|
4225
|
+
for (const [probe, toolName, toolInput] of [
|
|
4226
|
+
['trace summary', 'mcp__omx_trace__trace_summary', { workingDirectory: smokeCwd }],
|
|
4227
|
+
['LSP diagnostics', 'mcp__omx_code_intel__lsp_diagnostics', { file: 'src/runtime.ts' }],
|
|
4228
|
+
['wiki query', 'mcp__omx_wiki__wiki_query', { query: 'native hook', workingDirectory: smokeCwd }],
|
|
4229
|
+
['project memory read', 'mcp__omx_memory__project_memory_read', { workingDirectory: smokeCwd }],
|
|
4230
|
+
['notepad stats', 'mcp__omx_memory__notepad_stats', { workingDirectory: smokeCwd }],
|
|
4231
|
+
] as const) {
|
|
4232
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
4233
|
+
const output = runActorProbe(actor, probe, toolName, toolInput);
|
|
4234
|
+
if (Object.keys(output).length !== 0) {
|
|
4235
|
+
throw new Error(`native hook blocked audited read-only MCP operation: ${actor} ${probe}`);
|
|
4236
|
+
}
|
|
4237
|
+
}
|
|
4238
|
+
}
|
|
4239
|
+
|
|
4240
|
+
writeFileSync(join(stateDir, 'sessions', sessionId, 'skill-active-state.json'), JSON.stringify({
|
|
4241
|
+
active: true,
|
|
4242
|
+
skill: 'ralplan',
|
|
4243
|
+
phase: 'planning',
|
|
4244
|
+
session_id: sessionId,
|
|
4245
|
+
active_skills: [{ skill: 'ralplan', phase: 'planning', active: true, session_id: sessionId }],
|
|
4246
|
+
}));
|
|
4247
|
+
writeFileSync(join(stateDir, 'sessions', sessionId, 'ralplan-state.json'), JSON.stringify({
|
|
4248
|
+
active: true,
|
|
4249
|
+
mode: 'ralplan',
|
|
4250
|
+
current_phase: 'planning',
|
|
4251
|
+
session_id: sessionId,
|
|
4252
|
+
}));
|
|
4253
|
+
mkdirSync(join(stateDir, 'sessions', leaderAgentId), { recursive: true });
|
|
4254
|
+
writeFileSync(join(stateDir, 'sessions', leaderAgentId, 'skill-active-state.json'), JSON.stringify({
|
|
4255
|
+
active: true,
|
|
4256
|
+
skill: 'ralplan',
|
|
4257
|
+
phase: 'planning',
|
|
4258
|
+
session_id: leaderAgentId,
|
|
4259
|
+
active_skills: [{ skill: 'ralplan', phase: 'planning', active: true, session_id: leaderAgentId }],
|
|
4260
|
+
}));
|
|
4261
|
+
writeFileSync(join(stateDir, 'sessions', leaderAgentId, 'ralplan-state.json'), JSON.stringify({
|
|
4262
|
+
active: true,
|
|
4263
|
+
mode: 'ralplan',
|
|
4264
|
+
current_phase: 'planning',
|
|
4265
|
+
session_id: leaderAgentId,
|
|
4266
|
+
}));
|
|
4267
|
+
requireActorDeny('native-child', 'direct planning artifact write', runActorProbe(
|
|
4268
|
+
'native-child',
|
|
4269
|
+
'direct planning artifact write',
|
|
4270
|
+
'Write',
|
|
4271
|
+
{ file_path: '.omx/plans/native-child.md', content: '# unauthorized\n' },
|
|
4272
|
+
));
|
|
4273
|
+
requireActorDeny('native-child', 'sessionless planning state write', runActorProbe(
|
|
4274
|
+
'native-child',
|
|
4275
|
+
'sessionless planning state write',
|
|
4276
|
+
'mcp__omx_state__state_write',
|
|
4277
|
+
{ mode: 'ralplan', active: true, current_phase: 'planning' },
|
|
4278
|
+
), /PROVENANCE_DENIED/);
|
|
4279
|
+
requireActorDeny('native-child', 'foreign planning state write', runActorProbe(
|
|
4280
|
+
'native-child',
|
|
4281
|
+
'foreign planning state write',
|
|
4282
|
+
'mcp__omx_state__state_write',
|
|
4283
|
+
{ mode: 'ralplan', active: false, current_phase: 'complete', session_id: 'foreign-session' },
|
|
4284
|
+
), /PROVENANCE_DENIED/);
|
|
4285
|
+
symlinkSync(join(smokeCwd, 'src'), join(smokeCwd, '.omx', 'drafts'));
|
|
4286
|
+
for (const actor of ['main-root', 'native-child'] as const) {
|
|
4287
|
+
const output = runActorProbe(actor, 'linked planning artifact redirect', 'Bash', {
|
|
4288
|
+
command: 'printf owned > .omx/drafts/linked-plan.md',
|
|
4289
|
+
});
|
|
4290
|
+
if ((output.hookSpecificOutput as { permissionDecision?: string } | undefined)?.permissionDecision !== 'deny') {
|
|
4291
|
+
throw new Error(`packed ${actor} linked planning artifact redirect should be denied`);
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
writeFileSync(join(stateDir, 'sessions', sessionId, 'skill-active-state.json'), JSON.stringify({
|
|
4295
|
+
active: true,
|
|
4296
|
+
skill: 'ralph',
|
|
4297
|
+
phase: 'starting',
|
|
4298
|
+
session_id: sessionId,
|
|
4299
|
+
active_skills: [{ skill: 'ralph', phase: 'starting', active: true, session_id: sessionId }],
|
|
4300
|
+
}));
|
|
4301
|
+
writeFileSync(join(stateDir, 'sessions', sessionId, 'ralph-state.json'), JSON.stringify({
|
|
4302
|
+
active: true,
|
|
4303
|
+
mode: 'ralph',
|
|
4304
|
+
current_phase: 'starting',
|
|
4305
|
+
session_id: sessionId,
|
|
4306
|
+
}));
|
|
4307
|
+
requireActorDeny('native-child', 'Ralph starting write', runActorProbe('native-child', 'Ralph starting write', 'Write', {
|
|
4308
|
+
file_path: 'src/ralph-starting-bypass.ts',
|
|
4309
|
+
content: 'owned\n',
|
|
4310
|
+
}));
|
|
4311
|
+
const childReadOutput = runActorProbe('native-child', 'read-only', 'Read', { file_path: 'src/packed-read-only.ts' });
|
|
4312
|
+
if (Object.keys(childReadOutput).length !== 0) {
|
|
4313
|
+
throw new Error('native hook blocked a positively classified native-child read-only operation');
|
|
4314
|
+
}
|
|
4315
|
+
|
|
4316
|
+
// #3194: the installed Codex 0.144.5 documented surface fails closed before
|
|
4317
|
+
// authority state or an adapted role intent can be created.
|
|
4318
|
+
const roleIntentCwd = join(smokeCwd, 'issue-3194-role-intent');
|
|
4319
|
+
const roleIntentHome = join(roleIntentCwd, 'home');
|
|
4320
|
+
const roleIntentCodexHome = join(roleIntentCwd, 'codex-home');
|
|
4321
|
+
const roleIntentNativeSessionId = 'synthetic-3194-session';
|
|
4322
|
+
mkdirSync(roleIntentHome, { recursive: true });
|
|
4323
|
+
mkdirSync(roleIntentCodexHome, { recursive: true });
|
|
4324
|
+
const roleIntentEnvironment = {
|
|
4325
|
+
...buildPackedRegressionEnvironment({ name: 'issue-3194-role-intent' }),
|
|
4326
|
+
HOME: roleIntentHome,
|
|
4327
|
+
CODEX_HOME: roleIntentCodexHome,
|
|
4328
|
+
};
|
|
4329
|
+
const sessionStartResult = run(process.execPath, [realpathSync(hookScript)], {
|
|
4330
|
+
cwd: roleIntentCwd,
|
|
4331
|
+
env: roleIntentEnvironment,
|
|
4332
|
+
input: JSON.stringify({ hook_event_name: 'SessionStart', cwd: roleIntentCwd, session_id: roleIntentNativeSessionId }),
|
|
4333
|
+
});
|
|
4334
|
+
validateHookStdout('SessionStart', String(sessionStartResult.stdout || ''));
|
|
4335
|
+
const trackingPath = join(roleIntentCwd, '.omx', 'state', 'subagent-tracking.json');
|
|
4336
|
+
if (existsSync(trackingPath)) throw new Error('installed #3194 SessionStart unexpectedly created tracker authority');
|
|
4337
|
+
|
|
4338
|
+
const preToolUseResult = run(process.execPath, [realpathSync(hookScript)], {
|
|
4339
|
+
cwd: roleIntentCwd,
|
|
4340
|
+
env: roleIntentEnvironment,
|
|
4341
|
+
input: JSON.stringify({
|
|
4342
|
+
hook_event_name: 'PreToolUse',
|
|
4343
|
+
cwd: roleIntentCwd,
|
|
4344
|
+
session_id: roleIntentNativeSessionId,
|
|
4345
|
+
turn_id: 'synthetic-3194-turn',
|
|
4346
|
+
tool_name: 'Bash',
|
|
4347
|
+
tool_use_id: 'synthetic-3194-tool',
|
|
4348
|
+
tool_input: { command: 'omx ralplan role-intent write --role architect --parent-thread "$CODEX_THREAD_ID" --json' },
|
|
4349
|
+
}),
|
|
4350
|
+
});
|
|
4351
|
+
const preToolUseExpected = '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"unsupported_documented_leader_proof: Codex 0.144.5 hooks do not expose documented root identity required for adapted Ralplan."}}\n';
|
|
4352
|
+
if (String(preToolUseResult.stdout || '') !== preToolUseExpected) {
|
|
4353
|
+
throw new Error('installed #3194 PreToolUse did not emit the exact unsupported denial');
|
|
4354
|
+
}
|
|
4355
|
+
if (existsSync(trackingPath)) throw new Error('installed #3194 PreToolUse unexpectedly created tracker authority');
|
|
4356
|
+
|
|
4357
|
+
const roleIntentCliResult = spawnSync(process.execPath, [realpathSync(join(packageRoot, 'dist', 'cli', 'omx.js')), 'ralplan', 'role-intent', 'write', '--role', 'architect', '--parent-thread', roleIntentNativeSessionId, '--json'], {
|
|
4358
|
+
cwd: roleIntentCwd,
|
|
4359
|
+
env: roleIntentEnvironment,
|
|
4360
|
+
encoding: 'utf-8',
|
|
4361
|
+
stdio: 'pipe',
|
|
4362
|
+
});
|
|
4363
|
+
if (roleIntentCliResult.status === 0) throw new Error('installed #3194 role-intent CLI unexpectedly authorized an Architect receipt');
|
|
4364
|
+
if (String(roleIntentCliResult.stdout || '') !== '{"ok":false,"reason":"unsupported_documented_leader_proof"}\n') {
|
|
4365
|
+
throw new Error('installed #3194 role-intent CLI returned an unexpected denial');
|
|
4366
|
+
}
|
|
4367
|
+
if (existsSync(trackingPath)) throw new Error('installed #3194 role-intent CLI unexpectedly created tracker authority');
|
|
1463
4368
|
|
|
1464
4369
|
for (const [caseIndex, testCase] of PACKED_INSTALL_NATIVE_HOOK_REGRESSION_PROMPTS.entries()) {
|
|
1465
4370
|
const caseCwd = join(smokeCwd, testCase.name);
|
|
@@ -2033,13 +4938,265 @@ export async function smokePackedHookTrustLifecycle(
|
|
|
2033
4938
|
}
|
|
2034
4939
|
}
|
|
2035
4940
|
|
|
2036
|
-
|
|
4941
|
+
function assertBoundedProbeExit(
|
|
4942
|
+
label: string,
|
|
4943
|
+
result: ReturnType<typeof spawnSync>,
|
|
4944
|
+
expectedStatus: number,
|
|
4945
|
+
): void {
|
|
4946
|
+
if ((result.error as NodeJS.ErrnoException | undefined)?.code === 'ETIMEDOUT') {
|
|
4947
|
+
throw new Error(`${label} exceeded ${PACKED_INSTALL_OPERATIONAL_PROBE_TIMEOUT_MS}ms`);
|
|
4948
|
+
}
|
|
4949
|
+
if (result.status !== expectedStatus) {
|
|
4950
|
+
const detail = String(result.stderr ?? result.error?.message ?? '').trim();
|
|
4951
|
+
throw new Error(`${label} must exit ${expectedStatus}, received ${String(result.status)}${detail ? `: ${detail}` : ''}`);
|
|
4952
|
+
}
|
|
4953
|
+
}
|
|
4954
|
+
|
|
4955
|
+
interface PackedPluginHookDelegateCall {
|
|
4956
|
+
argv: string[];
|
|
4957
|
+
stdin: string;
|
|
4958
|
+
cwd: string;
|
|
4959
|
+
}
|
|
4960
|
+
|
|
4961
|
+
function readPackedPluginHookDelegateCalls(capturePath: string): PackedPluginHookDelegateCall[] {
|
|
4962
|
+
const capture = existsSync(capturePath) ? readFileSync(capturePath, 'utf-8').trim() : '';
|
|
4963
|
+
if (!capture) return [];
|
|
4964
|
+
return capture.split('\n').map((line) => {
|
|
4965
|
+
const record: unknown = JSON.parse(line);
|
|
4966
|
+
if (!isJsonRecord(record)
|
|
4967
|
+
|| !Array.isArray(record.argv)
|
|
4968
|
+
|| record.argv.some((arg) => typeof arg !== 'string')
|
|
4969
|
+
|| typeof record.stdin !== 'string'
|
|
4970
|
+
|| typeof record.cwd !== 'string') {
|
|
4971
|
+
throw new Error('pinned fake plugin hook delegate did not record a valid invocation');
|
|
4972
|
+
}
|
|
4973
|
+
return { argv: record.argv, stdin: record.stdin, cwd: record.cwd };
|
|
4974
|
+
});
|
|
4975
|
+
}
|
|
4976
|
+
|
|
4977
|
+
function smokeInstalledPluginHookLauncher(packageRoot: string, omxPath: string): void {
|
|
4978
|
+
const smokeRoot = mkdtempSync(join(tmpdir(), 'omx-packed-plugin-hook-smoke-'));
|
|
4979
|
+
const hookDir = join(packageRoot, 'plugins', 'oh-my-codex', 'hooks');
|
|
4980
|
+
const hookLauncherPath = join(hookDir, 'codex-native-hook.mjs');
|
|
4981
|
+
const pinnedLauncherPath = join(hookDir, 'omx-command.json');
|
|
4982
|
+
const originalPinnedLauncher = existsSync(pinnedLauncherPath) ? readFileSync(pinnedLauncherPath) : undefined;
|
|
4983
|
+
const delegatePath = join(smokeRoot, 'pinned-fake-delegate.cjs');
|
|
4984
|
+
const capturePath = join(smokeRoot, 'pinned-fake-delegate.jsonl');
|
|
4985
|
+
const hookCwd = join(smokeRoot, 'cwd');
|
|
4986
|
+
const home = join(smokeRoot, 'home');
|
|
4987
|
+
const codexHome = join(smokeRoot, 'codex-home');
|
|
4988
|
+
const sessionId = 'packed-plugin-hook-session';
|
|
4989
|
+
try {
|
|
4990
|
+
mkdirSync(hookCwd, { recursive: true });
|
|
4991
|
+
mkdirSync(home, { recursive: true });
|
|
4992
|
+
mkdirSync(codexHome, { recursive: true });
|
|
4993
|
+
writeFileSync(delegatePath, [
|
|
4994
|
+
'const { appendFileSync } = require("node:fs");',
|
|
4995
|
+
'(async () => {',
|
|
4996
|
+
'const chunks = [];',
|
|
4997
|
+
'for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));',
|
|
4998
|
+
'const stdin = Buffer.concat(chunks).toString("utf8");',
|
|
4999
|
+
'const payload = JSON.parse(stdin);',
|
|
5000
|
+
'appendFileSync(process.env.OMX_PACKED_PLUGIN_HOOK_CAPTURE_PATH, `${JSON.stringify({ argv: process.argv.slice(2), stdin, cwd: process.cwd() })}\\n`);',
|
|
5001
|
+
'if (payload.delegate_mode === "non-stop-failure") {',
|
|
5002
|
+
' process.stdout.write("delegate-failure\\n");',
|
|
5003
|
+
' process.exitCode = 23;',
|
|
5004
|
+
'} else if (payload.delegate_mode === "stop-failure") {',
|
|
5005
|
+
' process.exitCode = 23;',
|
|
5006
|
+
'} else if (payload.hook_event_name === "Stop") {',
|
|
5007
|
+
' process.stdout.write("{\\\"decision\\\":\\\"block\\\",\\\"reason\\\":\\\"pinned stop delegate\\\"}\\n");',
|
|
5008
|
+
'} else {',
|
|
5009
|
+
' process.stdout.write("{\\\"hookSpecificOutput\\\":{\\\"additionalContext\\\":\\\"pinned non-stop delegate\\\"}}\\n");',
|
|
5010
|
+
'}',
|
|
5011
|
+
'})().catch((error) => { console.error(error); process.exitCode = 1; });',
|
|
5012
|
+
'',
|
|
5013
|
+
].join('\n'));
|
|
5014
|
+
writeFileSync(pinnedLauncherPath, JSON.stringify({
|
|
5015
|
+
command: process.execPath,
|
|
5016
|
+
argsPrefix: [delegatePath],
|
|
5017
|
+
}));
|
|
5018
|
+
|
|
5019
|
+
const env = buildPackedProbeEnv({
|
|
5020
|
+
HOME: home,
|
|
5021
|
+
USERPROFILE: home,
|
|
5022
|
+
CODEX_HOME: codexHome,
|
|
5023
|
+
OMX_AUTO_UPDATE: '0',
|
|
5024
|
+
OMX_NOTIFY_FALLBACK: '0',
|
|
5025
|
+
OMX_HOOK_DERIVED_SIGNALS: '0',
|
|
5026
|
+
OMX_ROOT: join(smokeRoot, '.omx-root'),
|
|
5027
|
+
OMX_STATE_ROOT: '',
|
|
5028
|
+
OMX_TEAM_STATE_ROOT: '',
|
|
5029
|
+
OMX_TEAM_WORKER: '',
|
|
5030
|
+
OMX_TEAM_WORKER_LAUNCH_ARGS: '',
|
|
5031
|
+
OMX_NOTIFY_TEMP_CONTRACT: '',
|
|
5032
|
+
OMX_ENTRY_PATH: omxPath,
|
|
5033
|
+
OMX_CODEX_LAUNCH_ID: 'packed-plugin-hook-launch',
|
|
5034
|
+
OMX_PACKED_PLUGIN_HOOK_CAPTURE_PATH: capturePath,
|
|
5035
|
+
});
|
|
5036
|
+
const runProbe = (
|
|
5037
|
+
name: string,
|
|
5038
|
+
payload: Record<string, unknown>,
|
|
5039
|
+
expectedStatus: number,
|
|
5040
|
+
expectedStdout: string,
|
|
5041
|
+
): void => {
|
|
5042
|
+
writeFileSync(capturePath, '');
|
|
5043
|
+
const stdin = JSON.stringify(payload);
|
|
5044
|
+
const result = spawnSync(process.execPath, [hookLauncherPath], {
|
|
5045
|
+
cwd: hookCwd,
|
|
5046
|
+
encoding: 'utf-8',
|
|
5047
|
+
env,
|
|
5048
|
+
input: stdin,
|
|
5049
|
+
timeout: PACKED_INSTALL_OPERATIONAL_PROBE_TIMEOUT_MS,
|
|
5050
|
+
killSignal: 'SIGKILL',
|
|
5051
|
+
});
|
|
5052
|
+
assertBoundedProbeExit(`installed plugin hook ${name}`, result, expectedStatus);
|
|
5053
|
+
if (String(result.stdout ?? '') !== expectedStdout) {
|
|
5054
|
+
throw new Error(`installed plugin hook ${name} stdout changed: expected ${JSON.stringify(expectedStdout)}, received ${JSON.stringify(String(result.stdout ?? ''))}`);
|
|
5055
|
+
}
|
|
5056
|
+
assert.deepStrictEqual(readPackedPluginHookDelegateCalls(capturePath), [{
|
|
5057
|
+
argv: ['codex-native-hook'],
|
|
5058
|
+
stdin,
|
|
5059
|
+
cwd: hookCwd,
|
|
5060
|
+
}], `installed plugin hook ${name} must forward exact delegate argv and stdin`);
|
|
5061
|
+
};
|
|
5062
|
+
|
|
5063
|
+
runProbe('non-Stop', {
|
|
5064
|
+
hook_event_name: 'UserPromptSubmit',
|
|
5065
|
+
session_id: sessionId,
|
|
5066
|
+
cwd: hookCwd,
|
|
5067
|
+
prompt: 'packed plugin non-Stop probe',
|
|
5068
|
+
}, 0, '{"hookSpecificOutput":{"additionalContext":"pinned non-stop delegate"}}\n');
|
|
5069
|
+
runProbe('Stop', {
|
|
5070
|
+
hook_event_name: 'Stop',
|
|
5071
|
+
session_id: sessionId,
|
|
5072
|
+
cwd: hookCwd,
|
|
5073
|
+
}, 0, '{"decision":"block","reason":"pinned stop delegate"}\n');
|
|
5074
|
+
runProbe('non-Stop delegate failure', {
|
|
5075
|
+
hook_event_name: 'UserPromptSubmit',
|
|
5076
|
+
session_id: sessionId,
|
|
5077
|
+
cwd: hookCwd,
|
|
5078
|
+
delegate_mode: 'non-stop-failure',
|
|
5079
|
+
}, 23, 'delegate-failure\n');
|
|
5080
|
+
|
|
5081
|
+
writeFileSync(capturePath, '');
|
|
5082
|
+
const failedStopInput = JSON.stringify({
|
|
5083
|
+
hook_event_name: 'Stop',
|
|
5084
|
+
session_id: sessionId,
|
|
5085
|
+
cwd: hookCwd,
|
|
5086
|
+
delegate_mode: 'stop-failure',
|
|
5087
|
+
});
|
|
5088
|
+
const failedStop = spawnSync(process.execPath, [hookLauncherPath], {
|
|
5089
|
+
cwd: hookCwd,
|
|
5090
|
+
encoding: 'utf-8',
|
|
5091
|
+
env,
|
|
5092
|
+
input: failedStopInput,
|
|
5093
|
+
timeout: PACKED_INSTALL_OPERATIONAL_PROBE_TIMEOUT_MS,
|
|
5094
|
+
killSignal: 'SIGKILL',
|
|
5095
|
+
});
|
|
5096
|
+
assertBoundedProbeExit('installed plugin hook Stop delegate failure', failedStop, 0);
|
|
5097
|
+
const failedStopOutput: unknown = JSON.parse(String(failedStop.stdout ?? ''));
|
|
5098
|
+
if (!isJsonRecord(failedStopOutput)) {
|
|
5099
|
+
throw new Error('installed plugin hook Stop delegate failure must emit JSON fallback output');
|
|
5100
|
+
}
|
|
5101
|
+
assert.deepStrictEqual({
|
|
5102
|
+
decision: failedStopOutput.decision,
|
|
5103
|
+
stopReason: failedStopOutput.stopReason,
|
|
5104
|
+
}, {
|
|
5105
|
+
decision: 'block',
|
|
5106
|
+
stopReason: 'plugin_stop_hook_launcher_exit',
|
|
5107
|
+
});
|
|
5108
|
+
assertTextMatches(
|
|
5109
|
+
String(failedStopOutput.systemMessage ?? ''),
|
|
5110
|
+
/codex-native-hook exited with code 23/,
|
|
5111
|
+
'installed plugin hook Stop delegate failure',
|
|
5112
|
+
);
|
|
5113
|
+
assert.deepStrictEqual(readPackedPluginHookDelegateCalls(capturePath), [{
|
|
5114
|
+
argv: ['codex-native-hook'],
|
|
5115
|
+
stdin: failedStopInput,
|
|
5116
|
+
cwd: hookCwd,
|
|
5117
|
+
}], 'installed plugin hook Stop failure must still delegate exact argv and stdin');
|
|
5118
|
+
} finally {
|
|
5119
|
+
if (originalPinnedLauncher === undefined) {
|
|
5120
|
+
rmSync(pinnedLauncherPath, { force: true });
|
|
5121
|
+
} else {
|
|
5122
|
+
writeFileSync(pinnedLauncherPath, originalPinnedLauncher);
|
|
5123
|
+
}
|
|
5124
|
+
rmSync(smokeRoot, { recursive: true, force: true });
|
|
5125
|
+
}
|
|
5126
|
+
}
|
|
5127
|
+
|
|
5128
|
+
function smokeInstalledMcpTargets(omxPath: string): void {
|
|
5129
|
+
const smokeRoot = mkdtempSync(join(tmpdir(), 'omx-packed-mcp-smoke-'));
|
|
5130
|
+
const home = join(smokeRoot, 'home');
|
|
5131
|
+
const codexHome = join(smokeRoot, 'codex-home');
|
|
5132
|
+
const mcpInitialize = `${JSON.stringify({
|
|
5133
|
+
jsonrpc: '2.0',
|
|
5134
|
+
id: 1,
|
|
5135
|
+
method: 'initialize',
|
|
5136
|
+
params: {
|
|
5137
|
+
protocolVersion: '2024-11-05',
|
|
5138
|
+
capabilities: {},
|
|
5139
|
+
clientInfo: { name: 'packed-install-smoke', version: '1' },
|
|
5140
|
+
},
|
|
5141
|
+
})}\n`;
|
|
5142
|
+
try {
|
|
5143
|
+
mkdirSync(home, { recursive: true });
|
|
5144
|
+
mkdirSync(codexHome, { recursive: true });
|
|
5145
|
+
const env = buildPackedProbeEnv({
|
|
5146
|
+
HOME: home,
|
|
5147
|
+
USERPROFILE: home,
|
|
5148
|
+
CODEX_HOME: codexHome,
|
|
5149
|
+
OMX_AUTO_UPDATE: '0',
|
|
5150
|
+
OMX_NOTIFY_FALLBACK: '0',
|
|
5151
|
+
OMX_HOOK_DERIVED_SIGNALS: '0',
|
|
5152
|
+
OMX_ROOT: join(smokeRoot, '.omx-root'),
|
|
5153
|
+
OMX_STATE_ROOT: '',
|
|
5154
|
+
OMX_TEAM_STATE_ROOT: '',
|
|
5155
|
+
OMX_TEAM_WORKER: '',
|
|
5156
|
+
OMX_TEAM_WORKER_LAUNCH_ARGS: '',
|
|
5157
|
+
OMX_NOTIFY_TEMP_CONTRACT: '',
|
|
5158
|
+
});
|
|
5159
|
+
for (const [serverName, target, expectedServerName] of PACKED_INSTALL_PLUGIN_MCP_TARGETS) {
|
|
5160
|
+
const result = spawnSync(omxPath, ['mcp-serve', target], {
|
|
5161
|
+
cwd: smokeRoot,
|
|
5162
|
+
encoding: 'utf-8',
|
|
5163
|
+
env,
|
|
5164
|
+
input: mcpInitialize,
|
|
5165
|
+
timeout: PACKED_INSTALL_OPERATIONAL_PROBE_TIMEOUT_MS,
|
|
5166
|
+
killSignal: 'SIGKILL',
|
|
5167
|
+
});
|
|
5168
|
+
assertBoundedProbeExit(`installed MCP target ${serverName}`, result, 0);
|
|
5169
|
+
const responseText = String(result.stdout ?? '').trim();
|
|
5170
|
+
if (!responseText) {
|
|
5171
|
+
throw new Error(`installed MCP target ${serverName} did not respond before EOF`);
|
|
5172
|
+
}
|
|
5173
|
+
const responses = responseText.split('\n').map((line) => {
|
|
5174
|
+
try {
|
|
5175
|
+
return JSON.parse(line) as unknown;
|
|
5176
|
+
} catch (error) {
|
|
5177
|
+
throw new Error(`installed MCP target ${serverName} emitted non-JSON stdout: ${error instanceof Error ? error.message : String(error)}`);
|
|
5178
|
+
}
|
|
5179
|
+
});
|
|
5180
|
+
const initializeResponse = responses.find((response) => isJsonRecord(response) && response.id === 1);
|
|
5181
|
+
if (!isJsonRecord(initializeResponse)
|
|
5182
|
+
|| !isJsonRecord(initializeResponse.result)
|
|
5183
|
+
|| !isJsonRecord(initializeResponse.result.serverInfo)
|
|
5184
|
+
|| initializeResponse.result.serverInfo.name !== expectedServerName) {
|
|
5185
|
+
throw new Error(`installed MCP target ${serverName} must initialize ${expectedServerName}, received ${JSON.stringify(initializeResponse)}`);
|
|
5186
|
+
}
|
|
5187
|
+
}
|
|
5188
|
+
} finally {
|
|
5189
|
+
rmSync(smokeRoot, { recursive: true, force: true });
|
|
5190
|
+
}
|
|
5191
|
+
}
|
|
5192
|
+
|
|
5193
|
+
export function parseNpmPackJsonOutput(stdout: string): PackedInstallNpmPackResult[] {
|
|
2037
5194
|
const start = stdout.lastIndexOf('\n[');
|
|
2038
5195
|
const jsonText = (start >= 0 ? stdout.slice(start + 1) : stdout).trim();
|
|
2039
5196
|
if (!jsonText.startsWith('[')) {
|
|
2040
5197
|
throw new Error(`npm pack did not return JSON output: ${stdout.trim()}`);
|
|
2041
5198
|
}
|
|
2042
|
-
return JSON.parse(jsonText) as
|
|
5199
|
+
return JSON.parse(jsonText) as PackedInstallNpmPackResult[];
|
|
2043
5200
|
}
|
|
2044
5201
|
|
|
2045
5202
|
async function main(): Promise<void> {
|
|
@@ -2049,6 +5206,19 @@ async function main(): Promise<void> {
|
|
|
2049
5206
|
const tempRoot = mkdtempSync(join(tmpdir(), 'omx-packed-install-'));
|
|
2050
5207
|
const prefixDir = join(tempRoot, 'prefix');
|
|
2051
5208
|
mkdirSync(prefixDir, { recursive: true });
|
|
5209
|
+
const installHome = join(tempRoot, 'home');
|
|
5210
|
+
const installCodexHome = join(tempRoot, 'codex-home');
|
|
5211
|
+
const installNpmCache = join(tempRoot, 'npm-cache');
|
|
5212
|
+
mkdirSync(installHome, { recursive: true });
|
|
5213
|
+
mkdirSync(installCodexHome, { recursive: true });
|
|
5214
|
+
mkdirSync(installNpmCache, { recursive: true });
|
|
5215
|
+
const installEnv = buildPackedProbeEnv({
|
|
5216
|
+
HOME: installHome,
|
|
5217
|
+
USERPROFILE: installHome,
|
|
5218
|
+
CODEX_HOME: installCodexHome,
|
|
5219
|
+
npm_config_cache: installNpmCache,
|
|
5220
|
+
NPM_CONFIG_CACHE: installNpmCache,
|
|
5221
|
+
});
|
|
2052
5222
|
|
|
2053
5223
|
let tarballPath: string | undefined;
|
|
2054
5224
|
try {
|
|
@@ -2058,15 +5228,27 @@ async function main(): Promise<void> {
|
|
|
2058
5228
|
|
|
2059
5229
|
const pack = run('npm', ['pack', '--json'], { cwd: repoRoot });
|
|
2060
5230
|
const packOutput = parseNpmPackJsonOutput(pack.stdout as string);
|
|
2061
|
-
const
|
|
5231
|
+
const packedPackage = packOutput[0];
|
|
5232
|
+
const tarballName = packedPackage?.filename;
|
|
2062
5233
|
if (!tarballName) throw new Error('npm pack did not return a tarball filename');
|
|
5234
|
+
if (!packedPackage.files) throw new Error('npm pack did not return file metadata');
|
|
5235
|
+
assertPackedInstallFileMetadata(packedPackage.files);
|
|
2063
5236
|
tarballPath = join(repoRoot, tarballName);
|
|
2064
5237
|
|
|
2065
|
-
run('npm', ['install', '-g', tarballPath, '--prefix', prefixDir], { cwd: repoRoot });
|
|
5238
|
+
run('npm', ['install', '-g', tarballPath, '--prefix', prefixDir], { cwd: repoRoot, env: installEnv });
|
|
5239
|
+
|
|
5240
|
+
const globalNodeModules = resolveGlobalNodeModules(prefixDir);
|
|
5241
|
+
const packageRoot = join(globalNodeModules, 'oh-my-codex');
|
|
5242
|
+
assertInstalledRequiredArtifacts(packageRoot);
|
|
5243
|
+
await assertInstalledReasoningArtifacts(packageRoot);
|
|
2066
5244
|
|
|
2067
5245
|
const omxPath = join(prefixDir, process.platform === 'win32' ? '' : 'bin', npmBinName('omx'));
|
|
5246
|
+
smokeInstalledRootReasoningRejections(omxPath, repoRoot);
|
|
5247
|
+
smokeInstalledLaunchArgumentBoundary(omxPath);
|
|
5248
|
+
smokeInstalledPluginHookLauncher(packageRoot, omxPath);
|
|
5249
|
+
smokeInstalledMcpTargets(omxPath);
|
|
2068
5250
|
for (const argv of PACKED_INSTALL_SMOKE_CORE_COMMANDS) {
|
|
2069
|
-
run(omxPath, argv, { cwd: repoRoot });
|
|
5251
|
+
run(omxPath, argv, { cwd: repoRoot, env: installEnv });
|
|
2070
5252
|
}
|
|
2071
5253
|
smokeInstalledNativeHookDist(prefixDir);
|
|
2072
5254
|
const lifecycle = await smokePackedHookTrustLifecycle(omxPath);
|