Component
.github/workflows/graphify.yml — the single rebuild job; tests/unit/ci/WorkflowHygiene.test.ts — INSTALL_COMMAND and the keeps write access away from installs assertion.
Severity (your assessment)
LOW — same class and calibration as #622 (the README-badge job) and #621: a push credential in the same job as third-party install scripts, reachable only through an upstream compromise. Found while closing #1622; not folded in there because the fix is a job split, not a flag.
Exploit walkthrough
graphify.yml#rebuild holds contents: write (graphify.yml:47-48), runs python3 -m pip install --disable-pip-version-check 'graphifyy==0.9.46' (:63), executes the installed tool over the checkout (graphify update ., :66) and then git pushes to develop with the checkout's persisted credential (:78). So a third-party package's install hooks and its runtime execute inside a job that can write to the default branch. The top-level pin is exact, but pip has no lockfile here: the transitive closure resolves afresh on every weekly run, unpinned and un-hashed, and a compromised release of any dependency of graphifyy runs with a token that pushes to develop.
docs/…/operations/security/supply-chain.mdx states "no job that can write to the repository also runs third-party install scripts" and that WorkflowHygiene.test.ts asserts it. It does not see this one: INSTALL_COMMAND is /\b(bun install|bunx|npm ci|npm install|npx|pnpm install|yarn install)\b/ — no pip install, pipx, uv, pip3 — so the write-access assertion passes over this job by vocabulary rather than by verification.
Affected files
.github/workflows/graphify.yml:44-80
tests/unit/ci/WorkflowHygiene.test.ts — INSTALL_COMMAND (search for the regex) and the keeps write access away from installs test
docs/src/content/docs/operations/security/supply-chain.mdx (+ DE mirror) — the third claim in "Frozen installs, least privilege"
Fix sketch / approach
// 1. Split the job the way #622 split test.yml: an unprivileged `rebuild`
// job installs graphify, runs `graphify update .` and uploads
// graphify-out/ as an artifact; a `push` job with `contents: write` that
// installs nothing downloads it, commits and pushes. The concurrency
// group and the [skip ci] commit stay as they are.
// 2. Pin the Python closure while it exists anywhere in CI: a hashed
// requirements file (`pip install --require-hashes -r …`) generated once
// from the 0.9.46 resolve, bumped with the graphify version.
// 3. Teach INSTALL_COMMAND pip (`pip3? install`, `pipx`, `uv (pip|tool) install`).
// With the split landed first, the assertion goes green on the new shape
// and red on the old one — verify that order, do not add an exemption.
Acceptance criteria
- No job holding
contents: write in .github/workflows/ runs a pip/pipx/uv install, and INSTALL_COMMAND knows those spellings, so the write-access assertion covers them.
- Applied to the current tree first, the widened regex fails on
graphify.yml#rebuild and nothing else.
- The weekly graph refresh still lands as
chore(graphify): refresh knowledge graph [skip ci] on develop.
Disclosure status
Fresh — first public mention
Component
.github/workflows/graphify.yml— the singlerebuildjob;tests/unit/ci/WorkflowHygiene.test.ts—INSTALL_COMMANDand thekeeps write access away from installsassertion.Severity (your assessment)
LOW — same class and calibration as #622 (the README-badge job) and #621: a push credential in the same job as third-party install scripts, reachable only through an upstream compromise. Found while closing #1622; not folded in there because the fix is a job split, not a flag.
Exploit walkthrough
graphify.yml#rebuildholdscontents: write(graphify.yml:47-48), runspython3 -m pip install --disable-pip-version-check 'graphifyy==0.9.46'(:63), executes the installed tool over the checkout (graphify update ., :66) and thengit pushes todevelopwith the checkout's persisted credential (:78). So a third-party package's install hooks and its runtime execute inside a job that can write to the default branch. The top-level pin is exact, but pip has no lockfile here: the transitive closure resolves afresh on every weekly run, unpinned and un-hashed, and a compromised release of any dependency ofgraphifyyruns with a token that pushes todevelop.docs/…/operations/security/supply-chain.mdxstates "no job that can write to the repository also runs third-party install scripts" and thatWorkflowHygiene.test.tsasserts it. It does not see this one:INSTALL_COMMANDis/\b(bun install|bunx|npm ci|npm install|npx|pnpm install|yarn install)\b/— nopip install,pipx,uv,pip3— so the write-access assertion passes over this job by vocabulary rather than by verification.Affected files
.github/workflows/graphify.yml:44-80tests/unit/ci/WorkflowHygiene.test.ts—INSTALL_COMMAND(search for the regex) and thekeeps write access away from installstestdocs/src/content/docs/operations/security/supply-chain.mdx(+ DE mirror) — the third claim in "Frozen installs, least privilege"Fix sketch / approach
Acceptance criteria
contents: writein.github/workflows/runs apip/pipx/uvinstall, andINSTALL_COMMANDknows those spellings, so the write-access assertion covers them.graphify.yml#rebuildand nothing else.chore(graphify): refresh knowledge graph [skip ci]ondevelop.Disclosure status
Fresh — first public mention