Skip to content

ci: publish SBOMs per release and verify published provenance - #411

Merged
allxsmith merged 6 commits into
mainfrom
ci/supply-chain-evidence
Jul 30, 2026
Merged

allxsmith merged 6 commits into
mainfrom
ci/supply-chain-evidence

Conversation

@allxsmith

@allxsmith allxsmith commented Jul 29, 2026 •

Copy link
Copy Markdown
Owner

Adds .github/workflows/supply-chain.yml — two evidence artifacts our posture previously only claimed.

What

sbom job — Syft generates SPDX and CycloneDX SBOMs (SPDX is the ISO standard procurement asks for; CycloneDX is what most scanners ingest natively). Uploaded as workflow artifacts, and on a release: published event attached to the GitHub release as durable, citable assets. Workflow artifacts expire; release assets are what an auditor can link to.

verify-provenance job — installs the real published tarballs from npm into a scratch tree and runs npm audit signatures. SECURITY.md tells consumers to run this; until now we never ran it ourselves.

Why it's decoupled from the release

Triggers are release: published, weekly schedule, and workflow_dispatch — never ci.yml's publish job. Nothing in this workflow can fail a release.

Verified locally, not just written

$ npm install --ignore-scripts @allxsmith/bestax-bulma create-bestax
$ npm audit signatures
audited 17 packages in 2s
17 packages have verified registry signatures
4 packages have verified attestations
  • prettier --check passes
  • YAML parses; both jobs' permissions confirmed (contents: write on sbom only, for the release upload; verify-provenance inherits read)
  • All three actions SHA-pinned with version comments (anchore/sbom-action v0.24.0 dereferenced from its annotated tag)
  • SBOM attachment can only be exercised by a real release: published event — verify on the next release

⚠️ Found while testing this: bestax-migrate is broken on npm

verify-provenance originally installed all three published packages. It failed — and not because of this workflow:

$ npm install bestax-migrate
npm error code EUNSUPPORTEDPROTOCOL
npm error Unsupported URL Type "workspace:": workspace:^

bestax-migrate@1.0.0's published manifest contains "@allxsmith/bestax-bulma": "workspace:^" — an unresolved pnpm workspace specifier. npm install bestax-migrate fails for every consumer, on every package manager. Filed separately; this PR excludes that package from the audit (with a comment) so the check measures signatures rather than that bug. Add it back once fixed.

Summary by CodeRabbit

  • New Features
    • Generates SPDX and CycloneDX SBOMs for published releases and attaches them to the corresponding release assets.
  • Improvements
    • Strengthened provenance verification by improving how provenance is detected and adding retries with backoff before failing.
    • Ensures the verification run fails if any targeted package lacks required provenance.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d1dbb57d-4b3d-4077-91b9-94771ea716ac

📥 Commits

Reviewing files that changed from the base of the PR and between 3f123d5 and 32f2f89.

📒 Files selected for processing (1)
  • .github/workflows/supply-chain.yml

Walkthrough

Adds a GitHub Actions workflow that generates SPDX and CycloneDX SBOMs, uploads them to published releases in a separate job, and verifies npm package signatures and provenance attestations with retries.

Changes

Supply chain evidence

Layer / File(s) Summary
SBOM generation and release upload
.github/workflows/supply-chain.yml
Uses read-only generation permissions, disables automatic release uploads, and attaches generated SBOM artifacts to release tags with gh release upload.
Published package provenance verification
.github/workflows/supply-chain.yml
Installs published packages in a temporary npm project, audits signatures, parses string provenance predicates from JSON, retries missing results, and fails when provenance is absent.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GitHubRelease
  participant GitHubActions
  participant AnchoreSBOMAction
  GitHubRelease->>GitHubActions: trigger published release workflow
  GitHubActions->>AnchoreSBOMAction: generate SPDX and CycloneDX SBOM artifacts
  GitHubActions->>GitHubRelease: upload SBOM artifacts with gh release upload
Loading
sequenceDiagram
  participant GitHubActions
  participant NpmCLI
  participant NpmRegistry
  GitHubActions->>NpmCLI: install published packages without scripts
  NpmCLI->>NpmRegistry: query package signatures and attestations
  GitHubActions->>NpmCLI: run npm audit signatures
  GitHubActions->>NpmRegistry: query JSON provenance predicates
  GitHubActions->>GitHubActions: retry and fail when no string predicate is found
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the change, but it misses most required template sections like affected packages, issue links, and checklist items. Reformat the PR body to include the template sections and complete the missing fields for affected package(s), related issue(s), change type, and checklist.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main workflow change: publishing SBOMs and verifying provenance.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/supply-chain-evidence

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment

Preview URL: https://7f3d4871.bestax.pages.dev

@github-actions

Copy link
Copy Markdown
Contributor

AI triage — issues this PR may resolve

If this PR resolves one of these, add the line below to the PR description
so the issue closes on merge:

Fixes #412

@allxsmith

Copy link
Copy Markdown
Owner Author

The temporary bestax-migrate exclusion in verify-provenance is now tracked as #416, so it doesn't get forgotten once #412 lands. The in-workflow comment points at the same follow-up.

Two supply-chain evidence artifacts we previously only claimed:

- SPDX + CycloneDX SBOMs generated with Syft, uploaded as artifacts and
  attached to each GitHub release as durable, citable assets.
- npm audit signatures run against the real published tarballs, closing
  the loop on the verification SECURITY.md tells consumers to perform.

Deliberately decoupled from ci.yml's publish job so nothing here can
fail a release.

Refs #404
checkout, setup-node and pnpm/action-setup were pinned from a stale
feature branch. origin/main uses checkout v7 (3d3c42e5), setup-node v7
(82076278) and pnpm/action-setup v6.0.9 (0ebf4713).
@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment

Preview URL: https://b454ef49.bestax.pages.dev

@claude claude 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.

Deep review — 0 blocking · 2 advisory

# Severity Area Finding Location
1 🔵 Advisory Security npm audit signatures fails only on invalid signatures/attestations — it stays green when our own packages publish with missing provenance, so the job can't actually catch a future release that drops attestations .github/workflows/supply-chain.yml:107
2 🔵 Advisory Robustness The SBOM is generated from the full pnpm install dev workspace and path: ., so it enumerates the entire monorepo dev toolchain (storybook, playwright, docusaurus, jest, turbo…) rather than the published-package dependency closure .github/workflows/supply-chain.yml:48

Overall: The change is sound and well-scoped: it's decoupled from the release path (nothing here can fail a publish), all three actions are SHA-pinned in line with the rest of the repo, permissions are least-privilege (contents: write confined to the sbom job, verify-provenance inheriting read), and the release-upload step correctly passes tag_name through an env var rather than inline ${{ }}, so a hostile tag can't inject shell. I found no blocking defects. The riskiest part is not a bug but a strength-of-evidence gap: both artifacts promise slightly more than they enforce (see advisories). The human should focus on advisory #1 — decide whether "Verify published provenance" needs to actually assert attestation presence, or whether registry-signature verification is the intended (weaker) guarantee.

Residual risk:

  • Silent-pass on dropped provenance: npm audit signatures verifies attestations that exist (the PR's own run shows "4 packages have verified attestations") but does not fail on their absence. If a future release loses publishConfig.provenance: true or OIDC misconfigures, the packages keep valid registry signatures and this job stays green — provenance regressions go uncaught. Not blocking; the registry-signature check is still meaningful.
  • Transitive workspace:^ reintroduction: confirmed refuted — create-bestax/package.json depends only on chalk/commander/figures/fs-extra/prompts, and does not depend on @allxsmith/bestax-bulma or bestax-migrate, so the excluded broken package can't sneak back in through npm install create-bestax.
  • Release-event propagation: verify-provenance installs the latest dist-tag rather than the just-released version; semantic-release publishes to npm before creating the GitHub release, so latest should already be current, but a registry-propagation lag would audit the prior latest — harmless for a signature audit, and re-runnable via workflow_dispatch/weekly schedule.

🏄 Chill, clean set of evidence waves here, dude — pinned, decoupled, no injection undertow, totally safe to paddle out and merge. Just know the provenance check is more of a longboard cruiser than a big-wave charger: it'll spot a busted signature but won't yell if the attestation quietly ghosts. Ride it, but don't mistake green for a guarantee. 🌊

npm audit signatures only fails on invalid signatures; it stays green when
provenance is absent entirely, so it could never catch a release that lost
publishConfig.provenance or had OIDC misconfigured. Adds an explicit
presence assertion (verified against a positive and negative control).

Also documents the SBOM's scope: it is the repository/dev-toolchain SBOM,
not the published-package closure.

Addresses both advisories from the deep review on #411.
@allxsmith

Copy link
Copy Markdown
Owner Author

Both advisories addressed in 3f123d5 — #1 was a real weakness in the job's core promise.

Advisory 1 (provenance absence vs. invalidity) — fixed

Correct and the sharpest finding here: a step named "Verify published provenance" that stays green when provenance disappears is the same failure class as #405 — claiming a gate that isn't enforced. Added an explicit presence assertion:

predicate=$(npm view "$pkg" dist.attestations.provenance.predicateType)
[ -z "$predicate" ] && fail=1

Verified against both controls before committing:

ok    @allxsmith/bestax-bulma — https://slsa.dev/provenance/v1
ok    create-bestax           — https://slsa.dev/provenance/v1
FAIL  fs-extra                — no provenance attestation on the registry

fs-extra is published without provenance, so it's a genuine negative control — the assertion detects absence rather than just passing everything.

Advisory 2 (SBOM scope) — documented, not changed

Also correct: path: . over a full dev pnpm install enumerates storybook, playwright, docusaurus, jest, turbo — not what a consumer installs (which for bestax-bulma is Bulma alone).

I've scoped the claim rather than the artifact. A repository SBOM is legitimately useful — it's the audit trail for what builds our releases — the error would be letting it be read as the consumer closure. Added an explicit NOTE ON SCOPE comment saying so. A true per-package SBOM means running Syft over the packed tarballs, which is a bigger change and belongs in its own PR.

On the residual-risk notes

  • latest vs. just-released version — agreed and worth keeping on the record. semantic-release publishes to npm before creating the GitHub release, so latest is normally already current; a propagation lag would audit the prior version, which is harmless for a signature audit and re-runnable via workflow_dispatch.
  • Transitive workspace:^ reintroduction refuted — matches what I found: create-bestax depends only on chalk/commander/figures/fs-extra/prompts, so [Bug] bestax-migrate is uninstallable from npm — published manifest contains "workspace:^" #412's broken package can't re-enter through it.

@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment

Preview URL: https://39f75eb0.bestax.pages.dev

@allxsmith

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026 •

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/supply-chain.yml:
- Around line 54-60: Disable implicit release uploads by adding
upload-release-assets: false to both SBOM action configurations in
.github/workflows/supply-chain.yml at lines 54-60 and 64-70; leave the separate
gh release upload step unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5bc755a7-f7c6-4630-9ea0-4c099d939a34

📥 Commits

Reviewing files that changed from the base of the PR and between 69f9fe7 and 3f123d5.

📒 Files selected for processing (1)
  • .github/workflows/supply-chain.yml

Comment thread .github/workflows/supply-chain.yml
anchore/sbom-action defaults upload-release-assets to true, so on a release
event each of the two format steps would attach its own asset alongside the
explicit gh release upload — three uploads with two naming conventions.
Verified the default against action.yml at the pinned SHA.

Addresses the CodeRabbit finding on #411.
@allxsmith

Copy link
Copy Markdown
Owner Author

Valid finding — fixed in f498320. Verified the claim against action.yml at the exact pinned SHA rather than the tag:

$ gh api "repos/anchore/sbom-action/contents/action.yml?ref=e22c3899..." | base64 -d | grep -A4 upload-release-assets
  upload-release-assets:
    required: false
    description: "Upload release assets"
    default: "true"

Confirmed. On a release: published event that meant three uploads: one implicit per format step plus the explicit gh release upload, with two different naming conventions competing for the same release. Set upload-release-assets: false on both steps and kept the explicit uploader, which gives deterministic filenames and --clobber idempotency on re-runs.

Worth noting this was latent — nothing on a PR branch can fire a release event, so it would only have surfaced on the first real release after merge, as duplicate or oddly-named assets.

@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment

Preview URL: https://3790f6c1.bestax.pages.dev

@allxsmith
allxsmith requested a review from Copilot July 30, 2026 00:11
@allxsmith

Copy link
Copy Markdown
Owner Author

@claude deep review again

@claude

claude Bot commented Jul 30, 2026 •

Copy link
Copy Markdown

Claude finished @allxsmith's task in 2m 20s —— View job


Deep review again

  • Gather context (current diff vs origin/main, prior review threads)
  • Re-read .github/workflows/supply-chain.yml in full
  • Verify prior findings are actually applied correctly
  • Look for new issues introduced by latest commits
  • Post final review

Deep review — 0 blocking · 0 new advisories

Re-reviewed the file at HEAD (f498320), cross-checked against every prior finding on this PR.

Both prior advisories and the CodeRabbit actionable comment are correctly resolved:

  1. Provenance-absence gap (my earlier advisory Release new version #1) — fixed in 3f123d5. The new "Assert our packages actually carry provenance" step (supply-chain.yml:126-138) queries npm view $pkg dist.attestations.provenance.predicateType directly against the registry and hard-fails (exit "$fail") when it's empty, independent of whatever npm audit signatures did. Logic checks out: fail is set once and never reset across loop iterations, so any one package missing provenance fails the job. The 2>/dev/null || true fallback correctly prevents the step's set -e from aborting on a lookup error while still treating an empty result as failure.

  2. SBOM scope (my earlier advisory Release new version #2) — addressed as a documentation change, not a code change, which is the right call: supply-chain.yml:45-50 now explicitly scopes the claim ("this produces a repository SBOM... deliberately NOT the published-package dependency closure"). Not a defect, just needed the claim narrowed to match what's actually produced.

  3. Duplicate/racing release-asset uploads (CodeRabbit actionable, f498320) — upload-release-assets: false is now set on both the SPDX and CycloneDX anchore/sbom-action steps (lines 64, 75), leaving the explicit gh release upload --clobber step (79-88) as the single, deterministic uploader. Confirmed the action's default is indeed true per the pinned SHA's action.yml, so this was a real latent bug (would only have surfaced on the first real release: published run) — correctly fixed.

Fresh pass over the rest of the file found nothing new:

  • All action pins (actions/checkout, pnpm/action-setup, actions/setup-node, anchore/sbom-action) match the SHAs used elsewhere in this repo's other workflows — no drift.
  • node-version: '24' matches ci.yml and the repo's documented Node 24 CI target.
  • Permissions stay least-privilege: top-level contents: read, sbom job escalates to contents: write only for the release-upload step, verify-provenance has no job-level override and inherits read.
  • TAG: ${{ github.event.release.tag_name }} is passed through an env var rather than interpolated inline into the run: script — safe from shell injection via a hostile tag name.
  • The bestax-migrate exclusion in verify-provenance is commented in-file and tracked externally ([CI] Re-add bestax-migrate to the published-provenance check once #412 is fixed #416), so it won't be silently forgotten.
  • Bash semantics in both multi-line run: blocks are correct for the default bash -eo pipefail shell GitHub Actions uses.

Nothing here can fail a release (triggers are release: published, weekly schedule, workflow_dispatch only — never ci.yml's publish job), so the residual risk is entirely about whether the evidence is trustworthy, and at this point I think it is.

Note: I don't have working Bash tool access in this environment run (sandbox approval was denied for both git fetch and a YAML-parse check), so this review is based on a full manual read of the file plus Grep cross-checks against the rest of .github/workflows/ rather than executing linters/tests locally. The author's own prettier --check and manual verification (npm audit signatures output, action.yml SHA fetch) posted in the PR thread cover what I couldn't re-run.

Verdict: No blocking issues. This is ready to merge from a review standpoint; the only genuinely untestable path (SBOM attachment on a real release: published event) is already called out by the author as unverifiable pre-merge.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a new GitHub Actions workflow to generate auditable supply-chain evidence for the Bestax monorepo and to continuously verify the provenance/signature status of the published npm packages.

Changes:

  • Introduces a sbom job that generates SPDX and CycloneDX SBOM artifacts, and (on release: published) uploads them as GitHub Release assets.
  • Introduces a verify-provenance job that installs published tarballs with npm and runs npm audit signatures, plus an explicit provenance-presence assertion.
  • Runs on release: published, weekly schedule, and manual dispatch (intentionally decoupled from the release publish pipeline).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/supply-chain.yml Outdated
Comment thread .github/workflows/supply-chain.yml
…heck

Copilot review on #411:

- The sbom job ran pnpm install and third-party actions while holding
  contents:write on every trigger. Split generation (read-only) from the
  release upload (contents:write, gated on the release event), so a
  compromised dependency never inherits a write-scoped token.
- The provenance assertion accepted any non-empty npm view output. Now
  uses --json with a string type guard, so null/undefined/non-string all
  fail closed. Verified against positive, negative and 404 cases.
@allxsmith

Copy link
Copy Markdown
Owner Author

Both Copilot findings were valid and are fixed in 82c062b.

contents: write on non-release runs — fixed as suggested

Correct, and the stronger of the two. The sbom job ran pnpm install plus third-party actions while holding a write-scoped token on every trigger, including the weekly schedule — so any compromised dependency or action inherited write access for no benefit.

Split exactly as recommended:

job permissions when
sbom contents: read every trigger
attach-sbom contents: write needs: sbom, if: github.event_name == 'release'
verify-provenance inherits read every trigger

The upload job installs nothing — it only downloads the artifacts and calls gh release upload.

npm view non-empty ≠ present — fixed, with a caveat on the mechanism

Adopted. Now --json with a type guard:

predicate=$(npm view "$pkg" dist.attestations.provenance.predicateType --json 2>/dev/null \
  | jq -r 'select(type=="string")' 2>/dev/null || true)

Being straight about what I could and couldn't confirm: I could not reproduce the specific null/undefined output. Bare npm view printed empty (length 0) for every package I tried without attestations — fs-extra, lodash, chalk — so the original -z test wasn't actually producing a false positive.

That said the recommendation is right regardless: the type guard makes presence explicit rather than inferred, and it costs nothing. Verified across all three cases:

@allxsmith/bestax-bulma  -> [https://slsa.dev/provenance/v1]  ok
create-bestax            -> [https://slsa.dev/provenance/v1]  ok
fs-extra                 -> []                                FAIL
lodash                   -> []                                FAIL
nonexistent-pkg-xyz-9987 -> []                                FAIL

The 404 case matters most: any failure — network, missing package, missing dist-tag — yields empty and fails closed, which is the correct default for a security assertion.

Also caught in the process

Writing the attach-sbom job I initially used a download-artifact SHA from memory. It was fabricated — gh api returned "No commit found for SHA." The real pin on main is 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8. I've now verified every one of the five action pins in this file resolves to a real commit via the API, rather than trusting recall.

@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment

Preview URL: https://ba6d0ec4.bestax.pages.dev

@allxsmith

Copy link
Copy Markdown
Owner Author

deep-review: RE-REVIEW — verify prior review feedback was properly addressed. Head is 82c062b; no reviewer has examined this commit. Three reviewers raised five findings and the author claims all are fixed. Verify each INDEPENDENTLY against the code, do not trust the author’s reply comments:

  1. Deep review advisory: npm audit signatures passes when provenance is ABSENT. Author added an "Assert our packages actually carry provenance" step. Does it actually fail closed on a missing attestation, on npm view failure, and on a 404?
  2. Deep review advisory: SBOM covers the dev monorepo, not the published closure. Author documented the scope rather than changing it. Is the comment accurate and is the artifact honestly labelled?
  3. CodeRabbit (Major): anchore/sbom-action defaults upload-release-assets: true. Author set it false on both steps. Confirm against action.yml at the PINNED SHA, and confirm no duplicate-upload path remains.
  4. Copilot: contents: write held on non-release runs. Author split sbom (read) from attach-sbom (write, release-gated). Verify the write token is genuinely unreachable on schedule/workflow_dispatch, and that attach-sbom can still get the artifacts.
  5. Copilot: npm view non-empty treated as present. Author switched to --json + a jq string type guard.

CRITICAL — the author made TWO action-pin errors on these PRs: first citing a pin sourced from a stale branch, then writing a download-artifact SHA that did not exist at all. Independently resolve EVERY action pin in supply-chain.yml via the GitHub API, confirm each is a real commit, and confirm each matches what main uses for that action. Treat any pin claim in the PR body or comments as unverified.

Also check: does attach-sbom correctly obtain both SBOM files given the artifact names used by the generate steps? A silent mismatch there would make the release upload fail only on a real release.

@claude claude 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.

Deep review — 0 blocking · 2 advisory

Re-review of head 82c062b. I verified each of the five prior findings independently against the code and the live GitHub/npm/action sources, without trusting the author's replies, and re-resolved every action pin through the GitHub API.

# Severity Area Finding Location
1 🔵 Advisory Robustness Provenance assertion fails closed on any transient npm view/registry error — correct for a security gate, but the weekly schedule run can go red on network flake, not just on a real dropped attestation. .github/workflows/supply-chain.yml:145
2 🔵 Advisory Security SBOM is the dev-monorepo closure, not the published-package closure — honestly documented in-file and the artifact name is generic, so no mislabelling. On the record as an accepted scope limit. .github/workflows/supply-chain.yml:50

Verification of the five prior findings

1. npm audit signatures passes when provenance is absent → explicit presence assertion. Fixed and fails closed. npm view … predicateType --json | jq -r 'select(type=="string")' then [ -z "$predicate" ]:

  • Absent field → npm view emits empty/null/undefined; jq type-guard yields empty → fail=1.
  • Network / 404 / missing dist-tag → empty stdout (stderr suppressed), || true keeps the assignment empty → fail=1.
  • Present → JSON string passes type=="string" → ok. GH Actions default shell is bash -eo pipefail; || true correctly absorbs the pipe failure. Confirmed both packages set publishConfig.provenance: true (bulma-ui/package.json:205, create-bestax/package.json:77), so asserting on both is right.

2. SBOM covers dev monorepo, not published closure. Documented, not silently mislabelled — see advisory #2. Comment is accurate.

3. anchore/sbom-action defaults upload-release-assets: true. Fixed. Fetched action.yml at the pinned SHA e22c389: upload-release-assets default: "true" confirmed. Both generate steps set it false. The single deterministic uploader is attach-sbom; no duplicate path remains.

4. contents: write held on non-release runs. Fixed. Top-level and sbom are contents: read; verify-provenance inherits read. Write lives only on attach-sbom, gated if: github.event_name == 'release' — the job (and its token) never materialises on schedule/workflow_dispatch. needs: sbom still lets it pull the workflow artifacts.

5. npm view non-empty treated as present. Fixed via --json + jq 'select(type=="string")' (same code as #1).

Action-pin audit (every pin re-resolved via GitHub API)

Action Pinned SHA Real commit? Tag resolves Matches main?
actions/checkout 3d3c42e yes v7 → 3d3c42e yes
pnpm/action-setup 0ebf471 yes v6.0.9 (annotated) → 0ebf471 yes
actions/setup-node 8207627 yes v7 → 8207627 yes
actions/download-artifact 3e5f45b yes v8 → 3e5f45b yes
anchore/sbom-action e22c389 yes v0.24.0 → e22c389 n/a (new)

Every pin is a real commit whose version comment resolves correctly (annotated pnpm and lightweight sbom-action tags both dereferenced), and matches main. The two prior pin errors are corrected.

attach-sbom artifact retrieval. Correct. In SyftGithubAction.ts@e22c389, an explicit artifact-name becomes both the workflow-artifact name and the internal filename. So the two artifacts are bestax-sbom.spdx.json / bestax-sbom.cdx.json, each containing an identically-named file. download-artifact pattern: bestax-sbom.* + merge-multiple: true lands both in the CWD, exactly the two names gh release upload references. No silent mismatch that would surface only on a real release.

Overall: The change is sound and every prior finding is genuinely resolved — I confirmed each against the live source, not the author's replies. The riskiest surface was the action pins given the two earlier pin errors; all five now resolve to real commits matching their tags and main, and the attach-sbom artifact names line up end-to-end. A human need only sanity-check the one thing no CI here can exercise: the actual release: published upload path, which by design only runs on a real release.

Residual risk:

  • False-pass on a dropped attestation — refuted: absent/null/non-string all collapse to empty via the jq type guard → fail=1; only a JSON string passes.
  • Release upload fails on a real tag due to name mismatch — refuted: artifact names, internal filenames, download pattern, and gh release upload args all agree on bestax-sbom.spdx.json / bestax-sbom.cdx.json.
  • Write token leaking to dependency/action code — refuted: write is confined to attach-sbom, which installs nothing and runs only the artifact download, and never instantiates outside a release event.
  • Minor: if npm view ever returned the predicate as a single-element JSON array (bare-spec aggregation) the type guard would false-fail; latest-tag resolution returns a scalar, so not expected — the weekly schedule would surface it loudly if it ever happened.

🏄 Paddled out on every pin and every pipe, dude — the whole set holds, the write token is locked in the release-only barrel, and the SBOMs surf straight onto the release with matching names. Clean wave, no wipeouts. Good to go.

…station

The assertion fails closed, which is right for a security gate, but on the
weekly schedule a registry blip produced the same red as provenance
genuinely disappearing. A gate that cries wolf gets ignored, so retry three
times with backoff before believing the attestation is gone.

Also handles npm returning the predicate as a single-element array.

Addresses advisory 1 from the deep re-review of 82c062b.
@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment

Preview URL: https://d1018982.bestax.pages.dev

@allxsmith

Copy link
Copy Markdown
Owner Author

Advisory 1 is fixed in 32f2f89. Advisory 2 I'm deliberately not fixing here — filed as #424.

Advisory 1 (fails closed on transient registry error) — fixed

Right, and worth acting on: a weekly gate that goes red on a network blip is one people learn to ignore, which defeats the point. The lookup now retries three times with backoff before concluding provenance is gone, so flake and a genuinely dropped attestation no longer produce the same red.

Also handled the residual-risk note about npm returning the predicate as a single-element array — the guard is now if type=="array" then .[0] else . end | select(type=="string").

Verified by extracting the step's run: block straight out of the YAML and executing it verbatim under bash -eo pipefail (Actions' default shell):

ok: @allxsmith/bestax-bulma -> https://slsa.dev/provenance/v1
ok: create-bestax -> https://slsa.dev/provenance/v1
EXIT=0

Negative cases still fail closed — fs-extra, a nonexistent package, null, and [] all collapse to empty.

Advisory 2 (SBOM is the dev closure, not the consumer closure) — deferred to #424

Fixable, and worth fixing: verify-provenance already builds precisely the right tree — a scratch dir with the published packages — so pointing Syft at that yields the true consumer closure. For bestax-bulma that's essentially Bulma alone, which is a selling point the current dev-toolchain SBOM actively obscures.

I'm not bolting it on here because it changes the artifact set and the job's purpose, and there are real design questions to settle first (per-package vs. combined; --omit=dev; keeping the write-scoped token out of verify-provenance so the split Copilot asked for doesn't regress). That deserves its own review pass rather than riding along on a PR four reviewers have already cleared.

Note for anyone reading the review trail

The re-review reported "PR is already merged" for #406/#407 — correct, both merged at 00:01Z. That is also the real reason the re-triggered deep reviews on those two skipped: claude-review.yml's job requires pull_request.state == 'open'. I'd previously blamed the one-review-per-PR dedupe marker; that was wrong. #411 is still open, which is why its re-review ran normally.

@allxsmith
allxsmith merged commit c94a6ee into main Jul 30, 2026
12 checks passed
@allxsmith
allxsmith deleted the ci/supply-chain-evidence branch July 30, 2026 11:19
@bestax-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 4.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@bestax-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 2.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@bestax-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 5.8.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@bestax-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 1.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants