Skip to content

security: harden supply chain and release provenance - #98

Merged
0xzr merged 1 commit into
mainfrom
codex/supply-chain-hardening-20260729
Jul 29, 2026
Merged

security: harden supply chain and release provenance#98
0xzr merged 1 commit into
mainfrom
codex/supply-chain-hardening-20260729

Conversation

@0xzr

@0xzr 0xzr commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Closes #72.

Summary

  • require pinned Actions, CodeQL, Bandit, pip-audit, zizmor, Trivy, and fail-closed exception policy
  • build and scan one exact release image before a separate least-privilege publisher promotes it
  • generate package/image SPDX SBOMs and GitHub provenance attestations
  • document reproducible security/release workflows and live merge enforcement

Validation

  • 883 tests passed; 84% coverage
  • Ruff and actionlint clean
  • Bandit 1.9.4, pip-audit 2.10.1, and zizmor 1.28.0 clean
  • Trivy 0.72.0: zero high/critical findings on exact archive handoff
  • fresh wheel/sdist build, Twine check, and install smoke passed
  • Sol 5.6 xhigh approved exact pre-commit tree ce85abe95bfc05f670571d9610a698664b3c2ce4

Summary by Sourcery

Harden supply chain security and release provenance for packages and container images.

New Features:

  • Add dedicated security workflows for CodeQL, Python source/dependency analysis, GitHub Actions scanning, and container vulnerability auditing.
  • Introduce release evidence and Docker workflows that generate SBOMs and GitHub provenance attestations for packages and container images, with strict tag provenance checks.

Enhancements:

  • Pin all GitHub Actions and the container base image to exact commit or digest identifiers and enforce this via new supply-chain tests.
  • Refine Docker image build and publish process to separate scan and promotion stages with least-privilege publishing and high/critical vulnerability gates.
  • Extend release checklist and security documentation to cover local reproduction of security gates, exception handling, and provenance verification.
  • Add a security optional-dependency extra for pinned scanner tooling and update CI to use pinned action SHAs and tools.

Tests:

  • Add supply-chain tests to ensure action pinning, container base digest pinning, security workflows, exception policies, and release evidence workflows remain enforced.

@sourcery-ai

sourcery-ai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a comprehensive supply-chain security and release provenance framework: all GitHub Actions and Docker base images are digest-pinned, new security workflows run Bandit/pip-audit/zizmor/Trivy with a strict, time-bounded exception policy, release tags are provenance-validated and produce SBOMs plus attestations for packages and container images, and documentation/checklists are updated to describe and enforce these processes.

Sequence diagram for Docker release image gating and provenance

sequenceDiagram
    actor Operator
    participant GitHubActions as GitHub_Actions
    participant DockerGate as Docker_workflow_gate_job
    participant ArtifactStore as Actions_artifacts
    participant DockerPublish as Docker_workflow_publish_job
    participant GHCR as GHCR_registry
    participant Trivy as Trivy_scanner
    participant Attest as actions_attest

    Operator->>GitHubActions: push tag vX.Y.Z
    GitHubActions->>DockerGate: start gate job
    DockerGate->>DockerGate: Validate release tag provenance
    DockerGate->>DockerGate: scripts/security_exceptions.py validate
    DockerGate->>DockerGate: docker/build-push-action (build image archive)
    DockerGate->>Trivy: aquasecurity/trivy-action (scan archive)
    Trivy-->>DockerGate: fail on HIGH,CRITICAL vulns
    DockerGate->>ArtifactStore: upload-artifact scanned-release-image-${GITHUB_SHA}

    GitHubActions->>DockerPublish: start publish job (needs gate)
    DockerPublish->>ArtifactStore: download-artifact scanned-release-image-${GITHUB_SHA}
    DockerPublish->>DockerPublish: docker load exact scanned image
    DockerPublish->>GHCR: docker/login-action
    DockerPublish->>GHCR: docker tag && docker push tags
    DockerPublish->>DockerPublish: compute digest via docker buildx imagetools inspect
    DockerPublish->>Attest: anchore/sbom-action (generate container-sbom.spdx.json)
    DockerPublish->>Attest: actions/attest (image provenance)
    DockerPublish->>Attest: actions/attest (image SBOM)
    Attest-->>GHCR: push OCI attestations
Loading

Sequence diagram for release evidence SBOMs and package attestations

sequenceDiagram
    actor Operator
    participant GitHubActions as GitHub_Actions
    participant Gate as Release_evidence_gate_job
    participant Packages as Release_evidence_packages_job
    participant PipAudit as pip_audit
    participant Sbom as anchore_sbom_action
    participant Attest as actions_attest
    participant ArtifactStore as Actions_artifacts

    Operator->>GitHubActions: push tag vX.Y.Z

    GitHubActions->>Gate: start gate job
    Gate->>Gate: Validate release tag provenance
    Gate->>Gate: install pip-audit==2.10.1
    Gate->>Gate: scripts/security_exceptions.py validate
    Gate->>Gate: scripts/security_exceptions.py ids pip-audit
    Gate->>PipAudit: python -m pip_audit . --strict
    PipAudit-->>Gate: fail on any vulnerable dependency

    GitHubActions->>Packages: start packages job (needs gate)
    Packages->>Packages: Revalidate release tag provenance
    Packages->>Packages: python -m build (wheel, sdist)
    Packages->>Sbom: anchore/sbom-action (wheel.spdx.json)
    Packages->>Sbom: anchore/sbom-action (sdist.spdx.json)
    Packages->>Attest: actions/attest (package provenance)
    Packages->>Attest: actions/attest (wheel SBOM)
    Packages->>Attest: actions/attest (sdist SBOM)
    Packages->>ArtifactStore: upload-artifact python-release-evidence-${GITHUB_REF_NAME}
Loading

File-Level Changes

Change Details Files
Restructure Docker release workflow into a gated, least-privilege publish pipeline with Trivy scanning and SBOM/provenance attestations.
  • Split single Docker job into gate and publish jobs, wiring publish to depend on gate with stricter permissions.
  • Build an exact Docker image archive via buildx, scan it with pinned Trivy, and upload the tarball plus SHA256 as a short-lived artifact.
  • Download and reload the scanned image in the publish job, promote it to GHCR tags, verify digest consistency, and generate container SBOM plus provenance/SBOM attestations using anchore/sbom-action and actions/attest.
.github/workflows/docker.yml
Introduce dedicated security and CodeQL workflows plus a strict exception policy script that fail closed on misconfiguration or native suppressions.
  • Add a Security workflow running Bandit, pip-audit, zizmor, and Trivy with pinned versions and shared validation via a new scripts/security_exceptions.py helper.
  • Add a CodeQL workflow for Python using security-extended queries and pinned github/codeql-action SHAs.
  • Implement scripts/security_exceptions.py to validate a JSON exception registry, enforce scoped/time-bounded exceptions, surface IDs to scanners, and detect inline suppressions in source and YAML.
.github/workflows/security.yml
.github/workflows/codeql.yml
scripts/security_exceptions.py
.github/security-exceptions.json
Add a release evidence workflow that produces reproducible artifacts, SBOMs, and attestations for Python package releases.
  • Create a release-evidence workflow triggered on version tags that validates tag provenance against pyproject.toml and main ancestry.
  • Gate releases with pip-audit using the shared exception policy before building artifacts.
  • Build a single wheel and sdist, generate SPDX SBOMs for each via anchore/sbom-action, attest provenance and SBOMs via actions/attest, and upload them as a long-lived evidence artifact.
.github/workflows/release-evidence.yml
Pin all third-party GitHub Actions and tighten CI/workflow configuration, including digest-pinning the Docker base image and aligning tests.
  • Replace tag-based action references (e.g., actions/checkout@v7) with exact commit SHA pins across CI, benchmarks, pages, publish-opencode, and Docker workflows, and disable persist-credentials where appropriate.
  • Update cache, artifact, and deploy actions to pinned SHAs and adjust small CI details (e.g., loop variable rename) to satisfy lint/tests.
  • Change Dockerfile base image to a digest-pinned python:3.14-alpine image and adjust user creation and dependency install; update tests to enforce digest-pinned base images and action pins.
.github/workflows/ci.yml
.github/workflows/benchmarks.yml
.github/workflows/pages.yml
.github/workflows/publish-opencode.yml
Dockerfile
tests/test_ci_config.py
Extend tests and project configuration to cover supply-chain expectations and provide a security tooling extra.
  • Add tests/test_supply_chain.py to assert all third-party actions are commit-pinned, composite actions are followed, release workflows produce SBOMs/attestations, container workflows gate publishing on scanning, and security docs/enforcement files contain required content.
  • Add a security optional dependency extra with pinned Bandit/pip-audit/zizmor and ensure dev extra includes PyYAML required by new tests/scripts.
  • Introduce SECURITY.md and SECURITY_ENFORCEMENT.md to document reporting, automated gates, exception policy, merge enforcement, and provenance verification; reference SECURITY.md from README and expand RELEASE_CHECKLIST with security and provenance steps.
tests/test_supply_chain.py
pyproject.toml
SECURITY.md
docs/SECURITY_ENFORCEMENT.md
README.md
docs/RELEASE_CHECKLIST.md

Assessment against linked issues

Issue Objective Addressed Explanation
#72 Implement automated security scanning for source code, dependencies, workflows, and container images on PRs/main, with high/critical findings failing gates and a documented exception process.
#72 Generate and distribute SBOMs and verifiable provenance/attestations for release wheels, source archives, and container images.
#72 Harden the supply chain by pinning GitHub Actions and container base images, using least-privilege workflow permissions, and documenting local reproduction and false-positive handling.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • The Dockerfile now pins an Alpine base image but only installs httpx==0.28.1 instead of the project itself (pip install .), which will leave the container without the application code installed and likely break the runtime image; consider restoring an explicit install of the project or a clear rationale for this change.
  • In the Docker publish job, the image "digest" is derived by hashing docker buildx imagetools inspect output rather than using the registry's actual content digest, which means the recorded digest and attestations will not match the pushed image; it would be safer to parse the real OCI digest from imagetools/docker inspect instead of hashing the inspect output.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The Dockerfile now pins an Alpine base image but only installs `httpx==0.28.1` instead of the project itself (`pip install .`), which will leave the container without the application code installed and likely break the runtime image; consider restoring an explicit install of the project or a clear rationale for this change.
- In the Docker `publish` job, the image "digest" is derived by hashing `docker buildx imagetools inspect` output rather than using the registry's actual content digest, which means the recorded digest and attestations will not match the pushed image; it would be safer to parse the real OCI digest from `imagetools`/`docker inspect` instead of hashing the inspect output.

## Individual Comments

### Comment 1
<location path=".github/workflows/docker.yml" line_range="19-27" />
<code_context>

-      - name: Log in to GHCR
-        uses: docker/login-action@v4
+      - name: Validate release tag provenance
+        shell: bash
+        run: |
+          set -euo pipefail
+          package_version=$(python -c \
+            'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')
+          test "$GITHUB_REF_NAME" = "v$package_version"
+          git fetch --no-tags origin main
+          git merge-base --is-ancestor "$GITHUB_SHA" origin/main
+
+      - name: Validate security exceptions
</code_context>
<issue_to_address>
**issue (bug_risk):** `python` invocation may fail on runners where only `python3` is available or lacks `tomllib`

This step depends on a pre-existing `python` with `tomllib`, which isn’t guaranteed on GitHub-hosted runners (some only have `python3`, and system Python may be <3.11). That can cause this check to fail due to the runner image rather than the code.

Consider either running this after `actions/setup-python` so a known 3.11+ version is available, or explicitly using `python3` and installing a `tomllib` backport when on <3.11, so the provenance check is stable across runner images.
</issue_to_address>

### Comment 2
<location path=".github/workflows/release-evidence.yml" line_range="22-30" />
<code_context>

-      - name: Log in to GHCR
-        uses: docker/login-action@v4
+      - name: Validate release tag provenance
+        shell: bash
+        run: |
+          set -euo pipefail
+          package_version=$(python -c \
+            'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')
+          test "$GITHUB_REF_NAME" = "v$package_version"
+          git fetch --no-tags origin main
+          git merge-base --is-ancestor "$GITHUB_SHA" origin/main
+
+      - name: Validate security exceptions
</code_context>
<issue_to_address>
**issue (bug_risk):** Same `python` availability issue in release-evidence provenance gate

This job invokes `python` with `tomllib` before `actions/setup-python`, so it may fail on runners where `python` is missing or too old to include `tomllib`, causing flaky provenance checks.

To align with the security/docker provenance logic and improve reliability, either:
- Run `actions/setup-python` first and use that interpreter, or
- Call `python3` with an enforced minimum version / explicit `tomllib` handling.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread .github/workflows/docker.yml
Comment thread .github/workflows/release-evidence.yml
@0xzr
0xzr merged commit 0c2d031 into main Jul 29, 2026
14 checks passed
@0xzr
0xzr deleted the codex/supply-chain-hardening-20260729 branch July 29, 2026 12:18
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.

Add supply-chain scanning, SBOMs, and release provenance

2 participants