fix(drift): pin the Ollama artifact the drift job unpacks as root - #360
Merged
Conversation
test-drift.yml's provisioning step fetched https://ollama.com/install.sh and ran it with no digest check of any kind. Executing that step's own run: body verbatim with curl serving substituted content, `sh` was handed "ATTACKER-CONTROLLED PAYLOAD" and the step exited 0. The step holds no provider key itself, and that protects nothing. It runs before `Run drift tests`, which is handed OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_API_KEY, OPENROUTER_API_KEY, FAL_KEY, COHERE_API_KEY and ELEVENLABS_API_KEY. A payload that cannot read those keys can still plant a node/npx/git earlier on PATH, and the later key-holding steps execute it. Pinning install.sh would not have been enough — proven in fix-drift.yml's own writeup: the script streams an unversioned, undigested ollama-linux-<arch>.tar.zst through zstd -d into sudo tar -x, and it cannot be fixed in place because it never holds the file. So install.sh is not used. The release artifact is fetched from an immutable release tag and its sha256 checked before anything unpacks it, mirroring the shape fix-drift.yml is getting in #359. Digest dec2fa50…aadcfc for ollama-linux-amd64.tar.zst v0.32.6 (1420686963 bytes), agreed by three independent sources verified here: the release's own sha256sum.txt, the GitHub release API's asset digest field, and sha256 of the downloaded bytes. Archive layout confirmed by listing it — 54 entries, bin/ollama plus lib/ollama/*, so -C /usr/local puts the binary on the default PATH. This is NOT the last unpinned executable in the repo, and the new guard does not claim otherwise. Still unpinned and not fixed here: `npx pkg-pr-new publish` (publish-commit.yml) fetches an unlockfiled npm package and runs it; `pip install hatch` (publish-release.yml) runs an unpinned PyPI package in the job that holds PyPI OIDC publish rights; `pip install ./packages/aimock-pytest[test]` (test-pytest.yml) resolves unpinned transitive deps; and in this job the setup-node and pnpm/action-setup toolchain downloads carry no committed digest. fix-drift.yml:90 has the same Ollama defect in its weaker form and is #359's. New guard executes the step rather than reading it: tampered bytes must not reach `sh` or `tar`, and a positive control asserts tar was handed exactly the verified bytes. Three mutations confirmed red — neutering the digest comparison, pointing the fetch back at install.sh, and emptying the pin.
commit: |
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 this closes
test-drift.yml's Ollama provisioning step fetchedhttps://ollama.com/install.shand ran it with no digest check of any kind — not even the weaker script-only
pin
fix-drift.ymlcarries today.The step holds no provider key of its own, and that protects nothing. It runs
before
Run drift tests, which is handedOPENAI_API_KEY,ANTHROPIC_API_KEY,GOOGLE_API_KEY,OPENROUTER_API_KEY,FAL_KEY,COHERE_API_KEYandELEVENLABS_API_KEY. A payload that cannot read those keyscan still plant a
node/npx/gitearlier on PATH, and the later key-holdingstep executes it.
(The workflow's other 15
secrets.references live in thenotifyanddrift-live-prjobs — separate runners, not reachable from this step. Saying sorather than quoting "22 secrets".)
Watched red-green-rebreak
RED, the committed step's own
run:body executed verbatim withcurlserving substituted bytes:
Arbitrary third-party code executed and the step reported success.
GREEN, same harness after the fix:
No
SH_RAN, noTAR_RAN. The new guard asserts the absence of a payload at anexecutor, not an exit code — and its positive control asserts
tarwas handedexactly the verified bytes, so "always refuse" cannot satisfy it.
RE-BREAK, three mutations, each restored (workflow md5 identical before and
after all three): neutering the digest comparison, pointing the fetch back at
install.sh, and emptying the pin each red exactly their intended guard.The remedy
install.shis not used. Pinning it would not have been sufficient — it streamsan unversioned, undigested
ollama-linux-<arch>.tar.zstintosudo tar -xandcannot be fixed in place because it never holds the file. So the release artifact
is fetched from an immutable release tag and sha256-verified before anything
unpacks it. Same shape as #359 does for
fix-drift.yml; that file is untouchedhere.
Digest
dec2fa50…aadcfc(v0.32.6, 1,420,686,963 bytes) agreed by threeindependent sources verified in this PR: the release's own
sha256sum.txt, theGitHub release API's asset
digestfield, and sha256 of the downloaded bytes.Archive layout confirmed by listing it — 54 entries,
bin/ollama+lib/ollama/*.This is NOT the last unpinned executable in the repo
Swept all 15 workflows. Still unpinned, not fixed here, ranked by blast radius:
publish-release.yml:189—pip install hatch, unpinned PyPI package, in thejob with
environment: pypi+id-token: writethat publishesaimock-pytestto PyPI via OIDC.
fix-drift.yml:90— the same Ollama defect in its weaker form; fix(drift): the rejection lookup no longer saturates, and the Ollama artifact is pinned #359's.publish-commit.yml:20—npx pkg-pr-new publish;pkg-pr-newis in neitherpackage.jsonnorpnpm-lock.yaml, so npx fetcheslatestand runs it. Norepo secrets in that job.
test-pytest.yml:48—pip install ./packages/aimock-pytest[test]resolvesunpinned transitive deps. No secrets in that job.
actions/setup-node's Node download andpnpm/action-setup's pnpm(
packageManager: pnpm@10.28.2— a version, no integrity hash). Applies toevery job including this one.
Not a finding: the
git cloneof ag-ui is read by static regex parsing, neverexecuted. Every
npx tsx/npx vitestresolves from the lockfile-installed tree.Every
uses:is SHA-pinned.Gates
pnpm build0 ·pnpm test:drift0 ·tsc --noEmit0 ·pnpm lint0 ·pnpm test:exports0 · prettier clean · zizmor (medium) 0 findings ·bash -non 9run:bodies, 0 failures · commitlint 0.pnpm test: 5061 passed / 1 failed —ws-framing, a known pre-existing flake;passes in isolation (24/24, exit 0). Suite 5057 → 5062.
actionlintexits 1 on one pre-existing SC2086 info in thenotifyjob,identical on
origin/main(base L261 = head L329, exactly this diff's offset).