consolidation of some of the modules, localization fixes, product advisories work, qa work

This commit is contained in:
master
2026-03-05 03:54:22 +02:00
parent 7bafcc3eef
commit 8e1cb9448d
3878 changed files with 72600 additions and 46861 deletions

View File

@@ -0,0 +1,183 @@
#!/usr/bin/env python3
"""Deterministic JCS-style property checks for SBOM/attestation fixtures."""
from __future__ import annotations
import argparse
import difflib
import json
import pathlib
import random
import sys
import time
from typing import Any
TOOLS_DIR = pathlib.Path(__file__).resolve().parents[1] / "tools"
sys.path.insert(0, str(TOOLS_DIR))
from canonicalize_json import DuplicateKeyError, canonicalize_text, canonicalize_value, parse_json_strict # noqa: E402
from emit_artifacts import TestCaseResult, record_failure, write_junit # noqa: E402
def _shuffle_value(value: Any, rng: random.Random) -> Any:
if isinstance(value, dict):
items = list(value.items())
rng.shuffle(items)
return {k: _shuffle_value(v, rng) for k, v in items}
if isinstance(value, list):
return [_shuffle_value(item, rng) for item in value]
return value
def _load_fixture_texts(corpus_root: pathlib.Path) -> list[tuple[str, str]]:
fixture_paths = sorted(corpus_root.rglob("*.json"))
fixtures: list[tuple[str, str]] = []
for path in fixture_paths:
fixtures.append((path.name, path.read_text(encoding="utf-8")))
return fixtures
def _run(seed: int, output: pathlib.Path) -> int:
start = time.perf_counter()
rng = random.Random(seed)
corpus_root = pathlib.Path(__file__).resolve().parents[1] / "05-corpus" / "fixtures" / "sboms"
fixtures = _load_fixture_texts(corpus_root)
cases: list[TestCaseResult] = []
failures = 0
for index, (fixture_name, fixture_text) in enumerate(fixtures):
case_id = f"{fixture_name}-idempotence"
case_start = time.perf_counter()
try:
parsed = parse_json_strict(fixture_text)
canonical_1 = canonicalize_text(fixture_text)
canonical_2 = canonicalize_text(canonical_1)
if canonical_1 != canonical_2:
diff = "\n".join(
difflib.unified_diff(
canonical_1.splitlines(),
canonical_2.splitlines(),
fromfile="canonical_1",
tofile="canonical_2",
lineterm="",
)
)
raise AssertionError("Idempotence mismatch", diff)
shuffled = _shuffle_value(parsed, random.Random(rng.randint(0, 2**31 - 1) + index))
canonical_3 = canonicalize_value(shuffled)
if canonical_1 != canonical_3:
diff = "\n".join(
difflib.unified_diff(
canonical_1.splitlines(),
canonical_3.splitlines(),
fromfile="canonical_1",
tofile="canonical_shuffled",
lineterm="",
)
)
raise AssertionError("Permutation equality mismatch", diff)
cases.append(
TestCaseResult(
suite="01-jcs-property",
name=case_id,
passed=True,
duration_seconds=time.perf_counter() - case_start,
)
)
except Exception as exc: # noqa: BLE001
failures += 1
patch = None
message = str(exc)
if isinstance(exc, AssertionError) and len(exc.args) > 1:
patch = str(exc.args[1])
message = str(exc.args[0])
record_failure(
lane_output_dir=output,
case_id=case_id,
seed=seed,
payload_text=fixture_text,
error_class="canonicalization_invariant_failed",
message=message,
details={"fixture": fixture_name},
canonical_diff_patch=patch,
)
cases.append(
TestCaseResult(
suite="01-jcs-property",
name=case_id,
passed=False,
duration_seconds=time.perf_counter() - case_start,
failure_message=message,
)
)
duplicate_case = '{"a":1,"a":2}'
duplicate_case_id = "duplicate-key-rejection"
duplicate_start = time.perf_counter()
try:
parse_json_strict(duplicate_case)
raise AssertionError("Duplicate key payload was not rejected")
except DuplicateKeyError:
cases.append(
TestCaseResult(
suite="01-jcs-property",
name=duplicate_case_id,
passed=True,
duration_seconds=time.perf_counter() - duplicate_start,
)
)
except Exception as exc: # noqa: BLE001
failures += 1
message = str(exc)
record_failure(
lane_output_dir=output,
case_id=duplicate_case_id,
seed=seed,
payload_text=duplicate_case,
error_class="duplicate_key_rejection_failed",
message=message,
details={},
canonical_diff_patch=None,
)
cases.append(
TestCaseResult(
suite="01-jcs-property",
name=duplicate_case_id,
passed=False,
duration_seconds=time.perf_counter() - duplicate_start,
failure_message=message,
)
)
summary = {
"seed": seed,
"fixtureCount": len(fixtures),
"testCases": len(cases),
"failures": failures,
"durationSeconds": round(time.perf_counter() - start, 4),
}
output.mkdir(parents=True, exist_ok=True)
(output / "summary.json").write_text(json.dumps(summary, sort_keys=True, indent=2) + "\n", encoding="utf-8")
write_junit(output / "junit.xml", cases)
return 0 if failures == 0 else 1
def main() -> int:
parser = argparse.ArgumentParser(description="Run deterministic JCS property checks.")
parser.add_argument("--seed", type=int, default=20260226, help="Deterministic seed.")
parser.add_argument(
"--output",
type=pathlib.Path,
default=pathlib.Path("out/supply-chain/01-jcs-property"),
help="Output directory for junit and artifacts.",
)
args = parser.parse_args()
return _run(args.seed, args.output.resolve())
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,208 @@
#!/usr/bin/env python3
"""Schema-aware deterministic mutation lane for supply-chain fixtures."""
from __future__ import annotations
import argparse
import json
import pathlib
import random
import sys
import time
import unicodedata
from typing import Callable
TOOLS_DIR = pathlib.Path(__file__).resolve().parents[1] / "tools"
sys.path.insert(0, str(TOOLS_DIR))
from canonicalize_json import DuplicateKeyError, canonicalize_text, parse_json_strict # noqa: E402
from emit_artifacts import TestCaseResult, record_failure, write_junit # noqa: E402
MutationFn = Callable[[str], str]
def _truncate_payload(text: str) -> str:
return text[:-1] if text else text
def _append_garbage(text: str) -> str:
return text + " !!!"
def _inject_duplicate_key(text: str) -> str:
if text.lstrip().startswith("{"):
return text.replace("{", '{"bomFormat":"CycloneDX","bomFormat":"CycloneDX",', 1)
return text
def _unicode_normalization_toggle(text: str) -> str:
return unicodedata.normalize("NFD", text)
def _reorder_known_keys(text: str) -> str:
parsed = parse_json_strict(text)
if isinstance(parsed, dict):
reordered = {k: parsed[k] for k in sorted(parsed.keys(), reverse=True)}
return json.dumps(reordered, ensure_ascii=False, indent=2)
return text
MUTATORS: list[tuple[str, MutationFn]] = [
("truncate", _truncate_payload),
("append_garbage", _append_garbage),
("duplicate_key", _inject_duplicate_key),
("unicode_nfd", _unicode_normalization_toggle),
("reorder_keys", _reorder_known_keys),
]
def _load_inputs() -> list[tuple[str, str]]:
root = pathlib.Path(__file__).resolve().parents[1] / "05-corpus" / "fixtures"
files = sorted(path for path in root.rglob("*.json") if "malformed" not in path.parts)
return [(path.name, path.read_text(encoding="utf-8")) for path in files]
def main() -> int:
parser = argparse.ArgumentParser(description="Run deterministic schema mutation lane.")
parser.add_argument("--seed", type=int, default=20260226)
parser.add_argument("--limit", type=int, default=1000)
parser.add_argument("--time-seconds", type=int, default=60)
parser.add_argument(
"--output",
type=pathlib.Path,
default=pathlib.Path("out/supply-chain/02-schema-fuzz"),
)
args = parser.parse_args()
rng = random.Random(args.seed)
output = args.output.resolve()
output.mkdir(parents=True, exist_ok=True)
mutated_dir = output / "corpus" / "mutated"
mutated_dir.mkdir(parents=True, exist_ok=True)
fixture_inputs = _load_inputs()
if not fixture_inputs:
raise SystemExit("No mutation inputs found")
start = time.perf_counter()
counts = {
"accepted": 0,
"rejected_invalid_json": 0,
"rejected_duplicate_keys": 0,
"crash": 0,
}
lane_case_results: list[TestCaseResult] = []
mutation_records: list[dict[str, str | int]] = []
executed = 0
while executed < args.limit and (time.perf_counter() - start) < args.time_seconds:
fixture_name, payload = fixture_inputs[executed % len(fixture_inputs)]
mutator_name, mutator = MUTATORS[rng.randrange(0, len(MUTATORS))]
case_id = f"{executed:05d}-{fixture_name}-{mutator_name}"
case_start = time.perf_counter()
try:
mutated = mutator(payload)
canonicalize_text(mutated)
counts["accepted"] += 1
lane_case_results.append(
TestCaseResult(
suite="02-schema-fuzz",
name=case_id,
passed=True,
duration_seconds=time.perf_counter() - case_start,
)
)
except DuplicateKeyError as exc:
mutated = mutator(payload)
counts["rejected_duplicate_keys"] += 1
lane_case_results.append(
TestCaseResult(
suite="02-schema-fuzz",
name=case_id,
passed=True,
duration_seconds=time.perf_counter() - case_start,
)
)
mutation_records.append(
{"caseId": case_id, "result": "rejected_duplicate_keys", "reason": str(exc)}
)
except json.JSONDecodeError as exc:
mutated = mutator(payload)
counts["rejected_invalid_json"] += 1
lane_case_results.append(
TestCaseResult(
suite="02-schema-fuzz",
name=case_id,
passed=True,
duration_seconds=time.perf_counter() - case_start,
)
)
mutation_records.append(
{"caseId": case_id, "result": "rejected_invalid_json", "reason": str(exc)}
)
except Exception as exc: # noqa: BLE001
mutated = mutator(payload)
counts["crash"] += 1
record_failure(
lane_output_dir=output,
case_id=case_id,
seed=args.seed,
payload_text=mutated,
error_class="mutation_lane_crash",
message=str(exc),
details={
"fixture": fixture_name,
"mutator": mutator_name,
},
canonical_diff_patch=None,
)
lane_case_results.append(
TestCaseResult(
suite="02-schema-fuzz",
name=case_id,
passed=False,
duration_seconds=time.perf_counter() - case_start,
failure_message=str(exc),
)
)
if executed < 50:
mutated_path = mutated_dir / f"{executed:05d}-{mutator_name}.json"
mutated_path.write_text(mutated, encoding="utf-8", newline="\n")
executed += 1
report = {
"seed": args.seed,
"executed": executed,
"limit": args.limit,
"timeBudgetSeconds": args.time_seconds,
"durationSeconds": round(time.perf_counter() - start, 4),
"counts": counts,
"machineReadableErrorClasses": sorted(
{
"invalid_json",
"duplicate_key",
"mutation_lane_crash",
}
),
"records": mutation_records,
}
(output / "report.json").write_text(json.dumps(report, sort_keys=True, indent=2) + "\n", encoding="utf-8")
write_junit(output / "junit.xml", lane_case_results)
repro = (
"# Repro Playbook\n\n"
f"- Seed: `{args.seed}`\n"
f"- Executed mutations: `{executed}`\n"
"- Replay command:\n"
f" - `python tests/supply-chain/02-schema-fuzz/run_mutations.py --seed {args.seed} --limit {executed} --time-seconds {args.time_seconds}`\n"
)
(output / "repro_playbook.md").write_text(repro, encoding="utf-8", newline="\n")
return 0 if counts["crash"] == 0 else 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,69 @@
#!/usr/bin/env python3
"""Deterministic Rekor error-mode shim used by negative path test lane."""
from __future__ import annotations
from dataclasses import dataclass
@dataclass(frozen=True)
class RekorCase:
case_id: str
status_code: int
entry_type: str
expected_code: str
message: str
response_body: dict[str, object]
def default_cases() -> list[RekorCase]:
return [
RekorCase(
case_id="oversized-payload-413",
status_code=413,
entry_type="intoto",
expected_code="payload_too_large",
message="payload size exceeds configured limit",
response_body={"error": "payload too large", "maxBytes": 10_000_000},
),
RekorCase(
case_id="unsupported-entry-type-400",
status_code=400,
entry_type="unknown",
expected_code="unsupported_entry_type",
message="unsupported entry type",
response_body={"error": "unsupported entry type", "entryType": "unknown"},
),
RekorCase(
case_id="failed-dependency-424",
status_code=424,
entry_type="intoto",
expected_code="failed_dependency",
message="rekor backend dependency failure",
response_body={"error": "ledger gap", "reprocessToken": "rekor-gap-001"},
),
RekorCase(
case_id="gateway-timeout-504",
status_code=504,
entry_type="intoto",
expected_code="upstream_timeout",
message="rekor upstream timeout",
response_body={"error": "timeout", "retryAfterSeconds": 30},
),
RekorCase(
case_id="accepted-for-reprocess-202",
status_code=202,
entry_type="intoto",
expected_code="reprocess_pending",
message="accepted for asynchronous replay",
response_body={"status": "accepted", "reprocessToken": "rekor-async-001"},
),
]
def simulate_submit(case: RekorCase) -> tuple[int, dict[str, object], dict[str, str]]:
headers = {
"x-correlation-id": f"corr-{case.case_id}",
"content-type": "application/json",
}
return case.status_code, dict(case.response_body), headers

View File

@@ -0,0 +1,140 @@
#!/usr/bin/env python3
"""Run deterministic Rekor/DSSE negative-path verification suite."""
from __future__ import annotations
import argparse
import hashlib
import json
import pathlib
import tarfile
import time
from rekor_shim import RekorCase, default_cases, simulate_submit
import sys
TOOLS_DIR = pathlib.Path(__file__).resolve().parents[1] / "tools"
sys.path.insert(0, str(TOOLS_DIR))
from emit_artifacts import TestCaseResult, write_junit # noqa: E402
def _classify(case: RekorCase, status: int, body: dict[str, object]) -> tuple[str, str | None]:
reprocess_token = str(body.get("reprocessToken")) if body.get("reprocessToken") else None
if status == 413:
return "payload_too_large", None
if status == 424:
return "failed_dependency", reprocess_token or f"retry-{case.case_id}"
if status == 504:
return "upstream_timeout", f"timeout-{case.case_id}"
if status == 202:
return "reprocess_pending", reprocess_token or f"pending-{case.case_id}"
if status == 400 and case.entry_type == "unknown":
return "unsupported_entry_type", None
return "unexpected_rekor_status", None
def _token(case_id: str) -> str:
return hashlib.sha256(case_id.encode("utf-8")).hexdigest()[:16]
def _write_tar(source_dir: pathlib.Path, tar_path: pathlib.Path) -> None:
tar_path.parent.mkdir(parents=True, exist_ok=True)
with tarfile.open(tar_path, "w:gz") as archive:
for file in sorted(path for path in source_dir.rglob("*") if path.is_file()):
archive.add(file, arcname=file.relative_to(source_dir).as_posix())
def main() -> int:
parser = argparse.ArgumentParser(description="Run Rekor negative path suite.")
parser.add_argument(
"--output",
type=pathlib.Path,
default=pathlib.Path("out/supply-chain/03-rekor-neg"),
)
args = parser.parse_args()
start = time.perf_counter()
output = args.output.resolve()
output.mkdir(parents=True, exist_ok=True)
diagnostics_root = output / "diagnostics"
diagnostics_root.mkdir(parents=True, exist_ok=True)
cases = default_cases()
junit_cases: list[TestCaseResult] = []
report_cases: list[dict[str, object]] = []
failures = 0
for case in cases:
case_start = time.perf_counter()
status, body, headers = simulate_submit(case)
code, reprocess = _classify(case, status, body)
expected = case.expected_code
passed = code == expected
if not passed:
failures += 1
case_token = reprocess or _token(case.case_id)
diagnostic = {
"caseId": case.case_id,
"upstream": {
"statusCode": status,
"body": body,
"headers": headers,
},
"machineReadableErrorClass": code,
"expectedErrorClass": expected,
"reprocessToken": case_token,
}
case_dir = diagnostics_root / case.case_id
case_dir.mkdir(parents=True, exist_ok=True)
(case_dir / "diagnostic_blob.json").write_text(
json.dumps(diagnostic, sort_keys=True, indent=2) + "\n",
encoding="utf-8",
)
report_cases.append(
{
"caseId": case.case_id,
"statusCode": status,
"entryType": case.entry_type,
"machineReadableErrorClass": code,
"expectedErrorClass": expected,
"reprocessToken": case_token,
"passed": passed,
}
)
junit_cases.append(
TestCaseResult(
suite="03-rekor-neg",
name=case.case_id,
passed=passed,
duration_seconds=time.perf_counter() - case_start,
failure_message=None if passed else f"expected={expected} actual={code}",
)
)
_write_tar(diagnostics_root, output / "rekor_negative_cases.tar.gz")
report = {
"durationSeconds": round(time.perf_counter() - start, 4),
"failures": failures,
"cases": report_cases,
"machineReadableErrorClasses": sorted(
{
"payload_too_large",
"unsupported_entry_type",
"failed_dependency",
"upstream_timeout",
"reprocess_pending",
}
),
}
(output / "report.json").write_text(json.dumps(report, sort_keys=True, indent=2) + "\n", encoding="utf-8")
write_junit(output / "junit.xml", junit_cases)
return 0 if failures == 0 else 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,164 @@
#!/usr/bin/env python3
"""Large DSSE payload and OCI referrer edge-case deterministic suite."""
from __future__ import annotations
import argparse
import hashlib
import json
import pathlib
import tarfile
import time
import sys
TOOLS_DIR = pathlib.Path(__file__).resolve().parents[1] / "tools"
sys.path.insert(0, str(TOOLS_DIR))
from emit_artifacts import TestCaseResult, write_junit # noqa: E402
MAX_ACCEPTED_BYTES = 50 * 1024 * 1024
def _reprocess_token(case_id: str) -> str:
return hashlib.sha256(case_id.encode("utf-8")).hexdigest()[:20]
def _evaluate_big_payload(case_id: str, payload_size_bytes: int) -> dict[str, object]:
if payload_size_bytes > MAX_ACCEPTED_BYTES:
return {
"caseId": case_id,
"result": "rejected",
"machineReadableErrorClass": "payload_too_large",
"state": "unknown_state",
"reprocessToken": _reprocess_token(case_id),
}
return {
"caseId": case_id,
"result": "accepted",
"machineReadableErrorClass": "none",
"state": "verified",
"reprocessToken": None,
}
def _evaluate_referrer_case(case_id: str, issue: str) -> dict[str, object]:
mapping = {
"dangling": "missing_subject",
"invalid_media_type": "invalid_media_type",
"cycle": "referrer_cycle_detected",
"missing_symbol_bundle": "missing_symbol_bundle",
}
error_class = mapping[issue]
return {
"caseId": case_id,
"result": "rejected",
"machineReadableErrorClass": error_class,
"state": "unknown_state",
"reprocessToken": _reprocess_token(case_id),
}
def _write_tar(source_dir: pathlib.Path, tar_path: pathlib.Path) -> None:
tar_path.parent.mkdir(parents=True, exist_ok=True)
with tarfile.open(tar_path, "w:gz") as archive:
for file in sorted(path for path in source_dir.rglob("*") if path.is_file()):
archive.add(file, arcname=file.relative_to(source_dir).as_posix())
def main() -> int:
parser = argparse.ArgumentParser(description="Run deterministic large DSSE/referrer suite.")
parser.add_argument(
"--output",
type=pathlib.Path,
default=pathlib.Path("out/supply-chain/04-big-dsse-referrers"),
)
args = parser.parse_args()
output = args.output.resolve()
output.mkdir(parents=True, exist_ok=True)
case_root = output / "cases"
case_root.mkdir(parents=True, exist_ok=True)
start = time.perf_counter()
big_payload_cases = [
("dsse-100mb", 100 * 1024 * 1024),
("dsse-250mb", 250 * 1024 * 1024),
("dsse-1gb", 1024 * 1024 * 1024),
]
referrer_cases = [
("referrer-dangling", "dangling"),
("referrer-invalid-media-type", "invalid_media_type"),
("referrer-cycle", "cycle"),
("referrer-missing-symbol-bundle", "missing_symbol_bundle"),
]
results: list[dict[str, object]] = []
junit_cases: list[TestCaseResult] = []
failures = 0
for case_id, size_bytes in big_payload_cases:
case_start = time.perf_counter()
result = _evaluate_big_payload(case_id, size_bytes)
passed = result["result"] == "rejected" and result["state"] == "unknown_state"
if not passed:
failures += 1
(case_root / f"{case_id}.json").write_text(
json.dumps(result, sort_keys=True, indent=2) + "\n",
encoding="utf-8",
)
results.append(result)
junit_cases.append(
TestCaseResult(
suite="04-big-dsse-referrers",
name=case_id,
passed=passed,
duration_seconds=time.perf_counter() - case_start,
failure_message=None if passed else "payload case was not gracefully rejected",
)
)
for case_id, issue in referrer_cases:
case_start = time.perf_counter()
result = _evaluate_referrer_case(case_id, issue)
passed = result["result"] == "rejected" and result["state"] == "unknown_state"
if not passed:
failures += 1
(case_root / f"{case_id}.json").write_text(
json.dumps(result, sort_keys=True, indent=2) + "\n",
encoding="utf-8",
)
results.append(result)
junit_cases.append(
TestCaseResult(
suite="04-big-dsse-referrers",
name=case_id,
passed=passed,
duration_seconds=time.perf_counter() - case_start,
failure_message=None if passed else "referrer case was not gracefully rejected",
)
)
_write_tar(case_root, output / "big_dsse_payloads.tar.gz")
report = {
"durationSeconds": round(time.perf_counter() - start, 4),
"failures": failures,
"results": results,
"machineReadableErrorClasses": sorted(
{
"payload_too_large",
"missing_subject",
"invalid_media_type",
"referrer_cycle_detected",
"missing_symbol_bundle",
}
),
}
(output / "report.json").write_text(json.dumps(report, sort_keys=True, indent=2) + "\n", encoding="utf-8")
write_junit(output / "junit.xml", junit_cases)
return 0 if failures == 0 else 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,25 @@
# Supply-Chain Fuzz Corpus
This corpus is the deterministic seed set for `tests/supply-chain`.
## Layout
- `fixtures/sboms/`: CycloneDX-like SBOM samples used for JCS and mutation lanes.
- `fixtures/attestations/`: DSSE envelope examples.
- `fixtures/vex/`: OpenVEX-like samples.
- `fixtures/malformed/`: intentionally malformed JSON payloads.
## Update Procedure (Deterministic)
1. Add new fixture files under the correct `fixtures/*` directory.
2. Keep file names stable and monotonic (`*-001`, `*-002`, ...).
3. Regenerate archive manifest with:
- `python tests/supply-chain/05-corpus/build_corpus_archive.py --output out/supply-chain/05-corpus`
4. Run suite smoke profile:
- `python tests/supply-chain/run_suite.py --profile smoke --seed 20260226`
5. If a crash is fixed, add the minimized repro fixture before merge.
## Notes
- No network I/O is required to consume this corpus.
- All lane scripts use fixed seed defaults to keep replay deterministic.

View File

@@ -0,0 +1,86 @@
#!/usr/bin/env python3
"""Create deterministic archive metadata for supply-chain fuzz corpus."""
from __future__ import annotations
import argparse
import hashlib
import json
import pathlib
import tarfile
EPOCH = 1_706_300_800 # 2024-01-01T00:00:00Z
def _deterministic_tarinfo(name: str, data: bytes) -> tarfile.TarInfo:
info = tarfile.TarInfo(name=name)
info.size = len(data)
info.mtime = EPOCH
info.mode = 0o644
info.uid = 0
info.gid = 0
info.uname = "root"
info.gname = "root"
return info
def _sha256(path: pathlib.Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(64 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def main() -> int:
parser = argparse.ArgumentParser(description="Build deterministic fuzz corpus archive.")
parser.add_argument(
"--output",
type=pathlib.Path,
default=pathlib.Path("out/supply-chain/05-corpus"),
help="Output directory for archive and manifest.",
)
args = parser.parse_args()
root = pathlib.Path(__file__).resolve().parent
fixtures_root = root / "fixtures"
output = args.output.resolve()
output.mkdir(parents=True, exist_ok=True)
archive_path = output / "fuzz-corpus-v1.tar.gz"
manifest_path = output / "fuzz-corpus-v1.manifest.json"
files = sorted(path for path in fixtures_root.rglob("*") if path.is_file())
with tarfile.open(archive_path, "w:gz") as archive:
for path in files:
rel = path.relative_to(root).as_posix()
data = path.read_bytes()
info = _deterministic_tarinfo(rel, data)
with path.open("rb") as handle:
archive.addfile(info, fileobj=handle)
manifest = {
"archive": archive_path.name,
"fileCount": len(files),
"files": [
{
"path": path.relative_to(root).as_posix(),
"sha256": _sha256(path),
"sizeBytes": path.stat().st_size,
}
for path in files
],
}
with manifest_path.open("w", encoding="utf-8", newline="\n") as handle:
json.dump(manifest, handle, sort_keys=True, indent=2)
handle.write("\n")
print(f"archive={archive_path}")
print(f"manifest={manifest_path}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,10 @@
{
"payloadType": "application/vnd.in-toto+json",
"payload": "eyJzdGF0ZW1lbnRUeXBlIjoiaW4tdG90byIsInN1YmplY3QiOlt7Im5hbWUiOiJzZXJ2aWNlLWEiLCJkaWdlc3QiOnsic2hhMjU2IjoiYWFhYSJ9fV19",
"signatures": [
{
"keyid": "test-ed25519",
"sig": "MEUCIQDc3Hg4uY7jL5IyGmW1P2JX1i2fQf3m9QhXw5q5Xg9fSgIgY6c1v3Shy8qv8p9w2u5iX9p8VQb4Hf0yJ4j9QhN4Vtw="
}
]
}

View File

@@ -0,0 +1,5 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"specVersion": "1.4"
}

View File

@@ -0,0 +1,27 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"serialNumber": "urn:uuid:00000000-0000-0000-0000-000000000003",
"metadata": {
"timestamp": "2026-02-22T00:00:00Z",
"component": {
"type": "application",
"name": "service-c",
"version": "5.0.0"
}
},
"components": [
{ "type": "library", "name": "a", "version": "1.0.0", "purl": "pkg:generic/a@1.0.0" },
{ "type": "library", "name": "b", "version": "1.0.0", "purl": "pkg:generic/b@1.0.0" },
{ "type": "library", "name": "c", "version": "1.0.0", "purl": "pkg:generic/c@1.0.0" },
{ "type": "library", "name": "d", "version": "1.0.0", "purl": "pkg:generic/d@1.0.0" },
{ "type": "library", "name": "e", "version": "1.0.0", "purl": "pkg:generic/e@1.0.0" }
],
"dependencies": [
{ "ref": "pkg:generic/a@1.0.0", "dependsOn": [ "pkg:generic/b@1.0.0", "pkg:generic/c@1.0.0" ] },
{ "ref": "pkg:generic/b@1.0.0", "dependsOn": [ "pkg:generic/d@1.0.0" ] },
{ "ref": "pkg:generic/c@1.0.0", "dependsOn": [ "pkg:generic/d@1.0.0", "pkg:generic/e@1.0.0" ] },
{ "ref": "pkg:generic/d@1.0.0", "dependsOn": [] },
{ "ref": "pkg:generic/e@1.0.0", "dependsOn": [] }
]
}

View File

@@ -0,0 +1,50 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"serialNumber": "urn:uuid:00000000-0000-0000-0000-000000000002",
"metadata": {
"timestamp": "2026-02-22T00:00:00Z",
"component": {
"type": "application",
"name": "service-b",
"version": "2.4.1"
}
},
"components": [
{
"type": "library",
"name": "nginx",
"version": "1.25.4",
"purl": "pkg:generic/nginx@1.25.4"
},
{
"type": "library",
"name": "zlib",
"version": "1.3.1",
"purl": "pkg:generic/zlib@1.3.1"
},
{
"type": "library",
"name": "libxml2",
"version": "2.12.7",
"purl": "pkg:generic/libxml2@2.12.7"
}
],
"dependencies": [
{
"ref": "pkg:generic/nginx@1.25.4",
"dependsOn": [
"pkg:generic/zlib@1.3.1",
"pkg:generic/libxml2@2.12.7"
]
},
{
"ref": "pkg:generic/zlib@1.3.1",
"dependsOn": []
},
{
"ref": "pkg:generic/libxml2@2.12.7",
"dependsOn": []
}
]
}

View File

@@ -0,0 +1,27 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"serialNumber": "urn:uuid:00000000-0000-0000-0000-000000000001",
"metadata": {
"timestamp": "2026-02-22T00:00:00Z",
"component": {
"type": "application",
"name": "service-a",
"version": "1.0.0"
}
},
"components": [
{
"type": "library",
"name": "openssl",
"version": "3.0.2",
"purl": "pkg:generic/openssl@3.0.2"
}
],
"dependencies": [
{
"ref": "pkg:generic/openssl@3.0.2",
"dependsOn": []
}
]
}

View File

@@ -0,0 +1,16 @@
{
"@context": "https://openvex.dev/ns/v0.2.0",
"author": "stella-ops",
"timestamp": "2026-02-22T00:00:00Z",
"version": 1,
"statements": [
{
"vulnerability": "CVE-2026-1111",
"products": [
"pkg:oci/service-a@1.0.0"
],
"status": "under_investigation",
"justification": "component_not_present"
}
]
}

View File

@@ -0,0 +1,12 @@
.PHONY: test smoke nightly corpus
test: smoke
smoke:
python tests/supply-chain/run_suite.py --profile smoke --seed 20260226
nightly:
python tests/supply-chain/run_suite.py --profile nightly --seed 20260226
corpus:
python tests/supply-chain/05-corpus/build_corpus_archive.py --output out/supply-chain/05-corpus

View File

@@ -0,0 +1,47 @@
# Supply-Chain Hardening Suite
Deterministic, offline-safe hardening lanes for canonicalization, mutation fuzzing, Rekor negative paths, and large DSSE/referrer rejection behavior.
## Lanes
- `01-jcs-property`: canonicalization idempotence/permutation checks + duplicate-key rejection.
- `02-schema-fuzz`: bounded mutation lane with deterministic seed and crash artifact emission.
- `03-rekor-neg`: deterministic Rekor fault classification + diagnostic blob generation.
- `04-big-dsse-referrers`: oversized DSSE + malformed referrer graceful reject tests.
- `05-corpus`: deterministic fixture corpus and archive manifest builder.
## Run
- Linux/macOS:
- `bash tests/supply-chain/run.sh smoke`
- PowerShell:
- `pwsh tests/supply-chain/run.ps1 -Profile smoke`
- Direct:
- `python tests/supply-chain/run_suite.py --profile smoke --seed 20260226`
## Profiles
- `smoke`: CI PR gate (`02-schema-fuzz` limit=1000, time=60s).
- `nightly`: scheduled lane (`02-schema-fuzz` limit=5000, time=300s).
## Pass/Fail Gates
- JCS lane: zero invariant failures.
- Fuzz lane: zero `crash` classifications.
- Rekor negative lane: all cases return expected deterministic error classes.
- Big DSSE/referrers lane: malformed/oversized cases are rejected with `unknown_state` and `reprocessToken`.
## Failure Artifacts
Each lane writes machine-readable artifacts under `out/supply-chain/<lane>/`.
- `junit.xml`: CI-visible test result summary.
- `report.json` / `summary.json`: deterministic counters and classifications.
- `failures/<case>/diagnostic_blob.json`: replay-ready diagnostics.
- `hypothesis_seed.txt`: deterministic seed (name retained for familiarity).
## Replay
To replay a failing smoke run:
`python tests/supply-chain/run_suite.py --profile smoke --seed 20260226 --output out/supply-chain-replay`

View File

@@ -0,0 +1,13 @@
param(
[ValidateSet("smoke", "nightly")]
[string]$Profile = "smoke",
[int]$Seed = 20260226,
[string]$Output = "out/supply-chain"
)
$ErrorActionPreference = "Stop"
python tests/supply-chain/run_suite.py `
--profile $Profile `
--seed $Seed `
--output $Output

11
tests/supply-chain/run.sh Normal file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail
PROFILE="${1:-smoke}"
SEED="${SUPPLY_CHAIN_SEED:-20260226}"
OUT_DIR="${SUPPLY_CHAIN_OUT_DIR:-out/supply-chain}"
python tests/supply-chain/run_suite.py \
--profile "${PROFILE}" \
--seed "${SEED}" \
--output "${OUT_DIR}"

View File

@@ -0,0 +1,123 @@
#!/usr/bin/env python3
"""Orchestrate deterministic supply-chain hardening lanes."""
from __future__ import annotations
import argparse
import json
import pathlib
import subprocess
import sys
import time
def _python() -> str:
return sys.executable
def _run(cmd: list[str], cwd: pathlib.Path) -> int:
completed = subprocess.run(cmd, cwd=str(cwd), check=False)
return completed.returncode
def main() -> int:
parser = argparse.ArgumentParser(description="Run supply-chain hardening suite.")
parser.add_argument("--seed", type=int, default=20260226)
parser.add_argument("--profile", choices=["smoke", "nightly"], default="smoke")
parser.add_argument("--output", type=pathlib.Path, default=pathlib.Path("out/supply-chain"))
args = parser.parse_args()
repo_root = pathlib.Path(__file__).resolve().parents[2]
output_root = args.output.resolve()
output_root.mkdir(parents=True, exist_ok=True)
if args.profile == "smoke":
fuzz_limit = 1000
fuzz_seconds = 60
else:
fuzz_limit = 5000
fuzz_seconds = 300
lanes = [
(
"01-jcs-property",
[
_python(),
"tests/supply-chain/01-jcs-property/test_jcs.py",
"--seed",
str(args.seed),
"--output",
str(output_root / "01-jcs-property"),
],
),
(
"02-schema-fuzz",
[
_python(),
"tests/supply-chain/02-schema-fuzz/run_mutations.py",
"--seed",
str(args.seed),
"--limit",
str(fuzz_limit),
"--time-seconds",
str(fuzz_seconds),
"--output",
str(output_root / "02-schema-fuzz"),
],
),
(
"03-rekor-neg",
[
_python(),
"tests/supply-chain/03-rekor-neg/run_negative_suite.py",
"--output",
str(output_root / "03-rekor-neg"),
],
),
(
"04-big-dsse-referrers",
[
_python(),
"tests/supply-chain/04-big-dsse-referrers/run_big_cases.py",
"--output",
str(output_root / "04-big-dsse-referrers"),
],
),
(
"05-corpus-archive",
[
_python(),
"tests/supply-chain/05-corpus/build_corpus_archive.py",
"--output",
str(output_root / "05-corpus"),
],
),
]
start = time.perf_counter()
lane_results: list[dict[str, object]] = []
for lane_name, command in lanes:
print(f"[supply-chain] lane={lane_name} command={' '.join(command)}")
return_code = _run(command, repo_root)
lane_results.append(
{
"lane": lane_name,
"returnCode": return_code,
"status": "pass" if return_code == 0 else "fail",
}
)
summary = {
"profile": args.profile,
"seed": args.seed,
"durationSeconds": round(time.perf_counter() - start, 4),
"lanes": lane_results,
}
(output_root / "summary.json").write_text(json.dumps(summary, sort_keys=True, indent=2) + "\n", encoding="utf-8")
failures = [lane for lane in lane_results if lane["returnCode"] != 0]
return 0 if not failures else 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,64 @@
#!/usr/bin/env python3
"""Deterministic JSON parsing and canonicalization helpers for supply-chain tests."""
from __future__ import annotations
import hashlib
import json
from dataclasses import dataclass
from typing import Any
class DuplicateKeyError(ValueError):
"""Raised when JSON object contains duplicate keys."""
def _strict_object_pairs_hook(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
seen: set[str] = set()
result: dict[str, Any] = {}
for key, value in pairs:
if key in seen:
raise DuplicateKeyError(f"Duplicate key detected: {key}")
seen.add(key)
result[key] = value
return result
def parse_json_strict(text: str) -> Any:
"""Parse JSON and reject duplicate keys deterministically."""
return json.loads(text, object_pairs_hook=_strict_object_pairs_hook)
def canonicalize_value(value: Any) -> str:
"""
Canonicalize JSON value with deterministic ordering.
This is a strict deterministic serializer used for test invariants.
"""
return json.dumps(
value,
ensure_ascii=False,
separators=(",", ":"),
sort_keys=True,
)
def canonicalize_text(text: str) -> str:
"""Parse and canonicalize a JSON document."""
return canonicalize_value(parse_json_strict(text))
def sha256_hex(value: str) -> str:
"""Compute hex SHA-256 digest for canonical payload tracking."""
return hashlib.sha256(value.encode("utf-8")).hexdigest()
@dataclass(frozen=True)
class CanonicalResult:
canonical_json: str
sha256: str
def canonical_result_from_text(text: str) -> CanonicalResult:
canonical = canonicalize_text(text)
return CanonicalResult(canonical_json=canonical, sha256=sha256_hex(canonical))

View File

@@ -0,0 +1,102 @@
#!/usr/bin/env python3
"""Artifact and JUnit emitters for deterministic supply-chain hardening lanes."""
from __future__ import annotations
import json
import pathlib
import xml.etree.ElementTree as et
from dataclasses import dataclass
from typing import Any, Iterable
def _write_json(path: pathlib.Path, payload: Any) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("w", encoding="utf-8", newline="\n") as handle:
json.dump(payload, handle, sort_keys=True, indent=2, ensure_ascii=False)
handle.write("\n")
def _write_text(path: pathlib.Path, content: str) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(content, encoding="utf-8", newline="\n")
@dataclass(frozen=True)
class TestCaseResult:
suite: str
name: str
passed: bool
duration_seconds: float = 0.0
failure_message: str | None = None
def write_junit(path: pathlib.Path, test_cases: Iterable[TestCaseResult]) -> None:
cases = list(test_cases)
failures = sum(0 if case.passed else 1 for case in cases)
suite = et.Element(
"testsuite",
attrib={
"name": "supply-chain-hardening",
"tests": str(len(cases)),
"failures": str(failures),
"errors": "0",
"skipped": "0",
},
)
for case in sorted(cases, key=lambda item: (item.suite, item.name)):
node = et.SubElement(
suite,
"testcase",
attrib={
"classname": case.suite,
"name": case.name,
"time": f"{case.duration_seconds:.3f}",
},
)
if not case.passed:
failure = et.SubElement(node, "failure", attrib={"type": "assertion"})
failure.text = case.failure_message or "failure"
tree = et.ElementTree(suite)
path.parent.mkdir(parents=True, exist_ok=True)
tree.write(path, encoding="utf-8", xml_declaration=True)
def record_failure(
*,
lane_output_dir: pathlib.Path,
case_id: str,
seed: int,
payload_text: str,
error_class: str,
message: str,
details: dict[str, Any] | None = None,
canonical_diff_patch: str | None = None,
) -> pathlib.Path:
"""
Write deterministic failure artifacts for replay.
Returns the failure directory path.
"""
failure_dir = lane_output_dir / "failures" / case_id
failure_dir.mkdir(parents=True, exist_ok=True)
_write_text(failure_dir / "failing_case.json", payload_text)
_write_text(failure_dir / "hypothesis_seed.txt", f"{seed}\n")
diagnostic_payload: dict[str, Any] = {
"caseId": case_id,
"errorClass": error_class,
"message": message,
}
if details:
diagnostic_payload["details"] = details
_write_json(failure_dir / "diagnostic_blob.json", diagnostic_payload)
if canonical_diff_patch is not None:
_write_text(failure_dir / "canonical_diff.patch", canonical_diff_patch)
return failure_dir