ci(publish): build, sign, pack, sbom generation and publish jobs - #30
ci(publish): build, sign, pack, sbom generation and publish jobs#30turbobobbytraykov wants to merge 35 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds an opt-in GitHub Actions workflow to generate and (for releases) attach an SPDX 2.2 SBOM for the project’s NuGet package, using a pinned sbom-tool .NET local tool manifest stored under a nested .config directory.
Changes:
- Introduces
.github/workflows/sbom.ymlto generate SBOMs on PR label (generate sbom) and on published releases, and to upload/attach the resulting artifacts. - Adds
.config/sbom-tool/dotnet-tools.jsonto pinmicrosoft.sbom.dotnettoolfor reproducible restores isolated from the repo’s root tool manifest.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| .github/workflows/sbom.yml | New workflow to build/pack and generate SBOM artifacts, then attach SBOM to releases. |
| .config/sbom-tool/dotnet-tools.json | New nested local-tool manifest to pin sbom-tool used by the SBOM workflow. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…for improved security (we hope)
…for improved security (we hope)
16b3990 to
a4d1ec5
Compare
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
There was a problem hiding this comment.
🟡 Changes recommended
Release-tag interpolation permits PowerShell injection, and scan failures can publish a package without required evidence.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (2)
.github/workflows/publish.yml:427
- The release tag is interpolated into a double-quoted PowerShell command here. A valid tag containing a PowerShell subexpression such as
$()would be evaluated by this attestation-capable job; construct the path from the runtime environment variable instead.
-OutputFile "${{ runner.temp }}/sbom/cyclonedx/${{ env.PACKAGE_ID }}.${{ env.VERSION }}.cdx.json"
.github/workflows/publish.yml:433
- This second direct interpolation of the release tag has the same PowerShell command-injection path as the merge step. Use the runtime environment variable so the tag remains data rather than generated script text.
-BomPath "${{ runner.temp }}/sbom/cyclonedx/${{ env.PACKAGE_ID }}.${{ env.VERSION }}.cdx.json"
- Files reviewed: 19/20 changed files
- Comments generated: 2
- Review effort level: Balanced
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The security-sensitive publishing and hand-written SBOM merge pipeline warrants final human review despite successful end-to-end validation.
Review details
- Files reviewed: 19/20 changed files
- Comments generated: 0 new
- Review effort level: Balanced
… that SPDX SBOM generation uses
There was a problem hiding this comment.
🟡 Changes recommended
Release-tag validation and final digest verification are missing, and SPDX retry selection can retain an incomplete SBOM.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
.github/scripts/New-Sbom.ps1:143
- Selecting the “best” attempt solely by the absolute licensed count can replace a complete SBOM with an incomplete retry. For example, 81/81 packages replaces 80/100 and silently drops 19 components, contradicting the comment that a degraded retry can never replace a better document. Prioritize component count, then license count when totals match.
.github/workflows/publish.yml:42
- The linked alpha.9 run included a
Validate release tagstep invokingAssert-ReleaseVersion.ps1, but the current workflow proceeds directly from checkout to setup and that script is absent. Consequently, the cited end-to-end run does not validate this head, and unsupported release tags now flow into MSBuild properties, package paths, and feed URLs instead of failing at ingress. Re-add the validation step and script, then exercise the current workflow.
- name: Setup .NET
- Files reviewed: 19/20 changed files
- Comments generated: 1
- Review effort level: Balanced
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
Secret-backed signing, public publishing, attestations, and a custom SBOM merge warrant final human review despite the successful release validation.
Review details
- Files reviewed: 19/20 changed files
- Comments generated: 0 new
- Review effort level: Balanced
…I/IgniteUI.Blazor.GridLite into btraykov/sbom-generation
There was a problem hiding this comment.
🔵 Needs a closer look
Release-critical signing, publishing, and custom SBOM merging cross multiple trust boundaries and warrant final human review.
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
Bundle grouping does not enforce its exact-match contract, and one reported payload metric aggregates all themes despite claiming to model a single-theme page.
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
There was a problem hiding this comment.
🔵 Needs a closer look
The served-assets budget incorrectly sums all mutually exclusive themes, producing inaccurate evidence and possible false failures.
Review details
Suppressed comments (1)
eng/bundle-budgets.json:47
served-assetsincludes the entirethemesgroup, and totals sum every measurement in each selected group. Since the theme group contains all light/dark variants while its description says consumers reference one, this report overstates a page’s transfer size and can reject a release even when every actual one-theme page is within budget. Add an aggregation mode that uses the largest matching theme file (or model each selectable theme separately) before including it in this total.
"groups": ["entry-bundle", "themes"],
- Files reviewed: 21/23 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Goal
publish.ymlwas one unauthenticated job — checkout, build, pack, push to NuGet.org — with nosigning, no SBOM, no supply-chain evidence. This branch rebuilds it so a published GitHub release
produces a strong-named, Authenticode-signed, NuGet-signed
IgniteUI.Blazor.GridLitepackage withSPDX 2.2/3.0 + a merged CycloneDX SBOM, three attestations, and an advisory NuGet+npm dependency
scan — every job scoped to only the permissions and secrets it uses.
Decisions
build→sign-assemblies→pack→ (dependency-scan,sbom) →publish→attach-to-release.buildrunsnpm ci+ vite and holds the strong-name key in the same job (Release buildenv). Per Microsoft's guidance, a strong-name key is not a security boundary, so isolating it from the npm toolchain in a separatebuild-assetsjob (an earlier iteration of this branch) bought nothing worth the extra job — reverted per review. Onlysign-assemblies/pack/publishget Key Vault + OIDC (NuGet Deployenv).publishis the only job that can push and sparse-checks out just.github/scripts+ the cert pin.packrecorded (Get-PackageDigest.ps1)..github/scripts/; byte-identical toigniteui-blazorwhere shared.eng/IG.publickey.hex). The packed.nupkgis re-validated on both strong-name and Authenticode (Assert-PackageSignatures.ps1) —dotnet pack --no-buildonly re-zipsbin/. NuGet signature is certificate-fingerprint-pinned (Assert-NuGetSignature.ps1).packruns an explicitdotnet restorebeforedotnet pack --no-build --no-restore— without it pack failsNETSDK1127(targeting packs absent on the pack runner).Publish-NuGetPackage.ps1refuses to overwrite an existing NuGet.org version (no--skip-duplicate).packpassesRepositoryUrl/RepositoryCommitexplicitly.New-Sbom.ps1emits SPDX 2.2 + 3.0 from onesbom-toolrun (two runs disagreed on ClearlyDefined data and cross-detected each other's manifest); retries while licence coverage improves, fails only on the 0-of-N ClearlyDefined-outage signature.dotnet-CycloneDX(--exclude-dev) only sees the.csproj; the.nupkgalso ships the vite bundle +igniteui-webcomponentstheme CSS, covered byNew-NpmCycloneDxSbom.ps1(--omit dev, pinned devDependency).Merge-CycloneDxSbom.ps1combines them in pure PowerShell (cyclonedx-cliis GitHub-binary-only;cyclonedx-librarycan't deserialize JSON). Both halves pinned to spec 1.6.-ComponentScanExclusion '**/artifacts/**'keeps the packed.nupkgfrom being detected as its own dependency; here it is a no-op (-bcis the project dir) but is passed to match theigniteui-blazorcall, whose repo-root npm graph forces-bcup.Assert-Sbom.ps1 -PackageIdthen fails the release if any SPDX 2.2/3.0 package entry outside the document root carries the released id, so a dropped or mis-globbed exclusion cannot pass unnoticed.sbom-toolhas no dev/runtime filter, so SDK targeting packs and analyzers stay in the SPDX .NET section by design;--exclude-devkeeps the CycloneDX .NET half clear of them.purl+licenses(-RootLicenseExpression MIT, never inherited from the npm side). Inputmetadata.componentsub-roots are dropped fromcomponents[]— this removes theblazor-gridlite@0.0.0nodecyclonedx-npmreads from the private build-toolingpackage.json; that manifest's"version": "0.0.0"was also removed sosbom-toolstops emitting the same node into the SPDX. The root edge points straight at both ecosystems' first-level deps.metadata.tools.components[]is rebuilt from both inputs + aMerge-CycloneDxSbom.ps1entry (-MergeToolVersion= release SHA). A prune pass drops edges whosebom-refno component declares (cyclonedx-npm nested-path artifacts).Assert-CycloneDxSbom.ps1fails ifpkg:nuget/*orpkg:npm/*is absent..nupkg.sha256,.cdx.json.sha256) are LF-terminated<hash> <name>— CRLF / bare-hash forms brokesha256sum -coff Windows.dependency-scanis advisory (dotnet list package --vulnerable+npm audit --omit=dev); no PR-time blocking equivalent yet..config/dotnet-tools.jsonis the single tool manifest.evidencejob andeng/Check-BundleBudget.ps1/eng/bundle-budgets.json. WASM bundle-size budgets may return later as an npm build-time check instead of a release-workflow gate.Validation
Last fully-completed run (pre-dates this session's job merge and evidence removal):
0.9.2-alpha.17 /
run 34577960422.
All 9 jobs (the pre-merge graph) green.
Assert-Sbom.ps1ran with-PackageId, passed with 0 self-entries; the release SPDX no longer carriespkg:npm/blazor-gridlite@0.0.0(66 packages); the real bundled depigniteui-grid-lite@0.9.0is retained.gh release download: single synthetic root,purl+licenses: MIT,metadata.tools.components[]of 5, noISC, 0 danglingdependencies[]refs, 59 components (44 NuGet, 15 npm) — unchanged across every release checked, i.e. GridLite's npm CycloneDX half never had thetslib/@floating-ui/coregap blazor's did; the dependency-edge prune is defense-in-depth here.sha256sum -cOK on both sidecars;performance-report.mdrendered correctly; SPDX 2.2/3.0 both recorded 66 packages; all three attestations bound to the published.nupkgdigest.sbomjob survived a transient ClearlyDefined HTTP 524 on a licence-lookup attempt —New-Sbom.ps1's retry loop recovered on the next attempt.Assert-Sbom.ps1's self-reference gate verified against a clean SPDX (passes), an injected self-entry in both 2.2 and 3.0 (fails, both reported), and a wrong--PackageIdnegative control (passes)..github/scripts/*.ps1pass AST parsing;publish.ymlparses as valid YAML and its job graph resolves cleanly (build→sign-assemblies→pack→ (dependency-scan,sbom) →publish→attach-to-release).Open
build/build-assetsmerge and theevidence/bundle-budget removal — have not yet been through a release run. Local checks only: YAML parses, job graph has no danglingneeds, no leftoverbuild-assets/web-assets/evidence/budget references anywhere in the repo. Needs a release to confirm the combinedbuildjob produces identicalbuild-outputand thatattach-to-releasestill succeeds without the evidence artifact.Merge-CycloneDxSbom.ps1's JSON merge is hand-written. Covered: real BOMs + local synthetic edge cases. Not covered:vulnerabilitiesdata, duplicatebom-refacross the two inputs.sbom-toolhas no CLI filter; a post-filter was deliberately not added).cyclonedx-npmgains 1.7 support.