fix(cli): engage the gh stubs on Windows instead of calling the real API - #245
fix(cli): engage the gh stubs on Windows instead of calling the real API#245ophiocus wants to merge 5 commits into
Conversation
…alling the real API The gh stubs never engaged on Windows: the fixture PATH was joined with a hard-coded colon and the stub is an extensionless script, so eight tests resolved the developer's real gh and called api.github.com — including the one named "deterministic GitHub fixtures". Node refuses .cmd files without a shell (CVE-2024-27980), so no PATH arrangement can fix this; the templates now expose a shell-free seam (FACILITY_GH_BIN / FACILITY_GH_ARGS, default "gh" with no args) and the tests drive it. The stub env also pins GH_HOST/GH_TOKEN to invalid values so any future fallthrough dies offline. POSIX mode asserts are scoped to platforms with mode bits; the init e2e is skipped on win32 with theam#230 as its unskip criterion. A verify-windows CI job runs the CLI suite and guards on windows-latest so this whole class fails in CI instead of on contributors' machines. Closes theam#241 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The hosted image defaults core.autocrlf=true; the CRLF checkout blinded the newline-anchored watchtower-template assertions on the job's first run (95/1) while local Windows and Linux pass. LF checkout matches what every other environment tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
adrian-lorenzo
left a comment
There was a problem hiding this comment.
Thanks for the contribution!
FACILITY_GH_BIN and FACILITY_GH_ARGS are now trusted from the ambient environment by the shipped delivery verifier. The builder and repository checks run before the delivery steps, and either can persist these variables through $GITHUB_ENV. The verifier would then execute the selected binary with GH_TOKEN, allowing earlier code to replace gh and fake the GitHub evidence or run inside the privileged finalization step.
Please keep the test seam at the module boundary—for example, by injecting the GitHub runner into an exported entry point—while the production entry point always invokes the fixed executable. Add a regression test showing that hostile ambient variables cannot redirect it.
The Windows CI coverage and offline safeguard are worth keeping. Once this boundary is sealed, this will be a strong improvement.
Review round two: FACILITY_GH_BIN/FACILITY_GH_ARGS were readable from
the ambient environment by the shipped verifier and resolver, so an
earlier workflow step persisting variables through $GITHUB_ENV could
swap the executable they run while holding GH_TOKEN. The seam now lives
where the review asked: verify.mjs exports runDelivery(mode, { gh })
and resolve.mjs exports main({ gh }); the script entry paths construct
the fixed "gh" executable with nothing about the invocation readable
from env. The delivery and resolver integration tests inject the runner
through those exported entry points in-process, and two spawn-path
regression tests prove a hostile FACILITY_GH_BIN pointing at a
marker-writing binary is never executed - the fixed gh is attempted and
dies offline against the poisoned GH_HOST instead.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Sealed in 48e510a — and thank you for spelling out the attack path; The seam now lives where you pointed:
Verified: Linux 99 pass / 0 fail, Windows 98 pass / 0 fail on the same head; the |
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nly-cli-suite # Conflicts: # .github/workflows/ci.yml
|
Status note: |
Closes #241. The suite's
ghstubs never engaged on Windows — the fixture PATH was joined with a hard-coded:and the stub is an extensionless script — so eight tests resolved the developer's realghand called api.github.com, including the one named "deterministic GitHub fixtures". This PR makes the stubs engage on every platform, adds a belt so a future fallthrough can never reach the real API, and stands up theverify-windowsCI job so the whole Windows-only defect class fails in CI instead of on contributors' machines.Why a seam, not a shim
The obvious fix — a
gh.cmdwrapper beside the stub — cannot work: Node refuses to execute.cmd/.batthroughexecFilewithout a shell by design since CVE-2024-27980 (the same refusal behind #182'sspawn EINVAL). No PATH arrangement makes a script stub executable there. So the templates gain a minimal, shell-free seam:Production behavior is byte-identical (default
"gh", no args, no shell ever). Tests setFACILITY_GH_BIN=process.execPathand pass the stub script viaFACILITY_GH_ARGS— works identically on POSIX and Windows.The changes
templates/delivery/verify.mjs,templates/doctor/resolve.mjspath.delimitertest/delivery.test.mjs,test/doctor-policy.test.mjsGH_HOST=gh-stub.invalid+ dummy token, so if a stub ever falls through again the suite dies against an unresolvable host, never against the real API0600asserts scoped to platforms with mode bits (NTFS has none); the secret-not-logged assert still runs everywheretest/platform.test.mjstest/init.test.mjsverify-windowsjob: CLI suite + guards onwindows-latest, same pinned action SHAs asverify.github/workflows/ci.ymlWhy the CI job belongs here
Ten currently-open issues are Windows-only defects invisible to
ubuntu-latest(#182 #183 #167 #191 #217 #227 #228 #230 #240 #241 — inventory in #241). Every one shipped because nothing red ran on Windows. The job covers the pure-Node surfaces (CLI suite, guards) in ~3 minutes — no Docker, no Postgres — and this very PR is its first proof: before it, that job fails 11 tests; with it, green.Verified
Found while running the suite on a real Windows machine — happy to iterate on any of it, and the Windows arc continues in #240/#227.