@quantakrypto/core 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -56,6 +56,8 @@ export { buildReadinessReport, signReadinessReport, verifyReadinessReport } from
56
56
  export type { ReadinessReport, EvidenceFinding, ReadinessReportOptions, EvidenceSigner, SignEvidenceOptions, VerifyReadinessResult, } from "./evidence.js";
57
57
  export { buildPolicyMapping, parseCryptoPolicy } from "./policy.js";
58
58
  export type { CryptoPolicy, PolicyVerdict, PolicyMapping, PolicyFindingVerdict } from "./policy.js";
59
+ export { MANDATES, mandateIds, getMandate, assertKnownMandates, evaluateMandates, mandateGateFails, } from "./mandates.js";
60
+ export type { Mandate, MandateRule, MandateRuleTier, MandateStatus, MandateFindingVerdict, MandateEvaluation, MandateGateOptions, } from "./mandates.js";
59
61
  export { remediationFor, remediationForTier, remediationForProfile, TIER_PARAMS, STATEFUL_HBS_NOTE, PQC_TRANSITION_NOTE, statefulHbsApplies, } from "./remediation.js";
60
62
  export type { SecurityTier } from "./remediation.js";
61
63
  export { PQC_STANDARDS } from "./standards.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -64,6 +64,8 @@ export { buildCryptoAgilityManifest, validateCryptoAgilityManifest, CRYPTO_AGILI
64
64
  export { buildReadinessReport, signReadinessReport, verifyReadinessReport } from "./evidence.js";
65
65
  // Cryptography policy → per-finding verdicts (A.8.24 evidence §4).
66
66
  export { buildPolicyMapping, parseCryptoPolicy } from "./policy.js";
67
+ // Compliance mandates → dated, clause-named verdicts + gate (policy-as-code).
68
+ export { MANDATES, mandateIds, getMandate, assertKnownMandates, evaluateMandates, mandateGateFails, } from "./mandates.js";
67
69
  // Remediation lookup (family + tier-aware + profile-aware) and stateful-HBS guidance.
68
70
  export { remediationFor, remediationForTier, remediationForProfile, TIER_PARAMS, STATEFUL_HBS_NOTE, PQC_TRANSITION_NOTE, statefulHbsApplies, } from "./remediation.js";
69
71
  // Post-quantum standards source of truth + review cadence.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @quantakrypto/core — public API (LOCKED CONTRACT).\n *\n * The exported NAMES and SIGNATURES below are the stable contract that\n * @quantakrypto/qscan, @quantakrypto/mcp and the GitHub Action depend on — do not change\n * them without updating all consumers. The implementations live in focused\n * modules under src/ and are re-exported here; the public surface is identical\n * to the original stub file.\n */\nexport * from \"./types.js\";\n\n// Tool version, surfaced in reports. Keep in sync with package.json.\nexport { VERSION } from \"./version.js\";\n\n// Minimal SARIF 2.1.0 log shape, defined alongside the reporters.\nexport type { SarifLog } from \"./report.js\";\n\n// Core orchestration + built-in detector set.\nexport { scan, detectors, detectFile, compareFindings } from \"./scan.js\";\n\n// Snippet-level fix verification (shared by MCP verify_fix + remediation).\nexport { verifyFix, languageToExtension } from \"./verify.js\";\nexport type { VerifyResult } from \"./verify.js\";\n\n// Agent-plane shared types + the context redactor (offline; reused by MCP).\nexport type {\n ContextLevel,\n RedactedContext,\n TriageVerdict,\n Patch,\n FixProposal,\n} from \"./agent-types.js\";\nexport { buildContext, renderPreflight } from \"./redact.js\";\nexport { TRIAGE_RUBRIC, TRIAGE_VERDICT_SCHEMA, buildTriageRequest } from \"./triage.js\";\nexport type { TriageRequest } from \"./triage.js\";\nexport {\n REMEDIATE_RUBRIC,\n FIX_REQUEST_SCHEMA,\n buildRemediateRequest,\n} from \"./remediate-request.js\";\nexport type { RemediateRequest } from \"./remediate-request.js\";\nexport { checkPatchPolicy } from \"./patch-policy.js\";\nexport type { PolicyContext, PolicyDecision } from \"./patch-policy.js\";\nexport { withWorktree } from \"./worktree.js\";\nexport { codemodRegistry, codemodFor } from \"./codemods/registry.js\";\nexport type { Codemod } from \"./codemods/registry.js\";\nexport { configToggleCodemod } from \"./codemods/config-toggle.js\";\nexport { remediateFindings } from \"./remediate-pipeline.js\";\nexport type {\n RemediateOptions,\n RemediationResult,\n VerifiedPatch,\n RejectedPatch,\n} from \"./remediate-pipeline.js\";\n\n// Scan cancellation / work-budget errors.\nexport { AbortError, BudgetExceededError } from \"./errors.js\";\n\n// Parallel scanning (worker_threads pool). The chunk/merge helpers and their\n// SizedFile/ChunkResult types are internal plumbing (not part of the public API).\nexport { scanParallel } from \"./parallel.js\";\nexport type { ScanChunk } from \"./parallel.js\";\n\n// Detector registry (plugin point). `detectorScope` and the rule-catalog type are\n// internal.\nexport { DetectorRegistry, defaultRegistry } from \"./registry.js\";\n\n// Canonical baseline (shared by qScan + the Action).\nexport {\n fingerprintFinding,\n baselineFromFindings,\n applyBaseline,\n loadBaseline,\n saveBaseline,\n BASELINE_VERSION,\n} from \"./baseline.js\";\nexport type { Baseline } from \"./baseline.js\";\n\n// Incremental scanning: changed-files helper (git-aware, tolerant).\nexport { changedFiles } from \"./changed.js\";\n\n// Optional `quantakrypto.config.json` loader (P2-9; see docs/CONFIG.md).\nexport { loadConfig, ConfigError, CONFIG_FILENAME } from \"./config.js\";\nexport type { QuantakryptoFileConfig, LoadConfigResult } from \"./config.js\";\n\n// HNDL (harvest-now-decrypt-later) data-risk quantifier (see docs/HNDL.md).\nexport {\n HNDL_MODEL_VERSION,\n HNDL_FILENAME,\n SEVERITY_VULNERABILITY,\n CONFIDENCE_WEIGHT,\n CLASSIFICATION_SENSITIVITY,\n NON_HNDL_DISCOUNT,\n DEFAULT_QUANTUM_THREAT_YEARS,\n DEFAULT_MIGRATION_HORIZON_YEARS,\n DEFAULT_UNBOUND_CLASSIFICATION,\n HndlError,\n computeHndl,\n vulnerabilityFactor,\n moscaFactor,\n globMatch,\n findingFingerprint,\n findingScope,\n parseHndlMap,\n loadHndlMap,\n scaffoldHndlYaml,\n} from \"./hndl.js\";\nexport type {\n DataClassification,\n HndlScope,\n HndlDataAsset,\n HndlHorizon,\n HndlDefaults,\n HndlMap,\n ExposureRationale,\n FindingExposure,\n AssetExposure,\n HndlSummary,\n HndlReport,\n} from \"./hndl.js\";\n\n// Filesystem walker (relative POSIX paths, default ignores, size/binary filters).\nexport { walkFiles, isBinaryPath, looksMinified } from \"./walk.js\";\n\n// Analyzable-language coverage (which source languages the scanner inspects).\nexport {\n isAnalyzableSource,\n ANALYZABLE_SOURCE_EXTENSIONS,\n ANALYZABLE_LANGUAGES_LABEL,\n} from \"./detect-utils.js\";\n\n// Inventory + readiness score.\nexport { buildInventory } from \"./inventory.js\";\n\n// Vulnerable-dependency database (the manifest scanner is used internally by scan()).\n// `DEP_VULNERABLE_RULE` is the generic catalog entry for dependency findings\n// (which don't come from a Detector, so aren't in the registry's rule catalog).\nexport { vulnerableDependencies, DEP_VULNERABLE_RULE, isManifestFile } from \"./dependencies.js\";\n\n// Severity utilities (ordering, threshold, SARIF level) — shared across tools.\nexport { SEVERITY_ORDER, severityRank, meetsThreshold, sarifLevel } from \"./severity.js\";\n\n// Reporters.\nexport {\n toSarif,\n toJson,\n formatSummary,\n formatTierGuidance,\n formatProfileGuidance,\n} from \"./report.js\";\nexport type { ReportOptions } from \"./report.js\";\n\n// CycloneDX 1.6 cryptographic bill of materials (CBOM) export.\nexport { toCbom } from \"./cbom.js\";\nexport type { CycloneDxBom, CbomComponent } from \"./cbom.js\";\n// Merge multiple CBOMs (code + infra + live endpoints) into one combined BOM.\nexport { mergeCboms } from \"./cbom-merge.js\";\n// OpenVEX 0.2.0 export — quantum-readiness posture as VEX statements.\nexport { toOpenVex } from \"./vex.js\";\nexport type { OpenVexDocument, OpenVexStatement, OpenVexOptions } from \"./vex.js\";\n// Crypto-agility manifest: agent-consumable crypto-posture document.\nexport {\n buildCryptoAgilityManifest,\n validateCryptoAgilityManifest,\n CRYPTO_AGILITY_MANIFEST_VERSION,\n CRYPTO_AGILITY_WELL_KNOWN_PATH,\n} from \"./crypto-agility.js\";\nexport type {\n CryptoAgilityManifest,\n CryptoAgilityManifestOptions,\n CryptoAgilityPosture,\n CryptoAgilityCbomSummary,\n CryptoAgilityFamily,\n CryptoAgilityPolicy,\n ManifestValidation,\n} from \"./crypto-agility.js\";\n// ISO/IEC 27001 A.8.24 evidence-chain readiness report.\nexport { buildReadinessReport, signReadinessReport, verifyReadinessReport } from \"./evidence.js\";\nexport type {\n ReadinessReport,\n EvidenceFinding,\n ReadinessReportOptions,\n EvidenceSigner,\n SignEvidenceOptions,\n VerifyReadinessResult,\n} from \"./evidence.js\";\n\n// Cryptography policy → per-finding verdicts (A.8.24 evidence §4).\nexport { buildPolicyMapping, parseCryptoPolicy } from \"./policy.js\";\nexport type { CryptoPolicy, PolicyVerdict, PolicyMapping, PolicyFindingVerdict } from \"./policy.js\";\n\n// Remediation lookup (family + tier-aware + profile-aware) and stateful-HBS guidance.\nexport {\n remediationFor,\n remediationForTier,\n remediationForProfile,\n TIER_PARAMS,\n STATEFUL_HBS_NOTE,\n PQC_TRANSITION_NOTE,\n statefulHbsApplies,\n} from \"./remediation.js\";\nexport type { SecurityTier } from \"./remediation.js\";\n\n// Post-quantum standards source of truth + review cadence.\nexport { PQC_STANDARDS } from \"./standards.js\";\nexport type { PqcStandards, StandardsCitation, StandardsReviewStatus } from \"./standards.js\";\n\n// Selectable standards regime profiles (NIST / CNSA / BSI / ANSSI / NCSC).\nexport {\n STANDARDS_PROFILES,\n DEFAULT_PROFILE_ID,\n standardsProfileIds,\n getStandardsProfile,\n defaultStandardsProfile,\n} from \"./standards-profiles.js\";\nexport type { StandardsProfile, HybridStance } from \"./standards-profiles.js\";\n\n// CWE identifier constants.\nexport {\n CWE_BROKEN_CRYPTO,\n CWE_WEAK_STRENGTH,\n CWE_CERT_VALIDATION,\n CWE_HARDCODED_KEY,\n CWE_RISKY_PRIMITIVE,\n} from \"./cwe.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @quantakrypto/core — public API (LOCKED CONTRACT).\n *\n * The exported NAMES and SIGNATURES below are the stable contract that\n * @quantakrypto/qscan, @quantakrypto/mcp and the GitHub Action depend on — do not change\n * them without updating all consumers. The implementations live in focused\n * modules under src/ and are re-exported here; the public surface is identical\n * to the original stub file.\n */\nexport * from \"./types.js\";\n\n// Tool version, surfaced in reports. Keep in sync with package.json.\nexport { VERSION } from \"./version.js\";\n\n// Minimal SARIF 2.1.0 log shape, defined alongside the reporters.\nexport type { SarifLog } from \"./report.js\";\n\n// Core orchestration + built-in detector set.\nexport { scan, detectors, detectFile, compareFindings } from \"./scan.js\";\n\n// Snippet-level fix verification (shared by MCP verify_fix + remediation).\nexport { verifyFix, languageToExtension } from \"./verify.js\";\nexport type { VerifyResult } from \"./verify.js\";\n\n// Agent-plane shared types + the context redactor (offline; reused by MCP).\nexport type {\n ContextLevel,\n RedactedContext,\n TriageVerdict,\n Patch,\n FixProposal,\n} from \"./agent-types.js\";\nexport { buildContext, renderPreflight } from \"./redact.js\";\nexport { TRIAGE_RUBRIC, TRIAGE_VERDICT_SCHEMA, buildTriageRequest } from \"./triage.js\";\nexport type { TriageRequest } from \"./triage.js\";\nexport {\n REMEDIATE_RUBRIC,\n FIX_REQUEST_SCHEMA,\n buildRemediateRequest,\n} from \"./remediate-request.js\";\nexport type { RemediateRequest } from \"./remediate-request.js\";\nexport { checkPatchPolicy } from \"./patch-policy.js\";\nexport type { PolicyContext, PolicyDecision } from \"./patch-policy.js\";\nexport { withWorktree } from \"./worktree.js\";\nexport { codemodRegistry, codemodFor } from \"./codemods/registry.js\";\nexport type { Codemod } from \"./codemods/registry.js\";\nexport { configToggleCodemod } from \"./codemods/config-toggle.js\";\nexport { remediateFindings } from \"./remediate-pipeline.js\";\nexport type {\n RemediateOptions,\n RemediationResult,\n VerifiedPatch,\n RejectedPatch,\n} from \"./remediate-pipeline.js\";\n\n// Scan cancellation / work-budget errors.\nexport { AbortError, BudgetExceededError } from \"./errors.js\";\n\n// Parallel scanning (worker_threads pool). The chunk/merge helpers and their\n// SizedFile/ChunkResult types are internal plumbing (not part of the public API).\nexport { scanParallel } from \"./parallel.js\";\nexport type { ScanChunk } from \"./parallel.js\";\n\n// Detector registry (plugin point). `detectorScope` and the rule-catalog type are\n// internal.\nexport { DetectorRegistry, defaultRegistry } from \"./registry.js\";\n\n// Canonical baseline (shared by qScan + the Action).\nexport {\n fingerprintFinding,\n baselineFromFindings,\n applyBaseline,\n loadBaseline,\n saveBaseline,\n BASELINE_VERSION,\n} from \"./baseline.js\";\nexport type { Baseline } from \"./baseline.js\";\n\n// Incremental scanning: changed-files helper (git-aware, tolerant).\nexport { changedFiles } from \"./changed.js\";\n\n// Optional `quantakrypto.config.json` loader (P2-9; see docs/CONFIG.md).\nexport { loadConfig, ConfigError, CONFIG_FILENAME } from \"./config.js\";\nexport type { QuantakryptoFileConfig, LoadConfigResult } from \"./config.js\";\n\n// HNDL (harvest-now-decrypt-later) data-risk quantifier (see docs/HNDL.md).\nexport {\n HNDL_MODEL_VERSION,\n HNDL_FILENAME,\n SEVERITY_VULNERABILITY,\n CONFIDENCE_WEIGHT,\n CLASSIFICATION_SENSITIVITY,\n NON_HNDL_DISCOUNT,\n DEFAULT_QUANTUM_THREAT_YEARS,\n DEFAULT_MIGRATION_HORIZON_YEARS,\n DEFAULT_UNBOUND_CLASSIFICATION,\n HndlError,\n computeHndl,\n vulnerabilityFactor,\n moscaFactor,\n globMatch,\n findingFingerprint,\n findingScope,\n parseHndlMap,\n loadHndlMap,\n scaffoldHndlYaml,\n} from \"./hndl.js\";\nexport type {\n DataClassification,\n HndlScope,\n HndlDataAsset,\n HndlHorizon,\n HndlDefaults,\n HndlMap,\n ExposureRationale,\n FindingExposure,\n AssetExposure,\n HndlSummary,\n HndlReport,\n} from \"./hndl.js\";\n\n// Filesystem walker (relative POSIX paths, default ignores, size/binary filters).\nexport { walkFiles, isBinaryPath, looksMinified } from \"./walk.js\";\n\n// Analyzable-language coverage (which source languages the scanner inspects).\nexport {\n isAnalyzableSource,\n ANALYZABLE_SOURCE_EXTENSIONS,\n ANALYZABLE_LANGUAGES_LABEL,\n} from \"./detect-utils.js\";\n\n// Inventory + readiness score.\nexport { buildInventory } from \"./inventory.js\";\n\n// Vulnerable-dependency database (the manifest scanner is used internally by scan()).\n// `DEP_VULNERABLE_RULE` is the generic catalog entry for dependency findings\n// (which don't come from a Detector, so aren't in the registry's rule catalog).\nexport { vulnerableDependencies, DEP_VULNERABLE_RULE, isManifestFile } from \"./dependencies.js\";\n\n// Severity utilities (ordering, threshold, SARIF level) — shared across tools.\nexport { SEVERITY_ORDER, severityRank, meetsThreshold, sarifLevel } from \"./severity.js\";\n\n// Reporters.\nexport {\n toSarif,\n toJson,\n formatSummary,\n formatTierGuidance,\n formatProfileGuidance,\n} from \"./report.js\";\nexport type { ReportOptions } from \"./report.js\";\n\n// CycloneDX 1.6 cryptographic bill of materials (CBOM) export.\nexport { toCbom } from \"./cbom.js\";\nexport type { CycloneDxBom, CbomComponent } from \"./cbom.js\";\n// Merge multiple CBOMs (code + infra + live endpoints) into one combined BOM.\nexport { mergeCboms } from \"./cbom-merge.js\";\n// OpenVEX 0.2.0 export — quantum-readiness posture as VEX statements.\nexport { toOpenVex } from \"./vex.js\";\nexport type { OpenVexDocument, OpenVexStatement, OpenVexOptions } from \"./vex.js\";\n// Crypto-agility manifest: agent-consumable crypto-posture document.\nexport {\n buildCryptoAgilityManifest,\n validateCryptoAgilityManifest,\n CRYPTO_AGILITY_MANIFEST_VERSION,\n CRYPTO_AGILITY_WELL_KNOWN_PATH,\n} from \"./crypto-agility.js\";\nexport type {\n CryptoAgilityManifest,\n CryptoAgilityManifestOptions,\n CryptoAgilityPosture,\n CryptoAgilityCbomSummary,\n CryptoAgilityFamily,\n CryptoAgilityPolicy,\n ManifestValidation,\n} from \"./crypto-agility.js\";\n// ISO/IEC 27001 A.8.24 evidence-chain readiness report.\nexport { buildReadinessReport, signReadinessReport, verifyReadinessReport } from \"./evidence.js\";\nexport type {\n ReadinessReport,\n EvidenceFinding,\n ReadinessReportOptions,\n EvidenceSigner,\n SignEvidenceOptions,\n VerifyReadinessResult,\n} from \"./evidence.js\";\n\n// Cryptography policy → per-finding verdicts (A.8.24 evidence §4).\nexport { buildPolicyMapping, parseCryptoPolicy } from \"./policy.js\";\nexport type { CryptoPolicy, PolicyVerdict, PolicyMapping, PolicyFindingVerdict } from \"./policy.js\";\n\n// Compliance mandates → dated, clause-named verdicts + gate (policy-as-code).\nexport {\n MANDATES,\n mandateIds,\n getMandate,\n assertKnownMandates,\n evaluateMandates,\n mandateGateFails,\n} from \"./mandates.js\";\nexport type {\n Mandate,\n MandateRule,\n MandateRuleTier,\n MandateStatus,\n MandateFindingVerdict,\n MandateEvaluation,\n MandateGateOptions,\n} from \"./mandates.js\";\n\n// Remediation lookup (family + tier-aware + profile-aware) and stateful-HBS guidance.\nexport {\n remediationFor,\n remediationForTier,\n remediationForProfile,\n TIER_PARAMS,\n STATEFUL_HBS_NOTE,\n PQC_TRANSITION_NOTE,\n statefulHbsApplies,\n} from \"./remediation.js\";\nexport type { SecurityTier } from \"./remediation.js\";\n\n// Post-quantum standards source of truth + review cadence.\nexport { PQC_STANDARDS } from \"./standards.js\";\nexport type { PqcStandards, StandardsCitation, StandardsReviewStatus } from \"./standards.js\";\n\n// Selectable standards regime profiles (NIST / CNSA / BSI / ANSSI / NCSC).\nexport {\n STANDARDS_PROFILES,\n DEFAULT_PROFILE_ID,\n standardsProfileIds,\n getStandardsProfile,\n defaultStandardsProfile,\n} from \"./standards-profiles.js\";\nexport type { StandardsProfile, HybridStance } from \"./standards-profiles.js\";\n\n// CWE identifier constants.\nexport {\n CWE_BROKEN_CRYPTO,\n CWE_WEAK_STRENGTH,\n CWE_CERT_VALIDATION,\n CWE_HARDCODED_KEY,\n CWE_RISKY_PRIMITIVE,\n} from \"./cwe.js\";\n"]}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Policy-as-code compliance mandates → dated, clause-named verdicts for findings.
3
+ *
4
+ * `CryptoPolicy` (policy.ts) classifies findings by algorithm family but is
5
+ * date-blind. A mandate adds the missing dimension: named clauses with an effective
6
+ * DATE ("CNSA 2.0 disallows classical public-key crypto after 2035"). The evaluator
7
+ * compares each finding's algorithm against the selected mandates and today's date,
8
+ * so a finding on a prohibited family reads as `due` (every deadline still ahead),
9
+ * `deprecated` (the DEPRECATE deadline has passed — a warning), or `violation` (the
10
+ * DISALLOW deadline has passed — a failure), always naming the governing clause,
11
+ * deadline, and citation. This is what turns the inventory into an enforceable,
12
+ * mandate-mapped gate rather than a neutral list.
13
+ *
14
+ * Pure and deterministic (the caller supplies `now`), so it is trivially testable.
15
+ * qScan consumes it today for the `--mandate` gate; because it operates on the
16
+ * shared `Finding[]`, qProbe or the GitHub Action can reuse it unchanged.
17
+ *
18
+ * Catalog scope: the two regimes that carry hard algorithm deadlines — CNSA 2.0 and
19
+ * NIST IR 8547. DORA / NIS2 / PCI DSS require approved cryptography but set no
20
+ * independent algorithm date; they inherit these timelines and are cited in docs.
21
+ */
22
+ import type { AlgorithmFamily, Finding } from "./types.js";
23
+ /** Which enforcement tier a clause encodes: warn (`deprecate`) or fail (`disallow`). */
24
+ export type MandateRuleTier = "deprecate" | "disallow";
25
+ export interface MandateRule {
26
+ /** The named clause this rule encodes (verbatim in the gate's failure message). */
27
+ clause: string;
28
+ /** Enforcement tier: a passed `deprecate` date warns; a passed `disallow` date fails. */
29
+ tier: MandateRuleTier;
30
+ /** Algorithm families prohibited from the effective date onward. */
31
+ prohibits: AlgorithmFamily[];
32
+ /** ISO date (YYYY-MM-DD) the prohibition takes effect. */
33
+ effective: string;
34
+ /** One-line human description of the clause. */
35
+ note: string;
36
+ }
37
+ export interface Mandate {
38
+ id: string;
39
+ name: string;
40
+ authority: string;
41
+ citation: string;
42
+ /** When this catalog entry was last reviewed against the source. */
43
+ asOf: string;
44
+ rules: MandateRule[];
45
+ }
46
+ /** The bundled mandate catalog. Keyed by `--mandate <id>`. */
47
+ export declare const MANDATES: Record<string, Mandate>;
48
+ export declare const mandateIds: () => string[];
49
+ export declare const getMandate: (id: string) => Mandate | undefined;
50
+ /**
51
+ * Validate mandate ids loudly, matching `parseCryptoPolicy`'s fail-loud
52
+ * convention: a mistyped id must never silently evaluate to an empty gate.
53
+ * Throws an `Error` naming every unknown id and the known catalog.
54
+ * `evaluateMandates` itself stays lenient (unknown ids are skipped) so callers
55
+ * decide where to fail.
56
+ */
57
+ export declare function assertKnownMandates(ids: readonly string[]): void;
58
+ /**
59
+ * A finding's status against a mandate, worst last:
60
+ * - `conformant` — in scope (classical asymmetric) but prohibited by no selected
61
+ * clause (e.g. X25519 as the presumed hybrid leg).
62
+ * - `due` — prohibited, with every deadline still ahead.
63
+ * - `deprecated` — the DEPRECATE deadline has passed; a warning, not a failure.
64
+ * - `violation` — the DISALLOW deadline has passed; fails the default gate.
65
+ */
66
+ export type MandateStatus = "conformant" | "due" | "deprecated" | "violation";
67
+ export interface MandateFindingVerdict {
68
+ ruleId: string;
69
+ algorithm: AlgorithmFamily | "unknown";
70
+ file: string;
71
+ line: number;
72
+ /** Mandate id (e.g. "cnsa-2.0"). */
73
+ mandate: string;
74
+ /**
75
+ * The governing clause: the next upcoming clause when `due`, the passed
76
+ * DEPRECATE clause when `deprecated`, the passed DISALLOW clause on `violation`.
77
+ */
78
+ clause: string;
79
+ /** ISO effective date of the governing clause. */
80
+ effective: string;
81
+ status: MandateStatus;
82
+ /** Whole months from `now` to the governing deadline; negative once it has passed. */
83
+ monthsUntil: number;
84
+ /**
85
+ * ISO effective date of this mandate's DISALLOW clause for the family, or null
86
+ * when the mandate carries none. The gate's `leadMonths` measures against this.
87
+ */
88
+ disallowEffective: string | null;
89
+ /** Whole months from `now` to `disallowEffective`; null when there is none. */
90
+ monthsUntilDisallow: number | null;
91
+ citation: string;
92
+ }
93
+ export interface MandateEvaluation {
94
+ /** The `now` the evaluation was computed against (ISO). */
95
+ now: string;
96
+ /** Mandate ids evaluated. */
97
+ mandates: string[];
98
+ /**
99
+ * Counts of IN-SCOPE findings (classical asymmetric families) by their worst
100
+ * status across the selected mandates. Out-of-scope findings are excluded so
101
+ * the conformant count is an honest statement about asymmetric crypto only.
102
+ */
103
+ summary: Record<MandateStatus, number>;
104
+ /**
105
+ * Findings outside the mandate's scope (hashes, RNG, dependency, TLS-config…),
106
+ * which a PQC-asymmetric mandate does not adjudicate.
107
+ */
108
+ notInScope: number;
109
+ /** One row per (prohibited finding × applicable mandate). */
110
+ findings: MandateFindingVerdict[];
111
+ /** Earliest still-future deadline across non-violation rows, or null. */
112
+ nextDeadline: string | null;
113
+ /** True when at least one DISALLOW deadline has passed (a `violation` exists). */
114
+ hasViolation: boolean;
115
+ }
116
+ /**
117
+ * Evaluate findings against the selected mandates as of `now`. Unknown mandate
118
+ * ids are ignored — callers validate up front with {@link assertKnownMandates}.
119
+ * A finding outside the classical-asymmetric scope is counted in `notInScope`;
120
+ * an in-scope finding no selected mandate prohibits is `conformant`. Neither
121
+ * contributes a verdict row.
122
+ */
123
+ export declare function evaluateMandates(findings: readonly Finding[], mandateIdList: readonly string[], now: Date): MandateEvaluation;
124
+ export interface MandateGateOptions {
125
+ /** Fail when a DISALLOW deadline is within this many months (early enforcement). */
126
+ leadMonths?: number;
127
+ /** Fail on any mandate-prohibited finding regardless of the deadlines. */
128
+ failNow?: boolean;
129
+ }
130
+ /**
131
+ * The gate decision under the "deadline-aware" default: fail only once a DISALLOW
132
+ * deadline has passed (`violation`). A passed DEPRECATE date (`deprecated`) is a
133
+ * warning and does not fail the build. `leadMonths` fails early when a disallow
134
+ * deadline is within the window; `failNow` fails on any prohibited finding
135
+ * immediately.
136
+ */
137
+ export declare function mandateGateFails(ev: MandateEvaluation, opts?: MandateGateOptions): boolean;
138
+ //# sourceMappingURL=mandates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mandates.d.ts","sourceRoot":"","sources":["../src/mandates.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,228 @@
1
+ import { PQC_STANDARDS } from "./standards.js";
2
+ /**
3
+ * All Shor-broken classical asymmetric families — the mandate's SCOPE. A finding
4
+ * on one of these is adjudicated against the selected mandates; findings on
5
+ * anything else (hashes, RNG, dependency, or TLS-configuration findings) are out
6
+ * of scope for a PQC-asymmetric mandate and are tallied as `notInScope` instead
7
+ * of inflating the conformant count.
8
+ */
9
+ const CLASSICAL_PUBLIC_KEY = [
10
+ "RSA",
11
+ "ECDH",
12
+ "ECDSA",
13
+ "EdDSA",
14
+ "DH",
15
+ "DSA",
16
+ "X25519",
17
+ "X448",
18
+ "ECIES",
19
+ ];
20
+ /**
21
+ * The PROHIBITED subset the dated clauses apply to. X25519 and X448 are
22
+ * deliberately excluded: they are the classical half of the recommended hybrid
23
+ * key exchange (X25519MLKEM768 — permitted and recommended under the NIST
24
+ * profile), and a static scan cannot distinguish a standalone exchange from the
25
+ * hybrid's classical leg. Prohibiting them would false-positive exactly the orgs
26
+ * that hybridized correctly, so they stay in scope but read `conformant`.
27
+ */
28
+ const PROHIBITED_FAMILIES = CLASSICAL_PUBLIC_KEY.filter((family) => family !== "X25519" && family !== "X448");
29
+ /**
30
+ * Effective dates derived from the standards source of truth
31
+ * (`PQC_STANDARDS.transitionTimeline`), so a quarterly standards update moves the
32
+ * mandate deadlines automatically (test/standards.test.ts asserts they agree).
33
+ *
34
+ * Boundary choice: "deprecate AFTER 2030" leaves the whole stated year permitted,
35
+ * so each clause takes effect on the LAST day of its year (`YYYY-12-31`) —
36
+ * conservative by a single day, unlike `YYYY-01-01`, which would bite roughly a
37
+ * year early.
38
+ */
39
+ const { deprecateAfter, disallowAfter } = PQC_STANDARDS.transitionTimeline;
40
+ const DEPRECATE_EFFECTIVE = `${deprecateAfter}-12-31`;
41
+ const DISALLOW_EFFECTIVE = `${disallowAfter}-12-31`;
42
+ /** The bundled mandate catalog. Keyed by `--mandate <id>`. */
43
+ export const MANDATES = {
44
+ "cnsa-2.0": {
45
+ id: "cnsa-2.0",
46
+ name: "CNSA 2.0",
47
+ authority: "NSA",
48
+ citation: "NSA Commercial National Security Algorithm Suite 2.0 (CNSA 2.0)",
49
+ asOf: "2026-07",
50
+ rules: [
51
+ {
52
+ clause: `CNSA 2.0 — deprecate classical PKC after ${deprecateAfter}`,
53
+ tier: "deprecate",
54
+ prohibits: [...PROHIBITED_FAMILIES],
55
+ effective: DEPRECATE_EFFECTIVE,
56
+ note: "Classical public-key cryptography deprecated; systems should use CNSA 2.0 PQC exclusively.",
57
+ },
58
+ {
59
+ clause: `CNSA 2.0 — disallow classical PKC after ${disallowAfter}`,
60
+ tier: "disallow",
61
+ prohibits: [...PROHIBITED_FAMILIES],
62
+ effective: DISALLOW_EFFECTIVE,
63
+ note: "Classical public-key cryptography disallowed; the migration must be complete.",
64
+ },
65
+ ],
66
+ },
67
+ "nist-ir-8547": {
68
+ id: "nist-ir-8547",
69
+ name: "NIST IR 8547",
70
+ authority: "NIST",
71
+ citation: "NIST IR 8547 (Transition to Post-Quantum Cryptography Standards)",
72
+ asOf: "2026-07",
73
+ rules: [
74
+ {
75
+ clause: `NIST IR 8547 — deprecate classical PKC after ${deprecateAfter}`,
76
+ tier: "deprecate",
77
+ prohibits: [...PROHIBITED_FAMILIES],
78
+ effective: DEPRECATE_EFFECTIVE,
79
+ note: `112-bit-security classical public-key algorithms deprecated after ${deprecateAfter}.`,
80
+ },
81
+ {
82
+ clause: `NIST IR 8547 — disallow classical PKC after ${disallowAfter}`,
83
+ tier: "disallow",
84
+ prohibits: [...PROHIBITED_FAMILIES],
85
+ effective: DISALLOW_EFFECTIVE,
86
+ note: `Classical public-key algorithms disallowed after ${disallowAfter}.`,
87
+ },
88
+ ],
89
+ },
90
+ };
91
+ export const mandateIds = () => Object.keys(MANDATES);
92
+ export const getMandate = (id) => MANDATES[id];
93
+ /**
94
+ * Validate mandate ids loudly, matching `parseCryptoPolicy`'s fail-loud
95
+ * convention: a mistyped id must never silently evaluate to an empty gate.
96
+ * Throws an `Error` naming every unknown id and the known catalog.
97
+ * `evaluateMandates` itself stays lenient (unknown ids are skipped) so callers
98
+ * decide where to fail.
99
+ */
100
+ export function assertKnownMandates(ids) {
101
+ const unknown = ids.filter((id) => !MANDATES[id]);
102
+ if (unknown.length > 0) {
103
+ throw new Error(`unknown mandate id(s): ${unknown.join(", ")}; known mandates: ${mandateIds().join(", ")}`);
104
+ }
105
+ }
106
+ const MONTH_MS = 2_629_800_000; // average month
107
+ function monthsBetween(fromMs, toMs) {
108
+ return Math.round((toMs - fromMs) / MONTH_MS);
109
+ }
110
+ const STATUS_RANK = {
111
+ conformant: 0,
112
+ due: 1,
113
+ deprecated: 2,
114
+ violation: 3,
115
+ };
116
+ /**
117
+ * Evaluate findings against the selected mandates as of `now`. Unknown mandate
118
+ * ids are ignored — callers validate up front with {@link assertKnownMandates}.
119
+ * A finding outside the classical-asymmetric scope is counted in `notInScope`;
120
+ * an in-scope finding no selected mandate prohibits is `conformant`. Neither
121
+ * contributes a verdict row.
122
+ */
123
+ export function evaluateMandates(findings, mandateIdList, now) {
124
+ const nowMs = now.getTime();
125
+ const selected = mandateIdList.map(getMandate).filter((m) => Boolean(m));
126
+ const rows = [];
127
+ const perFindingWorst = [];
128
+ let notInScope = 0;
129
+ let nextDeadlineMs = null;
130
+ for (const f of findings) {
131
+ const algo = f.algorithm ?? "unknown";
132
+ if (!CLASSICAL_PUBLIC_KEY.includes(algo)) {
133
+ notInScope++;
134
+ continue;
135
+ }
136
+ let worst = "conformant";
137
+ for (const mandate of selected) {
138
+ // The applicable clauses for this family, earliest deadline first.
139
+ const applicable = mandate.rules
140
+ .filter((r) => r.prohibits.includes(algo))
141
+ .sort((a, b) => a.effective.localeCompare(b.effective));
142
+ if (applicable.length === 0)
143
+ continue;
144
+ // Tier the clauses so both stay live: a passed DISALLOW clause is a
145
+ // violation; a passed DEPRECATE clause (disallow still ahead) is the
146
+ // deprecated warning tier; otherwise the finding is due against the next
147
+ // upcoming clause.
148
+ const passed = applicable.filter((r) => nowMs >= new Date(r.effective).getTime());
149
+ const passedDisallow = passed.filter((r) => r.tier === "disallow");
150
+ let status;
151
+ let governing;
152
+ if (passedDisallow.length > 0) {
153
+ status = "violation";
154
+ governing = passedDisallow[0];
155
+ }
156
+ else if (passed.length > 0) {
157
+ status = "deprecated";
158
+ governing = passed[passed.length - 1];
159
+ }
160
+ else {
161
+ status = "due";
162
+ governing = applicable[0];
163
+ }
164
+ // The disallow clause (earliest, if several) anchors `leadMonths`.
165
+ const disallowRule = applicable.find((r) => r.tier === "disallow") ?? null;
166
+ const disallowMs = disallowRule ? new Date(disallowRule.effective).getTime() : null;
167
+ if (status !== "violation") {
168
+ for (const r of applicable) {
169
+ const effMs = new Date(r.effective).getTime();
170
+ if (effMs > nowMs && (nextDeadlineMs === null || effMs < nextDeadlineMs))
171
+ nextDeadlineMs = effMs;
172
+ }
173
+ }
174
+ if (STATUS_RANK[status] > STATUS_RANK[worst])
175
+ worst = status;
176
+ rows.push({
177
+ ruleId: f.ruleId,
178
+ algorithm: algo,
179
+ file: f.location.file,
180
+ line: f.location.line,
181
+ mandate: mandate.id,
182
+ clause: governing.clause,
183
+ effective: governing.effective,
184
+ status,
185
+ monthsUntil: monthsBetween(nowMs, new Date(governing.effective).getTime()),
186
+ disallowEffective: disallowRule ? disallowRule.effective : null,
187
+ monthsUntilDisallow: disallowMs !== null ? monthsBetween(nowMs, disallowMs) : null,
188
+ citation: mandate.citation,
189
+ });
190
+ }
191
+ perFindingWorst.push(worst);
192
+ }
193
+ const summary = {
194
+ conformant: 0,
195
+ due: 0,
196
+ deprecated: 0,
197
+ violation: 0,
198
+ };
199
+ for (const s of perFindingWorst)
200
+ summary[s]++;
201
+ return {
202
+ now: now.toISOString(),
203
+ mandates: selected.map((m) => m.id),
204
+ summary,
205
+ notInScope,
206
+ findings: rows,
207
+ nextDeadline: nextDeadlineMs !== null ? new Date(nextDeadlineMs).toISOString().slice(0, 10) : null,
208
+ hasViolation: summary.violation > 0,
209
+ };
210
+ }
211
+ /**
212
+ * The gate decision under the "deadline-aware" default: fail only once a DISALLOW
213
+ * deadline has passed (`violation`). A passed DEPRECATE date (`deprecated`) is a
214
+ * warning and does not fail the build. `leadMonths` fails early when a disallow
215
+ * deadline is within the window; `failNow` fails on any prohibited finding
216
+ * immediately.
217
+ */
218
+ export function mandateGateFails(ev, opts = {}) {
219
+ if (ev.hasViolation)
220
+ return true;
221
+ if (opts.failNow)
222
+ return ev.findings.length > 0;
223
+ if (opts.leadMonths !== undefined) {
224
+ return ev.findings.some((v) => v.monthsUntilDisallow !== null && v.monthsUntilDisallow <= opts.leadMonths);
225
+ }
226
+ return false;
227
+ }
228
+ //# sourceMappingURL=mandates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mandates.js","sourceRoot":"","sources":["../src/mandates.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C;;;;;;GAMG;AACH,MAAM,oBAAoB,GAA+B;IACvD,KAAK;IACL,MAAM;IACN,OAAO;IACP,OAAO;IACP,IAAI;IACJ,KAAK;IACL,QAAQ;IACR,MAAM;IACN,OAAO;CACR,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,mBAAmB,GAA+B,oBAAoB,CAAC,MAAM,CACjF,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,MAAM,CACrD,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,EAAE,cAAc,EAAE,aAAa,EAAE,GAAG,aAAa,CAAC,kBAAkB,CAAC;AAC3E,MAAM,mBAAmB,GAAG,GAAG,cAAc,QAAQ,CAAC;AACtD,MAAM,kBAAkB,GAAG,GAAG,aAAa,QAAQ,CAAC;AA4BpD,8DAA8D;AAC9D,MAAM,CAAC,MAAM,QAAQ,GAA4B;IAC/C,UAAU,EAAE;QACV,EAAE,EAAE,UAAU;QACd,IAAI,EAAE,UAAU;QAChB,SAAS,EAAE,KAAK;QAChB,QAAQ,EAAE,iEAAiE;QAC3E,IAAI,EAAE,SAAS;QACf,KAAK,EAAE;YACL;gBACE,MAAM,EAAE,4CAA4C,cAAc,EAAE;gBACpE,IAAI,EAAE,WAAW;gBACjB,SAAS,EAAE,CAAC,GAAG,mBAAmB,CAAC;gBACnC,SAAS,EAAE,mBAAmB;gBAC9B,IAAI,EAAE,4FAA4F;aACnG;YACD;gBACE,MAAM,EAAE,2CAA2C,aAAa,EAAE;gBAClE,IAAI,EAAE,UAAU;gBAChB,SAAS,EAAE,CAAC,GAAG,mBAAmB,CAAC;gBACnC,SAAS,EAAE,kBAAkB;gBAC7B,IAAI,EAAE,+EAA+E;aACtF;SACF;KACF;IACD,cAAc,EAAE;QACd,EAAE,EAAE,cAAc;QAClB,IAAI,EAAE,cAAc;QACpB,SAAS,EAAE,MAAM;QACjB,QAAQ,EAAE,kEAAkE;QAC5E,IAAI,EAAE,SAAS;QACf,KAAK,EAAE;YACL;gBACE,MAAM,EAAE,gDAAgD,cAAc,EAAE;gBACxE,IAAI,EAAE,WAAW;gBACjB,SAAS,EAAE,CAAC,GAAG,mBAAmB,CAAC;gBACnC,SAAS,EAAE,mBAAmB;gBAC9B,IAAI,EAAE,qEAAqE,cAAc,GAAG;aAC7F;YACD;gBACE,MAAM,EAAE,+CAA+C,aAAa,EAAE;gBACtE,IAAI,EAAE,UAAU;gBAChB,SAAS,EAAE,CAAC,GAAG,mBAAmB,CAAC;gBACnC,SAAS,EAAE,kBAAkB;gBAC7B,IAAI,EAAE,oDAAoD,aAAa,GAAG;aAC3E;SACF;KACF;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,GAAa,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAChE,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,EAAU,EAAuB,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAE5E;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAsB;IACxD,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IAClD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,0BAA0B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,qBAAqB,UAAU,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC3F,CAAC;IACJ,CAAC;AACH,CAAC;AA+DD,MAAM,QAAQ,GAAG,aAAa,CAAC,CAAC,gBAAgB;AAEhD,SAAS,aAAa,CAAC,MAAc,EAAE,IAAY;IACjD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,WAAW,GAAkC;IACjD,UAAU,EAAE,CAAC;IACb,GAAG,EAAE,CAAC;IACN,UAAU,EAAE,CAAC;IACb,SAAS,EAAE,CAAC;CACb,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAA4B,EAC5B,aAAgC,EAChC,GAAS;IAET,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAgB,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAEvF,MAAM,IAAI,GAA4B,EAAE,CAAC;IACzC,MAAM,eAAe,GAAoB,EAAE,CAAC;IAC5C,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,cAAc,GAAkB,IAAI,CAAC;IAEzC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,CAAC,CAAC,SAAS,IAAI,SAAS,CAAC;QACtC,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,IAAuB,CAAC,EAAE,CAAC;YAC5D,UAAU,EAAE,CAAC;YACb,SAAS;QACX,CAAC;QACD,IAAI,KAAK,GAAkB,YAAY,CAAC;QACxC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,mEAAmE;YACnE,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK;iBAC7B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAuB,CAAC,CAAC;iBAC5D,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;YAC1D,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YAEtC,oEAAoE;YACpE,qEAAqE;YACrE,yEAAyE;YACzE,mBAAmB;YACnB,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YAClF,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;YACnE,IAAI,MAAqB,CAAC;YAC1B,IAAI,SAAsB,CAAC;YAC3B,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC9B,MAAM,GAAG,WAAW,CAAC;gBACrB,SAAS,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;YAChC,CAAC;iBAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7B,MAAM,GAAG,YAAY,CAAC;gBACtB,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACxC,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,KAAK,CAAC;gBACf,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;YAC5B,CAAC;YAED,mEAAmE;YACnE,MAAM,YAAY,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,IAAI,IAAI,CAAC;YAC3E,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YAEpF,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;gBAC3B,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;oBAC3B,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;oBAC9C,IAAI,KAAK,GAAG,KAAK,IAAI,CAAC,cAAc,KAAK,IAAI,IAAI,KAAK,GAAG,cAAc,CAAC;wBACtE,cAAc,GAAG,KAAK,CAAC;gBAC3B,CAAC;YACH,CAAC;YACD,IAAI,WAAW,CAAC,MAAM,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC;gBAAE,KAAK,GAAG,MAAM,CAAC;YAC7D,IAAI,CAAC,IAAI,CAAC;gBACR,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,SAAS,EAAE,IAAI;gBACf,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI;gBACrB,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI;gBACrB,OAAO,EAAE,OAAO,CAAC,EAAE;gBACnB,MAAM,EAAE,SAAS,CAAC,MAAM;gBACxB,SAAS,EAAE,SAAS,CAAC,SAAS;gBAC9B,MAAM;gBACN,WAAW,EAAE,aAAa,CAAC,KAAK,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;gBAC1E,iBAAiB,EAAE,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;gBAC/D,mBAAmB,EAAE,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI;gBAClF,QAAQ,EAAE,OAAO,CAAC,QAAQ;aAC3B,CAAC,CAAC;QACL,CAAC;QACD,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;IAED,MAAM,OAAO,GAAkC;QAC7C,UAAU,EAAE,CAAC;QACb,GAAG,EAAE,CAAC;QACN,UAAU,EAAE,CAAC;QACb,SAAS,EAAE,CAAC;KACb,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,eAAe;QAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAE9C,OAAO;QACL,GAAG,EAAE,GAAG,CAAC,WAAW,EAAE;QACtB,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnC,OAAO;QACP,UAAU;QACV,QAAQ,EAAE,IAAI;QACd,YAAY,EACV,cAAc,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,cAAc,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI;QACtF,YAAY,EAAE,OAAO,CAAC,SAAS,GAAG,CAAC;KACpC,CAAC;AACJ,CAAC;AASD;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAqB,EAAE,OAA2B,EAAE;IACnF,IAAI,EAAE,CAAC,YAAY;QAAE,OAAO,IAAI,CAAC;IACjC,IAAI,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IAChD,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QAClC,OAAO,EAAE,CAAC,QAAQ,CAAC,IAAI,CACrB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,mBAAmB,KAAK,IAAI,IAAI,CAAC,CAAC,mBAAmB,IAAI,IAAI,CAAC,UAAW,CACnF,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["/**\n * Policy-as-code compliance mandates → dated, clause-named verdicts for findings.\n *\n * `CryptoPolicy` (policy.ts) classifies findings by algorithm family but is\n * date-blind. A mandate adds the missing dimension: named clauses with an effective\n * DATE (\"CNSA 2.0 disallows classical public-key crypto after 2035\"). The evaluator\n * compares each finding's algorithm against the selected mandates and today's date,\n * so a finding on a prohibited family reads as `due` (every deadline still ahead),\n * `deprecated` (the DEPRECATE deadline has passed — a warning), or `violation` (the\n * DISALLOW deadline has passed — a failure), always naming the governing clause,\n * deadline, and citation. This is what turns the inventory into an enforceable,\n * mandate-mapped gate rather than a neutral list.\n *\n * Pure and deterministic (the caller supplies `now`), so it is trivially testable.\n * qScan consumes it today for the `--mandate` gate; because it operates on the\n * shared `Finding[]`, qProbe or the GitHub Action can reuse it unchanged.\n *\n * Catalog scope: the two regimes that carry hard algorithm deadlines — CNSA 2.0 and\n * NIST IR 8547. DORA / NIS2 / PCI DSS require approved cryptography but set no\n * independent algorithm date; they inherit these timelines and are cited in docs.\n */\nimport type { AlgorithmFamily, Finding } from \"./types.js\";\nimport { PQC_STANDARDS } from \"./standards.js\";\n\n/**\n * All Shor-broken classical asymmetric families — the mandate's SCOPE. A finding\n * on one of these is adjudicated against the selected mandates; findings on\n * anything else (hashes, RNG, dependency, or TLS-configuration findings) are out\n * of scope for a PQC-asymmetric mandate and are tallied as `notInScope` instead\n * of inflating the conformant count.\n */\nconst CLASSICAL_PUBLIC_KEY: readonly AlgorithmFamily[] = [\n \"RSA\",\n \"ECDH\",\n \"ECDSA\",\n \"EdDSA\",\n \"DH\",\n \"DSA\",\n \"X25519\",\n \"X448\",\n \"ECIES\",\n];\n\n/**\n * The PROHIBITED subset the dated clauses apply to. X25519 and X448 are\n * deliberately excluded: they are the classical half of the recommended hybrid\n * key exchange (X25519MLKEM768 — permitted and recommended under the NIST\n * profile), and a static scan cannot distinguish a standalone exchange from the\n * hybrid's classical leg. Prohibiting them would false-positive exactly the orgs\n * that hybridized correctly, so they stay in scope but read `conformant`.\n */\nconst PROHIBITED_FAMILIES: readonly AlgorithmFamily[] = CLASSICAL_PUBLIC_KEY.filter(\n (family) => family !== \"X25519\" && family !== \"X448\",\n);\n\n/**\n * Effective dates derived from the standards source of truth\n * (`PQC_STANDARDS.transitionTimeline`), so a quarterly standards update moves the\n * mandate deadlines automatically (test/standards.test.ts asserts they agree).\n *\n * Boundary choice: \"deprecate AFTER 2030\" leaves the whole stated year permitted,\n * so each clause takes effect on the LAST day of its year (`YYYY-12-31`) —\n * conservative by a single day, unlike `YYYY-01-01`, which would bite roughly a\n * year early.\n */\nconst { deprecateAfter, disallowAfter } = PQC_STANDARDS.transitionTimeline;\nconst DEPRECATE_EFFECTIVE = `${deprecateAfter}-12-31`;\nconst DISALLOW_EFFECTIVE = `${disallowAfter}-12-31`;\n\n/** Which enforcement tier a clause encodes: warn (`deprecate`) or fail (`disallow`). */\nexport type MandateRuleTier = \"deprecate\" | \"disallow\";\n\nexport interface MandateRule {\n /** The named clause this rule encodes (verbatim in the gate's failure message). */\n clause: string;\n /** Enforcement tier: a passed `deprecate` date warns; a passed `disallow` date fails. */\n tier: MandateRuleTier;\n /** Algorithm families prohibited from the effective date onward. */\n prohibits: AlgorithmFamily[];\n /** ISO date (YYYY-MM-DD) the prohibition takes effect. */\n effective: string;\n /** One-line human description of the clause. */\n note: string;\n}\n\nexport interface Mandate {\n id: string;\n name: string;\n authority: string;\n citation: string;\n /** When this catalog entry was last reviewed against the source. */\n asOf: string;\n rules: MandateRule[];\n}\n\n/** The bundled mandate catalog. Keyed by `--mandate <id>`. */\nexport const MANDATES: Record<string, Mandate> = {\n \"cnsa-2.0\": {\n id: \"cnsa-2.0\",\n name: \"CNSA 2.0\",\n authority: \"NSA\",\n citation: \"NSA Commercial National Security Algorithm Suite 2.0 (CNSA 2.0)\",\n asOf: \"2026-07\",\n rules: [\n {\n clause: `CNSA 2.0 — deprecate classical PKC after ${deprecateAfter}`,\n tier: \"deprecate\",\n prohibits: [...PROHIBITED_FAMILIES],\n effective: DEPRECATE_EFFECTIVE,\n note: \"Classical public-key cryptography deprecated; systems should use CNSA 2.0 PQC exclusively.\",\n },\n {\n clause: `CNSA 2.0 — disallow classical PKC after ${disallowAfter}`,\n tier: \"disallow\",\n prohibits: [...PROHIBITED_FAMILIES],\n effective: DISALLOW_EFFECTIVE,\n note: \"Classical public-key cryptography disallowed; the migration must be complete.\",\n },\n ],\n },\n \"nist-ir-8547\": {\n id: \"nist-ir-8547\",\n name: \"NIST IR 8547\",\n authority: \"NIST\",\n citation: \"NIST IR 8547 (Transition to Post-Quantum Cryptography Standards)\",\n asOf: \"2026-07\",\n rules: [\n {\n clause: `NIST IR 8547 — deprecate classical PKC after ${deprecateAfter}`,\n tier: \"deprecate\",\n prohibits: [...PROHIBITED_FAMILIES],\n effective: DEPRECATE_EFFECTIVE,\n note: `112-bit-security classical public-key algorithms deprecated after ${deprecateAfter}.`,\n },\n {\n clause: `NIST IR 8547 — disallow classical PKC after ${disallowAfter}`,\n tier: \"disallow\",\n prohibits: [...PROHIBITED_FAMILIES],\n effective: DISALLOW_EFFECTIVE,\n note: `Classical public-key algorithms disallowed after ${disallowAfter}.`,\n },\n ],\n },\n};\n\nexport const mandateIds = (): string[] => Object.keys(MANDATES);\nexport const getMandate = (id: string): Mandate | undefined => MANDATES[id];\n\n/**\n * Validate mandate ids loudly, matching `parseCryptoPolicy`'s fail-loud\n * convention: a mistyped id must never silently evaluate to an empty gate.\n * Throws an `Error` naming every unknown id and the known catalog.\n * `evaluateMandates` itself stays lenient (unknown ids are skipped) so callers\n * decide where to fail.\n */\nexport function assertKnownMandates(ids: readonly string[]): void {\n const unknown = ids.filter((id) => !MANDATES[id]);\n if (unknown.length > 0) {\n throw new Error(\n `unknown mandate id(s): ${unknown.join(\", \")}; known mandates: ${mandateIds().join(\", \")}`,\n );\n }\n}\n\n/**\n * A finding's status against a mandate, worst last:\n * - `conformant` — in scope (classical asymmetric) but prohibited by no selected\n * clause (e.g. X25519 as the presumed hybrid leg).\n * - `due` — prohibited, with every deadline still ahead.\n * - `deprecated` — the DEPRECATE deadline has passed; a warning, not a failure.\n * - `violation` — the DISALLOW deadline has passed; fails the default gate.\n */\nexport type MandateStatus = \"conformant\" | \"due\" | \"deprecated\" | \"violation\";\n\nexport interface MandateFindingVerdict {\n ruleId: string;\n algorithm: AlgorithmFamily | \"unknown\";\n file: string;\n line: number;\n /** Mandate id (e.g. \"cnsa-2.0\"). */\n mandate: string;\n /**\n * The governing clause: the next upcoming clause when `due`, the passed\n * DEPRECATE clause when `deprecated`, the passed DISALLOW clause on `violation`.\n */\n clause: string;\n /** ISO effective date of the governing clause. */\n effective: string;\n status: MandateStatus;\n /** Whole months from `now` to the governing deadline; negative once it has passed. */\n monthsUntil: number;\n /**\n * ISO effective date of this mandate's DISALLOW clause for the family, or null\n * when the mandate carries none. The gate's `leadMonths` measures against this.\n */\n disallowEffective: string | null;\n /** Whole months from `now` to `disallowEffective`; null when there is none. */\n monthsUntilDisallow: number | null;\n citation: string;\n}\n\nexport interface MandateEvaluation {\n /** The `now` the evaluation was computed against (ISO). */\n now: string;\n /** Mandate ids evaluated. */\n mandates: string[];\n /**\n * Counts of IN-SCOPE findings (classical asymmetric families) by their worst\n * status across the selected mandates. Out-of-scope findings are excluded so\n * the conformant count is an honest statement about asymmetric crypto only.\n */\n summary: Record<MandateStatus, number>;\n /**\n * Findings outside the mandate's scope (hashes, RNG, dependency, TLS-config…),\n * which a PQC-asymmetric mandate does not adjudicate.\n */\n notInScope: number;\n /** One row per (prohibited finding × applicable mandate). */\n findings: MandateFindingVerdict[];\n /** Earliest still-future deadline across non-violation rows, or null. */\n nextDeadline: string | null;\n /** True when at least one DISALLOW deadline has passed (a `violation` exists). */\n hasViolation: boolean;\n}\n\nconst MONTH_MS = 2_629_800_000; // average month\n\nfunction monthsBetween(fromMs: number, toMs: number): number {\n return Math.round((toMs - fromMs) / MONTH_MS);\n}\n\nconst STATUS_RANK: Record<MandateStatus, number> = {\n conformant: 0,\n due: 1,\n deprecated: 2,\n violation: 3,\n};\n\n/**\n * Evaluate findings against the selected mandates as of `now`. Unknown mandate\n * ids are ignored — callers validate up front with {@link assertKnownMandates}.\n * A finding outside the classical-asymmetric scope is counted in `notInScope`;\n * an in-scope finding no selected mandate prohibits is `conformant`. Neither\n * contributes a verdict row.\n */\nexport function evaluateMandates(\n findings: readonly Finding[],\n mandateIdList: readonly string[],\n now: Date,\n): MandateEvaluation {\n const nowMs = now.getTime();\n const selected = mandateIdList.map(getMandate).filter((m): m is Mandate => Boolean(m));\n\n const rows: MandateFindingVerdict[] = [];\n const perFindingWorst: MandateStatus[] = [];\n let notInScope = 0;\n let nextDeadlineMs: number | null = null;\n\n for (const f of findings) {\n const algo = f.algorithm ?? \"unknown\";\n if (!CLASSICAL_PUBLIC_KEY.includes(algo as AlgorithmFamily)) {\n notInScope++;\n continue;\n }\n let worst: MandateStatus = \"conformant\";\n for (const mandate of selected) {\n // The applicable clauses for this family, earliest deadline first.\n const applicable = mandate.rules\n .filter((r) => r.prohibits.includes(algo as AlgorithmFamily))\n .sort((a, b) => a.effective.localeCompare(b.effective));\n if (applicable.length === 0) continue;\n\n // Tier the clauses so both stay live: a passed DISALLOW clause is a\n // violation; a passed DEPRECATE clause (disallow still ahead) is the\n // deprecated warning tier; otherwise the finding is due against the next\n // upcoming clause.\n const passed = applicable.filter((r) => nowMs >= new Date(r.effective).getTime());\n const passedDisallow = passed.filter((r) => r.tier === \"disallow\");\n let status: MandateStatus;\n let governing: MandateRule;\n if (passedDisallow.length > 0) {\n status = \"violation\";\n governing = passedDisallow[0];\n } else if (passed.length > 0) {\n status = \"deprecated\";\n governing = passed[passed.length - 1];\n } else {\n status = \"due\";\n governing = applicable[0];\n }\n\n // The disallow clause (earliest, if several) anchors `leadMonths`.\n const disallowRule = applicable.find((r) => r.tier === \"disallow\") ?? null;\n const disallowMs = disallowRule ? new Date(disallowRule.effective).getTime() : null;\n\n if (status !== \"violation\") {\n for (const r of applicable) {\n const effMs = new Date(r.effective).getTime();\n if (effMs > nowMs && (nextDeadlineMs === null || effMs < nextDeadlineMs))\n nextDeadlineMs = effMs;\n }\n }\n if (STATUS_RANK[status] > STATUS_RANK[worst]) worst = status;\n rows.push({\n ruleId: f.ruleId,\n algorithm: algo,\n file: f.location.file,\n line: f.location.line,\n mandate: mandate.id,\n clause: governing.clause,\n effective: governing.effective,\n status,\n monthsUntil: monthsBetween(nowMs, new Date(governing.effective).getTime()),\n disallowEffective: disallowRule ? disallowRule.effective : null,\n monthsUntilDisallow: disallowMs !== null ? monthsBetween(nowMs, disallowMs) : null,\n citation: mandate.citation,\n });\n }\n perFindingWorst.push(worst);\n }\n\n const summary: Record<MandateStatus, number> = {\n conformant: 0,\n due: 0,\n deprecated: 0,\n violation: 0,\n };\n for (const s of perFindingWorst) summary[s]++;\n\n return {\n now: now.toISOString(),\n mandates: selected.map((m) => m.id),\n summary,\n notInScope,\n findings: rows,\n nextDeadline:\n nextDeadlineMs !== null ? new Date(nextDeadlineMs).toISOString().slice(0, 10) : null,\n hasViolation: summary.violation > 0,\n };\n}\n\nexport interface MandateGateOptions {\n /** Fail when a DISALLOW deadline is within this many months (early enforcement). */\n leadMonths?: number;\n /** Fail on any mandate-prohibited finding regardless of the deadlines. */\n failNow?: boolean;\n}\n\n/**\n * The gate decision under the \"deadline-aware\" default: fail only once a DISALLOW\n * deadline has passed (`violation`). A passed DEPRECATE date (`deprecated`) is a\n * warning and does not fail the build. `leadMonths` fails early when a disallow\n * deadline is within the window; `failNow` fails on any prohibited finding\n * immediately.\n */\nexport function mandateGateFails(ev: MandateEvaluation, opts: MandateGateOptions = {}): boolean {\n if (ev.hasViolation) return true;\n if (opts.failNow) return ev.findings.length > 0;\n if (opts.leadMonths !== undefined) {\n return ev.findings.some(\n (v) => v.monthsUntilDisallow !== null && v.monthsUntilDisallow <= opts.leadMonths!,\n );\n }\n return false;\n}\n"]}
package/dist/version.d.ts CHANGED
@@ -3,5 +3,5 @@
3
3
  * the scan orchestrator can import it without creating a cycle through index.ts.
4
4
  * Keep in sync with packages/core/package.json.
5
5
  */
6
- export declare const VERSION = "0.6.0";
6
+ export declare const VERSION = "0.7.0";
7
7
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -3,5 +3,5 @@
3
3
  * the scan orchestrator can import it without creating a cycle through index.ts.
4
4
  * Keep in sync with packages/core/package.json.
5
5
  */
6
- export const VERSION = "0.6.0";
6
+ export const VERSION = "0.7.0";
7
7
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC","sourcesContent":["/**\n * The tool version surfaced in reports. Kept in its own module so reporters and\n * the scan orchestrator can import it without creating a cycle through index.ts.\n * Keep in sync with packages/core/package.json.\n */\nexport const VERSION = \"0.6.0\";\n"]}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC","sourcesContent":["/**\n * The tool version surfaced in reports. Kept in its own module so reporters and\n * the scan orchestrator can import it without creating a cycle through index.ts.\n * Keep in sync with packages/core/package.json.\n */\nexport const VERSION = \"0.7.0\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantakrypto/core",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Shared post-quantum readiness library: crypto detectors, vulnerable-dependency database, inventory + SARIF reporting. Zero runtime dependencies.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Dandelion Labs <hello@dandelionlabs.io> (https://dandelionlabs.io)",
package/src/index.ts CHANGED
@@ -189,6 +189,25 @@ export type {
189
189
  export { buildPolicyMapping, parseCryptoPolicy } from "./policy.js";
190
190
  export type { CryptoPolicy, PolicyVerdict, PolicyMapping, PolicyFindingVerdict } from "./policy.js";
191
191
 
192
+ // Compliance mandates → dated, clause-named verdicts + gate (policy-as-code).
193
+ export {
194
+ MANDATES,
195
+ mandateIds,
196
+ getMandate,
197
+ assertKnownMandates,
198
+ evaluateMandates,
199
+ mandateGateFails,
200
+ } from "./mandates.js";
201
+ export type {
202
+ Mandate,
203
+ MandateRule,
204
+ MandateRuleTier,
205
+ MandateStatus,
206
+ MandateFindingVerdict,
207
+ MandateEvaluation,
208
+ MandateGateOptions,
209
+ } from "./mandates.js";
210
+
192
211
  // Remediation lookup (family + tier-aware + profile-aware) and stateful-HBS guidance.
193
212
  export {
194
213
  remediationFor,
@@ -0,0 +1,365 @@
1
+ /**
2
+ * Policy-as-code compliance mandates → dated, clause-named verdicts for findings.
3
+ *
4
+ * `CryptoPolicy` (policy.ts) classifies findings by algorithm family but is
5
+ * date-blind. A mandate adds the missing dimension: named clauses with an effective
6
+ * DATE ("CNSA 2.0 disallows classical public-key crypto after 2035"). The evaluator
7
+ * compares each finding's algorithm against the selected mandates and today's date,
8
+ * so a finding on a prohibited family reads as `due` (every deadline still ahead),
9
+ * `deprecated` (the DEPRECATE deadline has passed — a warning), or `violation` (the
10
+ * DISALLOW deadline has passed — a failure), always naming the governing clause,
11
+ * deadline, and citation. This is what turns the inventory into an enforceable,
12
+ * mandate-mapped gate rather than a neutral list.
13
+ *
14
+ * Pure and deterministic (the caller supplies `now`), so it is trivially testable.
15
+ * qScan consumes it today for the `--mandate` gate; because it operates on the
16
+ * shared `Finding[]`, qProbe or the GitHub Action can reuse it unchanged.
17
+ *
18
+ * Catalog scope: the two regimes that carry hard algorithm deadlines — CNSA 2.0 and
19
+ * NIST IR 8547. DORA / NIS2 / PCI DSS require approved cryptography but set no
20
+ * independent algorithm date; they inherit these timelines and are cited in docs.
21
+ */
22
+ import type { AlgorithmFamily, Finding } from "./types.js";
23
+ import { PQC_STANDARDS } from "./standards.js";
24
+
25
+ /**
26
+ * All Shor-broken classical asymmetric families — the mandate's SCOPE. A finding
27
+ * on one of these is adjudicated against the selected mandates; findings on
28
+ * anything else (hashes, RNG, dependency, or TLS-configuration findings) are out
29
+ * of scope for a PQC-asymmetric mandate and are tallied as `notInScope` instead
30
+ * of inflating the conformant count.
31
+ */
32
+ const CLASSICAL_PUBLIC_KEY: readonly AlgorithmFamily[] = [
33
+ "RSA",
34
+ "ECDH",
35
+ "ECDSA",
36
+ "EdDSA",
37
+ "DH",
38
+ "DSA",
39
+ "X25519",
40
+ "X448",
41
+ "ECIES",
42
+ ];
43
+
44
+ /**
45
+ * The PROHIBITED subset the dated clauses apply to. X25519 and X448 are
46
+ * deliberately excluded: they are the classical half of the recommended hybrid
47
+ * key exchange (X25519MLKEM768 — permitted and recommended under the NIST
48
+ * profile), and a static scan cannot distinguish a standalone exchange from the
49
+ * hybrid's classical leg. Prohibiting them would false-positive exactly the orgs
50
+ * that hybridized correctly, so they stay in scope but read `conformant`.
51
+ */
52
+ const PROHIBITED_FAMILIES: readonly AlgorithmFamily[] = CLASSICAL_PUBLIC_KEY.filter(
53
+ (family) => family !== "X25519" && family !== "X448",
54
+ );
55
+
56
+ /**
57
+ * Effective dates derived from the standards source of truth
58
+ * (`PQC_STANDARDS.transitionTimeline`), so a quarterly standards update moves the
59
+ * mandate deadlines automatically (test/standards.test.ts asserts they agree).
60
+ *
61
+ * Boundary choice: "deprecate AFTER 2030" leaves the whole stated year permitted,
62
+ * so each clause takes effect on the LAST day of its year (`YYYY-12-31`) —
63
+ * conservative by a single day, unlike `YYYY-01-01`, which would bite roughly a
64
+ * year early.
65
+ */
66
+ const { deprecateAfter, disallowAfter } = PQC_STANDARDS.transitionTimeline;
67
+ const DEPRECATE_EFFECTIVE = `${deprecateAfter}-12-31`;
68
+ const DISALLOW_EFFECTIVE = `${disallowAfter}-12-31`;
69
+
70
+ /** Which enforcement tier a clause encodes: warn (`deprecate`) or fail (`disallow`). */
71
+ export type MandateRuleTier = "deprecate" | "disallow";
72
+
73
+ export interface MandateRule {
74
+ /** The named clause this rule encodes (verbatim in the gate's failure message). */
75
+ clause: string;
76
+ /** Enforcement tier: a passed `deprecate` date warns; a passed `disallow` date fails. */
77
+ tier: MandateRuleTier;
78
+ /** Algorithm families prohibited from the effective date onward. */
79
+ prohibits: AlgorithmFamily[];
80
+ /** ISO date (YYYY-MM-DD) the prohibition takes effect. */
81
+ effective: string;
82
+ /** One-line human description of the clause. */
83
+ note: string;
84
+ }
85
+
86
+ export interface Mandate {
87
+ id: string;
88
+ name: string;
89
+ authority: string;
90
+ citation: string;
91
+ /** When this catalog entry was last reviewed against the source. */
92
+ asOf: string;
93
+ rules: MandateRule[];
94
+ }
95
+
96
+ /** The bundled mandate catalog. Keyed by `--mandate <id>`. */
97
+ export const MANDATES: Record<string, Mandate> = {
98
+ "cnsa-2.0": {
99
+ id: "cnsa-2.0",
100
+ name: "CNSA 2.0",
101
+ authority: "NSA",
102
+ citation: "NSA Commercial National Security Algorithm Suite 2.0 (CNSA 2.0)",
103
+ asOf: "2026-07",
104
+ rules: [
105
+ {
106
+ clause: `CNSA 2.0 — deprecate classical PKC after ${deprecateAfter}`,
107
+ tier: "deprecate",
108
+ prohibits: [...PROHIBITED_FAMILIES],
109
+ effective: DEPRECATE_EFFECTIVE,
110
+ note: "Classical public-key cryptography deprecated; systems should use CNSA 2.0 PQC exclusively.",
111
+ },
112
+ {
113
+ clause: `CNSA 2.0 — disallow classical PKC after ${disallowAfter}`,
114
+ tier: "disallow",
115
+ prohibits: [...PROHIBITED_FAMILIES],
116
+ effective: DISALLOW_EFFECTIVE,
117
+ note: "Classical public-key cryptography disallowed; the migration must be complete.",
118
+ },
119
+ ],
120
+ },
121
+ "nist-ir-8547": {
122
+ id: "nist-ir-8547",
123
+ name: "NIST IR 8547",
124
+ authority: "NIST",
125
+ citation: "NIST IR 8547 (Transition to Post-Quantum Cryptography Standards)",
126
+ asOf: "2026-07",
127
+ rules: [
128
+ {
129
+ clause: `NIST IR 8547 — deprecate classical PKC after ${deprecateAfter}`,
130
+ tier: "deprecate",
131
+ prohibits: [...PROHIBITED_FAMILIES],
132
+ effective: DEPRECATE_EFFECTIVE,
133
+ note: `112-bit-security classical public-key algorithms deprecated after ${deprecateAfter}.`,
134
+ },
135
+ {
136
+ clause: `NIST IR 8547 — disallow classical PKC after ${disallowAfter}`,
137
+ tier: "disallow",
138
+ prohibits: [...PROHIBITED_FAMILIES],
139
+ effective: DISALLOW_EFFECTIVE,
140
+ note: `Classical public-key algorithms disallowed after ${disallowAfter}.`,
141
+ },
142
+ ],
143
+ },
144
+ };
145
+
146
+ export const mandateIds = (): string[] => Object.keys(MANDATES);
147
+ export const getMandate = (id: string): Mandate | undefined => MANDATES[id];
148
+
149
+ /**
150
+ * Validate mandate ids loudly, matching `parseCryptoPolicy`'s fail-loud
151
+ * convention: a mistyped id must never silently evaluate to an empty gate.
152
+ * Throws an `Error` naming every unknown id and the known catalog.
153
+ * `evaluateMandates` itself stays lenient (unknown ids are skipped) so callers
154
+ * decide where to fail.
155
+ */
156
+ export function assertKnownMandates(ids: readonly string[]): void {
157
+ const unknown = ids.filter((id) => !MANDATES[id]);
158
+ if (unknown.length > 0) {
159
+ throw new Error(
160
+ `unknown mandate id(s): ${unknown.join(", ")}; known mandates: ${mandateIds().join(", ")}`,
161
+ );
162
+ }
163
+ }
164
+
165
+ /**
166
+ * A finding's status against a mandate, worst last:
167
+ * - `conformant` — in scope (classical asymmetric) but prohibited by no selected
168
+ * clause (e.g. X25519 as the presumed hybrid leg).
169
+ * - `due` — prohibited, with every deadline still ahead.
170
+ * - `deprecated` — the DEPRECATE deadline has passed; a warning, not a failure.
171
+ * - `violation` — the DISALLOW deadline has passed; fails the default gate.
172
+ */
173
+ export type MandateStatus = "conformant" | "due" | "deprecated" | "violation";
174
+
175
+ export interface MandateFindingVerdict {
176
+ ruleId: string;
177
+ algorithm: AlgorithmFamily | "unknown";
178
+ file: string;
179
+ line: number;
180
+ /** Mandate id (e.g. "cnsa-2.0"). */
181
+ mandate: string;
182
+ /**
183
+ * The governing clause: the next upcoming clause when `due`, the passed
184
+ * DEPRECATE clause when `deprecated`, the passed DISALLOW clause on `violation`.
185
+ */
186
+ clause: string;
187
+ /** ISO effective date of the governing clause. */
188
+ effective: string;
189
+ status: MandateStatus;
190
+ /** Whole months from `now` to the governing deadline; negative once it has passed. */
191
+ monthsUntil: number;
192
+ /**
193
+ * ISO effective date of this mandate's DISALLOW clause for the family, or null
194
+ * when the mandate carries none. The gate's `leadMonths` measures against this.
195
+ */
196
+ disallowEffective: string | null;
197
+ /** Whole months from `now` to `disallowEffective`; null when there is none. */
198
+ monthsUntilDisallow: number | null;
199
+ citation: string;
200
+ }
201
+
202
+ export interface MandateEvaluation {
203
+ /** The `now` the evaluation was computed against (ISO). */
204
+ now: string;
205
+ /** Mandate ids evaluated. */
206
+ mandates: string[];
207
+ /**
208
+ * Counts of IN-SCOPE findings (classical asymmetric families) by their worst
209
+ * status across the selected mandates. Out-of-scope findings are excluded so
210
+ * the conformant count is an honest statement about asymmetric crypto only.
211
+ */
212
+ summary: Record<MandateStatus, number>;
213
+ /**
214
+ * Findings outside the mandate's scope (hashes, RNG, dependency, TLS-config…),
215
+ * which a PQC-asymmetric mandate does not adjudicate.
216
+ */
217
+ notInScope: number;
218
+ /** One row per (prohibited finding × applicable mandate). */
219
+ findings: MandateFindingVerdict[];
220
+ /** Earliest still-future deadline across non-violation rows, or null. */
221
+ nextDeadline: string | null;
222
+ /** True when at least one DISALLOW deadline has passed (a `violation` exists). */
223
+ hasViolation: boolean;
224
+ }
225
+
226
+ const MONTH_MS = 2_629_800_000; // average month
227
+
228
+ function monthsBetween(fromMs: number, toMs: number): number {
229
+ return Math.round((toMs - fromMs) / MONTH_MS);
230
+ }
231
+
232
+ const STATUS_RANK: Record<MandateStatus, number> = {
233
+ conformant: 0,
234
+ due: 1,
235
+ deprecated: 2,
236
+ violation: 3,
237
+ };
238
+
239
+ /**
240
+ * Evaluate findings against the selected mandates as of `now`. Unknown mandate
241
+ * ids are ignored — callers validate up front with {@link assertKnownMandates}.
242
+ * A finding outside the classical-asymmetric scope is counted in `notInScope`;
243
+ * an in-scope finding no selected mandate prohibits is `conformant`. Neither
244
+ * contributes a verdict row.
245
+ */
246
+ export function evaluateMandates(
247
+ findings: readonly Finding[],
248
+ mandateIdList: readonly string[],
249
+ now: Date,
250
+ ): MandateEvaluation {
251
+ const nowMs = now.getTime();
252
+ const selected = mandateIdList.map(getMandate).filter((m): m is Mandate => Boolean(m));
253
+
254
+ const rows: MandateFindingVerdict[] = [];
255
+ const perFindingWorst: MandateStatus[] = [];
256
+ let notInScope = 0;
257
+ let nextDeadlineMs: number | null = null;
258
+
259
+ for (const f of findings) {
260
+ const algo = f.algorithm ?? "unknown";
261
+ if (!CLASSICAL_PUBLIC_KEY.includes(algo as AlgorithmFamily)) {
262
+ notInScope++;
263
+ continue;
264
+ }
265
+ let worst: MandateStatus = "conformant";
266
+ for (const mandate of selected) {
267
+ // The applicable clauses for this family, earliest deadline first.
268
+ const applicable = mandate.rules
269
+ .filter((r) => r.prohibits.includes(algo as AlgorithmFamily))
270
+ .sort((a, b) => a.effective.localeCompare(b.effective));
271
+ if (applicable.length === 0) continue;
272
+
273
+ // Tier the clauses so both stay live: a passed DISALLOW clause is a
274
+ // violation; a passed DEPRECATE clause (disallow still ahead) is the
275
+ // deprecated warning tier; otherwise the finding is due against the next
276
+ // upcoming clause.
277
+ const passed = applicable.filter((r) => nowMs >= new Date(r.effective).getTime());
278
+ const passedDisallow = passed.filter((r) => r.tier === "disallow");
279
+ let status: MandateStatus;
280
+ let governing: MandateRule;
281
+ if (passedDisallow.length > 0) {
282
+ status = "violation";
283
+ governing = passedDisallow[0];
284
+ } else if (passed.length > 0) {
285
+ status = "deprecated";
286
+ governing = passed[passed.length - 1];
287
+ } else {
288
+ status = "due";
289
+ governing = applicable[0];
290
+ }
291
+
292
+ // The disallow clause (earliest, if several) anchors `leadMonths`.
293
+ const disallowRule = applicable.find((r) => r.tier === "disallow") ?? null;
294
+ const disallowMs = disallowRule ? new Date(disallowRule.effective).getTime() : null;
295
+
296
+ if (status !== "violation") {
297
+ for (const r of applicable) {
298
+ const effMs = new Date(r.effective).getTime();
299
+ if (effMs > nowMs && (nextDeadlineMs === null || effMs < nextDeadlineMs))
300
+ nextDeadlineMs = effMs;
301
+ }
302
+ }
303
+ if (STATUS_RANK[status] > STATUS_RANK[worst]) worst = status;
304
+ rows.push({
305
+ ruleId: f.ruleId,
306
+ algorithm: algo,
307
+ file: f.location.file,
308
+ line: f.location.line,
309
+ mandate: mandate.id,
310
+ clause: governing.clause,
311
+ effective: governing.effective,
312
+ status,
313
+ monthsUntil: monthsBetween(nowMs, new Date(governing.effective).getTime()),
314
+ disallowEffective: disallowRule ? disallowRule.effective : null,
315
+ monthsUntilDisallow: disallowMs !== null ? monthsBetween(nowMs, disallowMs) : null,
316
+ citation: mandate.citation,
317
+ });
318
+ }
319
+ perFindingWorst.push(worst);
320
+ }
321
+
322
+ const summary: Record<MandateStatus, number> = {
323
+ conformant: 0,
324
+ due: 0,
325
+ deprecated: 0,
326
+ violation: 0,
327
+ };
328
+ for (const s of perFindingWorst) summary[s]++;
329
+
330
+ return {
331
+ now: now.toISOString(),
332
+ mandates: selected.map((m) => m.id),
333
+ summary,
334
+ notInScope,
335
+ findings: rows,
336
+ nextDeadline:
337
+ nextDeadlineMs !== null ? new Date(nextDeadlineMs).toISOString().slice(0, 10) : null,
338
+ hasViolation: summary.violation > 0,
339
+ };
340
+ }
341
+
342
+ export interface MandateGateOptions {
343
+ /** Fail when a DISALLOW deadline is within this many months (early enforcement). */
344
+ leadMonths?: number;
345
+ /** Fail on any mandate-prohibited finding regardless of the deadlines. */
346
+ failNow?: boolean;
347
+ }
348
+
349
+ /**
350
+ * The gate decision under the "deadline-aware" default: fail only once a DISALLOW
351
+ * deadline has passed (`violation`). A passed DEPRECATE date (`deprecated`) is a
352
+ * warning and does not fail the build. `leadMonths` fails early when a disallow
353
+ * deadline is within the window; `failNow` fails on any prohibited finding
354
+ * immediately.
355
+ */
356
+ export function mandateGateFails(ev: MandateEvaluation, opts: MandateGateOptions = {}): boolean {
357
+ if (ev.hasViolation) return true;
358
+ if (opts.failNow) return ev.findings.length > 0;
359
+ if (opts.leadMonths !== undefined) {
360
+ return ev.findings.some(
361
+ (v) => v.monthsUntilDisallow !== null && v.monthsUntilDisallow <= opts.leadMonths!,
362
+ );
363
+ }
364
+ return false;
365
+ }
package/src/version.ts CHANGED
@@ -3,4 +3,4 @@
3
3
  * the scan orchestrator can import it without creating a cycle through index.ts.
4
4
  * Keep in sync with packages/core/package.json.
5
5
  */
6
- export const VERSION = "0.6.0";
6
+ export const VERSION = "0.7.0";