Skip to content

Fix supply chain vulnerabilities in CI/CD and liboqs advisory checks - #136

Merged
fnyaker merged 6 commits into
mainfrom
claude/festive-cori-xkq3xn
Oct 15, 2026
Merged

fnyaker merged 6 commits into
mainfrom
claude/festive-cori-xkq3xn

Conversation

@fnyaker

@fnyaker fnyaker commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

Summary

Hardens the CI/CD supply chain and closes a gap nothing watched: liboqs's own advisories. Published images are used only once their provenance is verified, no CI tool gets the Docker socket, releases publish only what the suite passed, and liboqs is built with only the two schemes a node uses.

Key Changes

Supply chain (BUGSVULNS 112–114)

  • Image provenance verification: .github/scripts/pull-verified.sh and .github/scripts/provide-base.sh use a published image only once gh attestation verify proves which workflow on main built it, then by that digest; otherwise the base is built locally. The reference they print is checked to be a plain image reference and passed between steps through a file in $RUNNER_TEMP, never $GITHUB_ENV.
  • No Docker socket: Trivy reads a docker save file (--input) or the registry by digest.
  • Scan before login: the GHCR login comes after the scan, so a poisoned scanner cannot reach a credential that pushes :latest.
  • Base image: base-image.yml publishes from main only, with a provenance attestation.
  • Release: waits for the suite on the tagged commit (reusable tests.yml), builds on a verified base, scans its image, attests an SBOM, leaves :latest to ci.yml, and attaches the zipapp with the runner's own gh instead of a third-party action.
  • SBOM: CycloneDX, attested beside provenance for ci and release images.

liboqs (BUGSVULNS 115)

  • liboqs 0.16.0 carries two advisories in its LMS code, which the build used to compile. scripts/build_liboqs.sh now builds only ML-KEM-768 and ML-DSA-65 (OQS_MINIMAL_BUILD), with stateful signatures and hazardous signing off; the library goes from about 26 MB to 600 KB.
  • tests/tools/liboqs_advisories.py, run by audit.yml on every change and weekly, checks liboqs's own advisories against the lock; exceptions live in scripts/liboqs.accepted with their reason.

Repository checks

  • New repo-checks.yml: zizmor (workflows), gitleaks (whole history), hadolint and Trivy misconfiguration rules (Dockerfiles). Exceptions with reasons in .github/zizmor.yml, .gitleaksignore, .hadolint.yaml, .trivyignore.
  • Dependabot waits 7 days before offering a release (cooldown); security updates are not held.
  • Harden-Runner in audit mode on the publishing jobs.
  • Workflows call $/.github/workflows/tests.yml (self-repository form).

Open

  • BUGSVULNS 116: the container still runs as root; moving it touches /data ownership, the documented /root/.ssh mount and fleet's Docker socket, so it is its own change. Trivy's DS-0002 is set aside until then.

Tests

  • tests/test_ci_scripts.py, tests/test_liboqs_advisories.py, TestWhatIsCompiled in tests/test_liboqs_lock.py, and new invariants in tests/test_workflows_pinned.py (no socket, scan before login, no docker pull outside the verifying scripts, release waits for the suite, no image reference through $GITHUB_ENV, Dependabot cooldown).

Documentation

  • BUGSVULNS.MD 112–116, TEST.md, SECURITY.md, Docs/Setup/guide, Docs/Architecture/gotchas.md, AGENTS.md. Version 0.4.114.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VrYW4X7e3ouazLWKRRnimK

…qs minimal build; 0.4.112

- Trivy reads a saved image or the registry by digest, never the Docker
  socket; the GHCR login comes after the scan (BUGSVULNS 110).
- release.yml runs the suite first (reusable tests.yml), scans its image,
  attests an SBOM, and leaves :latest to ci.yml; the zipapp is attached
  with gh instead of a third-party action (BUGSVULNS 111).
- base-image.yml publishes from main only and attests the base; every job
  uses a published image only once gh attestation verify proves it, by
  digest, else builds the base (.github/scripts, BUGSVULNS 112).
- repo-checks.yml: zizmor on the workflows, gitleaks over the whole
  history, hadolint and Trivy misconfiguration rules on the Dockerfiles.
- CycloneDX SBOM attested beside provenance for ci and release images.
- Dependabot cooldown of 7 days on every ecosystem.
- Harden-Runner (audit) on the publishing jobs.
- liboqs built with ML-KEM-768 and ML-DSA-65 only: LMS/XMSS and hazardous
  signing off, removing the code two 0.16.0 advisories land in; audit.yml
  checks liboqs's own advisories against the lock (BUGSVULNS 113).
- Container running as root recorded as open (BUGSVULNS 114).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VrYW4X7e3ouazLWKRRnimK
Comment thread .github/workflows/tests.yml Fixed
…UB_ENV; 0.4.113

CodeQL (actions) flagged the scripts' output written to $GITHUB_ENV: a
value with a newline in it would define whatever variable its second line
names. The scripts now print only a plain lowercase reference (name and
digest, or name and tag) and refuse anything else; the workflows hand it
to later steps in $RUNNER_TEMP/<name>.ref.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VrYW4X7e3ouazLWKRRnimK
@fnyaker

fnyaker commented Oct 15, 2026

Copy link
Copy Markdown
Owner Author

@copilot resolve the merge conflicts in this pull request

…kq3xn

# Conflicts:
#	BUGSVULNS.MD

Co-authored-by: fnyaker <76814730+fnyaker@users.noreply.github.com>

Copilot AI commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Resolved by merging origin/main into this branch and fixing the merge conflict in BUGSVULNS.MD in commit 5705950.

main took BUGSVULNS 110 and 111 (the base-image race moved to 111), so
this branch's entries are 112-116 and every reference follows. Version
moves past main's 0.4.113.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VrYW4X7e3ouazLWKRRnimK
… 0.4.114

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VrYW4X7e3ouazLWKRRnimK
@fnyaker

fnyaker commented Oct 15, 2026

Copy link
Copy Markdown
Owner Author

@copilot restart the checks, the "test" check does not seems to report anything

Branch protection requires the status check `test` by name. The reusable
tests.yml made it report as `test / test`, so the PR stayed blocked with
every check green. The steps now live in .github/actions/suite, which the
`test` job of ci.yml and of release.yml runs; zizmor and the pin checks
cover the action too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VrYW4X7e3ouazLWKRRnimK

fnyaker commented Oct 15, 2026

Copy link
Copy Markdown
Owner Author

The test check was never going to report: moving the suite into the reusable tests.yml made the job report as test / test, and branch protection waits for test by name, so restarting would not help. Fixed in 4c5df48: the steps are now a composite action (.github/actions/suite) that a job named test runs in both ci.yml and release.yml, and tests/test_workflows_pinned.py holds that name. The test check should report on this run.


Generated by Claude Code

@fnyaker
fnyaker merged commit 2d6afb7 into main Oct 15, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants