QVAC-23347 infra: stop persisting git credentials in wrapper publish jobs - #3795
Open
covert-oddity wants to merge 2 commits into
Open
QVAC-23347 infra: stop persisting git credentials in wrapper publish jobs#3795covert-oddity wants to merge 2 commits into
covert-oddity wants to merge 2 commits into
Conversation
…jobs
The 18 credentialed publish jobs across the nine on-merge-*.yml TypeScript
wrapper pipelines check out without `persist-credentials: false`, so the
checkout token is written into .git/config as an http.extraheader and stays
readable by any process in the job. Eight of those checkouts use
secrets.PAT_TOKEN; the rest persist GITHUB_TOKEN, and 11 of the 18 jobs hold
`contents: write`.
Each of these jobs also runs `npm install --ignore-scripts` followed by a tsc
invocation, so every package in the devDependency closure gets execution in a
job where that credential sits on disk. Removing the execution surface is
tracked separately; this is the cheapest independent cut and lands on its own.
Verified none of these jobs performs an authenticated git operation, so
dropping the credential is inert:
- .github/actions/publish-library-to-gpr only runs a local `git rev-parse`
(its SHORT_SHA is assigned and never used) plus `npm publish` with
NODE_AUTH_TOKEN.
- `check:generated` only runs `git diff --exit-code`.
- tetherto/qvac-actions/publish-library-to-npm sparse-checks out its gate
into a separate `_qvac-actions-gate` path and publishes with OIDC.
- Tag creation is a separate job (create-release-tag.yml) using the REST
API with GITHUB_TOKEN, not a git push.
Existing precedent for this flag in the repo: 20+ workflows, including
on-pr-classification-ggml.yml, on-pr-diffusion-cpp.yml, on-pr-ocr-ggml.yml
and on-pr-vla.yml.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
License compliance — cleanNo new dependency license findings in this PR. Warn-only (shadow) mode — this check does not block merges yet. Updated automatically by the canonical license compliance workflow. NOTICE presence (advisory)Missing NOTICE (advisory, does not block):
|
Contributor
Workflow security (shadow mode)zizmor found 1090 finding(s) in Findings are annotated inline on the changed files and listed in the job summary. Reproduce locally: pipx run zizmor==1.27.0 --offline .github/ |
Contributor
Review StatusCurrent Status: ❌ PENDING Pending reviews: Needs 1 Management or Team Lead, and 1 more from Management, Team Lead, or Member. |
covert-oddity
had a problem deploying
to
release
August 13, 2026 20:50 — with
GitHub Actions
Failure
…t-credentials-publish-jobs
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.
Problem
The 18 credentialed publish jobs across the nine
on-merge-*.ymlTypeScript wrapper pipelines check out withoutpersist-credentials: false.actions/checkouttherefore writes the checkout token into.git/configas anhttp.extraheader, where it stays readable by any process in the job for the job's lifetime. (Confirmed the default is stilltrueat the pinned revisionde0fac2e4500dabe0009e67214ff5f5447ce83dd.)What sits in those jobs:
secrets.PAT_TOKENpersistedpublish-npmonly)GITHUB_TOKENpersistedcontents: writeid-token: writepublish-npm(needed fornpm publish --provenance)Each of these jobs also runs
npm install --ignore-scripts --package-lock=falsefollowed by atscinvocation, so every package in the devDependency closure — eslint, typescript-eslint, brittle, cmake-bare, cmake-vcpkg and everything transitive — gets code execution in a job where that credential is on disk.--ignore-scriptsblocks install-time hooks but does nothing oncetscruns.Two notes that refine the original write-up, both in the direction of more exposure:
publish-npmundercounts: in diffusion-cpp, vla and llm-llamacpp therelease-environmentpublish-gprjob also checks out withPAT_TOKEN.id-token: writeis not in every step. Job-levelpermissions:fully replaces the workflow-level block, and everypublish-gpromitsid-token, so OIDC minting is confined to the ninepublish-npmjobs.What this PR does
Adds
persist-credentials: falseto all 18 checkouts. Nothing else. +23 lines, purely additive.This is the cheapest independent cut and lands on its own. Removing the execution surface itself — the
npm install+tscpair, which is redundant because every package commits its tsc output — is a separate PR so this one stays trivially reviewable.Why dropping the credential is inert here
None of these 18 jobs performs an authenticated git operation:
.github/actions/publish-library-to-gpr— only a localgit rev-parse --short HEAD(whoseSHORT_SHAis assigned and never used), thennpm publishwithNODE_AUTH_TOKEN.check:generated(llm-llamacpp) — onlygit diff --exit-code, no auth needed.tetherto/qvac-actions/publish-library-to-npm@38693d93— sparse-checks out its gate into a separate_qvac-actions-gatepath and publishes via OIDC--provenance.create-release-tag.yml, using the REST API withGITHUB_TOKEN— not a git push from these jobs.Verification
node .github/scripts/lint-workflows.mjson all nine: actionlint structural gate clean..github/workflows(untouchedpr-gate-merge.ymlreports the same); the only yamlfmt run these files trigger,on-pr-ocr-ggml.yml:142, is scoped toworkdir: packages/ocr-ggmland iscontinue-on-error: true, so it never sees these paths.Precedent
persist-credentials: falseis already used in 20+ workflows here, includingon-pr-classification-ggml.yml,on-pr-diffusion-cpp.yml,on-pr-ocr-ggml.ymlandon-pr-vla.yml.Asana: QVAC-23347. Distinct from the completed H1 (label-gated fork checkout), C2 (ungated secrets) and M5 (continue-on-error) work — this is post-merge publish-time exposure, not PR-time fork exposure.