ci (release): Refactor the release workflow to properly accomodate sbom generation & to use advisory practices - #28
Conversation
There was a problem hiding this comment.
Pull request overview
Refactors the release pipeline into isolated, least-privilege stages with verified artifact handoffs and release evidence.
Changes:
- Separates build, signing, packaging, attestation, publishing, and release attachment.
- Adds pinned strong-name and Authenticode identity validation.
- Generates SPDX 2.2/3.0 SBOMs and package attestations.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
eng/IG.publickey.hex |
Pins the approved strong-name public key. |
.github/workflows/sbom.yml |
Provides PR-triggered SBOM dry runs. |
.github/workflows/build-and-publish.yml |
Implements the staged release pipeline. |
.github/scripts/verify-strong-name.ps1 |
Validates assembly signatures and key identity. |
.github/scripts/artifact-checksums.ps1 |
Verifies artifacts across job boundaries. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…b.com/IgniteUI/Infragistics.QueryBuilder.Executor into btraykov/supply-chain-provenance-sbom
There was a problem hiding this comment.
🔵 Needs a closer look
The credential-bound release pipeline needs human validation, and its dependency narrative currently conflicts with the project manifest.
Review details
- Files reviewed: 20/22 changed files
- Comments generated: 2
- Review effort level: Balanced
… security concern Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…b.com/IgniteUI/Infragistics.QueryBuilder.Executor into btraykov/supply-chain-provenance-sbom
There was a problem hiding this comment.
🔵 Needs a closer look
The broad release, signing, OIDC publishing, and attestation changes warrant final human supply-chain review despite successful validation.
Review details
- Files reviewed: 21/23 changed files
- Comments generated: 0 new
- Review effort level: Balanced
There was a problem hiding this comment.
🟡 Changes recommended
SBOM path containment can reject valid paths, and CycloneDX author data is incorrectly reported as supplier coverage.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 21/23 changed files
- Comments generated: 2
- Review effort level: Balanced
| $resolvedOutputRoot = [System.IO.Path]::GetFullPath($OutputRoot) | ||
| $resolvedComponentPath = [System.IO.Path]::GetFullPath($BuildComponentPath) | ||
|
|
||
| if ($resolvedOutputRoot.StartsWith($resolvedComponentPath, [System.StringComparison]::OrdinalIgnoreCase)) { | ||
| throw "OutputRoot '$resolvedOutputRoot' is inside BuildComponentPath '$resolvedComponentPath'. The generated manifests would be scanned as components of the build." | ||
| } |
| } | ||
|
|
||
| $licensed = @($components | Where-Object { $_.licenses }) | ||
| $authored = @($components | Where-Object { $_.authors }) |
…ing for the SPDX SBOM generation
There was a problem hiding this comment.
🔵 Needs a closer look
CycloneDX’s fatal online licence lookup can block releases, and the BOM lacks the claimed supplier coverage.
Review details
Suppressed comments (3)
Previously missed (2) — in code that hasn't changed since the last review.
.github/scripts/New-CycloneDxSbom.ps1:58
- With the pinned CycloneDX 6.2.0, this option performs GitHub API calls and treats rate limits and transport failures as fatal. Because native-command errors stop this script, a transient licence lookup can block the release before the warn-only coverage check runs. Add a bounded retry and then fall back to generation without
--enable-github-licenses, emitting a warning for the reduced coverage.
.github/scripts/Copy-AttestationBundles.ps1:10 - The pinned
actions/attestcode derives the SPDX 2.2 predicate type ashttps://spdx.dev/Document/v2.2, not the unversioned URI shown here. Please document the exact type so verification instructions do not query for a predicate that was never emitted.
.github/scripts/New-CycloneDxSbom.ps1:93
- The PR and this script's synopsis promise supplier coverage, but these lines confirm the generator only emits
authorsand never populates or validates CycloneDXsupplier. Those fields have different semantics, so the produced SBOM does not provide the claimed supplier data. Either populate valid supplier organizations or update the stated requirement and validation claims to author coverage.
# CycloneDX models 'authors' (people, from the nuspec author metadata) and 'supplier' (an organisation)
# separately; cyclonedx-dotnet only ever populates the former, so this is reported as author coverage.
$authored = @($components | Where-Object { $_.authors })
- Files reviewed: 21/23 changed files
- Comments generated: 0 new
- Review effort level: Balanced
There was a problem hiding this comment.
🔵 Needs a closer look
CycloneDX author coverage is presented as supplier coverage, contradicting the stated release-evidence requirement.
Review details
Suppressed comments (1)
.github/scripts/New-CycloneDxSbom.ps1:93
- The PR says CycloneDX supplies and validates supplier metadata (and reports all 20 components with a supplier), but this implementation explicitly checks only
authorsbecause the tool does not populatesupplier. Those fields have different CycloneDX meanings, so the released SBOM does not meet the stated supplier-coverage claim. Either populate and validatesupplier, or update the script documentation and PR description to describe author coverage instead.
$licensed = @($components | Where-Object { $_.licenses })
# CycloneDX models 'authors' (people, from the nuspec author metadata) and 'supplier' (an organisation)
# separately; cyclonedx-dotnet only ever populates the former, so this is reported as author coverage.
$authored = @($components | Where-Object { $_.authors })
- Files reviewed: 21/23 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Summary
PublicKeyToken(7dd5c3163f2cd0cb), in addition tosn.exe -vfsignature validation.sbom-toolfrom a single invocation, retry when ClearlyDefined licence lookups degrade, guard against the manifests being scanned as build content, and generate a CycloneDX SBOM alongside them so licence and supplier data is not solely dependent on ClearlyDefined.SBOM generation changes
sbom-toolwas previously invoked twice per release — once per SPDX format — with output written inside the directory it also scanned for components. That produced three problems, each confirmed against real release logs and a local 10-iteration trial:BadGateway,TooManyRequests, and request timeouts across four separate CI runs).New-Sbom.ps1now makes onesbom-toolinvocation covering both formats (-mi 'SPDX:2.2,SPDX:3.0'), writes to a directory outside the component scan root, retries up to three times if licence coverage does not improve, and throws if the resolved output path is inside the scanned build-component path.Assert-Sbom.ps1gained checks that the two SPDX documents agree on package count and that neither document lists the other's manifest as a file, plus a non-fatal (warn-only) report of licence coverage and any reciprocal/copyleft licences detected.New-CycloneDxSbom.ps1is a new script that generates a CycloneDX 1.7 SBOM via thecyclonedxdotnet tool. Unlikesbom-tool, CycloneDX reads licence and supplier metadata directly from each package's own nuspec instead of depending on the ClearlyDefined API, so it fills in fields the SPDX documents otherwise leave asNOASSERTIONwhen that API is degraded or when a package (for exampleAutoMapper) declares its licence via a file rather than an SPDX expression.--enable-github-licenseswith the workflow'sGITHUB_TOKENresolves those file-declared licences from the package's GitHub repository. The CycloneDX document and its SHA-256 sidecar are attached to the release alongside the SPDX artifacts.Both SBOM formats are now attested independently:
actions/attestderives a distinct predicate type per format (https://spdx.dev/Documentfor SPDX 2.2,https://cyclonedx.org/bomfor CycloneDX), so both attestations bind to the same signed package digest without colliding.Copy-AttestationBundles.ps1was updated to collect and name all three bundles (provenance.sigstore.json,sbom-spdx.sigstore.json,sbom-cyclonedx.sigstore.json).Licence coverage and reciprocal-licence findings remain warnings, not build failures, so a ClearlyDefined outage cannot block a release — the underlying
sbom-toolproject has explicitly declined to add a hard-failure option for missing licence data (issue #733), and a local fork was ruled out to avoid taking on that maintenance cost.A related decision on the one licence finding this surfaced —
AutoMappermoved from MIT to a dual RPL-1.5/commercial licence starting with 15.0.0, and 14.x carries an unpatched high-severity CVE — is tracked separately in issue #31 rather than resolved in this change.Validation
Infragistics.QueryBuilder.Executor.1.0.2-prerelease.33against the pinned public key and token.32992310867to the repository-pinned Authenticode certificate fingerprint.sn.exeselection against those assemblies and confirmed an invalid explicit path is rejected.New-Sbom.ps1throws whenOutputRootis insideBuildComponentPath.Assert-Sbom.ps1fails when the two SPDX documents disagree on package count or when either lists an SBOM manifest as build content, and reports (without failing) licence coverage and reciprocal-licence findings.--enable-github-licenses; confirmed all 20 components carry both a resolved licence and a supplier, includingAutoMapper, which the SPDX/ClearlyDefined path could not resolve.