Repository navigation
feat(release): SBOMs for the WebAssembly modules, the npm tarballs and the Windows installer - #1596
Merged
Merged
Conversation
…d the Windows installer The wasm modules are described from the module information Go embeds in them, which go version -m and syft cannot read from this format. The npm tarballs are packed by a new build-npm-package job before the manifest, so tarballs and SBOMs are lines of SHA256SUMS.txt and publish-npm publishes the bytes the signed manifest lists; each SBOM describes the packed manifest with its dependencies resolved through the lockfile. The MSI's SBOM is derived from its inputs: the Go executables and the bundled Z3 declared from packaging/msi/z3.pin, listed in SHA256SUMS-windows-msi.txt. Co-Authored-By: jason.han <hanhuijun@gmail.com>
Contributor
Author
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
…re/release-sbom-remaining
…re/release-sbom-remaining Co-Authored-By: jason.han <hanhuijun@gmail.com> # Conflicts: # tests/hygiene/release_config_test.go
HuiJun
marked this pull request as ready for review
October 28, 2026 03:04
…the signed MSI SBOM An optional dependency the lockfile cannot resolve yet (the platform packages a release publishes beside the tarball) is recorded from the manifest's exact pin with a sourceInfo saying so, instead of failing the npm SBOM; required dependencies still must resolve. The signed installer's SBOM is written by the sign job from the executables SignPath returned, so its file digests are those of the bytes the signed MSI ships. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…the lockfile lacks them The manifest-only fallback of the npm SBOM applies to @openmbee/ optional dependencies pinned at the tarball's own version — the platform packages the release publishes beside it — so a third-party optional dependency the lockfile does not resolve is still refused. Co-Authored-By: jason.han <hanhuijun@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
PR #1592 (
feature/release-sbom) publishes an SPDX 2.3 SBOM per archive, wheel, sdist and raw binary, listed inSHA256SUMS.txtso the cosign signature and the provenance cover them, and deferred three asset classes. This PR covers them, with the samescripts/release-sbom.py, naming (<asset>.spdx.json) and pinned syft 1.55.0.Builds on #1592, now on develop; this PR targets
developand carries develop merged in.sysml-wasm.wasm,sysml-prod.wasm.gz). Go embeds the module information in a wasm module, butgo version -m sysml-wasm.wasmanswerscould not read Go build info … unrecognized file formatand syft writes a document naming nothing (both reproduced locally on a freshGOOS=js GOARCH=wasmbuild — the 32-byte\xff Go buildinf:header is absent from the wasm, only the sentinel-wrapped module list is there).release-sbom.pynow reads that list betweendebug/buildinfo'sinfoStart/infoEndmarkers and the runtime's uniquego1.x.ystring (refusing a module with none or several), and writes the SPDX document itself with the packages syft would record (main module, every dependency at its built version,stdlib). The same build-info check applies. Nogo list -depsfallback was needed.build-npm-packagejob (node executor, besidebuild-python-package) checks the tag againstclient/node/package.json, stamps the service digests, builds and packs the client (asserting its pins, as before), packs@openmbee/sysml-mcpand the generator-derived@openmbee/opensysml-mcp, and persistsdist/npm.build-releasewrites one SBOM per tarball — the packedpackage/package.json(exact packed version, the tarball's SHA-256) as the described package, dependencies catalogued by syft from the lockfile (client/node/package-lock.json;packaging/npm/sysml-mcp/package-lock.jsonfor the runner and its alias, which has no lockfile and pins the same five platform packages) — refusing a dependency the lock does not resolve or an exact pin at another version — except the release's own platform packages (@openmbee/optional dependencies pinned at the tarball's version), which cannot be in the lockfile before the release publishes them and are declared from the manifest's pin with asourceInfosaying so — and lists tarballs and SBOMs inSHA256SUMS.txt.publish-github-releaseuploads the tarballs;publish-npmno longer packs anything: it verifies the three tarballs against the signed manifest (sha256sum --check --strict) and publishes those bytes. Dev dependencies are not catalogued (syft default); peer dependencies are the installer's and are not required.release-windows.yml's Linuxbuildjob installs the same pinned syft, downloads the Z3 zippackaging/msi/z3.pinnames and refuses a digest other than the pin's (asbuild-msi.shdoes), and runsrelease-sbom.py --msi-inputs dist/unsigned --z3-pin … --z3-zip …: syft over the four Go executables (checked withgo version -m) plus a declaredz3package (version, zip URL, pinned SHA-256, purlpkg:github/z3prover/z3@z3-5.1.0) with the five files the installer takes from the zip and their SHA-256s. The document is uploaded as an artifact; themsijob requires it to be named after the MSI it built and lists both inSHA256SUMS-windows-msi.txt. Thesignjob writes the-signed.msi.spdx.jsonthe same way from the SignPath-returned executables indist/signed(--msi-signed), so its file digests are the signed bytes';publish-signedlists it inSHA256SUMS-windows-signed.txt. Documented as input-derived.scripts/release-sbom-test.py(47 tests: wasm module-info reading incl. gz and the toolchain ambiguity refusal, authored document shape and reproducibility apart fromcreated, npm manifest/name/dependency checks incl. the alias and the unpublished-platform-package case (a missing third-party optional dependency is still refused), Z3 pin/zip/digest/files, MSI naming asbuild-msi.shderives it);scripts/release-provenance-test.pyasserts a tarball and the wasm/npm SBOMs become subjects;tests/hygiene:TestNodePackageIsStampedWithItsReleaseDigestsandTestNpmRunnerIsPublishedWithTheClientnow holdbuild-npm-package/publish-npmto the new contract,TestReleaseSBOMsAreSignedWithTheManifestrequires the wasm and npm SBOMs before the manifest and the tarballs listed before the SBOMs, newTestWindowsInstallerSBOMIsDerivedFromItsInputs(same syft pin in both CI files, SBOM named after the MSI before it is listed),TestMCPServerShipsAsItsOwnArchiveadmitsbuild-npm-packagereadingdist/mcp.docs/project/releasing.md(asset inventory, SBOM section with the three routes, Windows installer section, npm job order),docs/downloads.md§ Verify, README. Changelog fragmentchanges/unreleased/release-sbom-remaining.added.md. (docs(security): publish the security policy on the site with security.txt and release verification #1591 also editsdocs/downloads.md§ Verify — expect a trivial conflict with whichever lands second.)How it was verified
Local rehearsal with syft 1.55.0 (pinned download, digest checked) and go1.25.11:
sysml-wasm.wasm(freshGOOS=js GOARCH=wasmbuild ofcmd/sysml-wasm),sysml-prod.wasm.gz(gzip of it)go version -mon the same file:unrecognized file format; syft: 1 package (the file)openmbee-opensysml-0.11.0.tgz(npm packof client/node afternpm run build)openmbee-sysml-mcp-0.11.0.tgz,openmbee-opensysml-mcp-0.11.0.tgz(alias frombuild-platform-packages.mjs --mcp)--msi-inputsoverGOOS=windowsbuilds ofcmd/sysmlandcmd/sysml-mcpwith the realz3-5.1.0-x64-win.zip(6ff368ea…), tagv0.12.0-rc1opensysml-0.12.0-windows-amd64.msi.spdx.json(and…-signed.msi.spdx.jsonwith--msi-signed): 41 Go module components + Z3 5.1.0 with its five files' digestsGates:
go build ./...,go vet ./...,gofmt -l .(empty),go test ./tests/hygiene(ok),python3 scripts/release-sbom-test.py(43 OK),python3 scripts/release-provenance-test.py(15 OK),circleci config validate(valid),actionlint .github/workflows/release-windows.yml(clean),scripts/check-doc-links.py(0 broken),scripts/changelog.py check. Fullgo test ./...before un-drafting.Not rehearsed end to end: the CircleCI release pipeline and the Windows runner path (
release_rehearsal=trueon the next merge to main exercisesbuild-npm-packageand the newbuild-releasesteps;workflow_dispatchofrelease-windows.ymlwith a dry-run version exercises the MSI SBOM).Checklist
make testandmake lintpass locallychanges/unreleased/<slug>.<section>.md, not as an edit toCHANGELOG.mdmake docs-countsrun if a gate count moved (compliance rows need nothing: the census is counted at docs build)F4,K5) in the body, docs, or changelogLink to Devin session: https://nasa-jpl-demo.devinenterprise.com/sessions/4ef7130ad284408b9dc99863b96b72b5
Open in Devin Desktop: https://nasa-jpl-demo.devinenterprise.com/desktop/session/4ef7130ad284408b9dc99863b96b72b5?variant=devin
Requested by: @HuiJun