Generated: 2026-05-01 Branch:
claude/kind-tereshkova-b39079Scope: Full repo audit against the 7-phase Bulletproof Plan, focused on the five pillars: determinism, cryptographic integrity, coverage, provenance, evidence-grade output. Method: static greps,radon cc -nc -a,pytest --collect-only, manual review of every file emitting a signature.
This document is the load-bearing baseline for v3.0.0. Every follow-up sprint references the line numbers and grades captured here. No fixes in this document β fixes happen in Phase 2+. Phase 1 is naming the disease.
| Tool | Status | Notes |
|---|---|---|
coverage (line + branch) |
β not configured | no .coveragerc, no [tool.coverage.*] in pyproject.toml, no CI step |
mutmut |
β not installed, never run | mutation score = unknown / 0 measured |
radon (cyclomatic complexity) |
β now installed locally; not yet wired into CI | average grade C (17.33) across 284 blocks β far above the Konjo bar |
hypothesis (property-based) |
β not in deps | zero property tests today |
atheris (fuzz) |
β not in deps | zero fuzz harnesses today |
mypy --strict |
β relaxed (ignore_missing_imports = true, no --strict) |
static type guarantees absent |
bandit / semgrep / pip-audit / trivy / gitleaks / OSV-Scanner |
β none configured | zero pre-commit / CI security scanners |
cyclonedx SBOM on release |
β not produced | dependency declared but unused for self-SBOM |
slsa-github-generator |
β not wired | no SLSA L3 provenance on wheels / Docker / action |
rfc8785 / JCS canonicalizer |
β not in deps and not in code | every signed payload uses json.dumps() variants |
| RFC 3161 trusted timestamping | β not integrated | no TSA token in any cert |
| Sigstore / Rekor transparency log | β not integrated | local-only βanchorβ log in squash/anchor.py |
| Input manifest at ingest (SHA-256 every file before analysis) | β not produced | scanners hash artifacts but no top-level manifest |
Test count: pytest --collect-only -q β 5,226 tests collected (the master plan still cites 4,308 / 4,471 β number is stale, plan needs an update). Coverage and branch coverage of those 5,226 tests is unmeasured β Phase G Sprint 1.1 below establishes the baseline.
A reproducibility test (run twice, diff SHA-256 of the canonical output) would currently fail on most of the modules below. The five-pillar promise is not yet honoured.
Every signed payload must be RFC 8785 (JCS) before hashing/signing. Today nothing uses RFC 8785; existing sort_keys=True, separators=(",", ":") is βcloseβ but not equivalent (no NFC normalisation, no number canonicalisation, no escape-sequence normalisation).
| File | Line | Violation | Severity |
|---|---|---|---|
squash/attest.py |
405 | json.dumps(obj, indent=2, default=str) β pretty-printed AND default=str silently coerces unknown types |
π΄ critical β Tier 0 signing path |
squash/attest.py |
461 | json.dumps(bom, indent=2) β no sort_keys, BOM written before signing |
π΄ critical |
squash/attest.py |
619 | json.dumps(parent_bom, indent=2) β same |
π΄ critical |
squash/slsa.py |
141 | json.dumps(statement, indent=2) β SLSA in-toto Statement written non-canonically |
π΄ critical β provenance |
squash/slsa.py |
271 | json.dumps(bom, indent=2) β same |
π΄ critical |
squash/anchor.py |
118 | json.dumps(value, sort_keys=True, separators=(",",":"), ensure_ascii=False) β close, but not RFC 8785; ensure_ascii=False invites homograph drift |
π‘ elevated |
squash/anchor.py |
569, 603 | log lines sort_keys=True only |
π‘ elevated |
squash/chain_attest.py |
247, 250, 487 | sort_keys=True only β composite chain digest |
π high (multi-component chain β drift compounds) |
squash/hallucination_attest.py |
815, 1048, 1077 | sort_keys=True only on signed cert body |
π high |
squash/drift_certificate.py |
95, 231, 236, 487 | same | π high |
squash/carbon_attest.py |
856, 867 | same | π high |
squash/carbon_attest.py |
962 | json.dumps(bom, indent=2, ensure_ascii=False) for written BOM |
π‘ elevated |
Resolution path (Phase 2): introduce squash/_canonical.py with a single canonical_bytes(obj) -> bytes that wraps rfc8785.dumps(); ban raw json.dumps in signing paths via a custom Semgrep rule (Phase 5).
Every attestation path that calls datetime.utcnow(), datetime.now(), or datetime.now(tz=...) inline is non-reproducible by construction. Each call must accept an injected clock: Callable[[], datetime] (default datetime.now(timezone.utc)).
| File | Line | Violation | Severity |
|---|---|---|---|
squash/attest.py |
481 | datetime.now(timezone.utc).strftime(...) baked into attestation timestamp |
π΄ critical β Tier 0 |
squash/hallucination_attest.py |
1034 | issued_at=datetime.now(tz=timezone.utc).isoformat() baked into signed cert |
π΄ critical β Tier 1 |
squash/data_lineage.py |
252 | f"{model_id}{datetime.datetime.now().isoformat()}".encode() β clock value mixed into a hash input β cert ID changes every call β reproducibility impossible without fix |
π΄ critical β repro killer |
squash/webhook_delivery.py |
441 | datetime.datetime.utcnow().isoformat() + "Z" β Python 3.12 deprecates utcnow; also non-injectable |
π high |
squash/oms_signer.py |
233β241 | datetime.datetime.now(datetime.timezone.utc).strftime("%Y%m%dT%H%M%SZ") for filename stamping |
π‘ elevated (filename only, not signed bytes β but still test-flaky) |
squash/asset_registry.py |
98, 360 | datetime.datetime.now() (no tz) in non-attestation registry paths |
π’ nuisance (non-Tier-0/1, but also wrong: naive datetime) |
squash/annual_review.py |
298 | datetime.datetime.now().year for default year |
π’ nuisance |
squash/vex.py |
848 (docstring) | references datetime.utcnow() default β needs API audit |
π‘ elevated |
Resolution path (Phase 2): every Tier 0/1 module gains a clock: Callable[[], datetime] = lambda: datetime.now(timezone.utc) parameter; default-call sites pull from squash._clock.utc_now() which test fixtures monkeypatch.
uuid.uuid4() in any field that becomes part of the signed body breaks reproducibility. Replace with uuid.uuid5(NAMESPACE, canonical_input) keyed on the canonical input bytes.
| File | Line | Field | Severity |
|---|---|---|---|
squash/slsa.py |
65, 122 | invocation_id of SLSA BuildDefinition β inside the signed in-toto Statement |
π΄ critical β provenance reproducibility |
squash/hallucination_attest.py |
1021 | cert_id="hac-" + uuid.uuid4().hex[:16] β inside signed cert |
π΄ critical |
squash/carbon_attest.py |
823 | cert_id=f"carbon-{uuid.uuid4().hex[:16]}" β same |
π΄ critical |
squash/anchor.py |
593 | anchor_id="anc-" + uuid.uuid4().hex[:12] |
π high |
squash/freeze.py |
244 | FREEZE-{uuid.uuid4().hex[:12]} |
π high |
squash/approval_workflow.py |
472, 544 | appr-{uuid4}, rec-{uuid4} |
π high |
squash/incident.py |
188 | INC-{uuid4} |
π high |
squash/hallucination_monitor.py |
367, 471 | mon-{uuid4}, brc-{uuid4} |
π high |
squash/vendor_registry.py |
266 | vendor_id from uuid4 | π‘ elevated |
squash/api.py |
1380, 1409, 2747, 2800, 2862 | API-layer record/job/event IDs (operational, not signed) | π’ nuisance |
squash/webhook_delivery.py |
304, 380, 412 | endpoint/payload/event IDs (operational) | π’ nuisance |
Resolution path (Phase 2): squash/_ids.py exposing cert_id(prefix: str, *, canonical_payload: bytes) -> str returning f"{prefix}-{uuid.uuid5(SQUASH_NS, canonical_payload).hex[:16]}". All Tier 0/1 IDs route through it.
| Location | Issue |
|---|---|
squash/benchmark.py:430 |
list(set(model_ids))[:20] β setβlist iteration order is platform-dependent before truncation |
squash/license_conflict.py:875,942,969 |
seen: set[...] used as iterator β must sorted() before serialisation |
squash/rag.py:159, squash/iso42001.py:414, squash/sarif.py:104, squash/hallucination_attest.py:693, squash/data_poison.py:1020, squash/board_report.py:437, squash/annual_review.py:320,469 |
set[...] defined; need audit of every downstream serialise |
No random.seed() calls in any Tier 0/1 module today β but no random.* calls either, so this is currently safe. Phase 2 will gate any future use behind a seeded RNG injected through the signature path. |
Β |
Resolution path (Phase 2): add a Semgrep rule no-unsorted-set-serialisation that flags any json.*(... set ...) or for x in some_set: where the result is hashed.
default=str time-bomb (very subtle, currently latent)squash/attest.py:405 uses json.dumps(..., default=str). Today every dataclass passed in has explicit __dict__/asdict conversion upstream, so the default= branch is never hit β but the moment a Path or a datetime slips into the dict, the serialised form changes shape silently and breaks reproducibility without raising. Replace with explicit canonical encoder that raises on unknown types.
| Capability | Present? | Notes |
|---|---|---|
| Input manifest at ingest (SHA-256 every input file) | β | scanners hash artifacts per-file but never emit a top-level input_manifest.json covering the entire input set of a run |
| Canonical signing payload (RFC 8785) | β | see Β§1.1 |
| Ed25519 signing | β
(squash/oms_signer.py) |
underlying cryptography library used correctly; key management out-of-scope |
| RFC 3161 trusted timestamp | β | no TSA integration; cert issued_at is self-asserted |
| Sigstore / Rekor inclusion proof | β | squash/anchor.py is a local-only transparency log β not a public, append-only Sigstore Rekor entry |
| SLSA Build Level 3 provenance | β | squash/slsa.py builds a self-asserted in-toto Statement; no slsa-github-generator workflow β not L3 |
squash self-verify CLI |
β | no command verifies squashβs own attestation chain |
Resolution path (Phase 3): add squash/_input_manifest.py (called as the very first step of every CLI subcommand that ingests files), squash/_tsa.py (DigiCert / FreeTSA client), .github/workflows/release.yml calling slsa-framework/slsa-github-generator, and squash self-verify CLI command.
[tool.coverage.run] with branch = true and a baseline run.Picked by: emits a signature, OR writes an attestation file, OR is loaded by every signed cert.
squash/oms_signer.py (signing primitive)squash/anchor.py (transparency log + canonical JSON helper)squash/attest.py (root attestation pipeline)squash/slsa.py (SLSA provenance emitter)squash/chain_attest.py (composite cert)squash/hallucination_attest.py (signed cert)squash/drift_certificate.py (signed cert)squash/carbon_attest.py (signed cert)squash/data_lineage.py (LineageCertificate)squash/sbom_builder.py (BOM that gets signed)radon cc squash/ -nc -a β average C (17.33) β the floor for Konjo is B (β€10) average, max D per function. Current state has 60 functions β₯ D and 7 functions at F (β₯30):
| File | Function | Grade |
|---|---|---|
squash/cli.py |
main (line 10609) |
F |
squash/cli.py |
_cmd_github_app |
F |
squash/cli.py |
_cmd_annex_iv_generate |
F |
squash/cli.py |
_cmd_anchor |
F |
squash/dashboard.py |
Dashboard.build |
F |
squash/attest.py |
AttestPipeline |
F |
squash/attest.py |
AttestPipeline.run |
F |
squash/cli.py is 10,829 lines. Every _cmd_* should split into its own module under squash/cli/<cmd>.py. Phase 5 ticket.
squash/license_conflict.py (1,357 lines) β the master plan already calls this out; Phase 5 splits into database.py / scanner.py / reporter.py.
E-grade functions worth flagging (CC 21β30, prime mutation targets):
InsuranceBuilder._profile_model, InsurancePackage.to_markdown, MunichReAdapter.format, WebhookHandler._handle_push, _render_1c, _cmd_attest_identity, _cmd_watch_regulatory, _cmd_deprecation_watch, _cmd_compliance_matrix, _cmd_vex, _cmd_drift_cert, _cmd_cloud_risk (Γ2), AnnexIVGenerator.generate, DataLineageTracer.trace, AttestationDiff.to_table.
setuptools, no SLSA generator, no Sigstore signature, no pyproject.toml [project.urls] Provenance link.Dockerfile exists, fly.toml present, no SLSA build attestation is uploaded with the image; not pushed to GHCR with --provenance=true today.action.yml): recently upgraded with attestation-id output, but no --build-provenance step in the actionβs own .github/workflows/.squash self-verify: β not implemented. Phase 3 deliverable.Spot-checked five recent attestations (Annex IV, Bias Audit, Carbon, Hallucination, Drift Cert) β the human-readable reports cite figures and ratings, but none of the cited figures embed a content-addressed pointer (sha256:β¦) back to the input that produced them. Every claim must be a re-verifiable hash reference. Phase 4/5 deliverable: evidence_pointer = (claim_id, sha256, byte_range) schema applied to every numeric or rating field.
Every line below becomes a unit-tested Phase 2 PR.
squash/attest.py:405 β replace json.dumps(obj, indent=2, default=str) with canonical_bytes(obj); raise on unknown types.squash/attest.py:461 β same.squash/attest.py:481 β replace inline datetime.now(...) with injected clock().squash/attest.py:619 β same as #1.squash/slsa.py:65 β replace uuid.uuid4() default factory with uuid5 keyed on canonical input.squash/slsa.py:122 β same.squash/slsa.py:141 β replace json.dumps(..., indent=2) with canonical encoder before write/sign.squash/slsa.py:271 β same.squash/anchor.py:118 β upgrade in-process canonicaliser to RFC 8785 (rfc8785.dumps).squash/anchor.py:593 β uuid5 not uuid4.squash/chain_attest.py:247,250,487 β canonical encoder.squash/hallucination_attest.py:815,1048,1077 β canonical encoder.squash/hallucination_attest.py:1021 β uuid5 not uuid4.squash/hallucination_attest.py:1034 β injected clock.squash/drift_certificate.py:95,231,236,487 β canonical encoder.squash/carbon_attest.py:856,867,962 β canonical encoder.squash/carbon_attest.py:823 β uuid5 not uuid4.squash/data_lineage.py:252 β stop mixing wallclock into hash input; inject clock OR drop time from the hashed key.squash/webhook_delivery.py:441 β datetime.now(timezone.utc) (drop deprecated utcnow); inject clock.squash/freeze.py:244, squash/incident.py:188, squash/approval_workflow.py:472,544, squash/hallucination_monitor.py:367,471 β uuid5 not uuid4 (signed paths only).squash/oms_signer.py:233β241 β accept injected clock for filename stamping (test stability).squash/_canonical.py, squash/_clock.py, squash/_ids.py, squash/_input_manifest.py, squash/_tsa.py, squash/_self_verify.py.radon installed and run; CC > 10 list captured (Β§4)json.dumps, datetime.now, uuid.uuid4, set serialisation sites in Tier 0/1 grepped and line-numbered (Β§1)TIER_MAP.md written (sibling file)pyproject.toml extended with [tool.coverage.run] branch = true, [tool.coverage.report] thresholds, and dev-deps for coverage, mutmut, radon, hypothesisscripts/coverage_baseline.sh lands a reproducible baseline commandThe repo today is functionally rich but evidentially weak: 5,226 tests of behaviour, zero tests of byte-identity; 60+ functions over the complexity threshold; a transparency log that only logs to itself; provenance that is self-asserted; and a signing pipeline that uses pretty-printed JSON with default=str four hundred lines into the most-trusted path in the codebase.
The fixes are surgical, not architectural β every nondeterminism site is one of three patterns (canonical JSON, injected clock, uuid5). Once Phase 2 lands, byte-identical attestations on rerun become the default, not a heroic exception. That is the unlock that makes Phase 3βs input manifest + RFC 3161 + SLSA L3 + Rekor anchor meaningful β there is no point timestamping or anchoring something that does not hash to the same value twice.
Make it konjo. The vessel is not yet seaworthy. We know exactly which planks need shaping.