Harden the CI harness itself: pip-audit, workflow linting, SHA pins - #245
Conversation
The workflows gate everything else but nothing gated the workflows, and Python dependencies had no in-gate vulnerability scan while Rust had cargo-audit. Four changes, all measured against the tree before landing so the gate is green on day one: pip-audit job (static-checks.yml): audits the resolved CI environment (cli + [mcp] extra + unit-test requirements) on every PR. A separate job, not a step in the required static-checks context -- it depends on the PyPI advisory service, and external flake must not couple into a required check until it earns trust. The ignore file (tools/pip_audit_ignore.txt) starts EMPTY: the tree resolved clean. Stated honestly in the job header: this covers what CI resolves, not what the box runtime resolves unpinned at image build -- that audit is follow-up work. The env's own seeded pip/setuptools are upgraded before auditing, so findings are about this project's tree, not the runner image's bootstrap tooling. zizmor (pinned, offline, low+ fails) and actionlint (pinned, its shellcheck/pyflakes integrations disabled -- this job already applies the repo's own chosen floors to shell) run inside the required static-checks job. Deliberate exceptions carry reasons in .github/zizmor.yml (the nightly's secrets: inherit) and .github/actionlint.yaml (the bench runner label). Every third-party action is SHA-pinned with a version comment, and every checkout sets persist-credentials: false -- no job here pushes, so the token has no business surviving into the workspace. The dependabot github-actions ecosystem keeps the pins moving. Every workflow now carries a default-token permissions block (contents: read); jobs needing more must say so themselves. For the reusable-workflow chain this is compatible both ways: the callers' default caps the called workflows at exactly what they declare. Bench workflows are touched (pins, permissions, persist-credentials) but trigger-neutrally; validated by a supervised dispatch before merge.
|
Rebuilt on top of the merged series (#231, #234, #243, #244, #246, #252): the branch was Validated against the full tree: zizmor exits clean at Remaining before undraft: one supervised |
a1bbbac to
495478e
Compare
The workflows gate everything else, but nothing gated the workflows -- and Python had no
in-gate dependency scan while Rust has cargo-audit. Four hardening changes, each measured
against the tree first so everything lands green:
pip-auditjob (new, in static-checks.yml): audits the resolved CI environment(cli +
[mcp]extra + unit-test requirements) on every PR. Deliberately a separate job,not a step inside the required
static-checkscontext: it depends on the PyPI advisoryservice, and that external flake must not couple into a required check until the job earns
trust during soak.
tools/pip_audit_ignore.txtstarts EMPTY -- the tree resolved clean.The job upgrades the runner's seeded pip/setuptools before auditing (their advisories are
about the image, not this project). Scope stated honestly in the header: this covers what
CI resolves; the box runtime resolves unpinned at image build and needs its own audit
(follow-up).
zizmor (pinned, offline mode, findings at
low+ fail) and actionlint (pinned,shellcheck/pyflakes integrations disabled -- this job already applies the repo's chosen
shell floors) now run inside the required
static-checksjob. Deliberate exceptions carryreasons:
.github/zizmor.yml(the nightly'ssecrets: inheritto the bench workflows),.github/actionlint.yaml(thelager-benchrunner label).SHA pins + credential hygiene: every third-party action pinned to a commit SHA with a
version comment (dependabot's github-actions ecosystem keeps them moving), and every
checkout sets
persist-credentials: false-- nothing in these workflows pushes.Default token permissions: every workflow now declares
permissions: contents: read;jobs needing more must say so themselves. Compatible with the reusable-workflow chain in
both directions.
MERGE ORDER NOTE: this PR should merge LAST of the currently open CI series (#231, #234,
#243, #244, and the Rust gate rework). Those add or touch workflow files with tag-pinned
actions; once zizmor gates here, unpinned refs go red. Before merging this one, it gets
rebased with a final pin sweep over whatever landed.
Validated locally: zizmor and actionlint exit 0 against this branch with the configs;
pip-audit runs clean over the freshly resolved environment (zero known vulnerabilities;
the ignore-file parsing exercised with a fake entry); all six workflows YAML-parse and
every embedded run block passes
bash -n;run_pip_audit.shis shellcheck-clean at thegate's own floor. Bench workflow edits are trigger-neutral; a supervised
workflow_dispatchof integration-tests from this branch is the pre-merge validation.