Repository navigation
git: refuse a remote name git would read as an option, where each module takes it (#952) - #1087
Conversation
…h module takes it (#952) A remote starting with - reached git fetch and ls-remote as an option, so --upload-pack=<cmd> ran <cmd>. One check (src/git-remote.ts) now runs where the coordinator, PR batcher, PR test runs, main watch, deploy gate, live config and claims take a remote from their caller, before any git call. Signed-off-by: Jayden Vasquez <jaydenvasquez1711@gmail.com>
Signed-off-by: Jayden Vasquez <jaydenvasquez1711@gmail.com>
|
Independent adversarial review (R87b), posted by the coordinator. VERDICT: PASS @0bdcc993033d0be77b3c863687fcae682d05d64b (adversarial) Reviewer r87b, critical set. Head confirmed with Findings
Follow-ups (not FAILs: no engine path, not claimed)
Ran
|
|
Independent final check (R87a), posted by the coordinator. VERDICT: PASS @0bdcc993033d0be77b3c863687fcae682d05d64b (final check) Reviewer r87a, PR #1087 (issue #952). Critical set: yes (L3 self-modification). This is one of the two reviews it needs. Head confirmed with Findings
Follow-ups (not FAILs)
RanThe |
Closes #952
What changed
src/git-remote.ts: one check,gitRemote(name). It returns the name only when it matches^[A-Za-z0-9][A-Za-z0-9._-]*$, the pattern the issue gives, and throws otherwise.git fetch/ls-remote/pushin these modules uses only the checked value:CoordinatorBase(this.remote). This covers the 5merge-flow.tscalls the issue names, and the otherthis.remotefetches incoordinator.ts,landing,learnings,pr-hygiene,fix-runs,gardener,optimize,checks-from-feedback,delegationanddecisions.PrBatcherandPrTestRunsconstructors.repoGit(main watch), which also coversacceptGit'sls-remotebecause it buildsrepoGitfirst.gateGit(deploy gate).LiveConfigandstartConfig.readClaim,claim,renewandrelease.Why this shape
The issue asks for the check in #927's git runner. #927 is still open, and the runner's env module is being changed under #1001, so this PR doesn't touch it. Checking where the remote comes in covers about 30 fetch call sites with 9 checks and leaves the call sites unchanged (inferred, from
rg "'fetch'" src). I didn't add--end-of-optionsat each site: once the name is checked, it adds nothing, and it would mean patching each site separately. If #927 lands,gitRemotecan move into its runner unchanged (inferred).Evidence
The attack (measured, git 2.43.0, scratch clone):
git fetch -q "--upload-pack=touch $M #" main, the refspec form with the same remote, andgit ls-remoteeach created the marker file.test/git-remote.test.tstest 2 shows the same thing in the suite.Fail-before (measured): I restored the 7 call-site files to
origin/mainwithgit checkout origin/main -- <files>, kept the helper so the test compiles, rannpm run build, then rannode --import ./dist/test/isolate.js --test dist/test/git-remote.test.js. Result: 2 pass, 5 fail. The failures are tests 3–7 (main-watch, deploy gate, live config, claims, constructors), with "Missing expected exception" or the git error instead of the refusal.Pass-after (measured): the same command with the fix gives 7/7 pass.
Neighbouring tests (measured):
git-remote,claims,live-config,main-watch,main-watch-coordinator,deploy-gate,pr-batch,pr-batch-coordinator,testrun-runsandbrand: 186/186 pass.Typecheck and denylist (measured):
npm run typecheckis clean, andnpm run check:denylistreports "1659 files clean".Full suite (measured):
flock … npm testran once on this head: 5721 pass, 17 fail, 40 todo. I believe all 17 come from the local sandbox and none from this change (inferred, not re-run on main):coordinator.testsees the sandbox's own pip cache variable;desktop-removed.testseesyamlresolve through the worktree's symlinkednode_modules;machine-sudoers-*compare a message that the long TMPDIR path clips.CI is authoritative.
Critical set: yes
Measured: the engine's
computeLevelon these files gives L3, withself-modification: src/coordinator/base.ts, src/coordinator/pr-batch.ts, src/coordinator/test-runs.ts. The change is also coordinator git hardening. It needs two independent reviews.Not done (follow-ups)
checkedPush(src/push-check.ts, the push gate) takes a remote too. Every engine caller passesoriginor the coordinator's already-checked remote (inferred). I left it alone because it is a guard outside this task's ownership.remoteTip(src/auto-merge-resume.ts,ls-remote) is only reached with the coordinator's checked remote (inferred).src/harness-evals/replay.tsfetches fromo.source(t), which is a path or URL rather than a remote name, so this pattern doesn't fit it.--end-of-optionsthere would be a separate change.scripts/use a hardcodedorigin(measured, grep).