Repository navigation
ci(lockfix): refuse a lock path that resolves through a symlink - #1374
Merged
Merged
Conversation
lockfix-apply.sh copied each regenerated lock onto pr/$rel without
checking the destination, which the PR tree controls. A lock path
committed as a symlink made cp write through it, for example over the
trusted apply script while bash was still running it, in the job that
holds the App token. Before each copy, refuse unless the resolved path
stays at pr/$rel and the index entry is mode 100644.
lockfix.mjs skipped main() and exited 0 ("commit") when invoked through
a symlink, because the run-as-CLI check compared the unresolved argv
path. Compare the realpath instead.
Tests run the apply script in a scratch git layout for a committed
symlink, a working-tree symlink and a 100755 entry, and run the CLI
through a symlink. Ported from mforce/collectify#225.
Owner
Author
|
A code-quality review of this PR at 33d843a found three things to fix before merge. A fix is in progress, so please hold the merge.
The |
Owner
Author
|
The review findings from my earlier comment are fixed in 2c7e754, and the PR description now covers that commit. Commit exits 4 and the apply script commits only on 4. The mode check reads the exact tree entry, and the tests clean up their temp directories. A second local review round on that commit found no defects. The technical reason for the hold is gone once CI at 2c7e754 is green; the merge itself is still my call. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
Closes #1373.
lockfix-apply.shchecked each artifact file, but not the destinationpr/$rel, which comes from the PR checkout. If the PR tree commits a lock path, or one of its parent directories, as a symlink,cpwrites through it. With a compromisedcomputejob supplying the artifact, that can overwritetrusted/.github/scripts/lockfix-apply.shwhile bash is still running it, inside thecommitjob that holds the App token. A local review of the collectify port of this workflow (mforce/collectify#225) found the hole and proved it in a sandbox. This PR ports collectify's fix.lockfix-apply.shrefuses unlessrealpath -m "pr/$rel"equals$(pwd -P)/pr/$rel, so no symlink can sit anywhere on the path. It also refuses unless the lock is tracked as a regular file with mode100644(the exact-entry form is described below).lockfix.mjs's run-as-CLI check now compares real paths. Before, a call through a symlink skippedmain()and exited 0, and the apply script then treated 0 as "commit"; the second commit below changes that.lockfix-apply.shcommits only on 4, so a classifier that exits 0 without runningmain()makes it refuse. Before, exit 0 meant commit, so any way of skippingmain()(a call through a symlink, ornode --preserve-symlinks-main) approved a commit. The header comment and the spec's exit-code list say so.git ls-tree --format='%(objectmode) %(path)' HEAD -- "$rel", which must be exactly100644 <path>. The oldgit ls-files -smatched a path prefix, so a directory at a lock path holding one regular file passed it. The classifier still refused that case later.after()hook removes, so they no longer leave temp directories behind.One correction to #1373: it says the apply script already rejects a symlinked artifact file. It doesn't; it checks only
-f, which follows symlinks. This PR leaves that alone, because the script only reads the artifact file, so a symlinked source can't make it write anywhere.How it was verified
node --test .github/scripts/lockfix.test.mjsprinted 17 passed, 0 failed, after the second commit. With the first commit alone it printed 15 passed. Seven tests are new: five in the first commit, and two in the second (an exit-0 stub classifier, and a directory at the lock path). Of the first five, four run the apply script in a scratch git repo with a local bare remote: a normal run that commits and pushes, a lock file committed as a symlink, a working-tree symlink over a100644entry, and a lock file tracked as100755. The fifth runs the CLI through a symlink.100755test went red.ls-filesfor the mode check turned the new directory test red.eec4d136, a lock path committed as a symlink to the trusted script let the artifact overwritelockfix-apply.sh, and the script exited 0. With this change, it aborts with "lock path goes through a symlink". The trusted script's hash stays the same, and nothing reaches the remote.trusted/(committed, or only in the working tree),src/as a symlink,pr/as a symlink, and a symlink to another directory in the PR. Every layout stopped beforecp, with nothing pushed. The review found no defects.ls-treecheck on a scratch repo (it accepts a normal lock and refuses a directory, a symlink,100755, a gitlink, a missing path, a trailing slash and a glob), and ran the suite with an emptyTMPDIR, leaving nothing behind.--formatneeds a recent git; the reviewer judged it safe because both lockfix jobs pinubuntu-26.04, and this PR's CI runs the new tests, which call it, on a hosted runner. It found no defects.shellcheckandactionlintare clean on the changed files.Checklist