ci: release tested source snapshots only

This commit is contained in:
Joseph Magly
2026-08-22 11:28:30 -04:00
parent c546d30e9e
commit beb4707401
15 changed files with 104 additions and 181 deletions
+5 -5
View File
@@ -49,7 +49,7 @@
"bt6-maintainer": {
"version": "0.3.0",
"source": "project-local",
"installedAt": "2026-08-22T14:56:47.190Z",
"installedAt": "2026-08-22T15:28:11.760Z",
"deployedTo": {
"codex": {
"agents": 5,
@@ -74,8 +74,8 @@
"skills/bt6-pr-audit/SKILL.md": "38be15ba6c6c12f958c7ced0bf6152654dc6df53ddc17156fda84083ae990ea9",
"skills/bt6-provider-review/SKILL.md": "a95ac6d2be20847626bb94e5038358148d4a19c25e00ade6049526f8a16d0c15",
"skills/bt6-queue-audit/SKILL.md": "aaa77f2e14b46a2f8329025923ed54745152cea6896c947580f7a1babff81312",
"skills/bt6-release-readiness/SKILL.md": "b51881662f27604c518c67e009187b3b0bad1d0755116efae1b0d1505dbb3dfe",
"skills/bt6-release-validation/SKILL.md": "3ecf342ad12cd1eed2f38ed8eb5b7fb4e396a4e25c79b54e5f649eef0c10c866"
"skills/bt6-release-readiness/SKILL.md": "26c65c1f64ce29f947be8a90987489a499814c360f4789ed80ef35f711c3d4e0",
"skills/bt6-release-validation/SKILL.md": "9c0dfe72690bdcae273624be730751951b42bab36b538989a8708c0fb48f76d1"
},
"deployedArtifactHashes": {
"codex": {
@@ -90,8 +90,8 @@
"skills/bt6-pr-audit/SKILL.md": "9a1e55bfd6972f3168f884d54cd6ede3673b541940b54352120ed4c0a4c49f2c",
"skills/bt6-provider-review/SKILL.md": "2de8b37f546dda852bee88d023caf82877baab988aebc219fd83a784c2bf58a3",
"skills/bt6-queue-audit/SKILL.md": "e0bdb36c8f8f85a2e9dff0180de2f502972bb8af9b7b13c4661be065354ef893",
"skills/bt6-release-readiness/SKILL.md": "e609a2f2197fd8c65239f563a0f8cc594ad0d420e4d18da5ba8362f26574ff52",
"skills/bt6-release-validation/SKILL.md": "942cd70729c5c13c384532214ebfff1763fd9374a85abda18925932b6dc79167"
"skills/bt6-release-readiness/SKILL.md": "db317a00f8f4198e93e0666a00af29ad645d4c3d50de2e42141d84d6e7c16eb1",
"skills/bt6-release-validation/SKILL.md": "e94720ea230a8206d7acf5b3c514c6bdf1b7f81865580b7b2c6df11472bdb384"
}
}
}
+3 -3
View File
@@ -28,7 +28,6 @@ validation:
- "mkdir -p test-results && python -m pytest -m 'not slow and not gpu and not mps and not mlx and not network and not download and not remote and not operator_ui' --cov-branch --cov-fail-under=0 --cov-report=json:test-results/coverage-release.json"
- "python scripts/check_coverage_thresholds.py test-results/coverage-release.json --min-line 75 --min-branch 60 --min-file obliteratus/device.py=70 --min-file obliteratus/models/loader.py=70 --min-file obliteratus/architecture_profiles.py=70 --min-file obliteratus/cli.py=70 --min-file obliteratus/mlx_backend.py=70 --min-file obliteratus/evaluation/metrics.py=70 --min-file obliteratus/evaluation/advanced_metrics.py=70 --min-file obliteratus/reporting/report.py=70 --min-file obliteratus/community.py=70 --min-file obliteratus/telemetry.py=70"
- "python scripts/check_quality_policy.py --policy ci/test-quality-policy.json --coverage test-results/coverage-release.json && python scripts/check_conditional_policy.py && python scripts/check_test_risk_map.py"
- "python -m build --sdist --wheel"
- "python -c 'import obliteratus; print(obliteratus.__version__)'"
- "python -m obliteratus --help"
qualityPolicy:
@@ -40,12 +39,13 @@ validation:
researchIntegrity:
- "python -m pytest tests/test_advanced_metrics.py tests/test_breakthrough_modules.py tests/test_community.py"
releaseEvidence:
artifactType: "source-zip"
hashAlgorithm: "sha256"
provenanceFormat: "slsa-v1"
attestationFormat: "in-toto"
signingMode: "sigstore-keyless"
sbomFormat: "cyclonedx"
buildOnce: true
snapshotOnce: true
verifyBeforePromotion: true
riskSurfaces:
- id: "model-loading"
@@ -67,7 +67,7 @@ riskSurfaces:
- id: "ci-supply-chain"
paths: [".github/workflows/**", "ci/**", "docs/SUPPLY_CHAIN_POLICY.md", "pyproject.toml", "scripts/check_supply_chain_policy.py", "uv.lock"]
concerns: ["workflow-permissions", "dependency-pinning", "untrusted-pull-request-code"]
requiredChecks: ["uv lock --check", "python -m pytest tests/test_ci_policy.py tests/test_supply_chain_policy.py", "python -m build --sdist --wheel"]
requiredChecks: ["uv lock --check", "python -m pytest tests/test_ci_policy.py tests/test_supply_chain_policy.py"]
research:
corpusPaths: ["obliteratus/prompts.py", "community_results/**"]
evidencePaths: ["paper/**", "docs/**", "community_results/**"]
@@ -102,21 +102,23 @@
"type": "object",
"additionalProperties": false,
"required": [
"artifactType",
"hashAlgorithm",
"provenanceFormat",
"attestationFormat",
"signingMode",
"sbomFormat",
"buildOnce",
"snapshotOnce",
"verifyBeforePromotion"
],
"properties": {
"artifactType": { "const": "source-zip" },
"hashAlgorithm": { "const": "sha256" },
"provenanceFormat": { "const": "slsa-v1" },
"attestationFormat": { "const": "in-toto" },
"signingMode": { "enum": ["sigstore-keyless", "project-defined"] },
"sbomFormat": { "enum": ["cyclonedx", "spdx"] },
"buildOnce": { "const": true },
"snapshotOnce": { "const": true },
"verifyBeforePromotion": { "const": true }
}
},
@@ -52,10 +52,12 @@ promote a tag.
5. Re-run focused checks after each repair, then re-run the complete applicable
pre-tag gate. Stop when all locally executable hard gates pass or a required
external/platform gate remains unresolved.
6. When `releaseEvidence` is configured, require one canonical SHA-256 manifest
for the build-once release set, a bound SBOM, SLSA/in-toto provenance, and
the configured signature mechanism. Downstream gates must consume the same
artifact bytes rather than rebuilding them.
6. When `releaseEvidence` is configured, require one deterministic source ZIP
of the tested commit, a canonical SHA-256 manifest, a bound source SBOM,
SLSA/in-toto provenance, and the configured signature mechanism. Do not
compile or publish installable packages unless a repository explicitly
overrides the source-snapshot policy. Downstream gates must consume the same
ZIP bytes rather than recreating them.
## Decision
@@ -45,9 +45,9 @@ and does not publish a release without separate explicit authorization.
1. Run every repository-profile `validation.full` command at the exact tagged
commit.
2. Run applicable `documentation`, `researchIntegrity`, and risk-surface checks.
3. Run repository-defined packaging, artifact-integrity, compatibility, and
supported-platform checks. Verify generated artifacts come from the tagged
source rather than an unrelated checkout.
3. Run repository-defined source-snapshot integrity, compatibility, and
supported-platform checks. Verify the release ZIP comes from the tagged
source rather than an unrelated checkout; do not require a compiled package.
4. Compare local evidence with CI for the same tag and commit. Record missing or
stale evidence as incomplete, not passing.
5. Treat warnings, flakes, skips, coverage changes, and conditional-gate gaps
@@ -55,8 +55,9 @@ and does not publish a release without separate explicit authorization.
turnaround budget as a release exemption.
6. When `releaseEvidence` is configured, verify every artifact against the
canonical checksum manifest, authenticate its SLSA/in-toto attestation,
validate the SBOM binding, and prove promotion uses the tested build-once
bytes. A checksum without authenticated provenance is incomplete evidence.
validate the source-SBOM binding, and prove promotion uses the tested
snapshot-once ZIP bytes. A checksum without authenticated provenance is
incomplete evidence.
## Decision
@@ -26,7 +26,7 @@ description: Pre-tag audit and hardening report for moving a candidate from the
| Full validation | `<profile validation.full>` | `<pass/fail/not run>` | `<artifact/log>` |
| Coverage and regression | `<repository/scope/module floors>` | `<pass/fail>` | `<coverage report>` |
| Quality depth | `<repeat/mutation/warnings>` | `<pass/fail/not run>` | `<artifact/log>` |
| Packaging and supply chain | `<policy>` | `<pass/fail/not run>` | `<artifact/log>` |
| Source snapshot and supply chain | `<policy>` | `<pass/fail/not run>` | `<ZIP/evidence/log>` |
| Conditional/platform checks | `<policy>` | `<pass/fail/deferred>` | `<artifact/CI URL>` |
## Repairs
@@ -33,7 +33,7 @@ Profile: `<path and hash>`
| Documentation | `<pass/fail/n-a>` | `<details>` |
| Research and provenance | `<pass/fail/n-a>` | `<details>` |
| Risk-surface checks | `<pass/fail/n-a>` | `<details>` |
| Packaging and artifact integrity | `<pass/fail/n-a>` | `<details>` |
| Source ZIP and artifact integrity | `<pass/fail/n-a>` | `<details>` |
| Supported platforms | `<pass/fail/incomplete>` | `<details>` |
## Decision
@@ -35,12 +35,13 @@ validation:
researchIntegrity:
- "<provenance/corpus/reproducibility check>"
releaseEvidence:
artifactType: "source-zip"
hashAlgorithm: "sha256"
provenanceFormat: "slsa-v1"
attestationFormat: "in-toto"
signingMode: "sigstore-keyless"
sbomFormat: "cyclonedx"
buildOnce: true
snapshotOnce: true
verifyBeforePromotion: true
riskSurfaces:
- id: "ingestion"
+29 -121
View File
@@ -49,54 +49,24 @@ jobs:
pyproject.toml
uv.lock
- name: Install build tooling
- name: Install release evidence tooling
run: |
python -m pip install "uv==${UV_VERSION}"
UV_PROJECT_ENVIRONMENT="$BUILD_TOOLS" uv sync --locked --only-group ci
- name: Build source and wheel distributions
run: >-
"$BUILD_TOOLS/bin/python" -m build --sdist --wheel
- name: Verify wheel contents and entry point
- name: Create deterministic tested-source snapshot
run: |
python - <<'PY'
from pathlib import Path
from zipfile import ZipFile
mkdir -p release package-evidence
snapshot="release/OBLITERATUS-${GITHUB_SHA}.zip"
git archive \
--format=zip \
--prefix="OBLITERATUS-${GITHUB_SHA}/" \
--output="$snapshot" \
"$GITHUB_SHA"
test "$(find release -maxdepth 1 -type f -name '*.zip' | wc -l)" -eq 1
unzip -t "$snapshot" | tee package-evidence/source-zip-check.txt
wheels = list(Path("dist").glob("*.whl"))
if len(wheels) != 1:
raise SystemExit(f"expected one wheel, found: {wheels}")
with ZipFile(wheels[0]) as archive:
names = set(archive.namelist())
required = {
"app.py",
"obliteratus/__init__.py",
"obliteratus/local_ui.py",
}
missing = sorted(required - names)
if missing:
raise SystemExit(f"wheel is missing required modules: {missing}")
entry_points = [
name for name in names if name.endswith(".dist-info/entry_points.txt")
]
if len(entry_points) != 1:
raise SystemExit(f"expected one entry_points.txt, found: {entry_points}")
contents = archive.read(entry_points[0]).decode("utf-8")
if "obliteratus = obliteratus.cli:main" not in contents:
raise SystemExit("wheel is missing the obliteratus console entry point")
print(f"verified wheel contents: {wheels[0]}")
PY
- name: Validate distribution metadata
run: |
mkdir -p package-evidence
"$BUILD_TOOLS/bin/python" -m twine check dist/* | tee package-evidence/twine-check.txt
- name: Generate and bind CycloneDX SBOM
- name: Generate and bind source CycloneDX SBOM
run: |
uv --preview-features sbom-export export \
--locked \
@@ -105,95 +75,33 @@ jobs:
--no-dev \
--no-editable \
--output-file package-evidence/obliteratus.cdx.unbound.json
mapfile -t wheels < <(find dist -maxdepth 1 -type f -name '*.whl' -print)
if [ "${#wheels[@]}" -ne 1 ]; then
echo "expected exactly one wheel, found ${#wheels[@]}"
mapfile -t snapshots < <(find release -maxdepth 1 -type f -name '*.zip' -print)
if [ "${#snapshots[@]}" -ne 1 ]; then
echo "expected exactly one source snapshot, found ${#snapshots[@]}"
exit 1
fi
python scripts/check_supply_chain_policy.py sbom \
--input package-evidence/obliteratus.cdx.unbound.json \
--wheel "${wheels[0]}" \
--artifact "${snapshots[0]}" \
--output package-evidence/obliteratus.cdx.json
rm package-evidence/obliteratus.cdx.unbound.json
- name: Generate canonical release checksums
run: >-
sha256sum dist/* package-evidence/obliteratus.cdx.json |
sha256sum release/*.zip package-evidence/obliteratus.cdx.json |
tee package-evidence/SHA256SUMS
- name: Verify installed wheel contract
run: |
mapfile -t wheels < <(find "$GITHUB_WORKSPACE/dist" -maxdepth 1 -type f -name '*.whl' -print)
if [ "${#wheels[@]}" -ne 1 ]; then
echo "expected exactly one wheel, found ${#wheels[@]}"
exit 1
fi
wheel_env="$RUNNER_TEMP/obliteratus-wheel-env"
wheel_cwd="$RUNNER_TEMP/obliteratus-wheel-cwd"
python -m venv "$wheel_env"
mkdir -p "$wheel_cwd"
"$wheel_env/bin/python" -m pip install --no-cache-dir "rich==15.0.0"
"$wheel_env/bin/python" -m pip install --no-cache-dir --no-deps "${wheels[0]}"
cd "$wheel_cwd"
"$wheel_env/bin/python" -I - <<'PY' | tee "$GITHUB_WORKSPACE/package-evidence/wheel-import.txt"
import importlib.metadata
from pathlib import Path
import obliteratus
origin = Path(obliteratus.__file__).resolve()
assert "site-packages" in origin.parts, origin
assert obliteratus.__version__ == importlib.metadata.version("obliteratus")
print(f"installed wheel import: {origin}")
print(f"version: {obliteratus.__version__}")
PY
"$wheel_env/bin/python" -I -m obliteratus --help > "$GITHUB_WORKSPACE/package-evidence/wheel-module-help.txt"
"$wheel_env/bin/obliteratus" --help > "$GITHUB_WORKSPACE/package-evidence/wheel-console-help.txt"
- name: Verify installed sdist contract
run: |
mapfile -t sdists < <(find "$GITHUB_WORKSPACE/dist" -maxdepth 1 -type f -name '*.tar.gz' -print)
if [ "${#sdists[@]}" -ne 1 ]; then
echo "expected exactly one sdist, found ${#sdists[@]}"
exit 1
fi
sdist_env="$RUNNER_TEMP/obliteratus-sdist-env"
sdist_cwd="$RUNNER_TEMP/obliteratus-sdist-cwd"
python -m venv "$sdist_env"
mkdir -p "$sdist_cwd"
"$sdist_env/bin/python" -m pip install --no-cache-dir "rich==15.0.0"
"$sdist_env/bin/python" -m pip install --no-cache-dir --no-deps "${sdists[0]}"
cd "$sdist_cwd"
"$sdist_env/bin/python" -I - <<'PY' | tee "$GITHUB_WORKSPACE/package-evidence/sdist-import.txt"
import importlib.metadata
from pathlib import Path
import obliteratus
origin = Path(obliteratus.__file__).resolve()
assert "site-packages" in origin.parts, origin
assert obliteratus.__version__ == importlib.metadata.version("obliteratus")
print(f"installed sdist import: {origin}")
print(f"version: {obliteratus.__version__}")
PY
"$sdist_env/bin/python" -I -m obliteratus --help > "$GITHUB_WORKSPACE/package-evidence/sdist-module-help.txt"
"$sdist_env/bin/obliteratus" --help > "$GITHUB_WORKSPACE/package-evidence/sdist-console-help.txt"
- name: Attest release-set SLSA provenance
id: provenance
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
with:
subject-checksums: package-evidence/SHA256SUMS
- name: Attest wheel SBOM
- name: Attest source snapshot SBOM
id: sbom-attestation
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
with:
subject-path: dist/*.whl
subject-path: release/*.zip
sbom-path: package-evidence/obliteratus.cdx.json
- name: Retain Sigstore attestation bundles
@@ -204,13 +112,13 @@ jobs:
package-evidence/sbom-attestation.sigstore.json
sha256sum -c package-evidence/SHA256SUMS
- name: Upload distributions and package evidence
- name: Upload tested source snapshot and release evidence
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: distributions-py3.12
name: tested-source-snapshot-py3.12
path: |
dist/
release/
package-evidence/
if-no-files-found: error
retention-days: 14
@@ -770,10 +678,10 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Download attested release set
- name: Download attested tested-source snapshot
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: distributions-py3.12
name: tested-source-snapshot-py3.12
path: /tmp/supply-chain-evidence/release
- name: Set up Python
@@ -841,18 +749,18 @@ jobs:
echo "$status" > "$EVIDENCE/uv-audit-py${version}.status"
done
- name: Verify attested release set and bound CycloneDX SBOM
- name: Verify attested source snapshot and bound CycloneDX SBOM
run: |
cd "$EVIDENCE/release"
sha256sum -c package-evidence/SHA256SUMS
mapfile -t wheels < <(find dist -maxdepth 1 -type f -name '*.whl' -print)
if [ "${#wheels[@]}" -ne 1 ]; then
echo "expected exactly one wheel, found ${#wheels[@]}"
mapfile -t snapshots < <(find release -maxdepth 1 -type f -name '*.zip' -print)
if [ "${#snapshots[@]}" -ne 1 ]; then
echo "expected exactly one source snapshot, found ${#snapshots[@]}"
exit 1
fi
python "$GITHUB_WORKSPACE/scripts/check_supply_chain_policy.py" sbom \
--input package-evidence/obliteratus.cdx.json \
--wheel "${wheels[0]}" \
--artifact "${snapshots[0]}" \
--output "$EVIDENCE/verified-obliteratus.cdx.json"
- name: Collect packaged-dependency license inventory
+11 -11
View File
@@ -70,16 +70,16 @@ profile permits merge integration, not GitHub rebase integration, because rebase
can recreate an audited signed head as an unsigned canonical commit. Verify the
actual `main` commit after every merge; never rewrite `main` to repair history.
Release CI additionally validates wheel and sdist metadata, installs each distribution
in an independent environment outside the checkout, exercises both CLI entry
paths, and retains the distributions plus evidence. Immutable CI action/tool
pins are recorded in [ci/digests.txt](ci/digests.txt).
Release CI creates one deterministic ZIP of the exact tested repository commit.
The ZIP is a source snapshot and release-quality marker, not an installable or
compiled distribution. Immutable CI action/tool pins are recorded in
[ci/digests.txt](ci/digests.txt).
The package job is the sole release-artifact producer. It binds wheel, sdist,
and CycloneDX SBOM digests into `SHA256SUMS`, creates keyless Sigstore-backed
SLSA and SBOM attestations, and retains their bundles. Downstream supply-chain
and publication jobs must verify and reuse those exact bytes rather than
rebuilding equivalent-looking artifacts.
The package job is the sole source-snapshot producer. It binds the ZIP and
CycloneDX source SBOM digests into `SHA256SUMS`, creates keyless
Sigstore-backed SLSA and SBOM attestations, and retains their bundles.
Downstream supply-chain and publication jobs must verify and reuse that exact
ZIP rather than recreating an equivalent-looking archive.
The source-to-test ownership graph is versioned in
[ci/test-risk-map.json](ci/test-risk-map.json). Coverage, JUnit, repeat, and
@@ -91,8 +91,8 @@ flaking twice in 30 days requires an active owner/issue-linked quarantine.
Python CI resolution is locked by `uv.lock`, including the official CPU-only
PyTorch source for Linux and Windows. The required Supply chain job scans all
supported Python versions for known vulnerabilities, scans the checkout for
secrets with fully redacted evidence, enforces the packaged-dependency license
allow list, and binds a CycloneDX SBOM to the built wheel. Exception and update
secrets with fully redacted evidence, enforces the dependency license allow
list, and binds a CycloneDX source SBOM to the repository ZIP. Exception and update
rules are documented in [docs/SUPPLY_CHAIN_POLICY.md](docs/SUPPLY_CHAIN_POLICY.md).
GPU, MPS, MLX, model-download, external-evaluation, network, operator-UI, and
+12 -12
View File
@@ -11,21 +11,21 @@ license-tool transitive dependencies are covered by `uv.lock`.
## Digest-bound release evidence
Release artifacts use a build-once evidence chain. The release package job is
the sole producer of the wheel and source distribution. It also emits a bound
CycloneDX SBOM and a canonical `SHA256SUMS` manifest covering both distributions
and the SBOM.
The release artifact is a deterministic ZIP snapshot of the tested repository
commit. No wheel, source distribution, executable, or other compiled package is
published. The release job creates the snapshot once with `git archive`, then
emits a bound CycloneDX source SBOM and a canonical `SHA256SUMS` manifest.
GitHub's keyless Sigstore-backed attestation service signs two in-toto
statements for that release set:
- SLSA build provenance for every subject named by `SHA256SUMS`;
- an SBOM attestation binding the CycloneDX document to the wheel digest.
- an SBOM attestation binding the CycloneDX document to the source ZIP digest.
The supply-chain job downloads the package job's retained artifact, verifies
`SHA256SUMS`, and revalidates the SBOM binding. It must not rebuild a substitute
wheel. Publication must use these same tested and attested bytes. A commit,
dependency lock, build instruction, artifact, checksum manifest, SBOM, or
`SHA256SUMS`, and revalidates the SBOM binding. It must not create a substitute
archive. Publication must use this same tested and attested ZIP. A commit,
dependency lock, snapshot instruction, artifact, checksum manifest, SBOM, or
release-policy change invalidates the evidence.
Consumers can perform the portable integrity check with:
@@ -34,7 +34,7 @@ Consumers can perform the portable integrity check with:
sha256sum --check SHA256SUMS
```
For authenticity and provenance, verify each distribution against the canonical
For authenticity and provenance, verify the source ZIP against the canonical
repository with GitHub's attestation verifier. A checksum without its signed
provenance proves byte integrity only; it does not prove who built the artifact
or which source and build instructions produced it.
@@ -45,9 +45,9 @@ The Supply chain job retains these artifacts for 14 days:
- one redacted Gitleaks JSON report for the checkout;
- OSV audit JSON and scanner status for Python 3.10, 3.11, and 3.12 on Linux;
- a JSON license inventory for all packaged extras;
- a CycloneDX 1.5 SBOM bound to the built wheel by SHA-256;
- the policy decisions and built wheel used by those checks.
- a JSON license inventory for all supported dependency extras;
- a CycloneDX 1.5 source SBOM bound to the repository ZIP by SHA-256;
- the policy decisions and exact tested source ZIP used by those checks.
Every secret finding and every OSV vulnerability is blocking by default. OSV
does not provide a normalized severity for every advisory, so the policy treats
+7 -7
View File
@@ -291,8 +291,8 @@ def evaluate_secrets(
}
def bind_sbom(sbom: object, wheel: Path) -> dict[str, Any]:
"""Bind a CycloneDX SBOM to the exact built wheel by SHA-256."""
def bind_sbom(sbom: object, artifact: Path) -> dict[str, Any]:
"""Bind a CycloneDX SBOM to the exact promoted source artifact by SHA-256."""
if not isinstance(sbom, dict) or sbom.get("bomFormat") != "CycloneDX":
raise ValueError("SBOM must be a CycloneDX JSON object")
if sbom.get("specVersion") != "1.5":
@@ -303,12 +303,12 @@ def bind_sbom(sbom: object, wheel: Path) -> dict[str, Any]:
component = metadata.get("component")
if not isinstance(component, dict) or component.get("name") != "obliteratus":
raise ValueError("SBOM metadata must describe obliteratus")
digest = hashlib.sha256(wheel.read_bytes()).hexdigest()
digest = hashlib.sha256(artifact.read_bytes()).hexdigest()
component["hashes"] = [{"alg": "SHA-256", "content": digest}]
properties = component.setdefault("properties", [])
if not isinstance(properties, list):
raise ValueError("SBOM component properties must be a list")
properties.append({"name": "obliteratus:distribution-file", "value": wheel.name})
properties.append({"name": "obliteratus:release-artifact", "value": artifact.name})
return sbom
@@ -343,7 +343,7 @@ def _parser() -> argparse.ArgumentParser:
licenses.add_argument("--decision", type=Path, required=True)
sbom = commands.add_parser("sbom")
sbom.add_argument("--input", type=Path, required=True)
sbom.add_argument("--wheel", type=Path, required=True)
sbom.add_argument("--artifact", type=Path, required=True)
sbom.add_argument("--output", type=Path, required=True)
return parser
@@ -354,9 +354,9 @@ def main() -> int:
failures = validate_policy(_read_json(args.policy))
return _write_decision(Path("/dev/null"), {"passed": not failures, "failures": failures})
if args.command == "sbom":
bound = bind_sbom(_read_json(args.input), args.wheel)
bound = bind_sbom(_read_json(args.input), args.artifact)
args.output.write_text(json.dumps(bound, indent=2) + "\n", encoding="utf-8")
print(f"bound SBOM to {args.wheel.name}")
print(f"bound SBOM to {args.artifact.name}")
return 0
policy = _read_json(args.policy)
+2 -1
View File
@@ -139,6 +139,7 @@ def test_release_profile_enforces_cpu_coverage_without_collecting_hardware_gates
"attestationFormat": "in-toto",
"signingMode": "sigstore-keyless",
"sbomFormat": "cyclonedx",
"buildOnce": True,
"artifactType": "source-zip",
"snapshotOnce": True,
"verifyBeforePromotion": True,
}
+9 -1
View File
@@ -41,7 +41,7 @@ def test_every_external_action_is_sha_pinned_and_manifested():
assert entries[("action", name)] == (pin, version)
def test_release_artifacts_are_built_once_attested_and_reused():
def test_tested_source_snapshot_is_created_once_attested_and_reused():
workflow = WORKFLOW.read_text(encoding="utf-8")
package = workflow.split(" package:\n", maxsplit=1)[1].split(
" lint:\n", maxsplit=1,
@@ -53,6 +53,12 @@ def test_release_artifacts_are_built_once_attested_and_reused():
assert "id-token: write" in package
assert "package-evidence/SHA256SUMS" in package
assert "package-evidence/obliteratus.cdx.json" in package
assert 'git archive \\' in package
assert '--format=zip' in package
assert 'subject-path: release/*.zip' in package
assert "python -m build" not in package
assert "*.whl" not in package
assert "*.tar.gz" not in package
assert package.count("uses: actions/attest@") == 2
assert "subject-checksums: package-evidence/SHA256SUMS" in package
assert "sbom-path: package-evidence/obliteratus.cdx.json" in package
@@ -60,6 +66,8 @@ def test_release_artifacts_are_built_once_attested_and_reused():
assert "needs: package" in supply_chain
assert "uses: actions/download-artifact@" in supply_chain
assert "sha256sum -c package-evidence/SHA256SUMS" in supply_chain
assert "tested-source-snapshot-py3.12" in supply_chain
assert "find release -maxdepth 1 -type f -name '*.zip'" in supply_chain
assert "-m build" not in supply_chain
+6 -6
View File
@@ -177,24 +177,24 @@ def test_secret_gate_requires_redaction_even_for_suppressed_finding():
assert any("not fully redacted" in failure for failure in decision["failures"])
def test_bind_sbom_adds_exact_wheel_hash(tmp_path):
wheel = tmp_path / "obliteratus-0.1.2-py3-none-any.whl"
wheel.write_bytes(b"wheel")
def test_bind_sbom_adds_exact_release_artifact_hash(tmp_path):
artifact = tmp_path / "OBLITERATUS-deadbeef.zip"
artifact.write_bytes(b"source snapshot")
sbom = {
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"metadata": {"component": {"name": "obliteratus"}},
}
bound = MODULE.bind_sbom(sbom, wheel)
bound = MODULE.bind_sbom(sbom, artifact)
component = bound["metadata"]["component"]
assert component["hashes"] == [
{
"alg": "SHA-256",
"content": "ba59926159d2aa256eb8739b8da7e2b574b960e1202c6d624cbe981cef996c91",
"content": "41637c7875947b50775c0ab2bb30a28b292be748376a5713eb8f527d9953433c",
},
]
assert component["properties"] == [
{"name": "obliteratus:distribution-file", "value": wheel.name},
{"name": "obliteratus:release-artifact", "value": artifact.name},
]