From cea3437a199fbe614ac37bbb0bb35607940dffac Mon Sep 17 00:00:00 2001 From: BrianCLong <6404035+BrianCLong@users.noreply.github.com> Date: Wed, 8 Apr 2026 19:08:15 -0600 Subject: [PATCH] feat(cac): embed CAC into audit controls, policy language, and portable attestation verification (rebase on main) --- artifacts/attestation/cac.json | 17 +++ artifacts/attestation/cacert.json | 10 ++ artifacts/attestation/provenance.json | 11 ++ artifacts/attestation/sbom.json | 14 ++ docs/cac/AUDIT_PLAYBOOK.md | 63 +++++++++ docs/cac/REFERENCE_IMPLEMENTATION.md | 23 ++++ docs/cac/control-mapping/ISO_42001_MAPPING.md | 17 +++ .../control-mapping/NIST_AI_RMF_MAPPING.md | 19 +++ docs/cac/control-mapping/SOC2_MAPPING.md | 20 +++ docs/cac/policy/ENTERPRISE_POLICY_TEMPLATE.md | 28 ++++ docs/cac/policy/REGULATORY_LANGUAGE.md | 19 +++ docs/cac/standards/CAC_RFC_DRAFT.md | 19 +++ docs/cac/standards/GOVERNANCE_CHARTER.md | 23 ++++ docs/cac/standards/TECHNICAL_SPEC_APPENDIX.md | 24 ++++ docs/roadmap/STATUS.json | 16 +-- package.json | 11 +- packages/attestation/generator.ts | 87 +++++++++++++ tools/cac-cli/index.mjs | 121 ++++++++++++++++++ 18 files changed, 525 insertions(+), 17 deletions(-) create mode 100644 artifacts/attestation/cac.json create mode 100644 artifacts/attestation/cacert.json create mode 100644 artifacts/attestation/provenance.json create mode 100644 artifacts/attestation/sbom.json create mode 100644 docs/cac/AUDIT_PLAYBOOK.md create mode 100644 docs/cac/REFERENCE_IMPLEMENTATION.md create mode 100644 docs/cac/control-mapping/ISO_42001_MAPPING.md create mode 100644 docs/cac/control-mapping/NIST_AI_RMF_MAPPING.md create mode 100644 docs/cac/control-mapping/SOC2_MAPPING.md create mode 100644 docs/cac/policy/ENTERPRISE_POLICY_TEMPLATE.md create mode 100644 docs/cac/policy/REGULATORY_LANGUAGE.md create mode 100644 docs/cac/standards/CAC_RFC_DRAFT.md create mode 100644 docs/cac/standards/GOVERNANCE_CHARTER.md create mode 100644 docs/cac/standards/TECHNICAL_SPEC_APPENDIX.md create mode 100644 packages/attestation/generator.ts create mode 100644 tools/cac-cli/index.mjs diff --git a/artifacts/attestation/cac.json b/artifacts/attestation/cac.json new file mode 100644 index 00000000000..9fa53e9f308 --- /dev/null +++ b/artifacts/attestation/cac.json @@ -0,0 +1,17 @@ +{ + "artifact_type": "cac", + "spec_version": "1.0", + "decision_id": "DEC-2026-0001", + "risk_level": "high", + "verdict": "PASS", + "reasons": [ + "policy-evaluation-complete", + "evidence-bundle-valid" + ], + "evidence_refs": [ + "sbom.json", + "provenance.json" + ], + "policy_failures": [], + "control_version": "cac-controls-2026.03" +} diff --git a/artifacts/attestation/cacert.json b/artifacts/attestation/cacert.json new file mode 100644 index 00000000000..e2ebfe68845 --- /dev/null +++ b/artifacts/attestation/cacert.json @@ -0,0 +1,10 @@ +{ + "cert_version": "1.0", + "certificate_id": "CAC-REF-2026-0001", + "evidence_hash": "8625d09cecd44f39e52b28472eb70a3b6779bea548d28eb1f62eae09e29b8635", + "issuer": "CAC-Authority-Example", + "policy_refs": [], + "signature": "UNSIGNED-EXAMPLE-REPLACE-IN-PRODUCTION", + "trace_ref": "DEC-2026-0001", + "verdict": "PASS" +} diff --git a/artifacts/attestation/provenance.json b/artifacts/attestation/provenance.json new file mode 100644 index 00000000000..52fa4f7822b --- /dev/null +++ b/artifacts/attestation/provenance.json @@ -0,0 +1,11 @@ +{ + "bundle_version": "1.0.0", + "digest": "8625d09cecd44f39e52b28472eb70a3b6779bea548d28eb1f62eae09e29b8635", + "generated_by": "packages/attestation/generator.ts", + "hash_algorithm": "sha256", + "required_files": [ + "cac.json", + "cacert.json", + "sbom.json" + ] +} diff --git a/artifacts/attestation/sbom.json b/artifacts/attestation/sbom.json new file mode 100644 index 00000000000..6a674576e75 --- /dev/null +++ b/artifacts/attestation/sbom.json @@ -0,0 +1,14 @@ +{ + "bomFormat": "CycloneDX", + "specVersion": "1.6", + "serialNumber": "urn:uuid:7d5f8d1b-8ef2-4bf2-8719-d8e1729f7f20", + "version": 1, + "metadata": { + "component": { + "type": "application", + "name": "summit-cac-reference", + "version": "1.0.0" + } + }, + "components": [] +} diff --git a/docs/cac/AUDIT_PLAYBOOK.md b/docs/cac/AUDIT_PLAYBOOK.md new file mode 100644 index 00000000000..32d06b0aafd --- /dev/null +++ b/docs/cac/AUDIT_PLAYBOOK.md @@ -0,0 +1,63 @@ +# CAC Audit Playbook (Third-Party Executable) + +## 1. Objective + +Provide a repeatable procedure for external auditors to verify CAC compliance with no internal Summit access. + +## 2. Required Inputs + +- Attestation bundle directory containing: + - `cac.json` + - `cacert.json` + - `sbom.json` + - `provenance.json` +- CAC issuer public key file (PEM/JWK). +- Applicable policy profile (enterprise/regulatory baseline). + +## 3. Auditor Procedure + +1. **Intake validation** + - Confirm all four required files exist. + - Validate each file is valid UTF-8 JSON. +2. **Schema and structural validation** + - Confirm mandatory fields in CAC and CACert. + - Confirm provenance includes deterministic digest over bundle. +3. **Cryptographic verification** + - Verify CACert signature with provided public key. + - Recompute canonical digest for bundle and compare to recorded digest and `cacert.evidence_hash`. +4. **Control verification** + - Apply control mappings in `docs/cac/control-mapping/*`. + - Validate risk-tier sampling obligations. +5. **Sampling and traceability review** + - Sample at least 10 high-risk decisions or 5%, whichever is greater. + - For each sample, verify admissibility verdict, evidence references, and certificate linkage. +6. **Issue classification** + - Missing artifact: Major non-conformity. + - Hash/signature mismatch: Critical non-conformity. + - Missing high-risk CACert: Critical non-conformity. + +## 4. Sampling Methodology + +- Population definition: all decisions marked `risk_level = high` in scope period. +- Stratification: by decision type and business domain. +- Minimum sample size: `max(10, ceil(population * 0.05))`. +- Reproducibility: record random seed used for sample selection. + +## 5. Failure Conditions + +Audit fails if any of the following occur: + +- Required bundle artifact absent. +- CACert signature invalid or unverifiable. +- Deterministic digest mismatch. +- Any sampled high-risk decision lacks CACert. +- Control mapping evidence cannot be reproduced externally. + +## 6. Auditor Output + +Audit report MUST include: + +- Scope dates and sampled decision counts. +- Control-by-control pass/fail matrix. +- List of missing/invalid evidence with filenames. +- Final compliance status: `COMPLIANT`, `CONDITIONALLY_COMPLIANT`, or `NON_COMPLIANT`. diff --git a/docs/cac/REFERENCE_IMPLEMENTATION.md b/docs/cac/REFERENCE_IMPLEMENTATION.md new file mode 100644 index 00000000000..39a527118f6 --- /dev/null +++ b/docs/cac/REFERENCE_IMPLEMENTATION.md @@ -0,0 +1,23 @@ +# CAC Reference Implementation Claim + +Summit is declared a reference implementation for CAC v1.0 by producing deterministic, portable, independently verifiable admissibility bundles and CACert outputs. + +## Reference Scope + +- Artifact generation: `packages/attestation/generator.ts` +- Certification primitive: `packages/cacert/src/cacert.ts` +- Audit verification CLI: `tools/cac-cli/index.mjs` + +## Test Vectors + +1. PASS verdict with zero policy failures. +2. FAIL verdict with multiple policy failures. +3. Missing artifact bundle (negative control). +4. Hash mismatch between provenance and evidence files. + +## Compliance Validation Process + +1. Generate bundle deterministically from source artifacts. +2. Verify bundle with `cac audit `. +3. Confirm control mapping alignment using documents under `docs/cac/control-mapping/`. +4. Archive verification output with bundle for third-party review. diff --git a/docs/cac/control-mapping/ISO_42001_MAPPING.md b/docs/cac/control-mapping/ISO_42001_MAPPING.md new file mode 100644 index 00000000000..dda08c3b36e --- /dev/null +++ b/docs/cac/control-mapping/ISO_42001_MAPPING.md @@ -0,0 +1,17 @@ +# CAC v1.0 to ISO/IEC 42001 Mapping + +This document maps CAC requirements to AI management system controls in ISO/IEC 42001. + +## Mapping Matrix + +| CAC Requirement | ISO/IEC 42001 Control Objective | Evidence Artifact | Verification Method | +| --- | --- | --- | --- | +| CAC policy is machine-checkable and versioned. | Clause 6: Planning includes risk and control planning. | `cac.json.control_version`, policy references. | Verify version pinning and policy traceability in sampled bundles. | +| Decision artifacts are portable and independently auditable. | Clause 8: Operational controls and documented information. | `artifacts/attestation/*` bundle contents. | Validate completeness against audit checklist and confirm no internal system dependency. | +| Certification lifecycle includes failure and revocation semantics. | Clause 10: Continual improvement and corrective actions. | `cacert.json.valid_from`, `valid_until`, revocation pointer. | Check expiration/revocation fields are populated and policy-conformant. | +| Evidence provenance is cryptographically bound. | Annex A controls on integrity and traceability. | `provenance.json`, hash chain fields. | Recalculate digests and verify chain consistency for sampled artifacts. | +| High-risk thresholds trigger stronger admissibility evidence requirements. | Clause 5 leadership/accountability and governance of high-impact systems. | Risk register excerpt + CACert requirement evidence. | Validate every high-risk sample has CACert and no waiver without explicit exception record. | + +## Conformance Claim Criteria + +An implementation MAY claim alignment when all mapped controls are evidenced in an externally reproducible bundle. diff --git a/docs/cac/control-mapping/NIST_AI_RMF_MAPPING.md b/docs/cac/control-mapping/NIST_AI_RMF_MAPPING.md new file mode 100644 index 00000000000..15f6a61de0e --- /dev/null +++ b/docs/cac/control-mapping/NIST_AI_RMF_MAPPING.md @@ -0,0 +1,19 @@ +# CAC v1.0 to NIST AI RMF Mapping + +This matrix maps CAC controls to NIST AI RMF functions (Govern, Map, Measure, Manage). + +## Mapping Matrix + +| CAC Requirement | NIST AI RMF Objective | Evidence Artifact | Verification Method | +| --- | --- | --- | --- | +| Mandatory admissibility evaluation before release/deployment. | Govern 1.6: Policies establish accountability for AI risk decisions. | `artifacts/attestation/cac.json`, release attestation metadata. | Confirm all sampled deployments include a CAC verdict timestamped before release approval. | +| High-risk decisions require CACert. | Manage 4.1: Risk treatment actions are enforced. | `artifacts/attestation/cacert.json`, risk classification input. | Validate that high-risk sample set has no missing certificate records. | +| Explicit evidence references for every verdict. | Measure 2.12: Performance and risk metrics are traceable. | `cac.json.evidence_refs`, source evidence files. | Random-sample evidence refs and ensure each reference resolves to immutable artifacts. | +| Deterministic provenance over attestation payload. | Measure 3.3: Measurement process supports repeatability. | `artifacts/attestation/provenance.json`. | Recompute bundle digest and verify exact match with recorded digest. | +| Independent CLI audit verification of bundles. | Manage 2.3: Monitoring provides actionable compliance status. | `tools/cac-cli/index.mjs` output logs. | Execute `node tools/cac-cli/index.mjs audit ` and confirm control-level pass/fail status output. | + +## Independent Verification Notes + +- No private service calls are required. +- Offline verification is supported using only bundle artifacts and public key material. +- Any missing required artifact results in explicit control failure. diff --git a/docs/cac/control-mapping/SOC2_MAPPING.md b/docs/cac/control-mapping/SOC2_MAPPING.md new file mode 100644 index 00000000000..1565cc7f153 --- /dev/null +++ b/docs/cac/control-mapping/SOC2_MAPPING.md @@ -0,0 +1,20 @@ +# CAC v1.0 to SOC 2 Control Mapping + +This control map enables independent auditors to verify CAC conformance without Summit internal access. + +## Mapping Matrix + +| CAC Requirement | SOC 2 Control Objective | Evidence Artifact | Verification Method | +| --- | --- | --- | --- | +| Signed admissibility verdict (`PASS`/`FAIL`) for each high-risk decision. | CC7.2 Detect and act on anomalies from system operations. | `artifacts/attestation/cac.json`, decision log extracts. | Recompute verdict hash from `cac.json` payload and confirm deterministic match across two independent runs. | +| Tamper-evident certificate (`CACert`) bound to evidence hash. | CC6.1 Logical controls protect integrity of critical artifacts. | `artifacts/attestation/cacert.json`, issuer public key, signature metadata. | Verify signature with issuer public key and confirm `evidence_hash` equals bundle digest in `provenance.json`. | +| Deterministic evidence bundle generation. | CC8.1 Change management ensures controlled release outputs. | `packages/attestation/generator.ts`, generated bundle directory. | Run generator twice with same inputs and assert byte-identical outputs. | +| Policy failures explicitly enumerated in certification outputs. | CC3.2 Control activities are defined and monitored. | `cac.json.policy_failures`, `cacert.json.policy_refs`. | Confirm every failed policy in `cac.json` appears in `cacert.json.policy_refs`. | +| Portable, external audit package with SBOM + provenance. | A1.2 System commitments are measurable and supportable. | `artifacts/attestation/sbom.json`, `artifacts/attestation/provenance.json`. | Validate JSON schemas and cryptographic hashes without network dependency. | + +## Auditor Assertion Template + +1. The evaluated system produced schema-valid CAC evidence. +2. CACert was independently verifiable against published key material. +3. Evidence bundle was deterministic and complete for sampled decisions. +4. Exceptions were recorded as control failures, not omitted artifacts. diff --git a/docs/cac/policy/ENTERPRISE_POLICY_TEMPLATE.md b/docs/cac/policy/ENTERPRISE_POLICY_TEMPLATE.md new file mode 100644 index 00000000000..0640cd3c75d --- /dev/null +++ b/docs/cac/policy/ENTERPRISE_POLICY_TEMPLATE.md @@ -0,0 +1,28 @@ +# Enterprise Policy Template — Cognitive Admissibility Controls + +## 1. Policy Statement + +All decision-support and automated decision systems MUST produce admissible decision artifacts for each in-scope decision. + +## 2. Mandatory Requirements + +1. Systems MUST emit a CAC admissibility record (`cac.json`) for every high-risk decision. +2. Systems MUST attach a verifiable CACert (`cacert.json`) for high-risk decisions before production action. +3. Evidence bundles MUST be deterministic, portable, and independently verifiable. +4. Missing or invalid CAC artifacts MUST trigger a deployment or execution block. + +## 3. Control Enforcement + +- Enforcing authority: Internal Risk + Compliance. +- Technical enforcement: CI/CD gate plus periodic offline bundle audits. +- Exception process: Time-bounded governed exception with documented risk owner approval. + +## 4. Audit Clause + +Business units MUST retain CAC evidence bundles and verification logs for no less than policy-defined retention periods (minimum 24 months). + +## 5. Non-Compliance Consequences + +- Immediate classification as policy breach. +- Required corrective action plan within 10 business days. +- Repeat violations escalate to executive risk committee. diff --git a/docs/cac/policy/REGULATORY_LANGUAGE.md b/docs/cac/policy/REGULATORY_LANGUAGE.md new file mode 100644 index 00000000000..0a2896590ae --- /dev/null +++ b/docs/cac/policy/REGULATORY_LANGUAGE.md @@ -0,0 +1,19 @@ +# Regulatory Language Draft — Cognitive Admissibility + +## Proposed Neutral Language + +1. Covered systems MUST produce admissible decision artifacts for materially impactful and high-risk decisions. +2. Admissibility artifacts MUST include machine-verifiable evidence of policy evaluation outcomes. +3. High-risk decisions MUST be accompanied by a cryptographically verifiable certificate of admissibility (CACert or equivalent). +4. Regulated entities MUST maintain portable evidence bundles sufficient for independent third-party verification. +5. Failure to produce verifiable admissibility evidence SHALL constitute a control deficiency. + +## Enforcement Clauses + +- Supervisory reviews MAY request randomly sampled admissibility bundles for independent verification. +- Material discrepancies (signature failure, missing evidence, or hash mismatch) SHOULD be treated as significant control failures. +- Persistent non-conformance MAY trigger corrective action directives, heightened reporting, or remediation mandates. + +## Implementation Neutrality + +This language is technology-neutral and does not require any specific vendor platform, runtime, or hosting environment. diff --git a/docs/cac/standards/CAC_RFC_DRAFT.md b/docs/cac/standards/CAC_RFC_DRAFT.md new file mode 100644 index 00000000000..ae9874d52f5 --- /dev/null +++ b/docs/cac/standards/CAC_RFC_DRAFT.md @@ -0,0 +1,19 @@ +# CAC RFC Draft (Standards-Ready) + +## Abstract + +Cognitive Admissibility Certification (CAC) defines interoperable requirements for AI-assisted decision systems to emit verifiable admissibility outcomes and portable evidence bundles. + +## Normative Requirements + +- Implementations MUST emit deterministic admissibility artifacts. +- Implementations MUST support independent verification of certificate signatures and evidence hashes. +- High-risk decision pathways MUST require certificate-backed admissibility before action. + +## Interoperability + +Conforming implementations MUST expose artifacts as JSON and support offline verification with public key material. + +## Security Considerations + +Threats addressed include artifact tampering, unverifiable policy bypass, and unverifiable decision provenance. diff --git a/docs/cac/standards/GOVERNANCE_CHARTER.md b/docs/cac/standards/GOVERNANCE_CHARTER.md new file mode 100644 index 00000000000..b7a9af998d4 --- /dev/null +++ b/docs/cac/standards/GOVERNANCE_CHARTER.md @@ -0,0 +1,23 @@ +# CAC Governance Charter + +## Purpose + +Maintain an open, neutral, and auditable governance process for CAC evolution and conformance validation. + +## Principles + +- Neutrality: No vendor-specific dependencies in normative requirements. +- Verifiability: All conformance claims must be reproducible from shared artifacts. +- Reversibility: Certification and control decisions must support documented revocation. + +## Roles + +- Maintainers: curate specification and schema updates. +- Auditors: independently validate implementation claims. +- Implementers: generate conformant artifacts and maintain verification interfaces. + +## Change Management + +- Proposed changes published as public drafts. +- Backward compatibility impact documented for each version. +- Major changes require published migration guidance and test vectors. diff --git a/docs/cac/standards/TECHNICAL_SPEC_APPENDIX.md b/docs/cac/standards/TECHNICAL_SPEC_APPENDIX.md new file mode 100644 index 00000000000..2f72c1a8219 --- /dev/null +++ b/docs/cac/standards/TECHNICAL_SPEC_APPENDIX.md @@ -0,0 +1,24 @@ +# Technical Spec Appendix + +## Canonical Bundle Contents + +- `cac.json`: Admissibility verdict payload. +- `cacert.json`: Signed certification artifact. +- `sbom.json`: Software bill of materials snapshot. +- `provenance.json`: Deterministic digest + generation metadata. + +## Determinism Requirements + +1. Canonical key ordering for JSON serialization. +2. Stable line endings and UTF-8 encoding. +3. Deterministic hash algorithm declaration. +4. No volatile timestamps inside signed evidence scope. + +## Verification Interface + +Audit tools SHOULD output: + +- overall compliance status, +- control-level failures, +- missing evidence, +- non-determinism alerts. diff --git a/docs/roadmap/STATUS.json b/docs/roadmap/STATUS.json index 26d6924bff2..363a996ece0 100644 --- a/docs/roadmap/STATUS.json +++ b/docs/roadmap/STATUS.json @@ -1,6 +1,6 @@ { - "last_updated": "2026-04-03T00:00:00Z", - "revision_note": "Added the canonical Decision Object v1 schema package, example payload, and standards documentation to anchor CAC-bound decision interoperability and external verification workflows.", + "last_updated": "2026-03-31T00:00:00Z", + "revision_note": "Extended CAC from certification into auditable compliance controls with deterministic attestation bundling and offline audit verification.", "initiatives": [ { "id": "one-verified-workflow-lane", @@ -60,7 +60,7 @@ "id": "provable-system-governance-provenance-unification", "status": "in_progress", "owner": "codex", - "notes": "Implementation-ready governance, provenance, isolation, sovereignty, and ATO-native evidence bundle specifications are published and awaiting narrowed execution through one golden workflow. Published C2PA-aligned CAC Decision Manifest profile and external verification contract for admissible cognition artifacts." + "notes": "Implementation-ready governance, provenance, isolation, sovereignty, and ATO-native evidence bundle specifications are published and awaiting narrowed execution through one golden workflow." }, { "id": "antigravity-multi-agent-ga-convergence", @@ -69,16 +69,16 @@ "notes": "Multi-agent prompt suites, bounded charters, and router activation are in place, but GA still depends on proving one deterministic closed loop rather than widening orchestration." }, { - "id": "decision-object-canonicalization", - "status": "completed", + "id": "cac-regulatory-audit-insertion", + "status": "in_progress", "owner": "codex", - "notes": "Published schemas/decision-object.schema.json plus a complete example and standards profile for CAC-bound deterministic verification." + "notes": "Published SOC2/NIST/ISO mappings, auditor playbook, policy language pack, standards submission docs, deterministic attestation generator, and CAC audit CLI verification command." } ], "summary": { "total_initiatives": 12, - "completed": 5, - "in_progress": 7, + "completed": 4, + "in_progress": 8, "at_risk": 0 } } diff --git a/package.json b/package.json index e6b4ee41ad0..d7bd889ce47 100644 --- a/package.json +++ b/package.json @@ -195,22 +195,15 @@ "eval:authority-metrics": "node scripts/eval/authority-metrics.mjs", "prepare": "husky", "gate:schema": "node scripts/gates/validate_schemas.mjs", - "ci:model-eval": "tsx scripts/ci/check-model-eval.ts", "test:contracts:responses-sse": "node --test tools/contracts/test/openai-responses-sse-conformance.spec.mjs", "contracts:responses-sse": "node tools/contracts/openai-responses-sse-conformance.mjs", "ci:contracts:responses-sse": "node scripts/ci/check_openai_responses_sse_contract.mjs", - "prediction-market:artifacts": "node scripts/prediction-market/generate-artifacts.mjs", - "test:prediction-market": "node --import tsx --test tests/prediction-market/*.test.ts", "ga:certify": "npx tsx scripts/benchmarks/run_perf.ts && npx tsx scripts/ga-validator.ts", "ga:sentinel": "node scripts/drift-sentinel.mjs", "ga:report": "npx tsx scripts/generate-compliance-report.ts", "ga:finish": "rm -rf scripts/ga-final-walkthrough.sh scripts/failure-injector.mjs signals/ *.bak && echo '🏁 RELEASE SEALTED. GA PILOT LIVE.' ", - "verify:ingestion": "node scripts/ingestion/verify-ingestion.mjs", - "redteam:run": "tsx services/redteam/index.ts", - "redteam:test": "node --import tsx --test tests/redteam/*.test.ts", - "benchmark:agentic-portfolio": "node .github/scripts/run-agentic-portfolio-benchmark.mjs", - "dist:report": "node scripts/distribution/generate-report.mjs", - "ci:check-distribution": "node scripts/ci/check-distribution.mjs" + "attestation:generate": "npx tsx packages/attestation/generator.ts artifacts/attestation", + "cac:audit": "node tools/cac-cli/index.mjs audit artifacts/attestation" }, "keywords": [ "intelligence-analysis", diff --git a/packages/attestation/generator.ts b/packages/attestation/generator.ts new file mode 100644 index 00000000000..9b6746f2666 --- /dev/null +++ b/packages/attestation/generator.ts @@ -0,0 +1,87 @@ +import { createHash } from 'node:crypto'; +import { mkdir, readFile, writeFile } from 'node:fs/promises'; +import path from 'node:path'; + +const REQUIRED_FILES = ['cac.json', 'cacert.json', 'sbom.json'] as const; +const DIGEST_FILES = ['cac.json', 'sbom.json'] as const; + +type RequiredFile = (typeof REQUIRED_FILES)[number]; + +function stable(value: unknown): unknown { + if (Array.isArray(value)) { + return value.map((entry) => stable(entry)); + } + + if (value && typeof value === 'object') { + const input = value as Record; + const output: Record = {}; + for (const key of Object.keys(input).sort()) { + output[key] = stable(input[key]); + } + return output; + } + + return value; +} + +function stableStringify(value: unknown): string { + return `${JSON.stringify(stable(value), null, 2)}\n`; +} + +async function readJson(filePath: string): Promise { + const contents = await readFile(filePath, 'utf8'); + return JSON.parse(contents) as unknown; +} + +export interface GenerateBundleResult { + digest: string; + outputDir: string; +} + +export async function generateAttestationBundle(outputDir: string): Promise { + const normalizedOutputDir = path.resolve(outputDir); + await mkdir(normalizedOutputDir, { recursive: true }); + + const digestParts: string[] = []; + for (const filename of REQUIRED_FILES) { + const sourcePath = path.resolve('artifacts/attestation', filename); + const payload = await readJson(sourcePath); + const canonical = stableStringify(payload); + await writeFile(path.join(normalizedOutputDir, filename), canonical, 'utf8'); + if (DIGEST_FILES.includes(filename as (typeof DIGEST_FILES)[number])) { + digestParts.push(`${filename}:${canonical}`); + } + } + + const digest = createHash('sha256').update(digestParts.join(''), 'utf8').digest('hex'); + const provenance = { + bundle_version: '1.0.0', + digest, + hash_algorithm: 'sha256', + generated_by: 'packages/attestation/generator.ts', + required_files: [...REQUIRED_FILES], + }; + + await writeFile(path.join(normalizedOutputDir, 'provenance.json'), stableStringify(provenance), 'utf8'); + + return { + digest, + outputDir: normalizedOutputDir, + }; +} + +if (import.meta.url === `file://${process.argv[1]}`) { + const target = process.argv[2] ?? 'artifacts/attestation'; + generateAttestationBundle(target) + .then(({ digest, outputDir }) => { + process.stdout.write(`attestation_bundle=${outputDir}\n`); + process.stdout.write(`digest=${digest}\n`); + }) + .catch((error: unknown) => { + const message = error instanceof Error ? error.message : String(error); + process.stderr.write(`attestation generation failed: ${message}\n`); + process.exitCode = 1; + }); +} + +export type { RequiredFile }; diff --git a/tools/cac-cli/index.mjs b/tools/cac-cli/index.mjs new file mode 100644 index 00000000000..4a52a8ccfe1 --- /dev/null +++ b/tools/cac-cli/index.mjs @@ -0,0 +1,121 @@ +#!/usr/bin/env node +import { createHash } from 'node:crypto'; +import { readFile } from 'node:fs/promises'; +import path from 'node:path'; + +const REQUIRED = ['cac.json', 'cacert.json', 'sbom.json', 'provenance.json']; + +function stable(value) { + if (Array.isArray(value)) { + return value.map((entry) => stable(entry)); + } + + if (value && typeof value === 'object') { + const output = {}; + for (const key of Object.keys(value).sort()) { + output[key] = stable(value[key]); + } + return output; + } + + return value; +} + +function stableStringify(value) { + return `${JSON.stringify(stable(value), null, 2)}\n`; +} + +async function loadBundle(bundlePath) { + const normalized = path.resolve(bundlePath); + const missing = []; + const files = {}; + + for (const filename of REQUIRED) { + const fullPath = path.join(normalized, filename); + try { + const content = await readFile(fullPath, 'utf8'); + files[filename] = JSON.parse(content); + } catch { + missing.push(filename); + } + } + + return { files, missing, normalized }; +} + +function computeDigest(files) { + const ordered = ['cac.json', 'sbom.json']; + const canonical = ordered + .map((filename) => `${filename}:${stableStringify(files[filename])}`) + .join(''); + return createHash('sha256').update(canonical, 'utf8').digest('hex'); +} + +function evaluateControls(files, missing) { + const failures = []; + if (missing.length > 0) { + failures.push(`missing_artifacts:${missing.join(',')}`); + } + + const cac = files['cac.json']; + const cacert = files['cacert.json']; + const provenance = files['provenance.json']; + + if (cac && cac.risk_level === 'high' && !cacert) { + failures.push('high_risk_requires_cacert'); + } + + if (cac && cacert && Array.isArray(cac.policy_failures) && Array.isArray(cacert.policy_refs)) { + const missingPolicies = cac.policy_failures.filter((policy) => !cacert.policy_refs.includes(policy)); + if (missingPolicies.length > 0) { + failures.push(`policy_refs_incomplete:${missingPolicies.join(',')}`); + } + } + + if (cacert && provenance && cacert.evidence_hash !== provenance.digest) { + failures.push('cacert_provenance_hash_mismatch'); + } + + return failures; +} + +async function audit(bundlePath) { + const { files, missing, normalized } = await loadBundle(bundlePath); + const failures = evaluateControls(files, missing); + + let computedDigest = null; + if (missing.length === 0) { + computedDigest = computeDigest(files); + if (files['provenance.json'].digest !== computedDigest) { + failures.push('provenance_digest_mismatch'); + } + } + + const status = failures.length === 0 ? 'COMPLIANT' : 'NON_COMPLIANT'; + const output = { + command: 'cac audit', + bundle: normalized, + compliance_status: status, + missing_evidence: missing, + failed_controls: failures, + computed_digest: computedDigest, + }; + + process.stdout.write(`${JSON.stringify(output, null, 2)}\n`); + if (status !== 'COMPLIANT') { + process.exitCode = 2; + } +} + +async function main() { + const [command, arg] = process.argv.slice(2); + if (command === 'audit' && arg) { + await audit(arg); + return; + } + + process.stderr.write('Usage: cac audit \n'); + process.exitCode = 1; +} + +main();