feat(qt): install the toolchain from a hash-locked closure - #183
Merged
Conversation
qt-ci.yml ran `uvx --from 'aqtinstall@3.3.0' --with 'py7zr==1.0.0'`, which pinned two names and left everything they pull unbounded: aqtinstall declares bs4, defusedxml, humanize, patch-ng, semantic-version and texttable with no upper bound, plus requests>=2.31.0. Two runs of the same workflow SHA could install different code without this repository changing. That mattered here more than most places. Driving aqtinstall directly was itself a supply-chain decision, taken to escape an action whose nested graph could not be pinned; escaping one unpinned graph into another is not an improvement. requirements-qt.txt is the closure -- 28 packages, all hashed, installed with --require-hashes into an isolated environment. This is a reusable workflow, so its checkout is the caller's tree and the lock is not in it. The lock is fetched from the exact commit of the workflow file (job.workflow_sha is the runner's own answer to what is executing, which the caller cannot forge) and its digest is checked against a value published in that file before anything installs. Pinning the workflow pins the closure, and --require-hashes means a substituted lock still cannot introduce a package. catalog/tools.yml registers both tools and the lock digest. check_qt_toolchain_lock.py holds the digest, the versions and the presence of hashes together. The receipt records toolchain_lock_sha256 beside aqt_version, because `aqt 3.3.0` said nothing about the 26 packages underneath it. Re-proven by fixture run 31855694582 before the ledger was updated.
Dependency ReviewThe following issues were found:
Vulnerabilitiesrequirements-qt.txt
Only included vulnerabilities with severity moderate or higher. License Issuesrequirements-qt.txt
OpenSSF ScorecardScorecard details
Scanned Files
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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 was open
qt-ci.ymlran:Two names pinned. Everything they pull, unbounded — aqtinstall 3.3.0 declares:
Two runs of the same workflow SHA could install different code without this
repository changing.
This mattered more here than it would elsewhere: driving aqtinstall directly was
itself a supply-chain decision, taken to escape
jurplel/install-qt-actionwhosenested action graph could not be pinned (#150). Escaping one unpinned graph into
another is not an improvement.
The closure
requirements-qt.txt— 28 packages, all hashed, installed with--require-hashesinto an isolated environment.
How a reusable workflow gets a lock it does not have
qt-ci.ymlruns in the caller's checkout, so the lock is not there. It is fetchedfrom the exact commit of the workflow file:
job.workflow_shais the runner's own answer to "what is executing" — a callercannot forge it, and the receipt already records it;
LOCK_SHA256, published in theworkflow file itself, before anything installs.
So pinning the workflow pins the closure. And
--require-hashesmeans that even asubstituted lock cannot introduce a package — it can only fail.
Three statements that must agree
check_qt_toolchain_lock.pyholds them together:qt-ci.ymlpublishes == the lock in the treecatalog/tools.ymlrecords--hashentries at all — otherwise--require-hashesfails on the runner rather than herecatalog/tools.ymlnow registersaqtinstallandpy7zr, which it did not before,each with the lock digest. The receipt records
toolchain_lock_sha256besideaqt_version, becauseaqt 3.3.0said nothing about the 26 packages underneath it.Evidence, before the ledger moved
Run 31855694582:
observe / qt-ciis the observer validating the receipt — including the new field.proven_digestandlast_runwere updated only after that succeeded.Verification
validate_all— all tiers OK withGH_TOKENactionlint— clean ·zizmor@1.26.1 --persona pedantic --min-severity low— no findings