diff --git a/.github/workflows/sbom-diff-and-risk-ci.yml b/.github/workflows/sbom-diff-and-risk-ci.yml index 05b9887..10056d9 100644 --- a/.github/workflows/sbom-diff-and-risk-ci.yml +++ b/.github/workflows/sbom-diff-and-risk-ci.yml @@ -1,164 +1,208 @@ -name: sbom-diff-and-risk-ci -run-name: sbom-diff-and-risk ci / ${{ github.event_name }} / ${{ github.ref_name }} - -on: - workflow_dispatch: - push: - # Version tags provide a minimal release-build scaffold without changing publishing. - tags: - - "v*" - paths: - - ".github/workflows/sbom-diff-and-risk-ci.yml" - - "tools/sbom-diff-and-risk/**" - pull_request: - paths: - - ".github/workflows/sbom-diff-and-risk-ci.yml" - - "tools/sbom-diff-and-risk/**" - -permissions: {} - -env: +name: sbom-diff-and-risk-ci +run-name: sbom-diff-and-risk ci / ${{ github.event_name }} / ${{ github.ref_name }} + +on: + workflow_dispatch: + push: + # Version tags provide a minimal release-build scaffold without changing publishing. + tags: + - "v*" + paths: + - ".github/workflows/sbom-diff-and-risk-ci.yml" + - "tools/sbom-diff-and-risk/**" + pull_request: + paths: + - ".github/workflows/sbom-diff-and-risk-ci.yml" + - "tools/sbom-diff-and-risk/**" + +permissions: {} + +env: SBOM_DIFF_RISK_PYTHON_VERSION: "3.11" SBOM_DIFF_RISK_DIST_ARTIFACT_NAME: sbom-diff-and-risk-dist + SBOM_DIFF_RISK_CHECKSUM_MANIFEST: sbom-diff-and-risk-SHA256SUMS.txt SBOM_DIFF_RISK_RELEASE_TITLE_PREFIX: sbom-diff-and-risk + +jobs: + test: + runs-on: ubuntu-latest + permissions: + contents: read + defaults: + run: + working-directory: tools/sbom-diff-and-risk + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: ${{ env.SBOM_DIFF_RISK_PYTHON_VERSION }} + + - name: Upgrade pip + run: python -m pip install --upgrade pip + + - name: Install project + run: python -m pip install -e .[dev] + + - name: Run test suite + run: python -m pytest + + - name: CLI smoke test + shell: bash + run: | + tmpdir="$(mktemp -d)" + python -m sbom_diff_risk.cli compare \ + --before examples/cdx_before.json \ + --after examples/cdx_after.json \ + --format auto \ + --out-json "$tmpdir/report.json" \ + --out-md "$tmpdir/report.md" + test -f "$tmpdir/report.json" + test -f "$tmpdir/report.md" + diff -u examples/sample-report.json "$tmpdir/report.json" + diff -u examples/sample-report.md "$tmpdir/report.md" + + build-and-attest: + # Keep provenance publication on trusted non-PR runs so consumers verify + # workflow-produced wheel/sdist artifacts from this repository workflow. + if: github.event_name != 'pull_request' + needs: test + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + attestations: write + defaults: + run: + working-directory: tools/sbom-diff-and-risk + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: ${{ env.SBOM_DIFF_RISK_PYTHON_VERSION }} + + - name: Upgrade pip + run: python -m pip install --upgrade pip + + - name: Install build tooling + run: python -m pip install build + + - name: Build distributable artifacts + run: python -m build -jobs: - test: - runs-on: ubuntu-latest - permissions: - contents: read - defaults: - run: - working-directory: tools/sbom-diff-and-risk - steps: - - name: Check out repository - uses: actions/checkout@v6 - - - name: Set up Python - uses: actions/setup-python@v6 - with: - python-version: ${{ env.SBOM_DIFF_RISK_PYTHON_VERSION }} - - - name: Upgrade pip - run: python -m pip install --upgrade pip - - - name: Install project - run: python -m pip install -e .[dev] - - - name: Run test suite - run: python -m pytest - - - name: CLI smoke test + - name: Generate SHA256 checksum manifest shell: bash run: | - tmpdir="$(mktemp -d)" - python -m sbom_diff_risk.cli compare \ - --before examples/cdx_before.json \ - --after examples/cdx_after.json \ - --format auto \ - --out-json "$tmpdir/report.json" \ - --out-md "$tmpdir/report.md" - test -f "$tmpdir/report.json" - test -f "$tmpdir/report.md" - diff -u examples/sample-report.json "$tmpdir/report.json" - diff -u examples/sample-report.md "$tmpdir/report.md" - - build-and-attest: - # Keep provenance publication on trusted non-PR runs so consumers verify - # workflow-produced wheel/sdist artifacts from this repository workflow. - if: github.event_name != 'pull_request' - needs: test - runs-on: ubuntu-latest - permissions: - contents: read - id-token: write - attestations: write - defaults: - run: - working-directory: tools/sbom-diff-and-risk - steps: - - name: Check out repository - uses: actions/checkout@v6 - - - name: Set up Python - uses: actions/setup-python@v6 - with: - python-version: ${{ env.SBOM_DIFF_RISK_PYTHON_VERSION }} + set -euo pipefail + shopt -s nullglob - - name: Upgrade pip - run: python -m pip install --upgrade pip + cd dist + artifacts=( *.tar.gz *.whl ) + IFS=$'\n' + artifacts=( $(printf '%s\n' "${artifacts[@]}" | LC_ALL=C sort) ) + unset IFS - - name: Install build tooling - run: python -m pip install build + if [ "${#artifacts[@]}" -ne 2 ]; then + echo "Expected exactly one source distribution and one wheel in dist/." >&2 + printf 'Found %s artifact(s):\n' "${#artifacts[@]}" >&2 + printf ' %s\n' "${artifacts[@]}" >&2 + exit 1 + fi - - name: Build distributable artifacts - run: python -m build + sha256sum "${artifacts[@]}" > "${SBOM_DIFF_RISK_CHECKSUM_MANIFEST}" + grep -E ' sbom_diff_and_risk-.+\.tar\.gz$' "${SBOM_DIFF_RISK_CHECKSUM_MANIFEST}" + grep -E ' sbom_diff_and_risk-.+\.whl$' "${SBOM_DIFF_RISK_CHECKSUM_MANIFEST}" + cat "${SBOM_DIFF_RISK_CHECKSUM_MANIFEST}" - - name: Upload wheel and source distribution artifact + - name: Upload distribution artifact and checksum manifest uses: actions/upload-artifact@v7 with: name: ${{ env.SBOM_DIFF_RISK_DIST_ARTIFACT_NAME }} path: | tools/sbom-diff-and-risk/dist/*.whl tools/sbom-diff-and-risk/dist/*.tar.gz + tools/sbom-diff-and-risk/dist/${{ env.SBOM_DIFF_RISK_CHECKSUM_MANIFEST }} if-no-files-found: error - name: Generate artifact attestation for built distributions uses: actions/attest@v4 with: - subject-path: ${{ github.workspace }}/tools/sbom-diff-and-risk/dist/* - - publish-release-assets: - # Publish the exact built wheel/sdist bytes from this run as release assets. - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') - needs: build-and-attest - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Check out repository - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Download built distribution artifact - uses: actions/download-artifact@v8 - with: - name: ${{ env.SBOM_DIFF_RISK_DIST_ARTIFACT_NAME }} - path: release-assets - - - name: Publish release assets from CI-built distributions - shell: bash - env: - GH_TOKEN: ${{ github.token }} - GH_REPO: ${{ github.repository }} - RELEASE_TAG: ${{ github.ref_name }} - RELEASE_TITLE_PREFIX: ${{ env.SBOM_DIFF_RISK_RELEASE_TITLE_PREFIX }} - run: | + subject-path: | + ${{ github.workspace }}/tools/sbom-diff-and-risk/dist/*.whl + ${{ github.workspace }}/tools/sbom-diff-and-risk/dist/*.tar.gz + + publish-release-assets: + # Publish the exact built wheel/sdist bytes and checksum manifest from this run. + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + needs: build-and-attest + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Download built distribution artifact and checksum manifest + uses: actions/download-artifact@v8 + with: + name: ${{ env.SBOM_DIFF_RISK_DIST_ARTIFACT_NAME }} + path: release-assets + + - name: Publish release assets from CI-built distributions + shell: bash + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + RELEASE_TAG: ${{ github.ref_name }} + RELEASE_TITLE_PREFIX: ${{ env.SBOM_DIFF_RISK_RELEASE_TITLE_PREFIX }} + run: | set -euo pipefail shopt -s nullglob assets=(release-assets/*.whl release-assets/*.tar.gz) - if [ "${#assets[@]}" -eq 0 ]; then - echo "No release assets found in release-assets/" >&2 + IFS=$'\n' + assets=( $(printf '%s\n' "${assets[@]}" | LC_ALL=C sort) ) + unset IFS + checksum_manifest="release-assets/${SBOM_DIFF_RISK_CHECKSUM_MANIFEST}" + + if [ "${#assets[@]}" -ne 2 ]; then + echo "Expected exactly one wheel and one source distribution in release-assets/." >&2 + printf 'Found %s artifact(s):\n' "${#assets[@]}" >&2 + printf ' %s\n' "${assets[@]}" >&2 exit 1 fi - title="${RELEASE_TITLE_PREFIX} ${RELEASE_TAG}" - - if gh release view "${RELEASE_TAG}" --repo "${GH_REPO}" >/dev/null 2>&1; then - is_draft="$(gh release view "${RELEASE_TAG}" --repo "${GH_REPO}" --json isDraft -q .isDraft)" - if [ "${is_draft}" != "true" ]; then - echo "Release ${RELEASE_TAG} already exists and is published; leaving assets unchanged." - exit 0 - fi - else - gh release create "${RELEASE_TAG}" \ - --repo "${GH_REPO}" \ - --draft \ - --verify-tag \ - --title "${title}" \ - --notes "Release assets for ${RELEASE_TAG}. See docs/release-provenance.md for provenance verification guidance." + if [ ! -f "${checksum_manifest}" ]; then + echo "Missing checksum manifest: ${checksum_manifest}" >&2 + exit 1 fi - gh release upload "${RELEASE_TAG}" "${assets[@]}" --repo "${GH_REPO}" --clobber - gh release edit "${RELEASE_TAG}" --repo "${GH_REPO}" --draft=false --title "${title}" + grep -E ' sbom_diff_and_risk-.+\.tar\.gz$' "${checksum_manifest}" + grep -E ' sbom_diff_and_risk-.+\.whl$' "${checksum_manifest}" + assets+=( "${checksum_manifest}" ) + + title="${RELEASE_TITLE_PREFIX} ${RELEASE_TAG}" + + if gh release view "${RELEASE_TAG}" --repo "${GH_REPO}" >/dev/null 2>&1; then + is_draft="$(gh release view "${RELEASE_TAG}" --repo "${GH_REPO}" --json isDraft -q .isDraft)" + if [ "${is_draft}" != "true" ]; then + echo "Release ${RELEASE_TAG} already exists and is published; leaving assets unchanged." + exit 0 + fi + else + gh release create "${RELEASE_TAG}" \ + --repo "${GH_REPO}" \ + --draft \ + --verify-tag \ + --title "${title}" \ + --notes "Release assets for ${RELEASE_TAG}. See docs/release-provenance.md for provenance verification guidance." + fi + + gh release upload "${RELEASE_TAG}" "${assets[@]}" --repo "${GH_REPO}" + gh release edit "${RELEASE_TAG}" --repo "${GH_REPO}" --draft=false --title "${title}" diff --git a/tools/sbom-diff-and-risk/README.md b/tools/sbom-diff-and-risk/README.md index 7dfef4b..81d0a05 100644 --- a/tools/sbom-diff-and-risk/README.md +++ b/tools/sbom-diff-and-risk/README.md @@ -239,9 +239,10 @@ This section is about verifying `sbom-diff-and-risk` itself. If you want the sho This repository also records provenance for `sbom-diff-and-risk` itself by generating GitHub artifact attestations for the wheel and source distribution produced by the `sbom-diff-and-risk-ci` workflow. - the attested files are the wheel and source distribution built by `python -m build` from `tools/sbom-diff-and-risk` -- the build files are uploaded together as the `sbom-diff-and-risk-dist` workflow artifact -- version-tag runs also publish those same built files as GitHub Release assets for the matching tag -- only trusted non-PR runs publish the attestation +- the build files are uploaded together as the `sbom-diff-and-risk-dist` workflow artifact +- version-tag runs also publish those same built files as GitHub Release assets for the matching tag +- releases produced by the updated workflow include `sbom-diff-and-risk-SHA256SUMS.txt` for local SHA256 verification of downloaded wheel and source distribution files +- only trusted non-PR runs publish the attestation - consumers can verify workflow-built artifacts with `gh attestation verify` - consumers can verify immutable releases and downloaded release assets with `gh release verify` and `gh release verify-asset` - this complements the tool's analysis of third-party supply-chain inputs, but it does not replace that analysis diff --git a/tools/sbom-diff-and-risk/docs/release-provenance.md b/tools/sbom-diff-and-risk/docs/release-provenance.md index 3913a23..b541744 100644 --- a/tools/sbom-diff-and-risk/docs/release-provenance.md +++ b/tools/sbom-diff-and-risk/docs/release-provenance.md @@ -1,25 +1,30 @@ # Release provenance and release asset verification -`sbom-diff-and-risk` now has two GitHub-hosted provenance surfaces for its packaged wheel and source distribution: +`sbom-diff-and-risk` now has two GitHub-hosted provenance surfaces for its packaged wheel and source distribution: 1. workflow-artifact attestations for the files built by `.github/workflows/sbom-diff-and-risk-ci.yml` 2. GitHub Release verification for version-tag releases that publish those same built files as release assets -This document is about the second surface: verifying a GitHub Release and a downloaded release asset. - -This page is only about the `sbom-diff-and-risk` tool's own GitHub Releases. If you want the quick "which verification page do I need?" guide, start with [verification.md](D:/OneDrive/Code/scientific-computing-toolkit/tools/sbom-diff-and-risk/docs/verification.md). +This document is about the second surface: verifying a GitHub Release and a downloaded release asset. + +This page is only about the `sbom-diff-and-risk` tool's own GitHub Releases. If you want the quick "which verification page do I need?" guide, start with [verification.md](D:/OneDrive/Code/scientific-computing-toolkit/tools/sbom-diff-and-risk/docs/verification.md). + +Release assets produced by the updated workflow also include a deterministic SHA256 checksum manifest named `sbom-diff-and-risk-SHA256SUMS.txt`. The manifest is written with filenames sorted in a stable order. It is not a separate provenance system; it is a local byte-integrity check that helps reviewers confirm downloaded wheel and source distribution files match the hashes published with the same GitHub Release. ## What the release workflow now does For version tags matching `v*`, the `sbom-diff-and-risk-ci` workflow: -1. builds the wheel and source distribution in `build-and-attest` -2. uploads them as the workflow artifact `sbom-diff-and-risk-dist` -3. generates a workflow artifact attestation for those built files -4. downloads that same workflow artifact in `publish-release-assets` -5. publishes those exact `.whl` and `.tar.gz` files as GitHub Release assets for the matching tag - -This intentionally reuses the same workflow-built bytes for both the workflow artifact and the release asset surfaces. It does not add PyPI publishing or a separate rebuild-only release pipeline. +1. builds the wheel and source distribution in `build-and-attest` +2. generates `dist/sbom-diff-and-risk-SHA256SUMS.txt` with SHA256 hashes for the built `.whl` and `.tar.gz` +3. uploads the distributions and checksum manifest as the workflow artifact `sbom-diff-and-risk-dist` +4. generates a workflow artifact attestation for the built distribution files +5. downloads that same workflow artifact in `publish-release-assets` +6. publishes those exact `.whl` and `.tar.gz` files plus the checksum manifest as GitHub Release assets for the matching tag + +This intentionally reuses the same workflow-built bytes for both the workflow artifact and the release asset surfaces. It does not add PyPI publishing or a separate rebuild-only release pipeline. + +The workflow artifact attestation subject remains the built wheel and source distribution. The checksum manifest verifies hashes for those files, but the manifest itself is not presented as a replacement for artifact attestation. ## What release verification covers @@ -35,53 +40,91 @@ If immutable releases are not enabled for the repository, the release may still ## Manual verification for a release -Use this path after a successful version-tag run such as `v0.4.0`. +Use this path after a successful version-tag run produced by the updated workflow. 1. Open the repository's **Releases** page. 2. Open the release for the version tag you want to verify. 3. Check whether the release is immutable before relying on release verification: -```bash -gh release view v0.4.0 --repo stacknil/scientific-computing-toolkit --json isImmutable,assets,url -``` - -4. Confirm the release includes the packaged assets: - - `sbom_diff_and_risk--py3-none-any.whl` - - `sbom_diff_and_risk-.tar.gz` -5. If the repository uses immutable releases, confirm GitHub shows `Immutable` on the release page. -6. Download one of the release assets locally. -7. Verify the release itself with GitHub CLI: - -```bash -gh release verify v0.4.0 --repo stacknil/scientific-computing-toolkit -``` - -8. Verify the downloaded asset against the release attestation: - -```bash -gh release verify-asset v0.4.0 path/to/sbom_diff_and_risk-0.4.0-py3-none-any.whl \ - --repo stacknil/scientific-computing-toolkit -``` +```bash +gh release view --repo stacknil/scientific-computing-toolkit --json isImmutable,assets,url +``` + +4. Confirm the release includes the packaged assets: + - `sbom_diff_and_risk--py3-none-any.whl` + - `sbom_diff_and_risk-.tar.gz` + - `sbom-diff-and-risk-SHA256SUMS.txt` +5. If the repository uses immutable releases, confirm GitHub shows `Immutable` on the release page. +6. Download the release assets locally. +7. Verify the release itself with GitHub CLI: + +```bash +gh release verify --repo stacknil/scientific-computing-toolkit +``` + +8. Verify the downloaded asset against the release attestation: + +```bash +gh release verify-asset path/to/sbom_diff_and_risk--py3-none-any.whl \ + --repo stacknil/scientific-computing-toolkit +``` If `isImmutable` is `false`, the release asset can still be downloaded, but the supported provenance path for this repository remains the workflow-artifact attestation flow from [self-provenance.md](D:/OneDrive/Code/scientific-computing-toolkit/tools/sbom-diff-and-risk/docs/self-provenance.md). You can inspect structured output as JSON: ```bash -gh release verify v0.4.0 \ - --repo stacknil/scientific-computing-toolkit \ - --format json +gh release verify \ + --repo stacknil/scientific-computing-toolkit \ + --format json ``` ```bash -gh release verify-asset v0.4.0 path/to/sbom_diff_and_risk-0.4.0.tar.gz \ - --repo stacknil/scientific-computing-toolkit \ - --format json -``` +gh release verify-asset path/to/sbom_diff_and_risk-.tar.gz \ + --repo stacknil/scientific-computing-toolkit \ + --format json +``` + +## Checksum manifest verification + +Download the wheel, source distribution, and checksum manifest from the same release: + +```bash +mkdir -p release-assets +gh release download \ + --repo stacknil/scientific-computing-toolkit \ + --pattern 'sbom_diff_and_risk-*' \ + --pattern 'sbom-diff-and-risk-SHA256SUMS.txt' \ + --dir release-assets +``` + +On Linux or macOS, verify both distribution files with `sha256sum`: + +```bash +cd release-assets +sha256sum --check sbom-diff-and-risk-SHA256SUMS.txt +``` + +On Windows PowerShell, verify the same manifest with `Get-FileHash`: + +```powershell +Set-Location release-assets +Get-Content .\sbom-diff-and-risk-SHA256SUMS.txt | ForEach-Object { + $expected, $file = $_ -split '\s+', 2 + $actual = ((Get-FileHash -Algorithm SHA256 -LiteralPath $file).Hash).ToLowerInvariant() + if ($actual -ne $expected) { + throw "Checksum mismatch for $file" + } + "$file OK" +} +``` + +A passing checksum check means the local downloaded wheel and source distribution match the hashes in the release manifest. It does not by itself prove who built or uploaded the artifacts, and the manifest itself is not the attested subject. Pair checksum verification with workflow artifact attestation or immutable release verification when you need provenance. ## Important boundary notes -- `gh release verify-asset` verifies a local file path against a release attestation. It does not verify a workflow artifact download directly unless that file is also a release asset. -- GitHub's generated source-code ZIP and tarball downloads are not covered by `gh release verify-asset`. +- `gh release verify-asset` verifies a local file path against a release attestation. It does not verify a workflow artifact download directly unless that file is also a release asset. +- `sbom-diff-and-risk-SHA256SUMS.txt` checks local file integrity against the release manifest. It does not replace provenance verification. +- GitHub's generated source-code ZIP and tarball downloads are not covered by `gh release verify-asset`. - A successful release verification does not replace the workflow-artifact attestation story; it complements it. - This repository now has a separate TestPyPI Trusted Publishing readiness workflow, but production PyPI publishing remains deferred. For the production decision gate, publisher identity, future workflow shape, and provenance boundary, see [pypi-production-publishing-decision.md](D:/OneDrive/Code/scientific-computing-toolkit/tools/sbom-diff-and-risk/docs/pypi-production-publishing-decision.md). diff --git a/tools/sbom-diff-and-risk/docs/reviewer-evidence-pack.md b/tools/sbom-diff-and-risk/docs/reviewer-evidence-pack.md index 4925302..f6b49ee 100644 --- a/tools/sbom-diff-and-risk/docs/reviewer-evidence-pack.md +++ b/tools/sbom-diff-and-risk/docs/reviewer-evidence-pack.md @@ -1,171 +1,192 @@ -# Reviewer evidence pack - -This page is a reproducible evidence checklist for reviewing `sbom-diff-and-risk`. It focuses on what can be verified from the repository, examples, GitHub release assets, and TestPyPI dry-run documentation. It does not introduce new CLI behavior. - -## Project Identity - -`sbom-diff-and-risk` is a local-first deterministic CLI for comparing SBOMs and dependency manifests. It is designed to produce stable review evidence for dependency changes. - -Core identity: - -- local deterministic SBOM/dependency diffing -- JSON, Markdown, and SARIF output -- local policy checks over diff and risk findings -- optional provenance-aware reporting through explicit PyPI enrichment -- optional OpenSSF Scorecard evidence when repository mapping is explicit enough -- release and distribution documentation that separates tool behavior from artifact provenance - -## Reproducible Demo Path - -From `tools/sbom-diff-and-risk`, install the package in editable development mode: - -```powershell -python -m pip install -e .[dev] -``` - -Generate the default CycloneDX example reports: - -```powershell -sbom-diff-risk compare ` - --before examples/cdx_before.json ` - --after examples/cdx_after.json ` - --format auto ` - --out-json outputs/report.json ` - --out-md outputs/report.md -``` - -Expected output files: - -- `outputs/report.json` -- `outputs/report.md` - -Compare the outputs against the checked-in sample reports: - -```powershell -Compare-Object (Get-Content examples/sample-report.json) (Get-Content outputs/report.json) -Compare-Object (Get-Content examples/sample-report.md) (Get-Content outputs/report.md) -``` - -No differences means the sample path reproduced the committed example output. - -Generate the strict-policy SARIF sample: - -```powershell -sbom-diff-risk compare ` - --before examples/sarif_before.json ` - --after examples/sarif_after.json ` - --policy examples/policy-strict.yml ` - --out-sarif outputs/report.sarif -``` - -Compare the SARIF output against the sample: - -```powershell -Compare-Object (Get-Content examples/sample-sarif.sarif) (Get-Content outputs/report.sarif) -``` - -The SARIF sample is intentionally conservative. It covers selected high-signal findings and explicit policy violations, not every enrichment fact. - -## Release Verification Path - -Start with the GitHub Release for the version under review. For `v0.5.0`, inspect the release and assets: - -```powershell -gh release view v0.5.0 --repo stacknil/scientific-computing-toolkit --json tagName,name,isDraft,isPrerelease,assets,url -``` - +# Reviewer evidence pack + +This page is a reproducible evidence checklist for reviewing `sbom-diff-and-risk`. It focuses on what can be verified from the repository, examples, GitHub release assets, and TestPyPI dry-run documentation. It does not introduce new CLI behavior. + +## Project Identity + +`sbom-diff-and-risk` is a local-first deterministic CLI for comparing SBOMs and dependency manifests. It is designed to produce stable review evidence for dependency changes. + +Core identity: + +- local deterministic SBOM/dependency diffing +- JSON, Markdown, and SARIF output +- local policy checks over diff and risk findings +- optional provenance-aware reporting through explicit PyPI enrichment +- optional OpenSSF Scorecard evidence when repository mapping is explicit enough +- release and distribution documentation that separates tool behavior from artifact provenance + +## Reproducible Demo Path + +From `tools/sbom-diff-and-risk`, install the package in editable development mode: + +```powershell +python -m pip install -e .[dev] +``` + +Generate the default CycloneDX example reports: + +```powershell +sbom-diff-risk compare ` + --before examples/cdx_before.json ` + --after examples/cdx_after.json ` + --format auto ` + --out-json outputs/report.json ` + --out-md outputs/report.md +``` + +Expected output files: + +- `outputs/report.json` +- `outputs/report.md` + +Compare the outputs against the checked-in sample reports: + +```powershell +Compare-Object (Get-Content examples/sample-report.json) (Get-Content outputs/report.json) +Compare-Object (Get-Content examples/sample-report.md) (Get-Content outputs/report.md) +``` + +No differences means the sample path reproduced the committed example output. + +Generate the strict-policy SARIF sample: + +```powershell +sbom-diff-risk compare ` + --before examples/sarif_before.json ` + --after examples/sarif_after.json ` + --policy examples/policy-strict.yml ` + --out-sarif outputs/report.sarif +``` + +Compare the SARIF output against the sample: + +```powershell +Compare-Object (Get-Content examples/sample-sarif.sarif) (Get-Content outputs/report.sarif) +``` + +The SARIF sample is intentionally conservative. It covers selected high-signal findings and explicit policy violations, not every enrichment fact. + +## Release Verification Path + +Start with the GitHub Release for the version under review. For `v0.5.0`, inspect the release and assets: + +```powershell +gh release view v0.5.0 --repo stacknil/scientific-computing-toolkit --json tagName,name,isDraft,isPrerelease,assets,url +``` + Expected release assets: - `sbom_diff_and_risk-0.5.0-py3-none-any.whl` - `sbom_diff_and_risk-0.5.0.tar.gz` -For workflow-built artifacts downloaded from a trusted workflow run, verify artifact attestations with the signer workflow: +Releases produced after the checksum-manifest workflow update also include `sbom-diff-and-risk-SHA256SUMS.txt`. Use it to check local downloaded distribution bytes before or alongside provenance verification: ```powershell -gh attestation verify path/to/sbom_diff_and_risk-0.5.0-py3-none-any.whl ` +gh release download ` --repo stacknil/scientific-computing-toolkit ` - --signer-workflow stacknil/scientific-computing-toolkit/.github/workflows/sbom-diff-and-risk-ci.yml + --pattern 'sbom_diff_and_risk-*' ` + --pattern 'sbom-diff-and-risk-SHA256SUMS.txt' ` + --dir release-assets +Set-Location release-assets +Get-Content .\sbom-diff-and-risk-SHA256SUMS.txt | ForEach-Object { + $expected, $file = $_ -split '\s+', 2 + $actual = ((Get-FileHash -Algorithm SHA256 -LiteralPath $file).Hash).ToLowerInvariant() + if ($actual -ne $expected) { + throw "Checksum mismatch for $file" + } + "$file OK" +} ``` -```powershell -gh attestation verify path/to/sbom_diff_and_risk-0.5.0.tar.gz ` - --repo stacknil/scientific-computing-toolkit ` - --signer-workflow stacknil/scientific-computing-toolkit/.github/workflows/sbom-diff-and-risk-ci.yml -``` - -`gh release verify` and `gh release verify-asset` are conditional on immutable releases. Use them only when the repository release is immutable and GitHub has generated release attestations: - -```powershell -gh release view v0.5.0 --repo stacknil/scientific-computing-toolkit --json isImmutable,assets,url -``` - -If `isImmutable` is true, release verification can check the release record and downloaded release assets: - -```powershell -gh release verify v0.5.0 --repo stacknil/scientific-computing-toolkit -gh release verify-asset v0.5.0 path/to/sbom_diff_and_risk-0.5.0-py3-none-any.whl --repo stacknil/scientific-computing-toolkit -``` - -If `isImmutable` is false, use the workflow artifact attestation path as the primary artifact verification story. - -## TestPyPI Evidence Path - -The TestPyPI Trusted Publishing dry-run completed for `sbom-diff-and-risk`. See `pypi-trusted-publishing-readiness.md` for the exact workflow identity and setup notes. - -What this proves: - -- the package metadata can render on TestPyPI -- the TestPyPI upload path can use Trusted Publishing / OIDC -- the workflow separates build/check from upload -- TestPyPI upload was manually gated - -What this does not prove: - -- production PyPI publishing is ready -- production PyPI has a project, pending publisher, or trusted publisher -- future production distributions will be byte-identical to GitHub Release assets -- dependency analysis results are safety verdicts - -Production PyPI is intentionally deferred. See `pypi-production-publishing-decision.md` before making any production publishing decision. - -## Code Scanning / SARIF Evidence Path - -The SARIF output is designed for GitHub code scanning consumption. Start with: - -- `docs/github-code-scanning.md` -- `examples/sample-sarif.sarif` -- `examples/sample-provenance-report.sarif` -- `examples/sample-scorecard-report.sarif` - -The SARIF renderer intentionally emits a conservative subset: - -- selected heuristic findings such as suspicious source, unknown license, and major upgrade -- explicit blocking policy decisions -- selected provenance or Scorecard policy violations when policy turns them into findings - -Avoid overclaiming: - -- SARIF output is not a CVE scanner -- SARIF output is not a malware or reputation verdict -- missing provenance is an evidence gap, not proof of compromise -- Scorecard evidence is advisory unless policy explicitly gates it - -## Non-Claims - -- No hidden network access occurs by default. -- No production PyPI package exists yet. -- No dependency safety verdicts are produced. -- No CVE resolution is performed. -- No advisory database or exploitability analysis is performed. -- No production PyPI publishing workflow is enabled. -- TestPyPI validation is not production PyPI readiness. - -## 30-Second Reviewer Checklist - -- Can I identify what the tool does? Read `README.md` and `reviewer-brief.md`. -- Can I reproduce a deterministic demo? Run the CycloneDX example and compare `outputs/report.*` to `examples/sample-report.*`. -- Can I see machine-readable security output? Inspect or regenerate `examples/sample-sarif.sarif`. -- Can I verify release/distribution evidence? Read `verification.md`, `self-provenance.md`, and `release-provenance.md`. -- Can I distinguish TestPyPI from production PyPI? Read `pypi-trusted-publishing-readiness.md` and `pypi-production-publishing-decision.md`. -- Can I state the non-claims? No CVE scanner, no reputation oracle, no dependency safety verdicts, no production PyPI package yet. +Checksum verification confirms local byte integrity against the release manifest; it does not replace workflow artifact attestations or immutable-release verification. The attestation subject remains the built wheel and source distribution. +For workflow-built artifacts downloaded from a trusted workflow run, verify artifact attestations with the signer workflow: + +```powershell +gh attestation verify path/to/sbom_diff_and_risk-0.5.0-py3-none-any.whl ` + --repo stacknil/scientific-computing-toolkit ` + --signer-workflow stacknil/scientific-computing-toolkit/.github/workflows/sbom-diff-and-risk-ci.yml +``` + +```powershell +gh attestation verify path/to/sbom_diff_and_risk-0.5.0.tar.gz ` + --repo stacknil/scientific-computing-toolkit ` + --signer-workflow stacknil/scientific-computing-toolkit/.github/workflows/sbom-diff-and-risk-ci.yml +``` + +`gh release verify` and `gh release verify-asset` are conditional on immutable releases. Use them only when the repository release is immutable and GitHub has generated release attestations: + +```powershell +gh release view v0.5.0 --repo stacknil/scientific-computing-toolkit --json isImmutable,assets,url +``` + +If `isImmutable` is true, release verification can check the release record and downloaded release assets: + +```powershell +gh release verify v0.5.0 --repo stacknil/scientific-computing-toolkit +gh release verify-asset v0.5.0 path/to/sbom_diff_and_risk-0.5.0-py3-none-any.whl --repo stacknil/scientific-computing-toolkit +``` + +If `isImmutable` is false, use the workflow artifact attestation path as the primary artifact verification story. + +## TestPyPI Evidence Path + +The TestPyPI Trusted Publishing dry-run completed for `sbom-diff-and-risk`. See `pypi-trusted-publishing-readiness.md` for the exact workflow identity and setup notes. + +What this proves: + +- the package metadata can render on TestPyPI +- the TestPyPI upload path can use Trusted Publishing / OIDC +- the workflow separates build/check from upload +- TestPyPI upload was manually gated + +What this does not prove: + +- production PyPI publishing is ready +- production PyPI has a project, pending publisher, or trusted publisher +- future production distributions will be byte-identical to GitHub Release assets +- dependency analysis results are safety verdicts + +Production PyPI is intentionally deferred. See `pypi-production-publishing-decision.md` before making any production publishing decision. + +## Code Scanning / SARIF Evidence Path + +The SARIF output is designed for GitHub code scanning consumption. Start with: + +- `docs/github-code-scanning.md` +- `examples/sample-sarif.sarif` +- `examples/sample-provenance-report.sarif` +- `examples/sample-scorecard-report.sarif` + +The SARIF renderer intentionally emits a conservative subset: + +- selected heuristic findings such as suspicious source, unknown license, and major upgrade +- explicit blocking policy decisions +- selected provenance or Scorecard policy violations when policy turns them into findings + +Avoid overclaiming: + +- SARIF output is not a CVE scanner +- SARIF output is not a malware or reputation verdict +- missing provenance is an evidence gap, not proof of compromise +- Scorecard evidence is advisory unless policy explicitly gates it + +## Non-Claims + +- No hidden network access occurs by default. +- No production PyPI package exists yet. +- No dependency safety verdicts are produced. +- No CVE resolution is performed. +- No advisory database or exploitability analysis is performed. +- No production PyPI publishing workflow is enabled. +- TestPyPI validation is not production PyPI readiness. + +## 30-Second Reviewer Checklist + +- Can I identify what the tool does? Read `README.md` and `reviewer-brief.md`. +- Can I reproduce a deterministic demo? Run the CycloneDX example and compare `outputs/report.*` to `examples/sample-report.*`. +- Can I see machine-readable security output? Inspect or regenerate `examples/sample-sarif.sarif`. +- Can I verify release/distribution evidence? Read `verification.md`, `self-provenance.md`, and `release-provenance.md`. +- Can I distinguish TestPyPI from production PyPI? Read `pypi-trusted-publishing-readiness.md` and `pypi-production-publishing-decision.md`. +- Can I state the non-claims? No CVE scanner, no reputation oracle, no dependency safety verdicts, no production PyPI package yet. + diff --git a/tools/sbom-diff-and-risk/docs/verification.md b/tools/sbom-diff-and-risk/docs/verification.md index bbd8665..cc10945 100644 --- a/tools/sbom-diff-and-risk/docs/verification.md +++ b/tools/sbom-diff-and-risk/docs/verification.md @@ -14,10 +14,11 @@ Use the tool provenance docs: Current boundaries: -- the workflow name is `sbom-diff-and-risk-ci` -- the workflow artifact name is `sbom-diff-and-risk-dist` -- version-tag runs matching `v*` can publish the same built files as GitHub Release assets -- release verification depends on immutable releases being enabled for the repository +- the workflow name is `sbom-diff-and-risk-ci` +- the workflow artifact name is `sbom-diff-and-risk-dist` +- version-tag runs matching `v*` can publish the same built files as GitHub Release assets +- release assets produced by the updated workflow include `sbom-diff-and-risk-SHA256SUMS.txt` for local SHA256 verification of the wheel and source distribution +- release verification depends on immutable releases being enabled for the repository - the TestPyPI readiness workflow is `sbom-diff-and-risk-testpypi` - the TestPyPI Trusted Publishing dry-run has completed for version `0.4.1` - production PyPI publishing remains deferred behind the gate in [pypi-production-publishing-decision.md](D:/OneDrive/Code/scientific-computing-toolkit/tools/sbom-diff-and-risk/docs/pypi-production-publishing-decision.md) @@ -39,6 +40,7 @@ Current boundaries: ## One-line summary -- Verify the tool itself: use `self-provenance.md` or `release-provenance.md` -- Decide whether production PyPI publishing is ready: use `pypi-production-publishing-decision.md` +- Verify the tool itself: use `self-provenance.md` or `release-provenance.md` +- Check downloaded release bytes: use the checksum manifest instructions in `release-provenance.md` +- Decide whether production PyPI publishing is ready: use `pypi-production-publishing-decision.md` - Analyze dependencies with the tool: use the README's dependency provenance sections