Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
73c8455
Pin native SBOM tool metadata and SPDX schema source
rmcdaniel Nov 5, 2026
7b2490f
Require explicit SPDX version semantics beyond upstream schema
rmcdaniel Nov 5, 2026
66261aa
Validate bounded SPDX document semantics and predicate identity
rmcdaniel Nov 5, 2026
1142340
Record immutable offline SPDX validator input closure
rmcdaniel Nov 5, 2026
16654a2
Refuse altered or incomplete raw SBOM Release assets
rmcdaniel Nov 5, 2026
90a8ae0
Bind SBOM verification requests to exact release children and attempts
rmcdaniel Nov 5, 2026
1610e8f
Bind SBOM subjects to exact two-platform index bytes
rmcdaniel Nov 5, 2026
6f6e264
Bind SBOM child and config bytes to release identity
rmcdaniel Nov 5, 2026
06afd75
Require SBOM attestation verification from each OCI registry
rmcdaniel Nov 5, 2026
83af727
Bind verified SBOM certificates to immutable repository identity
rmcdaniel Nov 5, 2026
daddccf
Match verified SBOM statements to retained child predicates
rmcdaniel Nov 5, 2026
ad70d15
Compile exact SBOM release asset checksum manifests
rmcdaniel Nov 5, 2026
1d8784b
Require complete current SBOM proofs before returning release assets
rmcdaniel Nov 5, 2026
34166c4
Bind the proposed SBOM validator installer source identity
rmcdaniel Nov 5, 2026
a8489b5
Specify offline SBOM export integrity and confinement boundaries
rmcdaniel Nov 5, 2026
7450cd0
Bind bounded OCI archive contents to the smoked config and layers
rmcdaniel Nov 5, 2026
ae11fbe
Bind the proposed OCI side output and known BuildKit metadata
rmcdaniel Nov 5, 2026
90c7796
Preserve the publication digest across proposed multiple exporters
rmcdaniel Nov 5, 2026
98070b8
Refresh native SBOM source components over accepted main
rmcdaniel Nov 5, 2026
fed5b11
Stage bounded SBOM source components for ordinary review [skip ci]
rmcdaniel Nov 5, 2026
1bb29ec
Run bounded native SBOM component tests in ordinary source CI
rmcdaniel Nov 5, 2026
d35f4d1
Prepare SBOM source tests with the ordered promotion source
rmcdaniel Nov 5, 2026
d417a3c
Keep promotion tests independent of SBOM test dependencies
rmcdaniel Nov 5, 2026
a516260
Clarify the SBOM development tooling and validator admission scopes
rmcdaniel Nov 5, 2026
19ba1bb
Merge remote-tracking branch 'origin/main' into feat/server-1012-nati…
rmcdaniel Nov 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/native-promotion-contracts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ on:
- scripts/ci/select-published-compose-image.sh
- scripts/smoke-published-compose.sh
- scripts/conformance/server-parity/native-image-corpus.sh
- tests/Unit/Support/native_*test.py
- tests/Unit/Support/native_alias_*test.py
- tests/Unit/Support/native_qualification_test.py
- .github/workflows/native-*.yml
- .github/workflows/published-compose.yml
permissions:
Expand All @@ -24,6 +25,8 @@ jobs:
with:
persist-credentials: false
- name: Countercheck provider receipts, cleanup and fail-before writes
run: python3 -m unittest discover -s tests/Unit/Support -p 'native_*test.py' -v
run: |
python3 -m unittest discover -s tests/Unit/Support -p 'native_qualification_test.py' -v
python3 -m unittest discover -s tests/Unit/Support -p 'native_alias_*test.py' -v
- name: Preserve release, candidate and monotonic alias contracts
run: bash scripts/ci/check-native-release-tags.sh
26 changes: 26 additions & 0 deletions .github/workflows/phpunit-feature.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,32 @@ jobs:
--target server
--workflow-directory .github/workflows

sbom-source:
name: Native SBOM source components
needs: action-policy
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Check out source
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- name: Set up source-test Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.13"
- name: Bootstrap patched pip trust
uses: durable-workflow/.github/.github/actions/bootstrap-patched-pip@75587fc33ff40c34f32ef777293dad56bb6f48c7 # clean-trust
- name: Install hash-closed SBOM source-test tooling
run: >-
python -I -m pip install --no-deps --require-hashes --only-binary=:all:
--requirement scripts/ci/native-sbom/source-test.requirements.txt

- name: Check bounded SBOM source components with synthetic inputs
run: |
python -I -B tests/Unit/Support/native_sbom_archive_test.py
python -I -B tests/Unit/Support/native_sbom_policy_test.py

preflight:
name: Repository contracts
runs-on: ubuntu-latest
Expand Down
131 changes: 131 additions & 0 deletions docs/contracts/native-sbom-generation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Native SBOM generation source proposal

This is the remaining generation design for Server #1012, targeting the future
3.2 line. It is not an executable, accepted producer format or operation
admission. `generation_mode` stays null until the complete source and tool
closure are reviewed. Frozen 3.1 artifacts keep their recorded SBOM-free status.

## Subject and export authority

Each native architecture job must retain its actual successful smoke
observation, immutable local config ID, source commit, release, platform and
provider run/attempt/job. The preferred source candidate emits an additional
OCI archive from the same single-platform build result. It may be produced
before smoke, but cannot supply SBOM authority until its complete config/layer
graph matches the actual successfully smoked config. Never rebuild or resolve
a mutable name to obtain a supposedly equivalent input. The independently verified
published index/child/config tuple remains the attestation authority. An archive
hash or an exporter-generated manifest is not a replacement registry subject.

The proposed offline input is one unnamed OCI-layout tar with uncompressed
layers. [Multiple exporters](https://docs.docker.com/build/exporters/)
allow a second output without a second build. The candidate OCI parameters are
`type=oci,dest=native-sbom/input.oci.tar,compression=uncompressed,force-compression=true,oci-mediatypes=true`.
Force compression is explicit so cached compressed layers cannot silently
enter this input profile. The native Release's existing named registry output
would remain separate; the PR image's daemon output would carry its existing
owned smoke name. Do not use global tags to name the additional archive:
[Buildx 0.38.1](https://github.com/docker/buildx/blob/v0.38.1/build/opt.go)
copies those names to every image exporter.

Output order is part of this proposal. Put the unnamed OCI exporter first and
the existing named registry (or PR daemon) exporter last. The exact
[BuildKit response merger](https://github.com/moby/buildkit/blob/v0.34.2/solver/llbsolver/export.go)
collects results by exporter position, then overwrites duplicate response keys
in that order. Appending OCI after the registry output could replace the
publisher's `containerimage.digest` with an archive-specific digest. Future
source controls must reject that order and independently bind the resulting
registry digest/config to the actual immutable registry and smoke observations.
An aggregate build response alone is not registry-subject authority. Preserve
the existing registry output arguments and never substitute the archive digest
when a publication or smoke read fails.

Read-only historical provider log `42056550973` / job `127346371667` records
Buildx 0.38.1, BuildKit 0.34.2 and Engine 29.4.2. This is existing-tool evidence,
not a new exporter invocation or future runtime qualification. The exact
[BuildKit OCI exporter](https://github.com/moby/buildkit/blob/v0.34.2/exporter/oci/export.go)
selects containerd's skip-Docker-manifest option and adds a UTC creation
annotation; a single manifest descriptor may omit its platform. Those known
presentation fields cannot replace config/platform authority. Unknown names,
annotations or additional graphs still refuse. Actual exporter identity and
produced format must be independently qualified before adoption.

Docker-save hybrid input remains unsupported. Its possible additional image
references/metadata require a different reviewed adapter; it is not a fallback
when the proposed OCI export or guard fails.

The archive guard must stream under a fixed total-byte/member/deadline bound,
without extracting to the host. It must reject duplicate names, traversal,
absolute paths, links, special/sparse files, unsupported header extensions,
truncation, unrelated image graphs and unrecognized formats. Require exactly
one selected image; hash the complete original config bytes against the smoked
config ID; and hash each uncompressed layer against that config's ordered
`rootfs.diff_ids`. Config equality alone does not authenticate layer contents.
Any additional OCI index/manifest must resolve to the same complete graph;
unreferenced or contradictory members refuse. A generated archive manifest may
differ from the registry manifest because its layer representation differs.

Export into an exclusively owned private directory. Retain the original regular
file identity, size and hash through validation, read-only scanner mounting and
post-scan revalidation. A replaced path, changed inode/bytes, unknown reader
state or failed export produces no SBOM authority. Exact exporter-format
fixtures and old-config/new-config, reordered/changed-layer, extra-image,
duplicate/path/link/truncation countercontrols are required before adoption.

## Confined generation and validation

The proposed scanner input is exclusively the held archive. The pinned
[Syft 1.55.0 source](https://github.com/anchore/syft/blob/f15c3343e29dfb20cf6d82c820e7090b49808c96/cmd/syft/internal/commands/scan.go)
supports explicit `--from oci-archive`; omitting the source selection can
fall back to a daemon or registry. The future fixed invocation must select the
archive source, exact platform, SPDX JSON and intended filesystem scope
explicitly, with automatic update/config discovery disabled by reviewed inputs.
No daemon socket, registry access, host credentials or repository configuration
may reach the scanner. Its platform-specific image child/config and actual
Syft version must match the committed tool pins before accepting output.

Both scanner and validator must run as an explicit nonroot UID/GID, with a
read-only root, no capabilities, no-new-privileges, no network or published
ports, and bounded tmpfs/scratch, CPU, memory, PIDs, output and absolute cleanup
deadlines. Mount only the sealed input and reviewed tool/dependency files
read-only. Bound scanner stdout to the existing 16-MiB document limit and retain
the original bytes; stderr is bounded diagnostics, never provider authority.
Source tests must inspect the complete effective create/start arguments and
reject an extra mount, environment key, source, privilege or mutable tool name.

The validator uses the independently pinned Python child/config and installer
source identity, held hash-checked wheels and the unchanged architecture-specific
requirements. Its install is offline with no dependency resolution or ambient
site/config fallback. Effective isolated Python/pip versions and complete
installed dependency identities must match before importing the schema checker.
The pinned vendored schema denies remote retrieval. Validation returns bounded
hash/count observations; it does not rewrite the original SPDX asset.

Use existing job deadlines and the reviewed exact-owner cleanup mechanism,
extended narrowly for the new kind/scope and bound helper bytes. Always-run
cleanup must inspect exact IDs plus owner labels, retain the original failure,
and refuse foreign/unknown resources. Do not add a by-name fallback or another
lifecycle controller. No positive receipt exists until body and cleanup both
succeed and an independent terminal absence readback passes.

## Provider and publication wiring

Retain each platform's original SPDX, config/export observations and cleanup
receipt as bounded provider artifacts bound to exact source/run/attempt/job.
The later publisher must independently verify those bindings against both
registries' actual immutable index/child/config tuples before signing each
platform predicate for each registry. Signing an index or provenance predicate
does not satisfy this contract.

The Release verifier must execute the fixed registry-bundle signature requests,
check current repository/owner identity and exact signer/source/tag/invocation,
and compare every current predicate with the retained document. All four
registry/platform slots must pass before returning the two raw SPDX files and
the checksum map. Existing assets require original byte equality; conflicting,
missing, failed or ambiguous evidence permits no writer or replacement.

Future 3.2 workflow wiring must preserve the shared promotion/Release lock and
revalidate provider evidence after protection approval. It must not apply the
new SBOM requirement retroactively to frozen 3.1. Normal source publication,
complete Security review and General's source/operation selections remain
separate prerequisites; this document selects no scan, build or release.
Loading
Loading