Expose SBOM controlled source evidence
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Container Image / build-and-push (push) Successful in 21s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a028de-e2c8-7732-8521-46a7fc5db82f
This commit is contained in:
tegwick 2026-08-23 01:39:38 +02:00
parent c1d5e4a9dd
commit 34867d784f
3 changed files with 61 additions and 10 deletions

View file

@ -762,6 +762,22 @@ def _sbom_catchup_report(instr: Any, catchup: dict) -> InstructionResult:
updated = [r for r in catchup.get("updated", []) if isinstance(r, dict)] updated = [r for r in catchup.get("updated", []) if isinstance(r, dict)]
skipped = [r for r in catchup.get("skipped", []) if isinstance(r, dict)] skipped = [r for r in catchup.get("skipped", []) if isinstance(r, dict)]
limit = catchup.get("limit") limit = catchup.get("limit")
selected_repos = []
controlled_source_count = 0
for repo in selected:
item = {
"repo_slug": repo.get("repo_slug"),
"sbom_age_days": repo.get("sbom_age_days"),
"has_sbom": repo.get("has_sbom"),
"last_sbom_at": repo.get("last_sbom_at"),
"checkout_available": repo.get("checkout_available"),
}
source_ref = _safe_sbom_source_ref(repo.get("source_ref"))
if source_ref is not None:
item["source_ref"] = source_ref
controlled_source_count += 1
selected_repos.append(item)
report: dict[str, Any] = { report: dict[str, Any] = {
"summary": ( "summary": (
f"SBOM catch-up: {len(selected)} selected (limit {limit}), " f"SBOM catch-up: {len(selected)} selected (limit {limit}), "
@ -773,19 +789,11 @@ def _sbom_catchup_report(instr: Any, catchup: dict) -> InstructionResult:
"deterministic": True, "deterministic": True,
"limit": limit, "limit": limit,
"selected_count": len(selected), "selected_count": len(selected),
"controlled_source_count": controlled_source_count,
"stale_count": catchup.get("stale_count"), "stale_count": catchup.get("stale_count"),
"never_count": catchup.get("never_count"), "never_count": catchup.get("never_count"),
"total_count": catchup.get("total_count"), "total_count": catchup.get("total_count"),
"selected_repos": [ "selected_repos": selected_repos,
{
"repo_slug": r.get("repo_slug"),
"sbom_age_days": r.get("sbom_age_days"),
"has_sbom": r.get("has_sbom"),
"last_sbom_at": r.get("last_sbom_at"),
"checkout_available": r.get("checkout_available"),
}
for r in selected
],
"updated_repos": [r.get("repo_slug") for r in updated], "updated_repos": [r.get("repo_slug") for r in updated],
"skipped_repos": [ "skipped_repos": [
{"repo_slug": r.get("repo_slug"), "reason": r.get("reason")} {"repo_slug": r.get("repo_slug"), "reason": r.get("reason")}
@ -803,6 +811,23 @@ def _sbom_catchup_report(instr: Any, catchup: dict) -> InstructionResult:
) )
def _safe_sbom_source_ref(raw: Any) -> dict[str, str] | None:
"""Keep only the reviewable controlled-source identity in progress evidence."""
if not isinstance(raw, dict) or raw.get("kind") != "forgejo-archive-v1":
return None
repository = raw.get("repository")
revision = raw.get("revision")
if not isinstance(repository, str) or not repository.strip():
return None
if not isinstance(revision, str) or re.fullmatch(r"[0-9a-f]{40}", revision) is None:
return None
return {
"kind": "forgejo-archive-v1",
"repository": repository,
"revision": revision,
}
def _validate_output( def _validate_output(
raw_output: Any, raw_output: Any,
instr: Any, instr: Any,

View file

@ -640,6 +640,12 @@ def test_deterministic_report_names_selected_repos() -> None:
"sbom_age_days": 9999, "sbom_age_days": 9999,
"has_sbom": False, "has_sbom": False,
"checkout_available": True, "checkout_available": True,
"source_ref": {
"kind": "forgejo-archive-v1",
"repository": "coulomb/never-scanned",
"revision": "a" * 40,
"archive_url": "must-not-enter-progress-evidence",
},
}, },
{ {
"repo_slug": "no-checkout", "repo_slug": "no-checkout",
@ -647,6 +653,11 @@ def test_deterministic_report_names_selected_repos() -> None:
"sbom_age_days": 112, "sbom_age_days": 112,
"has_sbom": True, "has_sbom": True,
"checkout_available": False, "checkout_available": False,
"source_ref": {
"kind": "forgejo-archive-v1",
"repository": "coulomb/no-checkout",
"revision": "not-a-full-sha",
},
}, },
], ],
"selected_count": 2, "selected_count": 2,
@ -663,6 +674,7 @@ def test_deterministic_report_names_selected_repos() -> None:
report = result.report report = result.report
assert report["deterministic"] is True assert report["deterministic"] is True
assert report["never_count"] == 93 assert report["never_count"] == 93
assert report["controlled_source_count"] == 1
assert [r["repo_slug"] for r in report["selected_repos"]] == [ assert [r["repo_slug"] for r in report["selected_repos"]] == [
"never-scanned", "never-scanned",
"no-checkout", "no-checkout",
@ -670,6 +682,12 @@ def test_deterministic_report_names_selected_repos() -> None:
assert "93 never scanned of 111 repos" in report["summary"] assert "93 never scanned of 111 repos" in report["summary"]
assert report["updated_repos"] == [] assert report["updated_repos"] == []
assert report["skipped_repos"] == [] assert report["skipped_repos"] == []
assert report["selected_repos"][0]["source_ref"] == {
"kind": "forgejo-archive-v1",
"repository": "coulomb/never-scanned",
"revision": "a" * 40,
}
assert "source_ref" not in report["selected_repos"][1]
def test_deterministic_report_carries_t02_ingest_outcomes() -> None: def test_deterministic_report_carries_t02_ingest_outcomes() -> None:

View file

@ -61,3 +61,11 @@ processed exactly that frozen set, spawned zero tasks, and produced three
provenance-bearing `no-manifest` snapshots. `never_count` advanced 94 to 91. provenance-bearing `no-manifest` snapshots. `never_count` advanced 94 to 91.
Keep open for the first unassisted weekday fire and sustainable projection of Keep open for the first unassisted weekday fire and sustainable projection of
each newly exposed oldest-N batch. each newly exposed oldest-N batch.
Preflight 2026-08-23: after the controlled batch advanced, the newly exposed
oldest three (`citation-work`, `clay-borg`, `config-atlas`) each carried a
matching `forgejo-archive-v1` reference and a 40-character revision, confirming
projection is not limited to the attended batch. The deterministic catch-up
report now retains only the allowlisted kind/repository/revision fields and a
`controlled_source_count`, so the first unassisted fire can prove provenance
directly in State Hub without copying Nexus payloads or archive URLs.