Skip to content

supply-chain: attestation, SBOM, checksums, CodeQL + Scorecard (#178)#190

Merged
cdeust merged 3 commits into
mainfrom
supply-chain/attestation-sbom-scorecard-178
Jul 25, 2026
Merged

supply-chain: attestation, SBOM, checksums, CodeQL + Scorecard (#178)#190
cdeust merged 3 commits into
mainfrom
supply-chain/attestation-sbom-scorecard-178

Conversation

@cdeust

@cdeust cdeust commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Closes #178. Part of the ecosystem supply-chain sweep tracked by cdeust/enterprise-backlog#27; adapts the automatised-pipeline #77 reference pattern to Cortex's stack (uv/pip, PyPI Trusted Publishing) rather than inventing a new approach. Scorecard is adopted as a workflow_call call site into AP's single definition, not copied.

Findings first (measured on branch, 2026-07-25)

  • grep -rlE "cosign|sigstore|sbom|slsa|scorecard|attest-build-provenance" .github/workflows → no matches before this PR. Confirmed the issue's measured state.
  • The "container image" is not published. ci.yml's docker-smoke job builds the image with load: true and never pushes it to a registry — it is a bare-container contract smoke test. There is therefore no published image artifact to attest; attesting a throwaway smoke build would be theater (§8). Stated in SECURITY.md rather than fabricating a registry push.
  • The install path already hashes what it downloads. The PyPI path installs through pip (verifies against PyPI's recorded hashes); the model download goes through HuggingFace Hub (its own integrity check); the supported marketplace path consumes the git tree directly (ADR-0050). The one download with no built-in check is a raw GitHub-release asset — so that is the one the new scripts/verify_release_artifact.py covers. Criterion 4 is implemented where the gap actually is, not everywhere for symmetry.
  • Unpinned actions were broader than the issue's table of 4. Besides the 4 named (pypa/gh-action-pypi-publish, softprops/action-gh-release, docker/build-push-action, docker/setup-buildx-action), ci.yml and the three sync/pin workflows floated actions/checkout|setup-python|cache|upload-artifact. Pinned all of them so grep for unpinned third-party actions is empty (§14 boy-scout, matching AP chore(deps): bump joserfc from 1.6.4 to 1.6.8 in the uv group across 1 directory #77's "zero unpinned remain").

What changed

Area Change
release.yml Build-provenance attestation on wheel + sdist + SBOM (Sigstore). <asset>.sha256 per GitHub-release asset. CycloneDX SBOM from uv.lock (fails if torch/transformers/scipy/scikit-learn are absent). PyPI keeps PEP 740 attestations (native to Trusted Publishing). Least-privilege per-job permissions, widened only for OIDC. All actions SHA-pinned.
scorecard.yml (new) uses: cdeust/automatised-pipeline/.github/workflows/scorecard.yml@main — call site, report-only, weekly.
codeql.yml (new) Python security-and-quality, weekly cron + PR.
scripts/verify_release_artifact.py (new) stdlib-only consumer-side SHA-256 verifier; raises ArtifactIntegrityError on mismatch; CLI exit codes 0/1/2.
tests_py/scripts/test_verify_release_artifact.py (new) 9 tests; the load-bearing ones assert a tampered artifact / bit-flip / stale checksum file are rejected (§13 A3, G4).
ci.yml, marketplace-pins.yml, publish-ccplugins.yml, sync-ccplugins-fork.yml Remaining third-party actions SHA-pinned.
SECURITY.md New "What Cortex accesses" + "Supply-chain assurance" sections with verify commands and honest limits.

Verified pre-merge

$ grep -rnE "uses: [a-z0-9_-]+/[^@]+@[^ ]*$" .github/workflows/ | grep -vE "@[0-9a-f]{40}" | grep -v cdeust/automatised-pipeline
(empty)

$ actionlint .github/workflows/{release,scorecard,codeql}.yml
(no findings on new files/steps; pre-existing shellcheck notes remain in the PostgreSQL-setup and changelog scripts, out of this issue's scope)

$ ruff check . && ruff format --check .
All checks passed! · 1004 files already formatted

$ .venv/bin/python -m pytest tests_py/scripts/test_verify_release_artifact.py -q
9 passed

pyright ratchet delta = 0, by construction: pyrightconfig.json excludes scripts/ and tests_py/, and no mcp_server/ file was touched. The two new Python files cannot move the per-rule ratchet.

Release-verified-later (named steps)

Attestation, SBOM upload, and PyPI PEP 740 attestations are tags: v*-triggered and cannot run on a PR. On the next tag, verification is:

  1. gh attestation verify hypermnesia_mcp-<v>-py3-none-any.whl --repo cdeust/Cortex → binds to this repo + workflow.
  2. Release page shows hypermnesia-mcp.cdx.json + .sha256 and the SBOM contains the ML stack (the workflow grep-asserts this and fails otherwise).
  3. PyPI project page shows a verified build (attestations).
  4. python scripts/verify_release_artifact.py <asset> --checksum-file <asset>.sha256 on a downloaded asset → exit 0; tamper → exit 1.

Completion Ledger (§13.2)

# Criterion Evidence
1 Provenance on wheel/sdist/(bundle)/(image) attest-build-provenance on wheel + sdist + SBOM. Plugin bundle = git tree (marketplace, ADR-0050): provenance is the tagged commit's attestation. Container image = smoke-only, never pushed → no artifact to attest (stated in SECURITY.md).
2 PyPI Trusted Publishing + attestations Pre-existing OIDC Trusted Publishing retained; PEP 740 attestations on by default with id-token: write. No long-lived token.
3 SBOM incl. ML stack CycloneDX from uv.lock; job fails unless torch/transformers/scipy/scikit-learn appear.
4 Checksums published + verified + tamper rejected .sha256 per asset; verify_release_artifact.py + 9 tests; tamper/bit-flip/stale-checksum rejection asserted.
5 CodeQL on a schedule codeql.yml, Python security-and-quality, weekly + PR.
6 Scorecard baseline scorecard.yml call site, report-only, weekly.
7 Actions pinned to SHAs The 4 named + all others; grep for unpinned = empty.
8 SECURITY.md access + assurance New sections: transcripts/hooks/filesystem access; attestation/SBOM/checksum assurance; honest limits.

Do not merge — sweep is sequential; see enterprise-backlog#27.

🤖 Generated with Claude Code

cdeust and others added 3 commits July 25, 2026 18:59
…178)

Cortex reads and persists every session transcript and installs session
hooks with the user's full filesystem access; the wheel shipped with
nothing a user could verify. This closes that.

- release.yml: build-provenance attestation on wheel/sdist/SBOM
  (Sigstore); SHA-256 companion per GitHub-release asset; CycloneDX SBOM
  from uv.lock (torch/transformers/scipy/scikit-learn enumerated); PyPI
  keeps PEP 740 attestations; all actions SHA-pinned.
- scorecard.yml: adopts the ecosystem's single Scorecard definition as a
  workflow_call CALL SITE (AP #77), not a copy; report-only baseline.
- codeql.yml: Python security-and-quality on a weekly schedule + PR.
- scripts/verify_release_artifact.py (+ tests): consumer-side checksum
  verifier that REJECTS a tampered artifact (criterion 4, §13 A3/G4).
- ci.yml + 3 sync/pin workflows: remaining actions SHA-pinned so a grep
  for unpinned third-party actions is empty (§14 boy-scout).
- SECURITY.md: states what Cortex accesses (transcripts, hooks, fs) and
  the assurance now offered, with verify commands and honest limits.

Co-Authored-By: Claude <noreply@anthropic.com>
…ut-file)

The cyclonedx-py `requirements` subcommand rejects `--outfile`; the flag is
`-o/--output-file` and the format value is uppercase JSON. Validated locally
against Cortex's uv.lock: SBOM generates and the ML-stack grep-assert
(torch/transformers/scipy/scikit-learn) passes.

Co-Authored-By: Claude <noreply@anthropic.com>
CI showed "CodeQL analyses from advanced configurations cannot be processed
when the default setup is enabled": Cortex already runs CodeQL via GitHub's
default code-scanning setup (python, javascript-typescript, actions) on a
schedule, which satisfies criterion 5. The advanced workflow only conflicted.
SECURITY.md updated to reference default setup.

Co-Authored-By: Claude <noreply@anthropic.com>
@cdeust
cdeust merged commit 1a45523 into main Jul 25, 2026
15 checks passed
@cdeust
cdeust deleted the supply-chain/attestation-sbom-scorecard-178 branch July 25, 2026 17:54
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.

supply-chain: no provenance, SBOM, signing or Scorecard — PyPI wheel + plugin + image published unattested, 4 unpinned actions

1 participant