Skip to main content

sbom_tools/quality/compliance/
registry.rs

1//! Compliance rule registry: the single source of truth for SARIF rule
2//! IDs, harmonised-standard cross-references, and remediation text, keyed
3//! by the stable internal `Violation::rule_id`.
4
5use super::{StandardKind, ViolationSeverity};
6
7/// Static metadata attached to a compliance rule. The `rule_id` on every
8/// [`Violation`] indexes into [`rule_meta`]; the registry — not the
9/// human-readable message — is the single source of truth for the
10/// externally-visible SARIF rule ID, the harmonised-standard cross-references,
11/// and the remediation text. Rewording a message can no longer silently
12/// re-bucket a GitHub code-scanning rule or drop a prEN/BSI reference.
13#[derive(Debug, Clone, Copy)]
14pub struct RuleMeta {
15    /// Externally-visible SARIF rule ID (e.g., `SBOM-CRA-MACHINE-READABLE`).
16    /// GitHub code scanning dedups on this value, so it must stay stable.
17    pub sarif_id: &'static str,
18    /// PascalCase SARIF `reportingDescriptor.name` for the externally-visible
19    /// rule identified by [`RuleMeta::sarif_id`]. Internal keys that alias to
20    /// a shared SARIF rule (key != `sarif_id`) carry the canonical
21    /// descriptor's name, so every alias renders identically.
22    pub name: &'static str,
23    /// SARIF `reportingDescriptor.shortDescription` text for the
24    /// externally-visible rule. Like [`RuleMeta::name`], aliased keys share
25    /// the canonical descriptor's text.
26    pub short_description: &'static str,
27    /// Documentation-default severity for the rule. Push sites may still
28    /// escalate/relax the concrete [`Violation::severity`] by product class or
29    /// CRA phase. The SARIF rule catalogue is generated from this value (see
30    /// `registry_severity_matches_sarif_catalogue` in
31    /// tests/sarif_rule_catalogue_tests.rs).
32    pub default_severity: ViolationSeverity,
33    /// Harmonised-standard / regulation cross-references, in display order.
34    pub refs: &'static [(StandardKind, &'static str)],
35    /// Remediation guidance shown in reports and the TUI.
36    pub remediation: &'static str,
37}
38
39/// NIST PQC readiness remediation, shared by the SBOM-PQC-* rules.
40///
41/// NIST IR 8547 is cited as its Initial Public Draft (Nov 2024) — still a
42/// draft as of 2026-07; SP 800-131A is at Rev. 2 (Rev. 3 is draft-only).
43const REMEDIATION_PQC: &str = "Migrate quantum-vulnerable algorithms per NIST IR 8547 ipd (Transition to Post-Quantum Cryptography Standards): adopt ML-KEM (FIPS 203), ML-DSA (FIPS 204), SLH-DSA (FIPS 205) or SP 800-208 stateful hash-based signatures, and retire algorithms disallowed by SP 800-131A Rev. 2.";
44
45/// Generic fallback remediation, shared by rules with no bespoke guidance.
46pub(crate) const REMEDIATION_GENERIC: &str = "Review the requirement and update the SBOM accordingly. Consult the EU CRA regulation (EU 2024/2847) for detailed guidance.";
47
48/// Standard-appropriate generic fallbacks: a rule's remediation must cite the
49/// regulation it belongs to, never default to the CRA (see the CNSA precedent).
50const REMEDIATION_GENERIC_NTIA: &str = "Review the requirement and update the SBOM accordingly. Consult the NTIA \"Minimum Elements for an SBOM\" (July 2021) for detailed guidance.";
51
52const REMEDIATION_GENERIC_FDA: &str = "Review the requirement and update the SBOM accordingly. Consult the FDA premarket cybersecurity guidance (2023) / FD&C \u{a7}524B for detailed guidance.";
53
54const REMEDIATION_GENERIC_AIACT: &str = "Review the requirement and update the SBOM accordingly. Consult the EU AI Act (Regulation (EU) 2024/1689) Annex IV technical-documentation requirements for detailed guidance.";
55
56const REMEDIATION_GENERIC_BSI: &str = "Review the requirement and update the SBOM accordingly. Consult BSI TR-03183-2 v2.1.0 for detailed guidance.";
57
58const REMEDIATION_GENERIC_CISA2026: &str = "Review the requirement and update the SBOM accordingly. Consult the 2026 Minimum Elements for an SBOM (CISA et al., July 2026) for detailed guidance.";
59
60const REMEDIATION_GENERIC_PCI: &str = "Review the requirement and update the SBOM accordingly. Consult PCI DSS v4.0.1 Requirement 6.3.2 and its testing procedures for detailed guidance.";
61
62const REMEDIATION_GENERIC_FSCT: &str = "Review the requirement and update the SBOM accordingly. Consult CISA Framing Software Component Transparency, 3rd ed. (2024) for detailed guidance.";
63
64/// SSDF practices share one remediation paragraph.
65const REMEDIATION_SSDF: &str = "Follow NIST SP 800-218 SSDF practices: include tool provenance, source VCS references, build metadata, and cryptographic hashes for all components.";
66
67/// EO 14028 §4 requirements share one remediation paragraph.
68const REMEDIATION_EO14028: &str = "Follow EO 14028 Section 4(e) requirements: use a machine-readable format (CycloneDX 1.4+, SPDX 2.3+, or SPDX 3.0+), auto-generate the SBOM, include unique identifiers, versions, hashes, dependencies, and supplier information.";
69
70/// EU AI Act not-applicable remediation.
71const REMEDIATION_AIACT_NA: &str = "EU AI Act Annex IV readiness applies only to SBOMs that describe AI/ML systems. Add machine-learning-model or dataset components (CycloneDX 1.5+ AI/ML BOM) to enable the assessment.";
72
73/// BSI/G7 SBOM-for-AI not-applicable remediation.
74const REMEDIATION_BSIAI_NA: &str = "BSI/G7 SBOM-for-AI minimum-elements readiness applies only to SBOMs that describe AI/ML systems. Add machine-learning-model or dataset components (CycloneDX 1.5+ AI/ML BOM, or an SPDX 3.0 AI/Dataset profile) to enable the assessment.";
75
76/// Mistyped-ML remediation, shared by the EU AI Act and BSI/G7 SBOM-for-AI
77/// applicability guards.
78const REMEDIATION_UNTYPED_ML: &str = "Components with pkg:huggingface PURLs or model-card references look like ML models; leaving them untyped hides them from every AI-BOM readiness check. Set their type to 'machine-learning-model' and attach the AI metadata (CycloneDX 1.5+ modelCard, or the SPDX 3.0 AI profile).";
79
80/// BSI/G7 SBOM-for-AI Models-cluster remediation.
81const REMEDIATION_BSIAI_MODELS: &str = "Declare the BSI/G7 SBOM-for-AI Models minimum elements for each MachineLearningModel component: name, version, a unique identifier (PURL/CPE/SWHID/SWID), a model-weight hash using a NIST-approved algorithm (SHA-256+), a model card, the architecture, training datasets, limitations, and a license.";
82
83/// BSI/G7 SBOM-for-AI Datasets-cluster remediation.
84const REMEDIATION_BSIAI_DATASETS: &str = "Declare the BSI/G7 SBOM-for-AI Datasets minimum elements for each Data component: name, a unique identifier, a hash value, a license, a sensitivity classification, and provenance / intended-use (SPDX 3.0 dataset_intendedUse / dataPreprocessing / anonymizationMethodUsed, or governance owners).";
85
86/// BSI/G7 SBOM-for-AI document/metadata/system/infra/security remediation.
87const REMEDIATION_BSIAI_GENERAL: &str = "Declare the BSI/G7 SBOM-for-AI minimum elements: document author, data-format name + version, timestamp, generation tool, and signature; the primary AI system, its producer, and its data-flow/usage; runtime/framework infrastructure links; and AI-specific security controls / exploitability references where they can be expressed.";
88
89/// EUCC Substantial remediation, shared by the EUCC evidence rules.
90const REMEDIATION_EUCC: &str = "Provide the Common Criteria evidence Implementing Regulation (EU) 2024/482 (EUCC) expects alongside the SBOM: set the sidecar fields eucc_protection_profile_id (Protection Profile), eucc_target_of_evaluation (ToE), eucc_itsef_identifier (evaluating ITSEF), and eucc_valid_until (certificate validity), and reference the EUCC certificate via a Certification/Attestation external reference.";
91
92/// CNSA 2.0 allowlist remediation, shared by the CNSA rules.
93const REMEDIATION_CNSA2: &str = "Migrate to the CNSA 2.0 suite: AES-256, SHA-384/SHA-512, ML-KEM-1024, ML-DSA-87, or SP 800-208 stateful hash-based signatures (LMS/XMSS/HSS); use TLS 1.3 for network protocols. Unclassifiable algorithms cannot be verified — declare an algorithmFamily, OID, or recognizable name.";
94
95/// NIST PQC protocol remediation.
96const REMEDIATION_PQC_PROTO: &str = "Disable SSL and TLS versions below 1.2 (SP 800-52 Rev. 2) and remove broken (SP 800-131A) or quantum-vulnerable (IR 8547) algorithms from negotiated cipher suites and IKEv2 transforms.";
97
98/// NIST PQC certificate remediation.
99const REMEDIATION_PQC_CERT: &str = "Re-issue the certificate with a NIST-approved post-quantum signature algorithm (FIPS 204 ML-DSA, FIPS 205 SLH-DSA, or SP 800-208 LMS/XMSS/HSS).";
100
101/// Remediation for unverifiable crypto evidence under the PQC standard.
102const REMEDIATION_PQC_UNKNOWN: &str = "Declare the asset's algorithm identity (algorithmFamily, OID, or a recognizable name), make bom-refs resolvable within the SBOM, and use parseable protocol versions so signature algorithms, cipher suites, and protocol references can be verified for PQC readiness.";
103
104/// Look up the static [`RuleMeta`] for a stable internal rule key.
105///
106/// The key is the [`Violation::rule_id`] set at each check site. Returns
107/// `None` for unregistered keys — the exhaustive test
108/// `every_emitted_violation_has_a_registered_rule_id` guarantees no live check
109/// site emits an unregistered key.
110#[must_use]
111pub fn rule_meta(rule_id: &str) -> Option<RuleMeta> {
112    use StandardKind as K;
113    const CRA: K = K::CraArticle;
114    const ANNEX: K = K::CraAnnex;
115    const PREN: K = K::Pren40000_1_3;
116    let meta = match rule_id {
117        // ---- CRA Articles ------------------------------------------------
118        "SBOM-CRA-ART-13-2" => RuleMeta {
119            sarif_id: "SBOM-CRA-GENERAL",
120            name: "CraGeneralRequirement",
121            short_description: "CRA general SBOM readiness requirement",
122            default_severity: ViolationSeverity::Warning,
123            refs: &[(CRA, "Art. 13(2)")],
124            remediation: REMEDIATION_GENERIC,
125        },
126        // SBOM freshness. Formerly cited Art. 13(3), which is the risk-
127        // assessment documentation paragraph; keeping the SBOM current is
128        // the Art. 13(7) systematic-documentation duty applied to the
129        // Annex I Part II (1) SBOM element.
130        "SBOM-CRA-SBOM-FRESHNESS" => RuleMeta {
131            sarif_id: "SBOM-CRA-SBOM-FRESHNESS",
132            name: "CraSbomFreshness",
133            short_description: "CRA Art. 13(7) / Annex I Part II (1): SBOM freshness — timely regeneration after changes",
134            default_severity: ViolationSeverity::Warning,
135            refs: &[(CRA, "Art. 13(7)"), (ANNEX, "Annex I Part II (1)")],
136            remediation: "Regenerate the SBOM when components are added, removed, or updated. CRA Art. 13(7) requires manufacturers to systematically document relevant cybersecurity aspects, and the Annex I Part II (1) SBOM must reflect the product's current components.",
137        },
138        // Machine-readable SBOM format. The mandate for a 'commonly used and
139        // machine-readable format' lives in Annex I Part II (1), not in
140        // Art. 13(4) (which puts the risk assessment into the technical
141        // documentation).
142        "SBOM-CRA-MACHINE-READABLE" => RuleMeta {
143            sarif_id: "SBOM-CRA-MACHINE-READABLE",
144            name: "CraMachineReadableFormat",
145            short_description: "CRA Annex I Part II (1): SBOM must be in a commonly used, machine-readable format (CycloneDX 1.4+, SPDX 2.3+, or SPDX 3.0+)",
146            default_severity: ViolationSeverity::Warning,
147            refs: &[(ANNEX, "Annex I Part II (1)"), (PREN, "PRE-7-RQ-04")],
148            remediation: "Ensure the SBOM is produced in CycloneDX 1.4+ (JSON or XML), SPDX 2.3+ (JSON or tag-value), or SPDX 3.0+ (JSON-LD). Older format versions may not be recognized as machine-readable under CRA Annex I Part II (1).",
149        },
150        // Component licence information. The CRA does not list licences as
151        // an SBOM element; they are evidence supporting the Art. 13(5)
152        // third-party due-diligence obligation.
153        "SBOM-CRA-ART-13-5" => RuleMeta {
154            sarif_id: "SBOM-CRA-ART-13-5",
155            name: "CraLicensedComponentTracking",
156            short_description: "CRA Art. 13(5): Third-party due diligence — license information for all components",
157            default_severity: ViolationSeverity::Warning,
158            refs: &[(CRA, "Art. 13(5)")],
159            remediation: "Record license information for every component to support the Art. 13(5) due diligence on integrated third-party components. CycloneDX: use component.licenses[]. SPDX 2.x: use PackageLicenseDeclared / PackageLicenseConcluded. SPDX 3.0: use HAS_DECLARED_LICENSE / HAS_CONCLUDED_LICENSE relationships.",
160        },
161        // Single point of contact for vulnerability reporting. Formerly
162        // cited Art. 13(6), which is the manufacturer's duty to report
163        // component vulnerabilities UPSTREAM to the component maintainer;
164        // the user-facing contact is Art. 13(17) / Annex I Part II (6) /
165        // Annex II (2).
166        "SBOM-CRA-ART-13-17-CONTACT" => RuleMeta {
167            sarif_id: "SBOM-CRA-ART-13-17-CONTACT",
168            name: "CraVulnerabilityContact",
169            short_description: "CRA Art. 13(17): Single point of contact for vulnerability reporting (Annex I Part II (6), Annex II (2))",
170            default_severity: ViolationSeverity::Warning,
171            refs: &[
172                (CRA, "Art. 13(17)"),
173                (ANNEX, "Annex I Part II (6)"),
174                (ANNEX, "Annex II (2)"),
175            ],
176            remediation: "Add a security contact or vulnerability disclosure URL. CycloneDX: add a component externalReference with type 'security-contact' or set metadata.manufacturer.contact. SPDX: add an SECURITY external reference.",
177        },
178        // Vulnerability severity/remediation metadata. Anchored to the
179        // Annex I Part II (4) duty to share information about fixed
180        // vulnerabilities (description, impacts, severity, remediation).
181        "SBOM-CRA-VULN-METADATA" => RuleMeta {
182            sarif_id: "SBOM-CRA-VULN-METADATA",
183            name: "CraVulnerabilityMetadata",
184            short_description: "CRA Annex I Part II (4): Vulnerability severity and remediation metadata completeness",
185            default_severity: ViolationSeverity::Warning,
186            refs: &[(ANNEX, "Annex I Part II (4)")],
187            remediation: "Add severity (e.g., CVSS score) and remediation details to each vulnerability entry. CycloneDX: use vulnerability.ratings[].score and vulnerability.analysis. SPDX: use annotation or externalRef.",
188        },
189        // Coordinated vulnerability disclosure policy. Formerly cited
190        // Art. 13(7) (systematic documentation); the CVD-policy duty is
191        // Annex I Part II (5), reinforced by Art. 13(8).
192        "SBOM-CRA-CVD-POLICY" => RuleMeta {
193            sarif_id: "SBOM-CRA-CVD-POLICY",
194            name: "CraCoordinatedDisclosure",
195            short_description: "CRA Annex I Part II (5): Coordinated vulnerability disclosure policy reference",
196            default_severity: ViolationSeverity::Warning,
197            refs: &[
198                (ANNEX, "Annex I Part II (5)"),
199                (CRA, "Art. 13(8)"),
200                (PREN, "RLS-2-RQ-03-RE"),
201            ],
202            remediation: "Reference a coordinated vulnerability disclosure policy. CycloneDX: add an externalReference of type 'advisories' linking to your disclosure policy. SPDX: add an external document reference.",
203        },
204        "SBOM-CRA-ART-13-8" => RuleMeta {
205            sarif_id: "SBOM-CRA-ART-13-8",
206            name: "CraSupportPeriod",
207            short_description: "CRA Art. 13(8) / 13(19): Support period and security update end date (Annex II (7))",
208            default_severity: ViolationSeverity::Info,
209            refs: &[
210                (CRA, "Art. 13(8)"),
211                (CRA, "Art. 13(19)"),
212                (ANNEX, "Annex II (7)"),
213            ],
214            remediation: "Specify when security updates will no longer be provided. CycloneDX 1.5+: use component.releaseNotes or metadata properties. SPDX: use an annotation with end-of-support date.",
215        },
216        // Documented vulnerability information. Formerly cited Art. 13(9),
217        // which is actually the 10-year availability of issued security
218        // updates; the documentation obligation is Annex I Part II (1).
219        "SBOM-CRA-VULN-STATEMENT" => RuleMeta {
220            sarif_id: "SBOM-CRA-VULN-STATEMENT",
221            name: "CraKnownVulnerabilities",
222            short_description: "CRA Annex I Part II (1): Documented vulnerability information — vulnerability data or assertion",
223            default_severity: ViolationSeverity::Info,
224            refs: &[(ANNEX, "Annex I Part II (1)")],
225            remediation: "Include vulnerability data or add a vulnerability-assertion external reference stating no known vulnerabilities. CycloneDX: use the vulnerabilities array. SPDX: use annotations or external references.",
226        },
227        // Component lifecycle / end-of-support. Formerly cited Art. 13(11),
228        // which is about OPTIONAL public software archives; lifecycle
229        // handling is the Art. 13(8) support-period duty plus the
230        // Annex II (7) support end-date disclosure.
231        "SBOM-CRA-LIFECYCLE" => RuleMeta {
232            sarif_id: "SBOM-CRA-LIFECYCLE",
233            name: "CraComponentLifecycle",
234            short_description: "CRA Art. 13(8) / Annex II (7): Component lifecycle and end-of-support status",
235            default_severity: ViolationSeverity::Info,
236            refs: &[(CRA, "Art. 13(8)"), (ANNEX, "Annex II (7)")],
237            remediation: "Include lifecycle or end-of-support metadata for components. CycloneDX: use component properties (e.g., cdx:lifecycle:status). SPDX: use annotations.",
238        },
239        // Product identification. Formerly cited Art. 13(12) (technical
240        // documentation + conformity assessment + DoC + CE marking); product
241        // identification is Art. 13(15) plus Annex II (3).
242        "SBOM-CRA-ART-13-15-PRODUCT" => RuleMeta {
243            sarif_id: "SBOM-CRA-ART-13-15-PRODUCT",
244            name: "CraProductIdentification",
245            short_description: "CRA Art. 13(15): Product identification (Annex II (3))",
246            default_severity: ViolationSeverity::Warning,
247            refs: &[(CRA, "Art. 13(15)"), (ANNEX, "Annex II (3)")],
248            remediation: "The SBOM must identify the product by name. CycloneDX: set metadata.component.name. SPDX: set documentDescribes with the primary package name.",
249        },
250        // Component version. Formerly cited Art. 13(12); versions are part
251        // of the Annex I Part II (1) SBOM element inventory.
252        "SBOM-CRA-COMPONENT-VERSION" => RuleMeta {
253            sarif_id: "SBOM-CRA-COMPONENT-VERSION",
254            name: "CraComponentVersion",
255            short_description: "CRA Annex I Part II (1): Component version identification",
256            default_severity: ViolationSeverity::Error,
257            refs: &[(ANNEX, "Annex I Part II (1)"), (PREN, "PRE-7-RQ-06")],
258            remediation: "Every component must have a version string. Use the actual release version (e.g., '1.2.3'), not a range or placeholder.",
259        },
260        "SBOM-CRA-ART-24-SUPPLIER" => RuleMeta {
261            sarif_id: "SBOM-CRA-ART-24-SUPPLIER",
262            name: "CraStewardComponentSupplier",
263            short_description: "CRA Art. 24: Component supplier identification (open-source steward SBOM floor)",
264            default_severity: ViolationSeverity::Warning,
265            refs: &[(CRA, "Art. 24")],
266            remediation: "Identify each component's supplier as part of the Art. 24 steward SBOM floor. CycloneDX: set component.supplier. SPDX: set PackageSupplier.",
267        },
268        // Manufacturer identification. Formerly cited Art. 13(15), which is
269        // PRODUCT identification (type/batch/serial number); manufacturer
270        // identification (name + postal/email/website) is Art. 13(16) plus
271        // Annex II (1).
272        "SBOM-CRA-ART-13-16" => RuleMeta {
273            sarif_id: "SBOM-CRA-ART-13-16",
274            name: "CraManufacturerIdentification",
275            short_description: "CRA Art. 13(16): Manufacturer identification and contact information (Annex II (1))",
276            default_severity: ViolationSeverity::Warning,
277            refs: &[(CRA, "Art. 13(16)"), (ANNEX, "Annex II (1)")],
278            remediation: "Identify the manufacturer. CycloneDX: set metadata.manufacturer. SPDX: add an Organization creator.",
279        },
280        "SBOM-CRA-ART-13-16-EMAIL" => RuleMeta {
281            sarif_id: "SBOM-CRA-ART-13-16-EMAIL",
282            name: "CraManufacturerEmail",
283            short_description: "CRA Art. 13(16): Valid manufacturer contact email (Annex II (1))",
284            default_severity: ViolationSeverity::Warning,
285            refs: &[(CRA, "Art. 13(16)"), (ANNEX, "Annex II (1)")],
286            remediation: "Provide a valid contact email for the manufacturer. The email must contain an @ sign with valid local and domain parts.",
287        },
288        // Per-component supplier identification. Distinct from the
289        // Art. 13(16) manufacturer-identification obligation: component
290        // suppliers are part of the Annex I Part II (1) SBOM inventory.
291        "SBOM-CRA-COMPONENT-SUPPLIER" => RuleMeta {
292            sarif_id: "SBOM-CRA-COMPONENT-SUPPLIER",
293            name: "CraComponentSupplier",
294            short_description: "CRA Annex I Part II (1): Component supplier identification",
295            default_severity: ViolationSeverity::Warning,
296            refs: &[(ANNEX, "Annex I Part II (1)"), (PREN, "PRE-7-RQ-03")],
297            remediation: "Identify each component's supplier. CycloneDX: set component.supplier. SPDX: set PackageSupplier.",
298        },
299        "SBOM-CRA-ART-14" => RuleMeta {
300            sarif_id: "SBOM-CRA-GENERAL",
301            name: "CraGeneralRequirement",
302            short_description: "CRA general SBOM readiness requirement",
303            default_severity: ViolationSeverity::Info,
304            refs: &[(CRA, "Art. 14")],
305            remediation: REMEDIATION_GENERIC,
306        },
307        "SBOM-CRA-ART-24" => RuleMeta {
308            sarif_id: "SBOM-CRA-GENERAL",
309            name: "CraGeneralRequirement",
310            short_description: "CRA general SBOM readiness requirement",
311            default_severity: ViolationSeverity::Warning,
312            refs: &[],
313            remediation: REMEDIATION_GENERIC,
314        },
315        // ---- CRA Annexes -------------------------------------------------
316        // Self-descriptor for the shared SBOM-CRA-ANNEX-I SARIF rule the
317        // SBOM-CRA-ANNEX-I-* keys below alias to. Never emitted by a check
318        // site; it anchors the SARIF reportingDescriptor generated from the
319        // registry (see `COMPLIANCE_SARIF_RULE_IDS`).
320        "SBOM-CRA-ANNEX-I" => RuleMeta {
321            sarif_id: "SBOM-CRA-ANNEX-I",
322            name: "CraTechnicalDocumentation",
323            short_description: "CRA Annex I: Technical documentation (unique identifiers, dependencies, primary component)",
324            default_severity: ViolationSeverity::Warning,
325            refs: &[(ANNEX, "Annex I")],
326            remediation: REMEDIATION_GENERIC,
327        },
328        "SBOM-CRA-ANNEX-I-IDENTIFIER" => RuleMeta {
329            sarif_id: "SBOM-CRA-ANNEX-I",
330            name: "CraTechnicalDocumentation",
331            short_description: "CRA Annex I: Technical documentation (unique identifiers, dependencies, primary component)",
332            default_severity: ViolationSeverity::Warning,
333            refs: &[(ANNEX, "Annex I"), (PREN, "PRE-7-RQ-07")],
334            remediation: "Add a PURL, CPE, or SWID tag to each component for unique identification. PURLs are preferred (e.g., pkg:npm/lodash@4.17.21).",
335        },
336        "SBOM-CRA-ANNEX-I-TRACEABILITY" => RuleMeta {
337            sarif_id: "SBOM-CRA-ANNEX-I",
338            name: "CraTechnicalDocumentation",
339            short_description: "CRA Annex I: Technical documentation (unique identifiers, dependencies, primary component)",
340            default_severity: ViolationSeverity::Warning,
341            refs: &[(ANNEX, "Annex I Part II"), (PREN, "PRE-7-RQ-07")],
342            remediation: "Add a PURL, CPE, or SWID tag to each component for unique identification. PURLs are preferred (e.g., pkg:npm/lodash@4.17.21).",
343        },
344        "SBOM-CRA-ANNEX-I-SUPPLY-CHAIN" => RuleMeta {
345            sarif_id: "SBOM-CRA-ANNEX-I",
346            name: "CraTechnicalDocumentation",
347            short_description: "CRA Annex I: Technical documentation (unique identifiers, dependencies, primary component)",
348            default_severity: ViolationSeverity::Warning,
349            refs: &[
350                (ANNEX, "Annex I Part II"),
351                (PREN, "PRE-7-RQ-01"),
352                (PREN, "PRE-7-RQ-03"),
353            ],
354            remediation: "Add dependency relationships between components. CycloneDX: use the dependencies array. SPDX: use DEPENDS_ON relationships.",
355        },
356        "SBOM-CRA-ANNEX-I-INTEGRITY" => RuleMeta {
357            sarif_id: "SBOM-CRA-ANNEX-I",
358            name: "CraTechnicalDocumentation",
359            short_description: "CRA Annex I: Technical documentation (unique identifiers, dependencies, primary component)",
360            default_severity: ViolationSeverity::Info,
361            refs: &[(ANNEX, "Annex I Part I (2)(f)")],
362            remediation: "Add cryptographic hashes (SHA-256 or stronger) to components for integrity verification.",
363        },
364        "SBOM-CRA-ANNEX-I-DEPENDENCY" => RuleMeta {
365            sarif_id: "SBOM-CRA-ANNEX-I",
366            name: "CraTechnicalDocumentation",
367            short_description: "CRA Annex I: Technical documentation (unique identifiers, dependencies, primary component)",
368            default_severity: ViolationSeverity::Error,
369            refs: &[(ANNEX, "Annex I")],
370            remediation: "Add dependency relationships between components. CycloneDX: use the dependencies array. SPDX: use DEPENDS_ON relationships.",
371        },
372        "SBOM-CRA-ANNEX-I-PRIMARY" => RuleMeta {
373            sarif_id: "SBOM-CRA-ANNEX-I",
374            name: "CraTechnicalDocumentation",
375            short_description: "CRA Annex I: Technical documentation (unique identifiers, dependencies, primary component)",
376            default_severity: ViolationSeverity::Warning,
377            refs: &[(ANNEX, "Annex I")],
378            remediation: "Identify the top-level product component. CycloneDX: set metadata.component. SPDX: use documentDescribes to point to the primary package.",
379        },
380        "SBOM-CRA-ANNEX-I-CONTROLS" => RuleMeta {
381            sarif_id: "SBOM-CRA-ANNEX-I",
382            name: "CraTechnicalDocumentation",
383            short_description: "CRA Annex I: Technical documentation (unique identifiers, dependencies, primary component)",
384            default_severity: ViolationSeverity::Warning,
385            refs: &[(ANNEX, "Annex I")],
386            remediation: REMEDIATION_GENERIC,
387        },
388        // Document-level integrity. Formerly mis-cited Annex III (the
389        // important-products class list); integrity protection is Annex I
390        // Part I (2)(f). The clause covers integrity only — it does not
391        // mention authenticity.
392        "SBOM-CRA-DOC-INTEGRITY" => RuleMeta {
393            sarif_id: "SBOM-CRA-DOC-INTEGRITY",
394            name: "CraDocumentIntegrity",
395            short_description: "CRA Annex I Part I (2)(f): Document integrity — serial number, hash, or digital signature",
396            default_severity: ViolationSeverity::Info,
397            refs: &[(ANNEX, "Annex I Part I (2)(f)")],
398            remediation: "Add document-level integrity metadata: a serial number (CycloneDX: serialNumber, SPDX: documentNamespace), or a digital signature/attestation with a cryptographic hash.",
399        },
400        "SBOM-CRA-ANNEX-IV" => RuleMeta {
401            sarif_id: "SBOM-CRA-GENERAL",
402            name: "CraGeneralRequirement",
403            short_description: "CRA general SBOM readiness requirement",
404            default_severity: ViolationSeverity::Info,
405            refs: &[(ANNEX, "Annex IV")],
406            remediation: REMEDIATION_GENERIC,
407        },
408        "SBOM-CRA-ANNEX-V" => RuleMeta {
409            sarif_id: "SBOM-CRA-ANNEX-V",
410            name: "CraDeclarationOfConformity",
411            short_description: "CRA Annex V: EU Declaration of Conformity reference",
412            default_severity: ViolationSeverity::Info,
413            refs: &[(ANNEX, "Annex V")],
414            remediation: "Reference the EU Declaration of Conformity. CycloneDX: add an externalReference of type 'attestation' or 'certification'. SPDX: add an external document reference.",
415        },
416        "SBOM-CRA-CYCLES" => RuleMeta {
417            sarif_id: "SBOM-CRA-CYCLES",
418            name: "CraDependencyCycles",
419            short_description: "CRA Annex I Part II (1): Dependency graph must be an acyclic inventory — cyclic dependency declarations detected",
420            default_severity: ViolationSeverity::Warning,
421            refs: &[(ANNEX, "Annex I Part II (1)")],
422            remediation: "Resolve cyclic dependency declarations so the SBOM's dependency graph is a directed acyclic inventory of the product's components.",
423        },
424        "SBOM-CRA-ANNEX-VIII" => RuleMeta {
425            sarif_id: "SBOM-CRA-ANNEX-VIII",
426            name: "CraConformityAssessment",
427            short_description: "CRA Annex VIII: Conformity-assessment evidence for the resolved assessment route",
428            default_severity: ViolationSeverity::Info,
429            refs: &[(ANNEX, "Annex VIII")],
430            remediation: REMEDIATION_GENERIC,
431        },
432        "SBOM-CRA-PRE-8-RQ-02" => RuleMeta {
433            sarif_id: "SBOM-CRA-PRE-8-RQ-02",
434            name: "CraHardwareInventory",
435            short_description: "CRA prEN 40000-1-3 [PRE-8-RQ-02]: Hardware components must be inventoried with producer, name, identifier, and firmware version",
436            default_severity: ViolationSeverity::Error,
437            refs: &[(PREN, "PRE-8-RQ-02")],
438            remediation: REMEDIATION_GENERIC,
439        },
440        "SBOM-CRA-PRE-7-RQ-07-RE" => RuleMeta {
441            sarif_id: "SBOM-CRA-PRE-7-RQ-07-RE",
442            name: "CraVendorHashCarryThrough",
443            short_description: "CRA prEN 40000-1-3 [PRE-7-RQ-07-RE]: Upstream vendor-supplied component hashes must be carried through into the SBOM",
444            default_severity: ViolationSeverity::Warning,
445            refs: &[
446                (ANNEX, "Annex I Part II"),
447                (PREN, "PRE-7-RQ-07"),
448                (PREN, "PRE-7-RQ-07-RE"),
449            ],
450            remediation: "Add cryptographic hashes (SHA-256 or stronger) to components for integrity verification.",
451        },
452        // ---- Generic CRA / document-level (no specific article) ----------
453        "SBOM-CRA-GENERAL" => RuleMeta {
454            sarif_id: "SBOM-CRA-GENERAL",
455            name: "CraGeneralRequirement",
456            short_description: "CRA general SBOM readiness requirement",
457            default_severity: ViolationSeverity::Warning,
458            refs: &[],
459            remediation: REMEDIATION_GENERIC,
460        },
461        // Generic bucket for quality-profile (Minimum/Standard/Comprehensive)
462        // findings whose check site has no specific registry mapping. The
463        // SARIF renderer re-buckets the `SBOM-CRA-GENERAL` fallback onto this
464        // rule for quality runs so they never surface under a CRA identity.
465        "SBOM-QUALITY-GENERAL" => RuleMeta {
466            sarif_id: "SBOM-QUALITY-GENERAL",
467            name: "QualityGeneralRequirement",
468            short_description: "SBOM quality: general quality-profile requirement",
469            default_severity: ViolationSeverity::Warning,
470            refs: &[],
471            remediation: "Review the requirement and update the SBOM accordingly.",
472        },
473        // ---- EUCC Substantial (reference-only profile) -------------------
474        "SBOM-EUCC-PP" => RuleMeta {
475            sarif_id: "SBOM-EUCC-PP",
476            name: "EuccProtectionProfile",
477            short_description: "EUCC (Reg. (EU) 2024/482): Common Criteria Protection Profile reference — sidecar eucc_protection_profile_id",
478            default_severity: ViolationSeverity::Error,
479            refs: &[(K::Eucc, "Protection Profile")],
480            remediation: REMEDIATION_EUCC,
481        },
482        "SBOM-EUCC-TOE" => RuleMeta {
483            sarif_id: "SBOM-EUCC-TOE",
484            name: "EuccTargetOfEvaluation",
485            short_description: "EUCC (Reg. (EU) 2024/482): Target of Evaluation reference — sidecar eucc_target_of_evaluation",
486            default_severity: ViolationSeverity::Error,
487            refs: &[(K::Eucc, "ToE")],
488            remediation: REMEDIATION_EUCC,
489        },
490        "SBOM-EUCC-ITSEF" => RuleMeta {
491            sarif_id: "SBOM-EUCC-ITSEF",
492            name: "EuccItsefIdentifier",
493            short_description: "EUCC (Reg. (EU) 2024/482): ITSEF (IT Security Evaluation Facility) identifier — sidecar eucc_itsef_identifier",
494            default_severity: ViolationSeverity::Error,
495            refs: &[(K::Eucc, "ITSEF")],
496            remediation: REMEDIATION_EUCC,
497        },
498        "SBOM-EUCC-VALIDITY" => RuleMeta {
499            sarif_id: "SBOM-EUCC-VALIDITY",
500            name: "EuccCertificateValidity",
501            short_description: "EUCC (Reg. (EU) 2024/482): certificate valid-until date present, not expired, not near expiry — sidecar eucc_valid_until",
502            default_severity: ViolationSeverity::Error,
503            refs: &[(K::Eucc, "Certificate validity")],
504            remediation: REMEDIATION_EUCC,
505        },
506        "SBOM-EUCC-CERTREF" => RuleMeta {
507            sarif_id: "SBOM-EUCC-CERTREF",
508            name: "EuccCertificationReference",
509            short_description: "EUCC (Reg. (EU) 2024/482): Certification/Attestation external reference to an EUCC certificate (recommended)",
510            default_severity: ViolationSeverity::Warning,
511            refs: &[(K::Eucc, "Certification reference")],
512            remediation: REMEDIATION_EUCC,
513        },
514        // Generic bucket for EUCC-run findings whose check site has no
515        // specific registry mapping (SARIF fallback re-bucketing).
516        "SBOM-EUCC-GENERAL" => RuleMeta {
517            sarif_id: "SBOM-EUCC-GENERAL",
518            name: "EuccGeneralRequirement",
519            short_description: "EUCC (Reg. (EU) 2024/482): general SBOM evidence requirement",
520            default_severity: ViolationSeverity::Warning,
521            refs: &[(K::Eucc, "Reg. (EU) 2024/482")],
522            remediation: REMEDIATION_EUCC,
523        },
524        // ---- EU AI Act Annex IV technical-documentation readiness --------
525        // Self-descriptors for the shared SBOM-AIACT-ANNEX-IV-* SARIF rules
526        // that the per-element keys below alias to. Never emitted by a check
527        // site; they anchor the registry-generated SARIF descriptors.
528        "SBOM-AIACT-ANNEX-IV-1" => RuleMeta {
529            sarif_id: "SBOM-AIACT-ANNEX-IV-1",
530            name: "AiActGeneralDescription",
531            short_description: "EU AI Act Annex IV §1: general description of the AI system (architecture, intended purpose)",
532            default_severity: ViolationSeverity::Warning,
533            refs: &[(K::EuAiAct, "Annex IV §1")],
534            remediation: REMEDIATION_GENERIC_AIACT,
535        },
536        "SBOM-AIACT-ANNEX-IV-2D" => RuleMeta {
537            sarif_id: "SBOM-AIACT-ANNEX-IV-2D",
538            name: "AiActTrainingData",
539            short_description: "EU AI Act Annex IV §2(d): training-data characteristics, provenance, and sensitivity classification",
540            default_severity: ViolationSeverity::Warning,
541            refs: &[(K::EuAiAct, "Annex IV §2(d)")],
542            remediation: REMEDIATION_GENERIC_AIACT,
543        },
544        "SBOM-AIACT-ANNEX-IV-2G" => RuleMeta {
545            sarif_id: "SBOM-AIACT-ANNEX-IV-2G",
546            name: "AiActValidationMetrics",
547            short_description: "EU AI Act Annex IV §2(g): validation/testing metrics (accuracy, robustness)",
548            default_severity: ViolationSeverity::Warning,
549            refs: &[(K::EuAiAct, "Annex IV §2(g)")],
550            remediation: REMEDIATION_GENERIC_AIACT,
551        },
552        "SBOM-AIACT-ANNEX-IV-2C" => RuleMeta {
553            sarif_id: "SBOM-AIACT-ANNEX-IV-2C",
554            name: "AiActComputationalResources",
555            short_description: "EU AI Act Annex IV §2(c): computational resources / training-energy disclosure",
556            default_severity: ViolationSeverity::Info,
557            refs: &[(K::EuAiAct, "Annex IV §2(c)")],
558            remediation: REMEDIATION_GENERIC_AIACT,
559        },
560        "SBOM-AIACT-ANNEX-IV-3" => RuleMeta {
561            sarif_id: "SBOM-AIACT-ANNEX-IV-3",
562            name: "AiActLimitations",
563            short_description: "EU AI Act Annex IV §3: foreseeable limitations and risks",
564            default_severity: ViolationSeverity::Info,
565            refs: &[(K::EuAiAct, "Annex IV §3")],
566            remediation: REMEDIATION_GENERIC_AIACT,
567        },
568        "SBOM-AIACT-NA" => RuleMeta {
569            sarif_id: "SBOM-AIACT-NA",
570            name: "AiActNotApplicable",
571            short_description: "EU AI Act Annex IV readiness not applicable — SBOM has no ML-model or dataset components",
572            default_severity: ViolationSeverity::Info,
573            refs: &[(K::EuAiAct, "Annex IV")],
574            remediation: REMEDIATION_AIACT_NA,
575        },
576        "SBOM-AIACT-ANNEX-IV-1-DESCRIPTION" => RuleMeta {
577            sarif_id: "SBOM-AIACT-ANNEX-IV-1",
578            name: "AiActGeneralDescription",
579            short_description: "EU AI Act Annex IV §1: general description of the AI system (architecture, intended purpose)",
580            default_severity: ViolationSeverity::Warning,
581            refs: &[(K::EuAiAct, "Annex IV §1")],
582            remediation: "Add a general description of the AI model: architecture family/name and a model-card reference. CycloneDX: set modelCard.modelParameters.architectureFamily / modelArchitecture and an external reference of type 'model-card'.",
583        },
584        "SBOM-AIACT-ANNEX-IV-1-PURPOSE" => RuleMeta {
585            sarif_id: "SBOM-AIACT-ANNEX-IV-1",
586            name: "AiActGeneralDescription",
587            short_description: "EU AI Act Annex IV §1: general description of the AI system (architecture, intended purpose)",
588            default_severity: ViolationSeverity::Warning,
589            refs: &[(K::EuAiAct, "Annex IV §1")],
590            remediation: "Document the intended purpose / use-cases of the AI model. CycloneDX: set modelCard.considerations.useCases.",
591        },
592        "SBOM-AIACT-ANNEX-IV-2D-DATASETS" => RuleMeta {
593            sarif_id: "SBOM-AIACT-ANNEX-IV-2D",
594            name: "AiActTrainingData",
595            short_description: "EU AI Act Annex IV §2(d): training-data characteristics, provenance, and sensitivity classification",
596            default_severity: ViolationSeverity::Warning,
597            refs: &[(K::EuAiAct, "Annex IV §2(d)")],
598            remediation: "Reference the training datasets used. CycloneDX: set modelCard.modelParameters.datasets with a {ref} to a data component.",
599        },
600        "SBOM-AIACT-ANNEX-IV-2D-SENSITIVITY" => RuleMeta {
601            sarif_id: "SBOM-AIACT-ANNEX-IV-2D",
602            name: "AiActTrainingData",
603            short_description: "EU AI Act Annex IV §2(d): training-data characteristics, provenance, and sensitivity classification",
604            default_severity: ViolationSeverity::Warning,
605            refs: &[(K::EuAiAct, "Annex IV §2(d)")],
606            remediation: "Declare a sensitivity classification for each dataset (e.g. 'none', 'pii', 'personal'). CycloneDX: set the data component's sensitiveData array.",
607        },
608        "SBOM-AIACT-ANNEX-IV-2D-PERSONAL-DATA" => RuleMeta {
609            sarif_id: "SBOM-AIACT-ANNEX-IV-2D",
610            name: "AiActTrainingData",
611            short_description: "EU AI Act Annex IV §2(d): training-data characteristics, provenance, and sensitivity classification",
612            default_severity: ViolationSeverity::Info,
613            refs: &[(K::EuAiAct, "Annex IV §2(d)")],
614            remediation: "Where training data involves personal data, document the GDPR lawful basis and data-protection measures alongside the SBOM (AI Act and GDPR apply in parallel).",
615        },
616        "SBOM-AIACT-ANNEX-IV-2G-METRICS" => RuleMeta {
617            sarif_id: "SBOM-AIACT-ANNEX-IV-2G",
618            name: "AiActValidationMetrics",
619            short_description: "EU AI Act Annex IV §2(g): validation/testing metrics (accuracy, robustness)",
620            default_severity: ViolationSeverity::Warning,
621            refs: &[(K::EuAiAct, "Annex IV §2(g)")],
622            remediation: "Record validation/testing metrics (accuracy, robustness). CycloneDX: set modelCard.quantitativeAnalysis.performanceMetrics.",
623        },
624        // Energy / computational-resources disclosure lives in Annex IV
625        // §2(c) ("the computational resources used to develop, train, test
626        // and validate the AI system"), NOT §2(g), which covers validation
627        // and testing procedures/metrics. Explicit energy reporting is the
628        // GPAI technical documentation (Annex XI).
629        "SBOM-AIACT-ANNEX-IV-2C-ENERGY" => RuleMeta {
630            sarif_id: "SBOM-AIACT-ANNEX-IV-2C",
631            name: "AiActComputationalResources",
632            short_description: "EU AI Act Annex IV §2(c): computational resources / training-energy disclosure",
633            default_severity: ViolationSeverity::Info,
634            refs: &[(K::EuAiAct, "Annex IV §2(c)")],
635            remediation: "Disclose computational resources / training energy. CycloneDX: set modelCard.considerations.environmentalConsiderations.energyConsumptions.",
636        },
637        "SBOM-AIACT-ANNEX-IV-3-LIMITATIONS" => RuleMeta {
638            sarif_id: "SBOM-AIACT-ANNEX-IV-3",
639            name: "AiActLimitations",
640            short_description: "EU AI Act Annex IV §3: foreseeable limitations and risks",
641            default_severity: ViolationSeverity::Info,
642            refs: &[(K::EuAiAct, "Annex IV §3")],
643            remediation: "State the foreseeable limitations and risks of the model, including ethical and fairness considerations. CycloneDX: set modelCard.considerations.technicalLimitations / ethicalConsiderations / fairnessAssessments.",
644        },
645        "SBOM-AIACT-UNTYPED-ML" => RuleMeta {
646            sarif_id: "SBOM-AIACT-UNTYPED-ML",
647            name: "AiActUntypedMlContent",
648            short_description: "EU AI Act readiness: ML content detected but not typed machine-learning-model",
649            default_severity: ViolationSeverity::Warning,
650            refs: &[(K::EuAiAct, "Annex IV: applicability")],
651            remediation: REMEDIATION_UNTYPED_ML,
652        },
653        // Generic bucket for EU-AI-Act-run findings whose check site has no
654        // specific registry mapping (SARIF fallback re-bucketing).
655        "SBOM-AIACT-GENERAL" => RuleMeta {
656            sarif_id: "SBOM-AIACT-GENERAL",
657            name: "AiActGeneralRequirement",
658            short_description: "EU AI Act Annex IV: general documentation-readiness requirement",
659            default_severity: ViolationSeverity::Warning,
660            refs: &[(K::EuAiAct, "Annex IV")],
661            remediation: "Review the EU AI Act Annex IV documentation requirement and update the AI-BOM metadata accordingly.",
662        },
663        // ---- BSI/G7 SBOM-for-AI Minimum Elements readiness ---------------
664        // Self-descriptors for the shared per-cluster SARIF rules the
665        // element-level keys below alias to. Never emitted by a check site;
666        // they anchor the registry-generated SARIF descriptors.
667        "SBOM-BSIAI-META" => RuleMeta {
668            sarif_id: "SBOM-BSIAI-META",
669            name: "BsiSbomForAiMetadata",
670            short_description: "BSI/G7 SBOM-for-AI Metadata cluster: author, data-format name + version, timestamp, generation tool, signature",
671            default_severity: ViolationSeverity::Error,
672            refs: &[(K::BsiSbomForAi, "Metadata")],
673            remediation: REMEDIATION_BSIAI_GENERAL,
674        },
675        "SBOM-BSIAI-SYS" => RuleMeta {
676            sarif_id: "SBOM-BSIAI-SYS",
677            name: "BsiSbomForAiSystemLevel",
678            short_description: "BSI/G7 SBOM-for-AI System-Level cluster: primary AI system, producer, data flow & usage",
679            default_severity: ViolationSeverity::Warning,
680            refs: &[(K::BsiSbomForAi, "System-Level")],
681            remediation: REMEDIATION_BSIAI_GENERAL,
682        },
683        "SBOM-BSIAI-MODEL" => RuleMeta {
684            sarif_id: "SBOM-BSIAI-MODEL",
685            name: "BsiSbomForAiModels",
686            short_description: "BSI/G7 SBOM-for-AI Models cluster: name, version, identifier, weight hash (NIST-approved algorithm), model card, architecture, datasets, limitations, license",
687            default_severity: ViolationSeverity::Error,
688            refs: &[(K::BsiSbomForAi, "Models")],
689            remediation: REMEDIATION_BSIAI_MODELS,
690        },
691        "SBOM-BSIAI-DATASET" => RuleMeta {
692            sarif_id: "SBOM-BSIAI-DATASET",
693            name: "BsiSbomForAiDatasets",
694            short_description: "BSI/G7 SBOM-for-AI Datasets cluster: name, identifier, hash, license, sensitivity classification, provenance & intended use",
695            default_severity: ViolationSeverity::Error,
696            refs: &[(K::BsiSbomForAi, "Datasets")],
697            remediation: REMEDIATION_BSIAI_DATASETS,
698        },
699        "SBOM-BSIAI-INFRA" => RuleMeta {
700            sarif_id: "SBOM-BSIAI-INFRA",
701            name: "BsiSbomForAiInfrastructure",
702            short_description: "BSI/G7 SBOM-for-AI Infrastructure cluster: runtime / framework dependency links",
703            default_severity: ViolationSeverity::Info,
704            refs: &[(K::BsiSbomForAi, "Infrastructure")],
705            remediation: REMEDIATION_BSIAI_GENERAL,
706        },
707        "SBOM-BSIAI-SEC" => RuleMeta {
708            sarif_id: "SBOM-BSIAI-SEC",
709            name: "BsiSbomForAiSecurity",
710            short_description: "BSI/G7 SBOM-for-AI Security cluster: AI-specific security controls, exploitability references",
711            default_severity: ViolationSeverity::Info,
712            refs: &[(K::BsiSbomForAi, "Security")],
713            remediation: REMEDIATION_BSIAI_GENERAL,
714        },
715        "SBOM-BSIAI-NA" => RuleMeta {
716            sarif_id: "SBOM-BSIAI-NA",
717            name: "BsiSbomForAiNotApplicable",
718            short_description: "BSI/G7 SBOM-for-AI minimum-elements readiness not applicable — SBOM has no ML-model or dataset components",
719            default_severity: ViolationSeverity::Info,
720            refs: &[(K::BsiSbomForAi, "Applicability")],
721            remediation: REMEDIATION_BSIAI_NA,
722        },
723        "SBOM-BSIAI-UNTYPED-ML" => RuleMeta {
724            sarif_id: "SBOM-BSIAI-UNTYPED-ML",
725            name: "BsiSbomForAiUntypedMlContent",
726            short_description: "BSI/G7 SBOM-for-AI readiness: ML content detected but not typed machine-learning-model",
727            default_severity: ViolationSeverity::Warning,
728            refs: &[(K::BsiSbomForAi, "Applicability")],
729            remediation: REMEDIATION_UNTYPED_ML,
730        },
731        // Metadata cluster
732        "SBOM-BSIAI-META-AUTHOR" => RuleMeta {
733            sarif_id: "SBOM-BSIAI-META",
734            name: "BsiSbomForAiMetadata",
735            short_description: "BSI/G7 SBOM-for-AI Metadata cluster: author, data-format name + version, timestamp, generation tool, signature",
736            default_severity: ViolationSeverity::Error,
737            refs: &[(K::BsiSbomForAi, "Metadata / Author")],
738            remediation: REMEDIATION_BSIAI_GENERAL,
739        },
740        "SBOM-BSIAI-META-FORMAT" => RuleMeta {
741            sarif_id: "SBOM-BSIAI-META",
742            name: "BsiSbomForAiMetadata",
743            short_description: "BSI/G7 SBOM-for-AI Metadata cluster: author, data-format name + version, timestamp, generation tool, signature",
744            default_severity: ViolationSeverity::Error,
745            refs: &[(K::BsiSbomForAi, "Metadata / Data format name + version")],
746            remediation: REMEDIATION_BSIAI_GENERAL,
747        },
748        "SBOM-BSIAI-META-TIMESTAMP" => RuleMeta {
749            sarif_id: "SBOM-BSIAI-META",
750            name: "BsiSbomForAiMetadata",
751            short_description: "BSI/G7 SBOM-for-AI Metadata cluster: author, data-format name + version, timestamp, generation tool, signature",
752            default_severity: ViolationSeverity::Error,
753            refs: &[(K::BsiSbomForAi, "Metadata / Timestamp")],
754            remediation: REMEDIATION_BSIAI_GENERAL,
755        },
756        "SBOM-BSIAI-META-TOOL" => RuleMeta {
757            sarif_id: "SBOM-BSIAI-META",
758            name: "BsiSbomForAiMetadata",
759            short_description: "BSI/G7 SBOM-for-AI Metadata cluster: author, data-format name + version, timestamp, generation tool, signature",
760            default_severity: ViolationSeverity::Warning,
761            refs: &[(K::BsiSbomForAi, "Metadata / Generation tool")],
762            remediation: REMEDIATION_BSIAI_GENERAL,
763        },
764        "SBOM-BSIAI-META-SIGNATURE" => RuleMeta {
765            sarif_id: "SBOM-BSIAI-META",
766            name: "BsiSbomForAiMetadata",
767            short_description: "BSI/G7 SBOM-for-AI Metadata cluster: author, data-format name + version, timestamp, generation tool, signature",
768            default_severity: ViolationSeverity::Info,
769            refs: &[(K::BsiSbomForAi, "Metadata / Signature")],
770            remediation: REMEDIATION_BSIAI_GENERAL,
771        },
772        // System-Level cluster
773        "SBOM-BSIAI-SYS-PRIMARY" => RuleMeta {
774            sarif_id: "SBOM-BSIAI-SYS",
775            name: "BsiSbomForAiSystemLevel",
776            short_description: "BSI/G7 SBOM-for-AI System-Level cluster: primary AI system, producer, data flow & usage",
777            default_severity: ViolationSeverity::Warning,
778            refs: &[(K::BsiSbomForAi, "System-Level / Primary AI system")],
779            remediation: REMEDIATION_BSIAI_GENERAL,
780        },
781        "SBOM-BSIAI-SYS-PRODUCER" => RuleMeta {
782            sarif_id: "SBOM-BSIAI-SYS",
783            name: "BsiSbomForAiSystemLevel",
784            short_description: "BSI/G7 SBOM-for-AI System-Level cluster: primary AI system, producer, data flow & usage",
785            default_severity: ViolationSeverity::Warning,
786            refs: &[(K::BsiSbomForAi, "System-Level / Producer")],
787            remediation: REMEDIATION_BSIAI_GENERAL,
788        },
789        "SBOM-BSIAI-SYS-DATAFLOW" => RuleMeta {
790            sarif_id: "SBOM-BSIAI-SYS",
791            name: "BsiSbomForAiSystemLevel",
792            short_description: "BSI/G7 SBOM-for-AI System-Level cluster: primary AI system, producer, data flow & usage",
793            default_severity: ViolationSeverity::Info,
794            refs: &[(K::BsiSbomForAi, "System-Level / Data flow & usage")],
795            remediation: REMEDIATION_BSIAI_GENERAL,
796        },
797        // Models cluster
798        "SBOM-BSIAI-MODEL-NAME" => RuleMeta {
799            sarif_id: "SBOM-BSIAI-MODEL",
800            name: "BsiSbomForAiModels",
801            short_description: "BSI/G7 SBOM-for-AI Models cluster: name, version, identifier, weight hash (NIST-approved algorithm), model card, architecture, datasets, limitations, license",
802            default_severity: ViolationSeverity::Error,
803            refs: &[(K::BsiSbomForAi, "Models / Model name")],
804            remediation: REMEDIATION_BSIAI_MODELS,
805        },
806        "SBOM-BSIAI-MODEL-VERSION" => RuleMeta {
807            sarif_id: "SBOM-BSIAI-MODEL",
808            name: "BsiSbomForAiModels",
809            short_description: "BSI/G7 SBOM-for-AI Models cluster: name, version, identifier, weight hash (NIST-approved algorithm), model card, architecture, datasets, limitations, license",
810            default_severity: ViolationSeverity::Error,
811            refs: &[(K::BsiSbomForAi, "Models / Model version")],
812            remediation: REMEDIATION_BSIAI_MODELS,
813        },
814        "SBOM-BSIAI-MODEL-IDENTIFIER" => RuleMeta {
815            sarif_id: "SBOM-BSIAI-MODEL",
816            name: "BsiSbomForAiModels",
817            short_description: "BSI/G7 SBOM-for-AI Models cluster: name, version, identifier, weight hash (NIST-approved algorithm), model card, architecture, datasets, limitations, license",
818            default_severity: ViolationSeverity::Error,
819            refs: &[(K::BsiSbomForAi, "Models / Model identifier")],
820            remediation: REMEDIATION_BSIAI_MODELS,
821        },
822        "SBOM-BSIAI-MODEL-HASH" => RuleMeta {
823            sarif_id: "SBOM-BSIAI-MODEL",
824            name: "BsiSbomForAiModels",
825            short_description: "BSI/G7 SBOM-for-AI Models cluster: name, version, identifier, weight hash (NIST-approved algorithm), model card, architecture, datasets, limitations, license",
826            default_severity: ViolationSeverity::Error,
827            refs: &[(K::BsiSbomForAi, "Models / Model hash value")],
828            remediation: REMEDIATION_BSIAI_MODELS,
829        },
830        "SBOM-BSIAI-MODEL-HASH-ALGO" => RuleMeta {
831            sarif_id: "SBOM-BSIAI-MODEL",
832            name: "BsiSbomForAiModels",
833            short_description: "BSI/G7 SBOM-for-AI Models cluster: name, version, identifier, weight hash (NIST-approved algorithm), model card, architecture, datasets, limitations, license",
834            default_severity: ViolationSeverity::Error,
835            refs: &[(K::BsiSbomForAi, "Models / Hash algorithm")],
836            remediation: REMEDIATION_BSIAI_MODELS,
837        },
838        "SBOM-BSIAI-MODEL-CARD" => RuleMeta {
839            sarif_id: "SBOM-BSIAI-MODEL",
840            name: "BsiSbomForAiModels",
841            short_description: "BSI/G7 SBOM-for-AI Models cluster: name, version, identifier, weight hash (NIST-approved algorithm), model card, architecture, datasets, limitations, license",
842            default_severity: ViolationSeverity::Warning,
843            refs: &[(K::BsiSbomForAi, "Models / Model card")],
844            remediation: REMEDIATION_BSIAI_MODELS,
845        },
846        "SBOM-BSIAI-MODEL-ARCHITECTURE" => RuleMeta {
847            sarif_id: "SBOM-BSIAI-MODEL",
848            name: "BsiSbomForAiModels",
849            short_description: "BSI/G7 SBOM-for-AI Models cluster: name, version, identifier, weight hash (NIST-approved algorithm), model card, architecture, datasets, limitations, license",
850            default_severity: ViolationSeverity::Warning,
851            refs: &[(K::BsiSbomForAi, "Models / Architecture")],
852            remediation: REMEDIATION_BSIAI_MODELS,
853        },
854        "SBOM-BSIAI-MODEL-DATASETS" => RuleMeta {
855            sarif_id: "SBOM-BSIAI-MODEL",
856            name: "BsiSbomForAiModels",
857            short_description: "BSI/G7 SBOM-for-AI Models cluster: name, version, identifier, weight hash (NIST-approved algorithm), model card, architecture, datasets, limitations, license",
858            default_severity: ViolationSeverity::Warning,
859            refs: &[(K::BsiSbomForAi, "Models / Training datasets")],
860            remediation: REMEDIATION_BSIAI_MODELS,
861        },
862        "SBOM-BSIAI-MODEL-LIMITATIONS" => RuleMeta {
863            sarif_id: "SBOM-BSIAI-MODEL",
864            name: "BsiSbomForAiModels",
865            short_description: "BSI/G7 SBOM-for-AI Models cluster: name, version, identifier, weight hash (NIST-approved algorithm), model card, architecture, datasets, limitations, license",
866            default_severity: ViolationSeverity::Warning,
867            refs: &[(K::BsiSbomForAi, "Models / Limitations")],
868            remediation: REMEDIATION_BSIAI_MODELS,
869        },
870        "SBOM-BSIAI-MODEL-LICENSE" => RuleMeta {
871            sarif_id: "SBOM-BSIAI-MODEL",
872            name: "BsiSbomForAiModels",
873            short_description: "BSI/G7 SBOM-for-AI Models cluster: name, version, identifier, weight hash (NIST-approved algorithm), model card, architecture, datasets, limitations, license",
874            default_severity: ViolationSeverity::Warning,
875            refs: &[(K::BsiSbomForAi, "Models / Model license")],
876            remediation: REMEDIATION_BSIAI_MODELS,
877        },
878        // Datasets cluster
879        "SBOM-BSIAI-DATASET-NAME" => RuleMeta {
880            sarif_id: "SBOM-BSIAI-DATASET",
881            name: "BsiSbomForAiDatasets",
882            short_description: "BSI/G7 SBOM-for-AI Datasets cluster: name, identifier, hash, license, sensitivity classification, provenance & intended use",
883            default_severity: ViolationSeverity::Error,
884            refs: &[(K::BsiSbomForAi, "Datasets / Dataset name")],
885            remediation: REMEDIATION_BSIAI_DATASETS,
886        },
887        "SBOM-BSIAI-DATASET-IDENTIFIER" => RuleMeta {
888            sarif_id: "SBOM-BSIAI-DATASET",
889            name: "BsiSbomForAiDatasets",
890            short_description: "BSI/G7 SBOM-for-AI Datasets cluster: name, identifier, hash, license, sensitivity classification, provenance & intended use",
891            default_severity: ViolationSeverity::Error,
892            refs: &[(K::BsiSbomForAi, "Datasets / Dataset identifier")],
893            remediation: REMEDIATION_BSIAI_DATASETS,
894        },
895        "SBOM-BSIAI-DATASET-HASH" => RuleMeta {
896            sarif_id: "SBOM-BSIAI-DATASET",
897            name: "BsiSbomForAiDatasets",
898            short_description: "BSI/G7 SBOM-for-AI Datasets cluster: name, identifier, hash, license, sensitivity classification, provenance & intended use",
899            default_severity: ViolationSeverity::Warning,
900            refs: &[(K::BsiSbomForAi, "Datasets / Dataset hash value")],
901            remediation: REMEDIATION_BSIAI_DATASETS,
902        },
903        "SBOM-BSIAI-DATASET-LICENSE" => RuleMeta {
904            sarif_id: "SBOM-BSIAI-DATASET",
905            name: "BsiSbomForAiDatasets",
906            short_description: "BSI/G7 SBOM-for-AI Datasets cluster: name, identifier, hash, license, sensitivity classification, provenance & intended use",
907            default_severity: ViolationSeverity::Warning,
908            refs: &[(K::BsiSbomForAi, "Datasets / Dataset license")],
909            remediation: REMEDIATION_BSIAI_DATASETS,
910        },
911        "SBOM-BSIAI-DATASET-SENSITIVITY" => RuleMeta {
912            sarif_id: "SBOM-BSIAI-DATASET",
913            name: "BsiSbomForAiDatasets",
914            short_description: "BSI/G7 SBOM-for-AI Datasets cluster: name, identifier, hash, license, sensitivity classification, provenance & intended use",
915            default_severity: ViolationSeverity::Warning,
916            refs: &[(K::BsiSbomForAi, "Datasets / Sensitivity classification")],
917            remediation: REMEDIATION_BSIAI_DATASETS,
918        },
919        "SBOM-BSIAI-DATASET-PROVENANCE" => RuleMeta {
920            sarif_id: "SBOM-BSIAI-DATASET",
921            name: "BsiSbomForAiDatasets",
922            short_description: "BSI/G7 SBOM-for-AI Datasets cluster: name, identifier, hash, license, sensitivity classification, provenance & intended use",
923            default_severity: ViolationSeverity::Warning,
924            refs: &[(K::BsiSbomForAi, "Datasets / Provenance & intended use")],
925            remediation: REMEDIATION_BSIAI_DATASETS,
926        },
927        // Infrastructure cluster
928        "SBOM-BSIAI-INFRA-RUNTIME" => RuleMeta {
929            sarif_id: "SBOM-BSIAI-INFRA",
930            name: "BsiSbomForAiInfrastructure",
931            short_description: "BSI/G7 SBOM-for-AI Infrastructure cluster: runtime / framework dependency links",
932            default_severity: ViolationSeverity::Info,
933            refs: &[(K::BsiSbomForAi, "Infrastructure / Runtime & framework")],
934            remediation: REMEDIATION_BSIAI_GENERAL,
935        },
936        // Security cluster
937        "SBOM-BSIAI-SEC-CONTROLS" => RuleMeta {
938            sarif_id: "SBOM-BSIAI-SEC",
939            name: "BsiSbomForAiSecurity",
940            short_description: "BSI/G7 SBOM-for-AI Security cluster: AI-specific security controls, exploitability references",
941            default_severity: ViolationSeverity::Info,
942            refs: &[(K::BsiSbomForAi, "Security / AI security controls")],
943            remediation: REMEDIATION_BSIAI_GENERAL,
944        },
945        "SBOM-BSIAI-SEC-EXPLOITABILITY" => RuleMeta {
946            sarif_id: "SBOM-BSIAI-SEC",
947            name: "BsiSbomForAiSecurity",
948            short_description: "BSI/G7 SBOM-for-AI Security cluster: AI-specific security controls, exploitability references",
949            default_severity: ViolationSeverity::Info,
950            refs: &[(K::BsiSbomForAi, "Security / Exploitability reference")],
951            remediation: REMEDIATION_BSIAI_GENERAL,
952        },
953        // Generic bucket for BSI/G7-SBOM-for-AI-run findings whose check site
954        // has no specific registry mapping (SARIF fallback re-bucketing).
955        "SBOM-BSIAI-GENERAL" => RuleMeta {
956            sarif_id: "SBOM-BSIAI-GENERAL",
957            name: "BsiSbomForAiGeneralRequirement",
958            short_description: "BSI/G7 SBOM-for-AI: general minimum-elements requirement",
959            default_severity: ViolationSeverity::Warning,
960            refs: &[(K::BsiSbomForAi, "Minimum Elements")],
961            remediation: REMEDIATION_BSIAI_GENERAL,
962        },
963        // ---- NTIA --------------------------------------------------------
964        "SBOM-NTIA-VERSION" => RuleMeta {
965            sarif_id: "SBOM-NTIA-VERSION",
966            name: "NtiaVersion",
967            short_description: "NTIA Minimum Elements: Component version string",
968            default_severity: ViolationSeverity::Error,
969            refs: &[(K::NtiaMinimum, "NTIA Minimum Elements")],
970            remediation: REMEDIATION_GENERIC_NTIA,
971        },
972        "SBOM-NTIA-TIMESTAMP" => RuleMeta {
973            sarif_id: "SBOM-NTIA-TIMESTAMP",
974            name: "NtiaTimestamp",
975            short_description: "NTIA Minimum Elements: Creation timestamp",
976            default_severity: ViolationSeverity::Error,
977            refs: &[(K::NtiaMinimum, "NTIA Minimum Elements")],
978            remediation: REMEDIATION_GENERIC_NTIA,
979        },
980        "SBOM-NTIA-SUPPLIER" => RuleMeta {
981            sarif_id: "SBOM-NTIA-SUPPLIER",
982            name: "NtiaSupplier",
983            short_description: "NTIA Minimum Elements: Supplier name",
984            default_severity: ViolationSeverity::Error,
985            refs: &[(K::NtiaMinimum, "NTIA Minimum Elements")],
986            remediation: REMEDIATION_GENERIC_NTIA,
987        },
988        "SBOM-NTIA-DEPENDENCY" => RuleMeta {
989            sarif_id: "SBOM-NTIA-DEPENDENCY",
990            name: "NtiaDependency",
991            short_description: "NTIA Minimum Elements: Dependency relationship",
992            default_severity: ViolationSeverity::Error,
993            refs: &[(K::NtiaMinimum, "NTIA Minimum Elements")],
994            remediation: REMEDIATION_GENERIC_NTIA,
995        },
996        // ---- FDA ---------------------------------------------------------
997        "SBOM-FDA-SUPPLIER" => RuleMeta {
998            sarif_id: "SBOM-FDA-SUPPLIER",
999            name: "FdaSupplier",
1000            short_description: "FDA Medical Device: Component supplier/manufacturer information",
1001            default_severity: ViolationSeverity::Error,
1002            refs: &[(K::FdaPremarket, "FDA Premarket")],
1003            remediation: REMEDIATION_GENERIC_FDA,
1004        },
1005        "SBOM-FDA-SUPPORT" => RuleMeta {
1006            sarif_id: "SBOM-FDA-SUPPORT",
1007            name: "FdaSupport",
1008            short_description: "FDA Medical Device: Component support/contact information",
1009            default_severity: ViolationSeverity::Warning,
1010            refs: &[(K::FdaPremarket, "FDA Premarket")],
1011            remediation: REMEDIATION_GENERIC_FDA,
1012        },
1013        "SBOM-FDA-NAME" => RuleMeta {
1014            sarif_id: "SBOM-FDA-GENERAL",
1015            name: "FdaGeneralRequirement",
1016            short_description: "FDA Medical Device: General SBOM requirement",
1017            default_severity: ViolationSeverity::Warning,
1018            refs: &[(K::FdaPremarket, "FDA Premarket")],
1019            remediation: REMEDIATION_GENERIC_FDA,
1020        },
1021        "SBOM-FDA-VERSION" => RuleMeta {
1022            sarif_id: "SBOM-FDA-VERSION",
1023            name: "FdaVersion",
1024            short_description: "FDA Medical Device: Component version information",
1025            default_severity: ViolationSeverity::Error,
1026            refs: &[(K::FdaPremarket, "FDA Premarket")],
1027            remediation: REMEDIATION_GENERIC_FDA,
1028        },
1029        "SBOM-FDA-IDENTIFIER" => RuleMeta {
1030            sarif_id: "SBOM-FDA-IDENTIFIER",
1031            name: "FdaIdentifier",
1032            short_description: "FDA Medical Device: Component unique identifier (PURL/CPE/SWID)",
1033            default_severity: ViolationSeverity::Error,
1034            refs: &[(K::FdaPremarket, "FDA Premarket")],
1035            remediation: REMEDIATION_GENERIC_FDA,
1036        },
1037        "SBOM-FDA-HASH" => RuleMeta {
1038            sarif_id: "SBOM-FDA-HASH",
1039            name: "FdaHash",
1040            short_description: "FDA Medical Device: Component cryptographic hash",
1041            default_severity: ViolationSeverity::Error,
1042            refs: &[(K::FdaPremarket, "FDA Premarket")],
1043            remediation: REMEDIATION_GENERIC_FDA,
1044        },
1045        // FDA rules emitted by the `validate` NTIA/FDA fast-path
1046        // (`cli::validate`), which builds violations directly without
1047        // populating `standard_refs`.
1048        "SBOM-FDA-CREATOR" => RuleMeta {
1049            sarif_id: "SBOM-FDA-CREATOR",
1050            name: "FdaCreator",
1051            short_description: "FDA Medical Device: SBOM creator/manufacturer information",
1052            default_severity: ViolationSeverity::Warning,
1053            refs: &[(K::FdaPremarket, "FDA Premarket")],
1054            remediation: REMEDIATION_GENERIC_FDA,
1055        },
1056        "SBOM-FDA-NAMESPACE" => RuleMeta {
1057            sarif_id: "SBOM-FDA-NAMESPACE",
1058            name: "FdaNamespace",
1059            short_description: "FDA Medical Device: SBOM serial number or document namespace",
1060            default_severity: ViolationSeverity::Warning,
1061            refs: &[(K::FdaPremarket, "FDA Premarket")],
1062            remediation: REMEDIATION_GENERIC_FDA,
1063        },
1064        "SBOM-FDA-DEPENDENCY" => RuleMeta {
1065            sarif_id: "SBOM-FDA-DEPENDENCY",
1066            name: "FdaDependency",
1067            short_description: "FDA Medical Device: Dependency relationships",
1068            default_severity: ViolationSeverity::Error,
1069            refs: &[(K::FdaPremarket, "FDA Premarket")],
1070            remediation: REMEDIATION_GENERIC_FDA,
1071        },
1072        "SBOM-FDA-SECURITY" => RuleMeta {
1073            sarif_id: "SBOM-FDA-SECURITY",
1074            name: "FdaSecurity",
1075            short_description: "FDA Medical Device: Security vulnerability information",
1076            default_severity: ViolationSeverity::Warning,
1077            refs: &[(K::FdaPremarket, "FDA Premarket")],
1078            remediation: REMEDIATION_GENERIC_FDA,
1079        },
1080        "SBOM-FDA-GENERAL" => RuleMeta {
1081            sarif_id: "SBOM-FDA-GENERAL",
1082            name: "FdaGeneralRequirement",
1083            short_description: "FDA Medical Device: General SBOM requirement",
1084            default_severity: ViolationSeverity::Warning,
1085            refs: &[(K::FdaPremarket, "FDA Premarket")],
1086            remediation: REMEDIATION_GENERIC_FDA,
1087        },
1088        // NTIA rules emitted by the `validate` fast-path.
1089        "SBOM-NTIA-AUTHOR" => RuleMeta {
1090            sarif_id: "SBOM-NTIA-AUTHOR",
1091            name: "NtiaAuthor",
1092            short_description: "NTIA Minimum Elements: Author/creator information",
1093            default_severity: ViolationSeverity::Error,
1094            refs: &[(K::NtiaMinimum, "NTIA Minimum Elements")],
1095            remediation: REMEDIATION_GENERIC_NTIA,
1096        },
1097        "SBOM-NTIA-NAME" => RuleMeta {
1098            sarif_id: "SBOM-NTIA-NAME",
1099            name: "NtiaComponentName",
1100            short_description: "NTIA Minimum Elements: Component name",
1101            default_severity: ViolationSeverity::Error,
1102            refs: &[(K::NtiaMinimum, "NTIA Minimum Elements")],
1103            remediation: REMEDIATION_GENERIC_NTIA,
1104        },
1105        "SBOM-NTIA-IDENTIFIER" => RuleMeta {
1106            sarif_id: "SBOM-NTIA-IDENTIFIER",
1107            name: "NtiaUniqueIdentifier",
1108            short_description: "NTIA Minimum Elements: Unique identifier (PURL/CPE/SWID)",
1109            default_severity: ViolationSeverity::Error,
1110            refs: &[(K::NtiaMinimum, "NTIA Minimum Elements")],
1111            remediation: REMEDIATION_GENERIC_NTIA,
1112        },
1113        "SBOM-NTIA-GENERAL" => RuleMeta {
1114            sarif_id: "SBOM-NTIA-GENERAL",
1115            name: "NtiaGeneralRequirement",
1116            short_description: "NTIA Minimum Elements: General requirement",
1117            default_severity: ViolationSeverity::Warning,
1118            refs: &[(K::NtiaMinimum, "NTIA Minimum Elements")],
1119            remediation: REMEDIATION_GENERIC_NTIA,
1120        },
1121        // Catch-all rule keys for the SSDF / EO 14028 profiles; not currently
1122        // emitted by any check site but kept so the registry mirrors the full
1123        // SARIF rule catalogue.
1124        "SBOM-SSDF-GENERAL" => RuleMeta {
1125            sarif_id: "SBOM-SSDF-GENERAL",
1126            name: "SsdfGeneralRequirement",
1127            short_description: "NIST SSDF: General secure development requirement",
1128            default_severity: ViolationSeverity::Warning,
1129            refs: &[(K::NistSsdf, "SP 800-218")],
1130            remediation: REMEDIATION_SSDF,
1131        },
1132        "SBOM-EO14028-GENERAL" => RuleMeta {
1133            sarif_id: "SBOM-EO14028-GENERAL",
1134            name: "Eo14028GeneralRequirement",
1135            short_description: "EO 14028: General SBOM requirement",
1136            default_severity: ViolationSeverity::Warning,
1137            refs: &[(K::Eo14028, "EO 14028 §4")],
1138            remediation: REMEDIATION_EO14028,
1139        },
1140        // ---- NIST SSDF ---------------------------------------------------
1141        "SBOM-SSDF-PS1" => RuleMeta {
1142            sarif_id: "SBOM-SSDF-PS1",
1143            name: "SsdfProvenance",
1144            short_description: "NIST SSDF PS.1: Provenance and creator identification",
1145            default_severity: ViolationSeverity::Error,
1146            refs: &[(K::NistSsdf, "PS.1")],
1147            remediation: REMEDIATION_SSDF,
1148        },
1149        "SBOM-SSDF-PS2" => RuleMeta {
1150            sarif_id: "SBOM-SSDF-PS2",
1151            name: "SsdfBuildIntegrity",
1152            short_description: "NIST SSDF PS.2: Build integrity — component cryptographic hashes",
1153            default_severity: ViolationSeverity::Error,
1154            refs: &[(K::NistSsdf, "PS.2")],
1155            remediation: REMEDIATION_SSDF,
1156        },
1157        "SBOM-SSDF-PS3" => RuleMeta {
1158            sarif_id: "SBOM-SSDF-PS3",
1159            name: "SsdfSupplierIdentification",
1160            short_description: "NIST SSDF PS.3: Supplier identification for components",
1161            default_severity: ViolationSeverity::Warning,
1162            refs: &[(K::NistSsdf, "PS.3")],
1163            remediation: REMEDIATION_SSDF,
1164        },
1165        "SBOM-SSDF-PO1" => RuleMeta {
1166            sarif_id: "SBOM-SSDF-PO1",
1167            name: "SsdfSourceProvenance",
1168            short_description: "NIST SSDF PO.1: Source code provenance — VCS references",
1169            default_severity: ViolationSeverity::Warning,
1170            refs: &[(K::NistSsdf, "PO.1")],
1171            remediation: REMEDIATION_SSDF,
1172        },
1173        "SBOM-SSDF-PO3" => RuleMeta {
1174            sarif_id: "SBOM-SSDF-PO3",
1175            name: "SsdfBuildMetadata",
1176            short_description: "NIST SSDF PO.3: Build provenance — build system metadata",
1177            default_severity: ViolationSeverity::Info,
1178            refs: &[(K::NistSsdf, "PO.3")],
1179            remediation: REMEDIATION_SSDF,
1180        },
1181        "SBOM-SSDF-PW4" => RuleMeta {
1182            sarif_id: "SBOM-SSDF-PW4",
1183            name: "SsdfDependencyManagement",
1184            short_description: "NIST SSDF PW.4: Dependency management — relationships",
1185            default_severity: ViolationSeverity::Error,
1186            refs: &[(K::NistSsdf, "PW.4")],
1187            remediation: REMEDIATION_SSDF,
1188        },
1189        "SBOM-SSDF-PW6" => RuleMeta {
1190            sarif_id: "SBOM-SSDF-PW6",
1191            name: "SsdfVulnerabilityInfo",
1192            short_description: "NIST SSDF PW.6: Vulnerability information and security references",
1193            default_severity: ViolationSeverity::Info,
1194            refs: &[(K::NistSsdf, "PW.6")],
1195            remediation: REMEDIATION_SSDF,
1196        },
1197        "SBOM-SSDF-RV1" => RuleMeta {
1198            sarif_id: "SBOM-SSDF-RV1",
1199            name: "SsdfComponentIdentification",
1200            short_description: "NIST SSDF RV.1: Component identification — unique identifiers",
1201            default_severity: ViolationSeverity::Warning,
1202            refs: &[(K::NistSsdf, "RV.1")],
1203            remediation: REMEDIATION_SSDF,
1204        },
1205        // ---- EO 14028 ----------------------------------------------------
1206        "SBOM-EO14028-FORMAT" => RuleMeta {
1207            sarif_id: "SBOM-EO14028-FORMAT",
1208            name: "Eo14028MachineReadable",
1209            short_description: "EO 14028 Sec 4(e): Machine-readable SBOM format requirement",
1210            default_severity: ViolationSeverity::Error,
1211            refs: &[(K::Eo14028, "EO 14028 §4")],
1212            remediation: REMEDIATION_EO14028,
1213        },
1214        "SBOM-EO14028-AUTOGEN" => RuleMeta {
1215            sarif_id: "SBOM-EO14028-AUTOGEN",
1216            name: "Eo14028AutoGeneration",
1217            short_description: "EO 14028 Sec 4(e): Automated SBOM generation",
1218            default_severity: ViolationSeverity::Warning,
1219            refs: &[(K::Eo14028, "EO 14028 §4")],
1220            remediation: REMEDIATION_EO14028,
1221        },
1222        "SBOM-EO14028-CREATOR" => RuleMeta {
1223            sarif_id: "SBOM-EO14028-CREATOR",
1224            name: "Eo14028Creator",
1225            short_description: "EO 14028 Sec 4(e): SBOM creator identification",
1226            default_severity: ViolationSeverity::Error,
1227            refs: &[(K::Eo14028, "EO 14028 §4")],
1228            remediation: REMEDIATION_EO14028,
1229        },
1230        "SBOM-EO14028-IDENTIFIER" => RuleMeta {
1231            sarif_id: "SBOM-EO14028-IDENTIFIER",
1232            name: "Eo14028Identifier",
1233            short_description: "EO 14028 Sec 4(e): Component unique identification",
1234            default_severity: ViolationSeverity::Error,
1235            refs: &[(K::Eo14028, "EO 14028 §4")],
1236            remediation: REMEDIATION_EO14028,
1237        },
1238        "SBOM-EO14028-DEPENDENCY" => RuleMeta {
1239            sarif_id: "SBOM-EO14028-DEPENDENCY",
1240            name: "Eo14028Dependency",
1241            short_description: "EO 14028 Sec 4(e): Dependency relationship information",
1242            default_severity: ViolationSeverity::Error,
1243            refs: &[(K::Eo14028, "EO 14028 §4")],
1244            remediation: REMEDIATION_EO14028,
1245        },
1246        "SBOM-EO14028-VERSION" => RuleMeta {
1247            sarif_id: "SBOM-EO14028-VERSION",
1248            name: "Eo14028Version",
1249            short_description: "EO 14028 Sec 4(e): Component version information",
1250            default_severity: ViolationSeverity::Error,
1251            refs: &[(K::Eo14028, "EO 14028 §4")],
1252            remediation: REMEDIATION_EO14028,
1253        },
1254        "SBOM-EO14028-INTEGRITY" => RuleMeta {
1255            sarif_id: "SBOM-EO14028-INTEGRITY",
1256            name: "Eo14028Integrity",
1257            short_description: "EO 14028 Sec 4(e): Component integrity verification (hashes)",
1258            default_severity: ViolationSeverity::Warning,
1259            refs: &[(K::Eo14028, "EO 14028 §4")],
1260            remediation: REMEDIATION_EO14028,
1261        },
1262        "SBOM-EO14028-DISCLOSURE" => RuleMeta {
1263            sarif_id: "SBOM-EO14028-DISCLOSURE",
1264            name: "Eo14028Disclosure",
1265            short_description: "EO 14028 Sec 4(g): Vulnerability disclosure process",
1266            default_severity: ViolationSeverity::Warning,
1267            refs: &[(K::Eo14028, "EO 14028 §4")],
1268            remediation: REMEDIATION_EO14028,
1269        },
1270        "SBOM-EO14028-SUPPLIER" => RuleMeta {
1271            sarif_id: "SBOM-EO14028-SUPPLIER",
1272            name: "Eo14028Supplier",
1273            short_description: "EO 14028 Sec 4(e): Supplier identification",
1274            default_severity: ViolationSeverity::Error,
1275            refs: &[(K::Eo14028, "EO 14028 §4")],
1276            remediation: REMEDIATION_EO14028,
1277        },
1278        "SBOM-EO14028-TIMESTAMP" => RuleMeta {
1279            sarif_id: "SBOM-EO14028-TIMESTAMP",
1280            name: "Eo14028Timestamp",
1281            short_description: "EO 14028 Sec 4(e): SBOM creation timestamp (NTIA baseline)",
1282            default_severity: ViolationSeverity::Error,
1283            refs: &[(K::Eo14028, "EO 14028 §4"), (K::NtiaMinimum, "Timestamp")],
1284            remediation: REMEDIATION_EO14028,
1285        },
1286        "SBOM-EO14028-NAME" => RuleMeta {
1287            sarif_id: "SBOM-EO14028-NAME",
1288            name: "Eo14028ComponentName",
1289            short_description: "EO 14028 Sec 4(e): Component name (NTIA baseline)",
1290            default_severity: ViolationSeverity::Error,
1291            refs: &[
1292                (K::Eo14028, "EO 14028 §4"),
1293                (K::NtiaMinimum, "Component Name"),
1294            ],
1295            remediation: REMEDIATION_EO14028,
1296        },
1297        // ---- BSI TR-03183-2 (v2.1.0, 2025-08-20) --------------------------
1298        "SBOM-BSI-TR-03183-2-4" => RuleMeta {
1299            sarif_id: "SBOM-BSI-TR-03183-2-4",
1300            name: "BsiTr03183FormatEligibility",
1301            short_description: "BSI TR-03183-2 v2.1.0 §4: Newly generated/updated SBOMs must be CycloneDX 1.6+ or SPDX 3.0.1+",
1302            default_severity: ViolationSeverity::Error,
1303            refs: &[(K::BsiTr03183_2, "§4"), (K::BsiTr03183_2, "§7")],
1304            remediation: "Regenerate the SBOM as CycloneDX 1.6+ or SPDX 3.0.1+ in JSON or XML. TR-03183-2 v2.1.0 §4 lists the eligible specifications for newly generated or updated SBOMs; the §7 transitional grace for the v2.0.0 minimums (CycloneDX 1.5 / SPDX 2.2.1) ended 2026-02-20.",
1305        },
1306        "SBOM-BSI-TR-03183-2-5-1" => RuleMeta {
1307            sarif_id: "SBOM-BSI-TR-03183-2-5-1",
1308            name: "BsiTr03183SbomCreator",
1309            short_description: "BSI TR-03183-2 §5.2.1: Creator of the SBOM (email, or URL if no email)",
1310            default_severity: ViolationSeverity::Error,
1311            refs: &[(K::BsiTr03183_2, "§5.2.1")],
1312            remediation: "Identify the SBOM creator with an email address, or a URL (e.g. the creator's home page) if no email is available. CycloneDX: metadata.authors[].email or metadata.manufacturer; SPDX: CreationInfo creators.",
1313        },
1314        "SBOM-BSI-TR-03183-2-5-1-CONTACT" => RuleMeta {
1315            sarif_id: "SBOM-BSI-TR-03183-2-5-1-CONTACT",
1316            name: "BsiTr03183SbomCreatorContact",
1317            short_description: "BSI TR-03183-2 §5.2.1: SBOM creator must carry an email address or URL",
1318            default_severity: ViolationSeverity::Warning,
1319            refs: &[(K::BsiTr03183_2, "§5.2.1")],
1320            remediation: "Add an email address to the SBOM creator entry, or a URL (creator home page / project web page) when no email exists — a bare name does not satisfy §5.2.1.",
1321        },
1322        "SBOM-BSI-TR-03183-2-5-2" => RuleMeta {
1323            sarif_id: "SBOM-BSI-TR-03183-2-5-2",
1324            name: "BsiTr03183Timestamp",
1325            short_description: "BSI TR-03183-2 §5.2.1: Timestamp of the SBOM data compilation",
1326            default_severity: ViolationSeverity::Error,
1327            refs: &[(K::BsiTr03183_2, "§5.2.1")],
1328            remediation: "Add the date and time of the SBOM data compilation (UTC 'Zulu' timestamps recommended). CycloneDX: metadata.timestamp; SPDX: CreationInfo created.",
1329        },
1330        "SBOM-BSI-TR-03183-2-5-3" => RuleMeta {
1331            sarif_id: "SBOM-BSI-TR-03183-2-5-3",
1332            name: "BsiTr03183ComponentName",
1333            short_description: "BSI TR-03183-2 §5.2.2: Component name (fallback: actual filename)",
1334            default_severity: ViolationSeverity::Error,
1335            refs: &[(K::BsiTr03183_2, "§5.2.2")],
1336            remediation: "Name every component. When the component creator assigned no name, the actual filename MUST be used instead.",
1337        },
1338        "SBOM-BSI-TR-03183-2-VERSION" => RuleMeta {
1339            sarif_id: "SBOM-BSI-TR-03183-2-VERSION",
1340            name: "BsiTr03183ComponentVersion",
1341            short_description: "BSI TR-03183-2 §5.2.2: Component version (fallback: RFC 3339 modification date)",
1342            default_severity: ViolationSeverity::Error,
1343            refs: &[(K::BsiTr03183_2, "§5.2.2")],
1344            remediation: "Version every component (SemVer/CalVer recommended). When no version is assigned, the modification date of the file as RFC 3339 date-time MUST be used instead.",
1345        },
1346        "SBOM-BSI-TR-03183-2-LICENSE" => RuleMeta {
1347            sarif_id: "SBOM-BSI-TR-03183-2-LICENSE",
1348            name: "BsiTr03183DistributionLicence",
1349            short_description: "BSI TR-03183-2 §5.2.2: Distribution licence(s) per component",
1350            default_severity: ViolationSeverity::Error,
1351            refs: &[(K::BsiTr03183_2, "§5.2.2"), (K::BsiTr03183_2, "§6.1")],
1352            remediation: "Declare the distribution licence(s) of every component, named by SPDX licence identifier or expression (§6.1). CycloneDX: component.licenses[]; SPDX: PackageLicenseDeclared / concluded-licence relationships.",
1353        },
1354        "SBOM-BSI-TR-03183-2-LICENSE-SPDX" => RuleMeta {
1355            sarif_id: "SBOM-BSI-TR-03183-2-LICENSE-SPDX",
1356            name: "BsiTr03183SpdxLicenceNaming",
1357            short_description: "BSI TR-03183-2 §6.1: Licences must be named by SPDX identifier/expression",
1358            default_severity: ViolationSeverity::Warning,
1359            refs: &[(K::BsiTr03183_2, "§6.1")],
1360            remediation: "Name licences by SPDX identifier or expression; consult Scancode LicenseDB (LicenseRef-scancode-*) or use LicenseRef-<entity>-* for unlisted licences. Licence text MUST NOT be used as a substitute for an identifier.",
1361        },
1362        "SBOM-BSI-TR-03183-2-CREATOR" => RuleMeta {
1363            sarif_id: "SBOM-BSI-TR-03183-2-CREATOR",
1364            name: "BsiTr03183ComponentCreator",
1365            short_description: "BSI TR-03183-2 §5.2.2: Component creator (email, or URL if no email)",
1366            default_severity: ViolationSeverity::Warning,
1367            refs: &[(K::BsiTr03183_2, "§5.2.2")],
1368            remediation: "Record the component creator — the email address (or URL if no email) of the entity that created/maintains the component. CycloneDX: component.supplier / authors; SPDX: PackageSupplier / PackageOriginator.",
1369        },
1370        "SBOM-BSI-TR-03183-2-5-4" => RuleMeta {
1371            sarif_id: "SBOM-BSI-TR-03183-2-5-4",
1372            name: "BsiTr03183ComponentHash",
1373            short_description: "BSI TR-03183-2 §5.2.2: Hash of the deployable component as SHA-512",
1374            default_severity: ViolationSeverity::Error,
1375            refs: &[(K::BsiTr03183_2, "§5.2.2")],
1376            remediation: "Provide the hash of the deployed/deployable component as SHA-512 — §5.2.2 names the algorithm, so SHA-256 or other algorithms alone do not satisfy the required field (additional hashes may coexist).",
1377        },
1378        "SBOM-BSI-TR-03183-2-5-4-MISSING" => RuleMeta {
1379            sarif_id: "SBOM-BSI-TR-03183-2-5-4-MISSING",
1380            name: "BsiTr03183ComponentHashMissing",
1381            short_description: "BSI TR-03183-2 §5.2.2/§3.2.1: Component has no hash of the deployable form",
1382            default_severity: ViolationSeverity::Warning,
1383            refs: &[(K::BsiTr03183_2, "§5.2.2"), (K::BsiTr03183_2, "§3.2.1")],
1384            remediation: "Add a SHA-512 hash of the deployable component. §3.2.1 permits omission only when the information cannot exist due to the way the component is assembled (e.g. logical components).",
1385        },
1386        "SBOM-BSI-TR-03183-2-5-5" => RuleMeta {
1387            sarif_id: "SBOM-BSI-TR-03183-2-5-5",
1388            name: "BsiTr03183Dependencies",
1389            short_description: "BSI TR-03183-2 §5.2.2: Dependencies on other components",
1390            default_severity: ViolationSeverity::Error,
1391            refs: &[(K::BsiTr03183_2, "§5.2.2")],
1392            remediation: "Enumerate all direct dependencies of each component. CycloneDX: dependencies[]; SPDX: DEPENDS_ON / DEPENDENCY_OF relationships.",
1393        },
1394        "SBOM-BSI-TR-03183-2-5-5-COMPLETENESS" => RuleMeta {
1395            sarif_id: "SBOM-BSI-TR-03183-2-5-5-COMPLETENESS",
1396            name: "BsiTr03183DependencyCompleteness",
1397            short_description: "BSI TR-03183-2 §5.2.2: Completeness of the dependency enumeration must be clearly indicated",
1398            default_severity: ViolationSeverity::Warning,
1399            refs: &[(K::BsiTr03183_2, "§5.2.2")],
1400            remediation: "Clearly indicate the completeness of the dependency enumeration, e.g. CycloneDX compositions[].aggregate = complete / incomplete.",
1401        },
1402        "SBOM-BSI-TR-03183-2-5-2-4" => RuleMeta {
1403            sarif_id: "SBOM-BSI-TR-03183-2-5-2-4",
1404            name: "BsiTr03183UniqueIdentifier",
1405            short_description: "BSI TR-03183-2 §5.2.4: Other unique identifiers (purl/CPE) — additional tier",
1406            default_severity: ViolationSeverity::Warning,
1407            refs: &[(K::BsiTr03183_2, "§5.2.4")],
1408            remediation: "Add unique identifiers (purl, CPE) to components. §5.2.4 is the additional tier: the field MUST be provided when an identifier exists for the component.",
1409        },
1410        "SBOM-BSI-TR-03183-2-3-1" => RuleMeta {
1411            sarif_id: "SBOM-BSI-TR-03183-2-3-1",
1412            name: "BsiTr03183NoVulnerabilityInfo",
1413            short_description: "BSI TR-03183-2 §3.1: An SBOM must not contain vulnerability information",
1414            default_severity: ViolationSeverity::Warning,
1415            refs: &[(K::BsiTr03183_2, "§3.1")],
1416            remediation: "Remove vulnerability information from the SBOM and publish it separately (e.g. as CSAF advisories); a document containing both SBOM and vulnerability information does not conform to TR-03183-2.",
1417        },
1418        // Catch-all descriptor for the BSI TR-03183-2 profile; not emitted by
1419        // any check site, kept so the registry-generated SARIF catalogue
1420        // mirrors the historical hand-maintained rule table.
1421        "SBOM-BSI-TR-03183-2-GENERAL" => RuleMeta {
1422            sarif_id: "SBOM-BSI-TR-03183-2-GENERAL",
1423            name: "BsiTr03183General",
1424            short_description: "BSI TR-03183-2 general SBOM requirement",
1425            default_severity: ViolationSeverity::Warning,
1426            refs: &[(K::BsiTr03183_2, "TR-03183-2")],
1427            remediation: REMEDIATION_GENERIC_BSI,
1428        },
1429        // ---- CNSA 2.0 ----------------------------------------------------
1430        "SBOM-CNSA2-000" => RuleMeta {
1431            sarif_id: "SBOM-CNSA2-000",
1432            name: "Cnsa2CryptoInventory",
1433            short_description: "CNSA 2.0: cryptographic inventory (CBOM) with evaluable assets required — compliance cannot be verified without one",
1434            default_severity: ViolationSeverity::Error,
1435            refs: &[(K::Cnsa2, "CNSA 2.0")],
1436            remediation: REMEDIATION_CNSA2,
1437        },
1438        "SBOM-CNSA2-ALG-001" => RuleMeta {
1439            sarif_id: "SBOM-CNSA2-ALG-001",
1440            name: "Cnsa2SymmetricAlgorithm",
1441            short_description: "CNSA 2.0: symmetric encryption must be AES-256",
1442            default_severity: ViolationSeverity::Error,
1443            refs: &[(K::Cnsa2, "CNSA 2.0")],
1444            remediation: REMEDIATION_CNSA2,
1445        },
1446        "SBOM-CNSA2-ALG-002" => RuleMeta {
1447            sarif_id: "SBOM-CNSA2-ALG-002",
1448            name: "Cnsa2HashAlgorithm",
1449            short_description: "CNSA 2.0: hashing must be SHA-384 or SHA-512",
1450            default_severity: ViolationSeverity::Error,
1451            refs: &[(K::Cnsa2, "CNSA 2.0")],
1452            remediation: REMEDIATION_CNSA2,
1453        },
1454        "SBOM-CNSA2-ALG-003" => RuleMeta {
1455            sarif_id: "SBOM-CNSA2-ALG-003",
1456            name: "Cnsa2KeyEstablishment",
1457            short_description: "CNSA 2.0: key establishment must be ML-KEM-1024",
1458            default_severity: ViolationSeverity::Error,
1459            refs: &[(K::Cnsa2, "CNSA 2.0")],
1460            remediation: REMEDIATION_CNSA2,
1461        },
1462        "SBOM-CNSA2-ALG-004" => RuleMeta {
1463            sarif_id: "SBOM-CNSA2-ALG-004",
1464            name: "Cnsa2SignatureAlgorithm",
1465            short_description: "CNSA 2.0: digital signatures must be ML-DSA-87 or SP 800-208 stateful hash-based signatures",
1466            default_severity: ViolationSeverity::Error,
1467            refs: &[(K::Cnsa2, "CNSA 2.0")],
1468            remediation: REMEDIATION_CNSA2,
1469        },
1470        "SBOM-CNSA2-ALG-006" => RuleMeta {
1471            sarif_id: "SBOM-CNSA2-ALG-006",
1472            name: "Cnsa2QuantumVulnerable",
1473            short_description: "CNSA 2.0: quantum-vulnerable classical algorithm must migrate to the CNSA 2.0 suite",
1474            default_severity: ViolationSeverity::Error,
1475            refs: &[(K::Cnsa2, "CNSA 2.0")],
1476            remediation: REMEDIATION_CNSA2,
1477        },
1478        "SBOM-CNSA2-ALG-007" => RuleMeta {
1479            sarif_id: "SBOM-CNSA2-ALG-007",
1480            name: "Cnsa2QuantumSecurityLevel",
1481            short_description: "CNSA 2.0: declared quantum security level below Level 5",
1482            default_severity: ViolationSeverity::Error,
1483            refs: &[(K::Cnsa2, "CNSA 2.0")],
1484            remediation: REMEDIATION_CNSA2,
1485        },
1486        // Broken/legacy algorithm (SHA-1, MD5, DES, RC4, ...) under the CNSA
1487        // 2.0 allowlist.
1488        "SBOM-CNSA2-ALG-005" => RuleMeta {
1489            sarif_id: "SBOM-CNSA2-ALG-005",
1490            name: "Cnsa2BrokenAlgorithm",
1491            short_description: "CNSA 2.0: broken legacy algorithm (SHA-1, MD5, DES, RC4, …) in use",
1492            default_severity: ViolationSeverity::Error,
1493            refs: &[(K::Cnsa2, "CNSA 2.0")],
1494            remediation: REMEDIATION_CNSA2,
1495        },
1496        // Recognized algorithm that is simply not on the CNSA 2.0 allowlist
1497        // (ChaCha20, Camellia, SHA-3, SLH-DSA, Falcon, ...).
1498        "SBOM-CNSA2-ALG-008" => RuleMeta {
1499            sarif_id: "SBOM-CNSA2-ALG-008",
1500            name: "Cnsa2UnapprovedAlgorithm",
1501            short_description: "CNSA 2.0: recognized algorithm that is not on the CNSA 2.0 allowlist",
1502            default_severity: ViolationSeverity::Error,
1503            refs: &[(K::Cnsa2, "CNSA 2.0")],
1504            remediation: REMEDIATION_CNSA2,
1505        },
1506        // Unclassifiable algorithm asset — CNSA 2.0 compliance cannot be
1507        // verified (Warning, never a silent pass).
1508        "SBOM-CNSA2-ALG-UNKNOWN" => RuleMeta {
1509            sarif_id: "SBOM-CNSA2-ALG-UNKNOWN",
1510            name: "Cnsa2UnclassifiableAlgorithm",
1511            short_description: "CNSA 2.0: algorithm cannot be classified — compliance cannot be verified",
1512            default_severity: ViolationSeverity::Warning,
1513            refs: &[(K::Cnsa2, "CNSA 2.0")],
1514            remediation: REMEDIATION_CNSA2,
1515        },
1516        "SBOM-CNSA2-CERT-001" => RuleMeta {
1517            sarif_id: "SBOM-CNSA2-CERT-001",
1518            name: "Cnsa2CertificateSignature",
1519            short_description: "CNSA 2.0: certificate signature algorithm must be CNSA 2.0 approved",
1520            default_severity: ViolationSeverity::Error,
1521            refs: &[(K::Cnsa2, "CNSA 2.0")],
1522            remediation: REMEDIATION_CNSA2,
1523        },
1524        // Certificate signature-algorithm ref cannot be resolved/classified —
1525        // CNSA 2.0 compliance cannot be verified (Warning, never a silent
1526        // pass).
1527        "SBOM-CNSA2-CERT-UNKNOWN" => RuleMeta {
1528            sarif_id: "SBOM-CNSA2-CERT-UNKNOWN",
1529            name: "Cnsa2CertificateUnverifiable",
1530            short_description: "CNSA 2.0: certificate signature algorithm cannot be resolved — compliance cannot be verified",
1531            default_severity: ViolationSeverity::Warning,
1532            refs: &[(K::Cnsa2, "CNSA 2.0")],
1533            remediation: REMEDIATION_CNSA2,
1534        },
1535        // Protocol version gate: CNSA 2.0 network protocols require TLS 1.3.
1536        "SBOM-CNSA2-PROTO-001" => RuleMeta {
1537            sarif_id: "SBOM-CNSA2-PROTO-001",
1538            name: "Cnsa2ProtocolVersion",
1539            short_description: "CNSA 2.0: network protocols must use TLS 1.3",
1540            default_severity: ViolationSeverity::Error,
1541            refs: &[(K::Cnsa2, "CNSA 2.0")],
1542            remediation: REMEDIATION_CNSA2,
1543        },
1544        // Protocol cipher suites / IKEv2 transforms / crypto refs must use
1545        // CNSA 2.0 algorithms.
1546        "SBOM-CNSA2-PROTO-002" => RuleMeta {
1547            sarif_id: "SBOM-CNSA2-PROTO-002",
1548            name: "Cnsa2ProtocolAlgorithms",
1549            short_description: "CNSA 2.0: protocol cipher suites / IKEv2 transforms must use CNSA 2.0 algorithms",
1550            default_severity: ViolationSeverity::Error,
1551            refs: &[(K::Cnsa2, "CNSA 2.0")],
1552            remediation: REMEDIATION_CNSA2,
1553        },
1554        // Protocol asset with unresolvable/unclassifiable crypto references,
1555        // or with nothing evaluable at all — CNSA 2.0 compliance cannot be
1556        // verified (Warning, never a silent pass).
1557        "SBOM-CNSA2-PROTO-UNKNOWN" => RuleMeta {
1558            sarif_id: "SBOM-CNSA2-PROTO-UNKNOWN",
1559            name: "Cnsa2ProtocolUnverifiable",
1560            short_description: "CNSA 2.0: protocol crypto references cannot be resolved — compliance cannot be verified",
1561            default_severity: ViolationSeverity::Warning,
1562            refs: &[(K::Cnsa2, "CNSA 2.0")],
1563            remediation: REMEDIATION_CNSA2,
1564        },
1565        // Generic bucket for CNSA-2.0-run findings whose check site has no
1566        // specific registry mapping (SARIF fallback re-bucketing).
1567        "SBOM-CNSA2-GENERAL" => RuleMeta {
1568            sarif_id: "SBOM-CNSA2-GENERAL",
1569            name: "Cnsa2GeneralRequirement",
1570            short_description: "CNSA 2.0: general algorithm-suite requirement",
1571            default_severity: ViolationSeverity::Warning,
1572            refs: &[(K::Cnsa2, "CNSA 2.0")],
1573            remediation: REMEDIATION_CNSA2,
1574        },
1575        // ---- NIST PQC ----------------------------------------------------
1576        "SBOM-PQC-000" => RuleMeta {
1577            sarif_id: "SBOM-PQC-000",
1578            name: "PqcCryptoInventory",
1579            short_description: "NIST PQC: cryptographic inventory (CBOM) with evaluable assets required — readiness cannot be verified without one",
1580            default_severity: ViolationSeverity::Error,
1581            refs: &[(K::NistPqc, "IR 8547 ipd")],
1582            remediation: REMEDIATION_PQC,
1583        },
1584        "SBOM-PQC-001" => RuleMeta {
1585            sarif_id: "SBOM-PQC-001",
1586            name: "PqcQuantumVulnerable",
1587            short_description: "NIST IR 8547: quantum-vulnerable algorithm must migrate to a NIST PQC standard",
1588            default_severity: ViolationSeverity::Error,
1589            refs: &[
1590                (K::NistPqc, "IR 8547 ipd"),
1591                (K::NistPqc, "SP 800-131A Rev. 2"),
1592            ],
1593            remediation: REMEDIATION_PQC,
1594        },
1595        "SBOM-PQC-012" => RuleMeta {
1596            sarif_id: "SBOM-PQC-012",
1597            name: "PqcQuantumAssessmentMissing",
1598            short_description: "NIST IR 8547: missing quantum security level assessment (nistQuantumSecurityLevel)",
1599            default_severity: ViolationSeverity::Warning,
1600            refs: &[(K::NistPqc, "IR 8547 ipd")],
1601            remediation: REMEDIATION_PQC,
1602        },
1603        "SBOM-PQC-010" => RuleMeta {
1604            sarif_id: "SBOM-PQC-010",
1605            name: "PqcHybridCombiner",
1606            short_description: "NIST PQC: hybrid PQC combiner — recommended transition practice (IR 8547)",
1607            default_severity: ViolationSeverity::Warning,
1608            refs: &[(K::NistPqc, "FIPS 203/204/205")],
1609            remediation: REMEDIATION_PQC,
1610        },
1611        "SBOM-PQC-005" => RuleMeta {
1612            sarif_id: "SBOM-PQC-005",
1613            name: "PqcDisallowedAlgorithm",
1614            short_description: "NIST SP 800-131A: disallowed (broken) algorithm in use",
1615            default_severity: ViolationSeverity::Error,
1616            refs: &[(K::NistPqc, "SP 800-131A Rev. 2")],
1617            remediation: REMEDIATION_PQC,
1618        },
1619        "SBOM-PQC-008" => RuleMeta {
1620            sarif_id: "SBOM-PQC-008",
1621            name: "PqcEcbModeDisallowed",
1622            short_description: "NIST SP 800-131A: ECB mode of operation disallowed",
1623            default_severity: ViolationSeverity::Error,
1624            refs: &[(K::NistPqc, "SP 800-131A Rev. 2")],
1625            remediation: REMEDIATION_PQC,
1626        },
1627        "SBOM-PQC-009" => RuleMeta {
1628            sarif_id: "SBOM-PQC-009",
1629            name: "PqcApprovedAlgorithm",
1630            short_description: "NIST PQC: NIST-approved post-quantum algorithm in use (FIPS 203/204/205, SP 800-208)",
1631            default_severity: ViolationSeverity::Info,
1632            refs: &[(K::NistPqc, "FIPS 203/204/205"), (K::NistPqc, "SP 800-208")],
1633            remediation: REMEDIATION_PQC,
1634        },
1635        "SBOM-PQC-KEY-001" => RuleMeta {
1636            sarif_id: "SBOM-PQC-KEY-001",
1637            name: "PqcMinimumKeySize",
1638            short_description: "NIST SP 800-131A: key size below the approved minimum",
1639            default_severity: ViolationSeverity::Error,
1640            refs: &[(K::NistPqc, "SP 800-131A Rev. 2")],
1641            remediation: REMEDIATION_PQC,
1642        },
1643        // Certificate signed with a broken or quantum-vulnerable algorithm.
1644        "SBOM-PQC-CERT-001" => RuleMeta {
1645            sarif_id: "SBOM-PQC-CERT-001",
1646            name: "PqcCertificateSignature",
1647            short_description: "NIST IR 8547: certificate signed with a broken or quantum-vulnerable algorithm",
1648            default_severity: ViolationSeverity::Error,
1649            refs: &[(K::NistPqc, "IR 8547 ipd")],
1650            remediation: REMEDIATION_PQC_CERT,
1651        },
1652        // Certificate signature-algorithm ref cannot be resolved/classified —
1653        // PQC readiness cannot be verified (Warning, never a silent pass).
1654        "SBOM-PQC-CERT-UNKNOWN" => RuleMeta {
1655            sarif_id: "SBOM-PQC-CERT-UNKNOWN",
1656            name: "PqcCertificateUnverifiable",
1657            short_description: "NIST PQC: certificate signature algorithm cannot be resolved — readiness cannot be verified",
1658            default_severity: ViolationSeverity::Warning,
1659            refs: &[(K::NistPqc, "IR 8547 ipd")],
1660            remediation: REMEDIATION_PQC_UNKNOWN,
1661        },
1662        // Protocol version gate: SSL / TLS below 1.2 disallowed.
1663        "SBOM-PQC-PROTO-001" => RuleMeta {
1664            sarif_id: "SBOM-PQC-PROTO-001",
1665            name: "PqcProtocolVersion",
1666            short_description: "NIST SP 800-52 Rev. 2: SSL and TLS below 1.2 are disallowed",
1667            default_severity: ViolationSeverity::Error,
1668            refs: &[(K::NistPqc, "SP 800-52 Rev. 2")],
1669            remediation: REMEDIATION_PQC_PROTO,
1670        },
1671        // Protocol cipher suites / IKEv2 transforms / crypto refs contain
1672        // broken or quantum-vulnerable algorithms.
1673        "SBOM-PQC-PROTO-002" => RuleMeta {
1674            sarif_id: "SBOM-PQC-PROTO-002",
1675            name: "PqcProtocolAlgorithms",
1676            short_description: "NIST PQC: protocol negotiates broken or quantum-vulnerable algorithms (SP 800-131A / IR 8547)",
1677            default_severity: ViolationSeverity::Error,
1678            refs: &[(K::NistPqc, "SP 800-131A / IR 8547")],
1679            remediation: REMEDIATION_PQC_PROTO,
1680        },
1681        // Protocol asset with unresolvable/unclassifiable crypto references,
1682        // an unparseable TLS version, or nothing evaluable at all — PQC
1683        // readiness cannot be verified (Warning, never a silent pass).
1684        "SBOM-PQC-PROTO-UNKNOWN" => RuleMeta {
1685            sarif_id: "SBOM-PQC-PROTO-UNKNOWN",
1686            name: "PqcProtocolUnverifiable",
1687            short_description: "NIST PQC: protocol crypto references cannot be verified — readiness cannot be verified",
1688            default_severity: ViolationSeverity::Warning,
1689            refs: &[(K::NistPqc, "IR 8547 ipd")],
1690            remediation: REMEDIATION_PQC_UNKNOWN,
1691        },
1692        // Generic bucket for NIST-PQC-run findings whose check site has no
1693        // specific registry mapping (SARIF fallback re-bucketing).
1694        "SBOM-PQC-GENERAL" => RuleMeta {
1695            sarif_id: "SBOM-PQC-GENERAL",
1696            name: "PqcGeneralRequirement",
1697            short_description: "NIST PQC: general post-quantum readiness requirement",
1698            default_severity: ViolationSeverity::Warning,
1699            refs: &[(K::NistPqc, "IR 8547 ipd")],
1700            remediation: REMEDIATION_PQC,
1701        },
1702        // ---- CISA 2026 Minimum Elements (v2.1, July 29, 2026) --------------
1703        // Successor to the NTIA 2021 Minimum Elements. Severity convention
1704        // mirrors the NTIA profile: required data fields = Error;
1705        // evidence-limited / practice checks = Warning (CISA assigns none).
1706        "SBOM-CISA2026-AUTHOR" => RuleMeta {
1707            sarif_id: "SBOM-CISA2026-AUTHOR",
1708            name: "Cisa2026SbomAuthor",
1709            short_description: "CISA 2026: SBOM Author — a person or organization (not tool-only) created the SBOM data",
1710            default_severity: ViolationSeverity::Error,
1711            refs: &[(K::CisaMinimum2026, "SBOM Author")],
1712            remediation: "Add a Person or Organization creator naming the entity that created the SBOM data — the entity operating the generation tool, not the tool itself, so tool-only creator lists do not satisfy the element. Use full names, no acronyms. CycloneDX: metadata.authors; SPDX: Creator: Person/Organization.",
1713        },
1714        "SBOM-CISA2026-SIGNATURE" => RuleMeta {
1715            sarif_id: "SBOM-CISA2026-SIGNATURE",
1716            name: "Cisa2026AuthorSignature",
1717            short_description: "CISA 2026: SBOM Author Signature — digital signature attributable to the SBOM author",
1718            default_severity: ViolationSeverity::Warning,
1719            refs: &[(K::CisaMinimum2026, "SBOM Author Signature")],
1720            remediation: "Sign the SBOM with a digital signature attributable to the SBOM author, using an algorithm approved per NIST DSS, ISO/IEC 14888-4:2024, or the ENISA Agreed Cryptographic Mechanisms. In-document evidence is read from CycloneDX JSF signatures and SPDX 3 verifiedUsing signature entries; SPDX 2.x has no in-document signature field and detached signatures are invisible to this check — hence Warning, not Error.",
1721        },
1722        "SBOM-CISA2026-FORMAT" => RuleMeta {
1723            sarif_id: "SBOM-CISA2026-FORMAT",
1724            name: "Cisa2026DataFormat",
1725            short_description: "CISA 2026: SBOM Data Format Name/Version — machine-processable format, no deprecated format versions",
1726            default_severity: ViolationSeverity::Warning,
1727            refs: &[
1728                (K::CisaMinimum2026, "SBOM Data Format Name"),
1729                (K::CisaMinimum2026, "SBOM Data Format Version"),
1730                (K::CisaMinimum2026, "Machine-Processable Data"),
1731            ],
1732            remediation: "Produce the SBOM in a widely used machine-processable format — SPDX (ISO/IEC 5962:2021) or CycloneDX (ECMA-424); SWID tags were dropped from the 2026 format list — and avoid format versions declared deprecated by the format maintainers. CISA names no deprecated versions: the enforced floor (CycloneDX 1.4+ / SPDX 2.2+, mirroring the repo's EO 14028 gate) is tool policy, not CISA text. Unparseable spec versions skip the gate rather than false-failing.",
1733        },
1734        "SBOM-CISA2026-GENERATION-CONTEXT" => RuleMeta {
1735            sarif_id: "SBOM-CISA2026-GENERATION-CONTEXT",
1736            name: "Cisa2026GenerationContext",
1737            short_description: "CISA 2026: SBOM Generation Context — software lifecycle phase at SBOM generation",
1738            default_severity: ViolationSeverity::Warning,
1739            refs: &[(K::CisaMinimum2026, "SBOM Generation Context")],
1740            remediation: "Declare the lifecycle phase the SBOM was generated in — 'before build', 'build', 'after build', or a more specific identifier. CycloneDX 1.5+: metadata.lifecycles. SPDX 2.x has no standard field (parsers yield no phase for it), hence Warning severity.",
1741        },
1742        "SBOM-CISA2026-TIMESTAMP" => RuleMeta {
1743            sarif_id: "SBOM-CISA2026-TIMESTAMP",
1744            name: "Cisa2026Timestamp",
1745            short_description: "CISA 2026: SBOM Timestamp — date and time of the most recent update to the SBOM data",
1746            default_severity: ViolationSeverity::Error,
1747            refs: &[(K::CisaMinimum2026, "SBOM Timestamp")],
1748            remediation: "Record the date and time of the most recent update to the SBOM data; the 2026 element targets RFC 9557 syntax (source-syntax conformance is not verified by this check — parsers normalize timestamps). CycloneDX: metadata.timestamp; SPDX: Created.",
1749        },
1750        "SBOM-CISA2026-TOOL" => RuleMeta {
1751            sarif_id: "SBOM-CISA2026-TOOL",
1752            name: "Cisa2026ToolName",
1753            short_description: "CISA 2026: SBOM Tool Name — tool used to generate or amend the SBOM",
1754            default_severity: ViolationSeverity::Error,
1755            refs: &[(K::CisaMinimum2026, "SBOM Tool Name")],
1756            remediation: "Identify the tool used by the SBOM author to generate or amend the SBOM (full name, no acronyms unless official). CycloneDX: metadata.tools; SPDX: 'Creator: Tool:'.",
1757        },
1758        "SBOM-CISA2026-TOOL-VERSION" => RuleMeta {
1759            sarif_id: "SBOM-CISA2026-TOOL-VERSION",
1760            name: "Cisa2026ToolVersion",
1761            short_description: "CISA 2026: SBOM Tool Version — version of the SBOM generation tool (or explicit unknown)",
1762            default_severity: ViolationSeverity::Warning,
1763            refs: &[(K::CisaMinimum2026, "SBOM Tool Version")],
1764            remediation: "Declare the version of the tool named in SBOM Tool Name, or explicitly indicate it is unknown. Parsers concatenate tool name and version into one creator name, so the check is heuristic (a trailing version-like token or explicit unknown marker satisfies it) until the model grows a dedicated tool-version field.",
1765        },
1766        "SBOM-CISA2026-SBOM-VERSION" => RuleMeta {
1767            sarif_id: "SBOM-CISA2026-SBOM-VERSION",
1768            name: "Cisa2026SbomVersion",
1769            short_description: "CISA 2026: SBOM Version — the document declares its own version",
1770            default_severity: ViolationSeverity::Warning,
1771            refs: &[(K::CisaMinimum2026, "SBOM Version")],
1772            remediation: "Declare the SBOM document's own version: CycloneDX bom.version (an omitted bom.version is treated as undeclared, not backfilled with the spec default of 1) or a version-distinguishing serial identifier (CycloneDX serialNumber / SPDX documentNamespace; RFC 9562-style UUIDs). Warning because SPDX 2.x has no dedicated document-version field.",
1773        },
1774        "SBOM-CISA2026-PRODUCER" => RuleMeta {
1775            sarif_id: "SBOM-CISA2026-PRODUCER",
1776            name: "Cisa2026ComponentProducer",
1777            short_description: "CISA 2026: Component Producer — entity that creates, defines, and identifies the component",
1778            default_severity: ViolationSeverity::Error,
1779            refs: &[(K::CisaMinimum2026, "Component Producer")],
1780            remediation: "Identify each component's producer (the 2026 rename of the ambiguous Supplier Name): SPDX PackageOriginator / component author is preferred as the entity that created the component, supplier is accepted; if no clear producer exists, explicitly mark the component as of unknown provenance. File-type entries are exempt.",
1781        },
1782        "SBOM-CISA2026-NAME" => RuleMeta {
1783            sarif_id: "SBOM-CISA2026-NAME",
1784            name: "Cisa2026ComponentName",
1785            short_description: "CISA 2026: Component Name — name assigned by the component producer",
1786            default_severity: ViolationSeverity::Error,
1787            refs: &[(K::CisaMinimum2026, "Component Name")],
1788            remediation: "Give every enumerated component the name assigned by its producer (full names, no acronyms); alternate names belong in alias/identifier fields, which the 2026 element allows as multiple entries.",
1789        },
1790        "SBOM-CISA2026-VERSION" => RuleMeta {
1791            sarif_id: "SBOM-CISA2026-VERSION",
1792            name: "Cisa2026ComponentVersion",
1793            short_description: "CISA 2026: Component Version — version present or explicitly marked unknown",
1794            default_severity: ViolationSeverity::Error,
1795            refs: &[(K::CisaMinimum2026, "Component Version")],
1796            remediation: "Declare each component's version; when the producer provides none, explicitly indicate the version is unknown (NOASSERTION/'unknown') per the 2026 escape hatch. An explicit unknown passes this rule — silent absence fails.",
1797        },
1798        "SBOM-CISA2026-IDENTIFIER" => RuleMeta {
1799            sarif_id: "SBOM-CISA2026-IDENTIFIER",
1800            name: "Cisa2026ComponentIdentifiers",
1801            short_description: "CISA 2026: Component Identifiers — at least one machine-processable identifier (PURL/CPE/SWHID/SWID)",
1802            default_severity: ViolationSeverity::Error,
1803            refs: &[(K::CisaMinimum2026, "Component Identifiers")],
1804            remediation: "Add at least one common machine-processable identifier per component — the document names CPE and PURL (ECMA-427); UUIDs, organization-specific identifiers, commit hashes, and intrinsic identifiers (OmniBOR, SWHID / ISO/IEC 18670:2025) also qualify. Include all known identifiers.",
1805        },
1806        "SBOM-CISA2026-HASH" => RuleMeta {
1807            sarif_id: "SBOM-CISA2026-HASH",
1808            name: "Cisa2026ComponentHash",
1809            short_description: "CISA 2026: Component Hash Value — cryptographic hash of the executable component artifact",
1810            default_severity: ViolationSeverity::Error,
1811            refs: &[(K::CisaMinimum2026, "Component Hash Value")],
1812            remediation: "Provide an ASCII-hexadecimal cryptographic hash of each executable component artifact; when the SBOM author lacks access to the artifact, explicitly indicate the value is unknown.",
1813        },
1814        "SBOM-CISA2026-HASH-ALGO" => RuleMeta {
1815            sarif_id: "SBOM-CISA2026-HASH-ALGO",
1816            name: "Cisa2026HashAlgorithm",
1817            short_description: "CISA 2026: Component Hash Algorithm — recognized, authority-approved hash algorithm",
1818            default_severity: ViolationSeverity::Warning,
1819            refs: &[(K::CisaMinimum2026, "Component Hash Algorithm")],
1820            remediation: "Identify hash algorithms using IANA Hash Function Textual Names, and use algorithms approved by a relevant authority such as NIST: MD5 is not NIST-approved; SHA-1 is deprecated and slated for withdrawal by 2030 — use SHA-256 or stronger.",
1821        },
1822        "SBOM-CISA2026-LICENSE" => RuleMeta {
1823            sarif_id: "SBOM-CISA2026-LICENSE",
1824            name: "Cisa2026ComponentLicense",
1825            short_description: "CISA 2026: Component License — license identifier, license pointer, or explicit unknown",
1826            default_severity: ViolationSeverity::Error,
1827            refs: &[(K::CisaMinimum2026, "Component License")],
1828            remediation: "Declare each component's license(s), preferring machine-processable SPDX license identifiers; a LicenseRef-* expression or a pointer to where the full license details are available also satisfies the element, and an explicit unknown (NOASSERTION) is required when the author is unaware. Silent absence fails.",
1829        },
1830        "SBOM-CISA2026-DEPENDENCY" => RuleMeta {
1831            sarif_id: "SBOM-CISA2026-DEPENDENCY",
1832            name: "Cisa2026DependencyRelationship",
1833            short_description: "CISA 2026: Component Dependency Relationship — dependency graph or external SBOM links",
1834            default_severity: ViolationSeverity::Error,
1835            refs: &[(K::CisaMinimum2026, "Component Dependency Relationship")],
1836            remediation: "Declare the relationships where one component is necessary for the operation of the other (CycloneDX: dependencies array; SPDX: DEPENDS_ON). Links to separate SBOM documents per dependency are acceptable alternative evidence.",
1837        },
1838        "SBOM-CISA2026-COVERAGE" => RuleMeta {
1839            sarif_id: "SBOM-CISA2026-COVERAGE",
1840            name: "Cisa2026Coverage",
1841            short_description: "CISA 2026: Coverage / Explicitly Identifying Unknown Information — completeness declaration present",
1842            default_severity: ViolationSeverity::Warning,
1843            refs: &[
1844                (K::CisaMinimum2026, "Coverage"),
1845                (
1846                    K::CisaMinimum2026,
1847                    "Explicitly Identifying Unknown Information",
1848                ),
1849            ],
1850            remediation: "Declare the SBOM's completeness (CycloneDX compositions aggregate): the 2026 Coverage element expects all components including transitive dependencies, and information gaps must be explicitly stated as unknown or deliberately withheld. This rule verifies the declaration, not actual completeness — the document itself points to external repositories / binary analysis for that.",
1851        },
1852        // Generic bucket for CISA-2026-run findings whose check site has no
1853        // specific registry mapping (SARIF fallback re-bucketing).
1854        "SBOM-CISA2026-GENERAL" => RuleMeta {
1855            sarif_id: "SBOM-CISA2026-GENERAL",
1856            name: "Cisa2026GeneralRequirement",
1857            short_description: "CISA 2026 Minimum Elements: general requirement",
1858            default_severity: ViolationSeverity::Warning,
1859            refs: &[(K::CisaMinimum2026, "Minimum Elements")],
1860            remediation: REMEDIATION_GENERIC_CISA2026,
1861        },
1862        "SBOM-PCI-6-3-2-INVENTORY" => RuleMeta {
1863            sarif_id: "SBOM-PCI-6-3-2-INVENTORY",
1864            name: "PciDssInventory",
1865            short_description: "PCI DSS Req. 6.3.2: SBOM is a non-empty inventory with a resolvable primary component",
1866            default_severity: ViolationSeverity::Error,
1867            refs: &[(K::PciDss4, "Req. 6.3.2"), (K::PciDss4, "TP 6.3.2.b")],
1868            remediation: "The SBOM must inventory at least one component and identify the bespoke/custom application it describes (CycloneDX: metadata.component; SPDX: documentDescribes). An empty or headless document cannot serve as the Req. 6.3.2 inventory.",
1869        },
1870        "SBOM-PCI-6-3-2-NAME" => RuleMeta {
1871            sarif_id: "SBOM-PCI-6-3-2-NAME",
1872            name: "PciDssComponentName",
1873            short_description: "PCI DSS Req. 6.3.2: every inventoried (non-file) component has a name",
1874            default_severity: ViolationSeverity::Error,
1875            refs: &[(K::PciDss4, "Req. 6.3.2")],
1876            remediation: "Name every inventoried component — an unnamed entry cannot be correlated with vendor advisories or patches. File/snippet inventory records are exempt, so a file-cataloguing SBOM does not auto-fail the profile.",
1877        },
1878        "SBOM-PCI-6-3-2-VERSION" => RuleMeta {
1879            sarif_id: "SBOM-PCI-6-3-2-VERSION",
1880            name: "PciDssComponentVersion",
1881            short_description: "PCI DSS Req. 6.3.2: every inventoried (non-file) component has a concrete version",
1882            default_severity: ViolationSeverity::Error,
1883            refs: &[(K::PciDss4, "Req. 6.3.2")],
1884            remediation: "Declare a concrete release version for every inventoried component (a version range is acceptable only for external components) — patch management, the requirement's stated purpose, is impossible without versions. File/snippet inventory records are exempt.",
1885        },
1886        "SBOM-PCI-6-3-2-SUPPLIER" => RuleMeta {
1887            sarif_id: "SBOM-PCI-6-3-2-SUPPLIER",
1888            name: "PciDssComponentSupplier",
1889            short_description: "PCI DSS Req. 6.3.2: third-party components identify their supplier/source",
1890            default_severity: ViolationSeverity::Warning,
1891            refs: &[(K::PciDss4, "Req. 6.3.2")],
1892            remediation: "Identify each third-party component's supplier (CycloneDX: component.supplier; SPDX: PackageSupplier) — or fall back to author / group / ecosystem-bearing PURL evidence — so vendor security-patch availability can be monitored.",
1893        },
1894        "SBOM-PCI-6-3-2-IDENTIFIER" => RuleMeta {
1895            sarif_id: "SBOM-PCI-6-3-2-IDENTIFIER",
1896            name: "PciDssComponentIdentifier",
1897            short_description: "PCI DSS Req. 6.3.2: components carry a stable unique identifier for vulnerability correlation",
1898            default_severity: ViolationSeverity::Warning,
1899            refs: &[(K::PciDss4, "Req. 6.3.2"), (K::PciDss4, "TP 6.3.2.a")],
1900            remediation: "Add a stable unique identifier (PURL preferred, else CPE/SWID) so the inventory can be machine-correlated with vulnerability sources per the 'facilitate vulnerability and patch management' clause. PCI DSS prescribes no identifier scheme — this is enabling evidence, not a mandated field.",
1901        },
1902        "SBOM-PCI-6-3-2-THIRD-PARTY" => RuleMeta {
1903            sarif_id: "SBOM-PCI-6-3-2-THIRD-PARTY",
1904            name: "PciDssThirdPartyComponents",
1905            short_description: "PCI DSS TP 6.3.2.b: inventory enumerates incorporated third-party components, not just the application",
1906            default_severity: ViolationSeverity::Warning,
1907            refs: &[(K::PciDss4, "Req. 6.3.2"), (K::PciDss4, "TP 6.3.2.b")],
1908            remediation: "Enumerate the third-party components incorporated into the bespoke/custom software, not only the application itself; a primary-only SBOM passes only when its completeness declaration is Complete (a genuinely dependency-free application). This is an inference — TP 6.3.2.b's real comparison against the software is assessor work.",
1909        },
1910        "SBOM-PCI-6-3-2-COMPLETENESS" => RuleMeta {
1911            sarif_id: "SBOM-PCI-6-3-2-COMPLETENESS",
1912            name: "PciDssCompleteness",
1913            short_description: "PCI DSS TP 6.3.2.b: completeness declaration — self-declared inventory gaps flagged",
1914            default_severity: ViolationSeverity::Warning,
1915            refs: &[(K::PciDss4, "TP 6.3.2.b")],
1916            remediation: "Declare the inventory Complete (CycloneDX compositions aggregate). Explicit Incomplete* declarations warn as self-declared gaps against TP 6.3.2.b; Unknown (no declaration made / explicitly unknown) and NotSpecified (declared but unrecognized, or a no-assertion value) are informational.",
1917        },
1918        "SBOM-PCI-6-3-2-FRESHNESS" => RuleMeta {
1919            sarif_id: "SBOM-PCI-6-3-2-FRESHNESS",
1920            name: "PciDssFreshness",
1921            short_description: "PCI DSS Req. 6.3.2: 'is maintained' — the SBOM carries a creation timestamp",
1922            default_severity: ViolationSeverity::Warning,
1923            refs: &[(K::PciDss4, "Req. 6.3.2"), (K::PciDss4, "TP 6.3.2.a")],
1924            remediation: "Carry a creation timestamp so the inventory's maintenance can be evidenced. The SBOM proves generation time, not the inventory process — a stale timestamp is advisory only.",
1925        },
1926        "SBOM-PCI-6-3-2-VULN-EVIDENCE" => RuleMeta {
1927            sarif_id: "SBOM-PCI-6-3-2-VULN-EVIDENCE",
1928            name: "PciDssVulnerabilityEvidence",
1929            short_description: "PCI DSS TP 6.3.2.a: vulnerability-management hooks (embedded data, advisory refs, or security contact)",
1930            default_severity: ViolationSeverity::Info,
1931            refs: &[(K::PciDss4, "TP 6.3.2.a")],
1932            remediation: "Surface vulnerability-management hooks: embedded vulnerability entries, an Advisories / vulnerability-assertion / linked-VDR external reference, a security contact, or a disclosure URL. Absence is not a Req. 6.3.2 failure — the inventory may feed an external scanner; actual use of the inventory is assessor-verified.",
1933        },
1934        "SBOM-PCI-11-3-1-1-SEVERITY" => RuleMeta {
1935            sarif_id: "SBOM-PCI-11-3-1-1-SEVERITY",
1936            name: "PciDssVulnerabilityRiskRanking",
1937            short_description: "PCI DSS Req. 6.3.1 / 11.3.1.1: embedded vulnerability entries carry a risk ranking",
1938            default_severity: ViolationSeverity::Warning,
1939            refs: &[(K::PciDss4, "Req. 11.3.1.1"), (K::PciDss4, "Req. 6.3.1")],
1940            remediation: "Give every embedded vulnerability entry a risk ranking — a Critical/High/Medium/Low severity or a CVSS score (an entry with only Info/None/Unknown severity and no CVSS is unranked) — so non-high-risk findings can be managed per the entity's Req. 6.3.1 rankings and the 11.3.1.1 targeted risk analysis. Emitted only when vulnerability data is present (no vacuous pass/fail).",
1941        },
1942        // Generic bucket for PCI-DSS-run findings whose check site has no
1943        // specific registry mapping (SARIF fallback re-bucketing).
1944        "SBOM-PCI-GENERAL" => RuleMeta {
1945            sarif_id: "SBOM-PCI-GENERAL",
1946            name: "PciDssGeneralRequirement",
1947            short_description: "PCI DSS v4.0.1 Req. 6.3.2: general software-inventory requirement",
1948            default_severity: ViolationSeverity::Warning,
1949            refs: &[(K::PciDss4, "Req. 6.3.2")],
1950            remediation: REMEDIATION_GENERIC_PCI,
1951        },
1952        "SBOM-FSCT-AUTHOR" => RuleMeta {
1953            sarif_id: "SBOM-FSCT-AUTHOR",
1954            name: "FsctAuthorName",
1955            short_description: "CISA FSCT 3e §2.2.1.1 (Minimum): Author Name — person/organization author, not tool-only",
1956            default_severity: ViolationSeverity::Error,
1957            refs: &[(K::CisaFsct, "§2.2.1.1")],
1958            remediation: "Name the entity that prompted the SBOM's creation (organization, project team, or individual) with unique identification (email address or website) where possible — a tool-only creator list does not satisfy the element. CycloneDX: metadata.authors; SPDX: Creator: Person/Organization.",
1959        },
1960        "SBOM-FSCT-AUTHOR-TOOL" => RuleMeta {
1961            sarif_id: "SBOM-FSCT-AUTHOR-TOOL",
1962            name: "FsctAuthorTool",
1963            short_description: "CISA FSCT 3e §2.2.1.1 (Recommended): tool(s) and version(s) that assisted SBOM creation",
1964            default_severity: ViolationSeverity::Warning,
1965            refs: &[(K::CisaFsct, "§2.2.1.1")],
1966            remediation: "In addition to the authoring entity, identify the tool(s) and version(s) that assisted in creating the SBOM. CycloneDX: metadata.tools (name + version); SPDX: 'Creator: Tool: name-version'.",
1967        },
1968        "SBOM-FSCT-TIMESTAMP" => RuleMeta {
1969            sarif_id: "SBOM-FSCT-TIMESTAMP",
1970            name: "FsctTimestamp",
1971            short_description: "CISA FSCT 3e §2.2.1.2 (Minimum): creation timestamp in a common international format (ISO 8601)",
1972            default_severity: ViolationSeverity::Error,
1973            refs: &[(K::CisaFsct, "§2.2.1.2")],
1974            remediation: "Record the date and time the SBOM was produced in a common international format such as ISO 8601 (e.g., 2024-05-23T13:51:37Z), consistent across time zones and locales.",
1975        },
1976        "SBOM-FSCT-SBOM-TYPE" => RuleMeta {
1977            sarif_id: "SBOM-FSCT-SBOM-TYPE",
1978            name: "FsctSbomType",
1979            short_description: "CISA FSCT 3e §2.2.1.3 (optional/aspirational): SBOM Type declared (design/source/build/analyzed/deployed/runtime)",
1980            default_severity: ViolationSeverity::Info,
1981            refs: &[(K::CisaFsct, "§2.2.1.3")],
1982            remediation: "Declare how/why the SBOM was created per the 'Types of SBOM' taxonomy. CycloneDX 1.5+: metadata.lifecycles; SPDX 3.0: Software.Sbom.sbomType. This tool currently parses neither SPDX 2.x CreatorComment type mapping nor SPDX 3.0 sbomType, so the check is gated to CycloneDX input rather than failing SPDX documents.",
1983        },
1984        "SBOM-FSCT-PRIMARY" => RuleMeta {
1985            sarif_id: "SBOM-FSCT-PRIMARY",
1986            name: "FsctPrimaryComponent",
1987            short_description: "CISA FSCT 3e §2.2.1.4: Primary Component (root of dependencies) identified as the subject of the SBOM",
1988            default_severity: ViolationSeverity::Error,
1989            refs: &[(K::CisaFsct, "§2.2.1.4")],
1990            remediation: "Identify the Primary Component the SBOM is about. CycloneDX: metadata.component; SPDX 2.x: documentDescribes / DESCRIBES relationship; SPDX 3.0: Software.Sbom.rootElement.",
1991        },
1992        "SBOM-FSCT-DIRECT-DEPS" => RuleMeta {
1993            sarif_id: "SBOM-FSCT-DIRECT-DEPS",
1994            name: "FsctDirectDependencies",
1995            short_description: "CISA FSCT 3e §2.2.2 / §2.3.3 (Minimum): all static direct dependencies of the Primary Component identified",
1996            default_severity: ViolationSeverity::Error,
1997            refs: &[(K::CisaFsct, "§2.2.2"), (K::CisaFsct, "§2.3.3")],
1998            remediation: "Identify all static, direct dependencies of the Primary Component (or carry an explicit completeness declaration covering their absence), and indicate when the dependency list is incomplete. 'All' is not verifiable from the document alone — the check uses dependency edges from the primary plus the completeness declaration as its evidence.",
1999        },
2000        "SBOM-FSCT-TRANSITIVE-DEPS" => RuleMeta {
2001            sarif_id: "SBOM-FSCT-TRANSITIVE-DEPS",
2002            name: "FsctTransitiveDependencies",
2003            short_description: "CISA FSCT 3e §2.2.2 (Recommended): subcomponent levels beyond direct dependencies identified",
2004            default_severity: ViolationSeverity::Warning,
2005            refs: &[(K::CisaFsct, "§2.2.2")],
2006            remediation: "Identify as many levels of subcomponents beyond the direct dependencies as possible, or declare their absence via the completeness declaration. Heuristic: 'as many as possible' is not crisply verifiable — the depth>=2 threshold is profile policy.",
2007        },
2008        "SBOM-FSCT-DYNAMIC-DEPS" => RuleMeta {
2009            sarif_id: "SBOM-FSCT-DYNAMIC-DEPS",
2010            name: "FsctDynamicDependencies",
2011            short_description: "CISA FSCT 3e §2.2.2 / §2.2.2.6 (Aspirational): dynamic and/or remote dependencies uniquely identified",
2012            default_severity: ViolationSeverity::Info,
2013            refs: &[(K::CisaFsct, "§2.2.2"), (K::CisaFsct, "§2.2.2.6")],
2014            remediation: "Make efforts to uniquely and unambiguously identify dependencies that are dynamic and/or remote. The positive signal is SPDX relationship types DYNAMIC_LINK / RUNTIME_DEPENDENCY_OF / PROVIDED_DEPENDENCY_OF (CycloneDX's parsed model cannot express it, so the check is SPDX-gated). Absence surfaces as an informational readiness note, never a failure.",
2015        },
2016        "SBOM-FSCT-COMPONENT-NAME" => RuleMeta {
2017            sarif_id: "SBOM-FSCT-COMPONENT-NAME",
2018            name: "FsctComponentName",
2019            short_description: "CISA FSCT 3e §2.2.2.1 (Minimum): commonly used public name declared for every component",
2020            default_severity: ViolationSeverity::Error,
2021            refs: &[(K::CisaFsct, "§2.2.2.1")],
2022            remediation: "Declare the commonly used public name for every component (a namespace:name construct is acceptable for conveying the supplier); placeholder values do not satisfy the element.",
2023        },
2024        "SBOM-FSCT-VERSION" => RuleMeta {
2025            sarif_id: "SBOM-FSCT-VERSION",
2026            name: "FsctComponentVersion",
2027            short_description: "CISA FSCT 3e §2.2.2.2 (Minimum): supplier-provided version string (or authored hash as the documented fallback)",
2028            default_severity: ViolationSeverity::Error,
2029            refs: &[(K::CisaFsct, "§2.2.2.2")],
2030            remediation: "Record the version string as provided by the Supplier (semantic versioning preferred; accurate transcription is the primary goal). A component without a unique version passes only when an author-provided cryptographic hash is present — the element's documented fallback.",
2031        },
2032        "SBOM-FSCT-SUPPLIER" => RuleMeta {
2033            sarif_id: "SBOM-FSCT-SUPPLIER",
2034            name: "FsctSupplierName",
2035            short_description: "CISA FSCT 3e §2.2.2.3 (Minimum): Supplier Name declared for all components (explicit 'unknown' permitted)",
2036            default_severity: ViolationSeverity::Error,
2037            refs: &[(K::CisaFsct, "§2.2.2.3")],
2038            remediation: "Declare the Supplier Name for all components: the upstream supplier's legal-entity name (commercial) or project name (OSS); the domain URL / PURL namespace or an explicit 'unknown' are permitted last resorts. Silent absence fails; an explicit 'unknown' declaration satisfies the letter of the clause.",
2039        },
2040        "SBOM-FSCT-IDENTIFIER" => RuleMeta {
2041            sarif_id: "SBOM-FSCT-IDENTIFIER",
2042            name: "FsctUniqueIdentifier",
2043            short_description: "CISA FSCT 3e §2.2.2.4 (Minimum): globally unique identifier per component (PURL/CPE/SWID/SWHID; hash accepted)",
2044            default_severity: ViolationSeverity::Error,
2045            refs: &[(K::CisaFsct, "§2.2.2.4")],
2046            remediation: "Declare a globally unique identifier for each component — PURL, CPE, SWID, SWHID, UUID/GUID, or OmniBOR Artifact ID; a cryptographic hash also functions as an intrinsic identifier. Profile policy: the document's letter is satisfied by format-native IDs (SPDX namespace + SPDXID, CycloneDX serialNumber + version) and only 'prefers' global uniqueness — this profile deliberately enforces the preferred clause.",
2047        },
2048        "SBOM-FSCT-IDENTIFIER-MULTI" => RuleMeta {
2049            sarif_id: "SBOM-FSCT-IDENTIFIER-MULTI",
2050            name: "FsctIdentifierMultiplicity",
2051            short_description: "CISA FSCT 3e §2.2.2.4 (Recommended): as many globally unique identifiers as available",
2052            default_severity: ViolationSeverity::Warning,
2053            refs: &[(K::CisaFsct, "§2.2.2.4")],
2054            remediation: "List as many globally unique identifiers as are available for the component. Heuristic: the >=2-distinct-identifier-kinds (PURL/CPE/SWHID/SWID) threshold is profile policy — 'as available' is unverifiable from the document alone.",
2055        },
2056        "SBOM-FSCT-HASH" => RuleMeta {
2057            sarif_id: "SBOM-FSCT-HASH",
2058            name: "FsctCryptographicHash",
2059            short_description: "CISA FSCT 3e §2.2.2.5 (Minimum): author-provided cryptographic hash with algorithm, or explicit unknown",
2060            default_severity: ViolationSeverity::Error,
2061            refs: &[(K::CisaFsct, "§2.2.2.5")],
2062            remediation: "Provide a hash (with its algorithm, for reproducibility) for any component whose hash was provided or can be generated; otherwise indicate it as unknown. Accepted at this tier: MD5, SHA1, and SHA2 families — MD5/SHA1 are no longer recommended and are formally discontinued in 2030. Only author-provided hashes count; tool-enriched hashes are not author evidence.",
2063        },
2064        "SBOM-FSCT-HASH-PRIMARY-SHA2" => RuleMeta {
2065            sarif_id: "SBOM-FSCT-HASH-PRIMARY-SHA2",
2066            name: "FsctPrimaryHashSha2",
2067            short_description: "CISA FSCT 3e §2.2.2.5 (Recommended): Primary Component hashed; SHA-256-or-stronger SHA-2 hash on hashed components",
2068            default_severity: ViolationSeverity::Warning,
2069            refs: &[(K::CisaFsct, "§2.2.2.5")],
2070            remediation: "Provide at least one hash of the Primary Component, and use the cryptographically secure SHA-2 family (SHA-256 and higher) for hashed components; wherever less-secure hashes (MD5/SHA1) appear, add an additional cryptographically secure hash.",
2071        },
2072        "SBOM-FSCT-RELATIONSHIP" => RuleMeta {
2073            sarif_id: "SBOM-FSCT-RELATIONSHIP",
2074            name: "FsctRelationship",
2075            short_description: "CISA FSCT 3e §2.2.2.6 (Minimum): relationships declared for the Primary Component and its direct dependencies",
2076            default_severity: ViolationSeverity::Error,
2077            refs: &[(K::CisaFsct, "§2.2.2.6")],
2078            remediation: "Declare relationships (primary, included-in/includes) and relationship completeness for the Primary Component and its direct dependencies — the primary must be identified and connected to its direct dependencies in the edge set.",
2079        },
2080        "SBOM-FSCT-RELATIONSHIP-ALL" => RuleMeta {
2081            sarif_id: "SBOM-FSCT-RELATIONSHIP-ALL",
2082            name: "FsctRelationshipAll",
2083            short_description: "CISA FSCT 3e §2.2.2.6 (Recommended): relationships declared for ALL included components (no orphans)",
2084            default_severity: ViolationSeverity::Warning,
2085            refs: &[(K::CisaFsct, "§2.2.2.6")],
2086            remediation: "Declare relationships and relationship completeness for all included components — components that appear in the inventory but in no dependency edge are orphans.",
2087        },
2088        "SBOM-FSCT-COMPLETENESS" => RuleMeta {
2089            sarif_id: "SBOM-FSCT-COMPLETENESS",
2090            name: "FsctRelationshipCompleteness",
2091            short_description: "CISA FSCT 3e §2.2.2.6.4 (supplemental/optional): relationship-completeness assertion recorded",
2092            default_severity: ViolationSeverity::Warning,
2093            refs: &[(K::CisaFsct, "§2.2.2.6.4"), (K::CisaFsct, "§2.3.3")],
2094            remediation: "Record a relationship-completeness assertion (Unknown/None/Partial/Known) — mapped to the document-level completeness declaration (CycloneDX compositions). Warning, not Error: the document labels the attribute supplemental and optional, with Unknown as the open-world default.",
2095        },
2096        "SBOM-FSCT-LICENSE-PRIMARY" => RuleMeta {
2097            sarif_id: "SBOM-FSCT-LICENSE-PRIMARY",
2098            name: "FsctLicensePrimary",
2099            short_description: "CISA FSCT 3e §2.2.2.7 (Minimum): license information for the Primary Component",
2100            default_severity: ViolationSeverity::Error,
2101            refs: &[(K::CisaFsct, "§2.2.2.7")],
2102            remediation: "Provide license information for the Primary Component, using SPDX license identifiers in standard form where available; NOASSERTION placeholders do not satisfy this check.",
2103        },
2104        "SBOM-FSCT-LICENSE-COVERAGE" => RuleMeta {
2105            sarif_id: "SBOM-FSCT-LICENSE-COVERAGE",
2106            name: "FsctLicenseCoverage",
2107            short_description: "CISA FSCT 3e §2.2.2.7 (Recommended): license information for as many components as possible",
2108            default_severity: ViolationSeverity::Warning,
2109            refs: &[(K::CisaFsct, "§2.2.2.7")],
2110            remediation: "Provide license information for as many components as possible. The coverage threshold is profile policy — 'as possible' is unverifiable from the document alone.",
2111        },
2112        "SBOM-FSCT-LICENSE-ALL" => RuleMeta {
2113            sarif_id: "SBOM-FSCT-LICENSE-ALL",
2114            name: "FsctLicenseAll",
2115            short_description: "CISA FSCT 3e §2.2.2.7 (Aspirational): license information incl. concluded-license attestation for ALL components",
2116            default_severity: ViolationSeverity::Info,
2117            refs: &[(K::CisaFsct, "§2.2.2.7")],
2118            remediation: "Provide license information for all listed components, including concluded-license attestation (SPDX PackageLicenseConcluded; the CycloneDX licenses[].acknowledgement field is not currently parsed, so the concluded prong is SPDX-gated).",
2119        },
2120        "SBOM-FSCT-COPYRIGHT-PRIMARY" => RuleMeta {
2121            sarif_id: "SBOM-FSCT-COPYRIGHT-PRIMARY",
2122            name: "FsctCopyrightPrimary",
2123            short_description: "CISA FSCT 3e §2.2.2.8 (Minimum): copyright notice for the Primary Component",
2124            default_severity: ViolationSeverity::Error,
2125            refs: &[(K::CisaFsct, "§2.2.2.8")],
2126            remediation: "Provide the copyright notice for the Primary Component — it identifies the legal rights holder, and conveying notices is a standard condition of many OSS licenses. SPDX: PackageCopyrightText; CycloneDX: component copyright.",
2127        },
2128        "SBOM-FSCT-COPYRIGHT-COVERAGE" => RuleMeta {
2129            sarif_id: "SBOM-FSCT-COPYRIGHT-COVERAGE",
2130            name: "FsctCopyrightCoverage",
2131            short_description: "CISA FSCT 3e §2.2.2.8 (Recommended): copyright notices for as many components as possible",
2132            default_severity: ViolationSeverity::Warning,
2133            refs: &[(K::CisaFsct, "§2.2.2.8")],
2134            remediation: "Provide copyright notices for as many components as possible. The coverage threshold is profile policy — 'as possible' is unverifiable from the document alone.",
2135        },
2136        "SBOM-FSCT-COPYRIGHT-ALL" => RuleMeta {
2137            sarif_id: "SBOM-FSCT-COPYRIGHT-ALL",
2138            name: "FsctCopyrightAll",
2139            short_description: "CISA FSCT 3e §2.2.2.8 (Aspirational): copyright notice on every listed component",
2140            default_severity: ViolationSeverity::Info,
2141            refs: &[(K::CisaFsct, "§2.2.2.8")],
2142            remediation: "Provide a copyright notice for every listed SBOM component.",
2143        },
2144        "SBOM-FSCT-NOASSERTION" => RuleMeta {
2145            sarif_id: "SBOM-FSCT-NOASSERTION",
2146            name: "FsctNoAssertion",
2147            short_description: "CISA FSCT 3e §2.3.1 (Minimum): baseline attributes populated or explicitly declared no-assertion/no-value",
2148            default_severity: ViolationSeverity::Error,
2149            refs: &[(K::CisaFsct, "§2.3.1")],
2150            remediation: "Provide every baseline attribute, or explicitly differentiate 'no assertion' (data missing) from 'no value' (not applicable). This rule fires only where an attribute is neither populated nor explicitly (or format-default) declared — the document sanctions explicit declarations as the recommended graceful handling and lets formats treat missing attributes as default no-assertion. Placeholders never satisfy the other SBOM-FSCT-* checks.",
2151        },
2152        "SBOM-FSCT-UPSTREAM-SBOM" => RuleMeta {
2153            sarif_id: "SBOM-FSCT-UPSTREAM-SBOM",
2154            name: "FsctUpstreamSbom",
2155            short_description: "CISA FSCT 3e §2.3.3 (Recommended): upstream supplier SBOM data provided or linked for third-party direct dependencies",
2156            default_severity: ViolationSeverity::Warning,
2157            refs: &[(K::CisaFsct, "§2.3.3")],
2158            remediation: "Obtain the upstream Supplier's SBOM and provide the component data nested within the Primary Component's SBOM or linked separately (BOM-type external references on third-party direct dependencies are the positive evidence). Advisory heuristic — contacting suppliers is unobservable in the document.",
2159        },
2160        "SBOM-FSCT-SIGNATURE" => RuleMeta {
2161            sarif_id: "SBOM-FSCT-SIGNATURE",
2162            name: "FsctSignature",
2163            short_description: "CISA FSCT 3e §2.4 (supplemental): SBOM carries a verifiable digital signature",
2164            default_severity: ViolationSeverity::Info,
2165            refs: &[(K::CisaFsct, "§2.4")],
2166            remediation: "Digitally sign the SBOM so consumers can verify authenticity and integrity (requires a digital signature plus PKI). Info severity: §2.4 is a supplemental element, not a Baseline Attribute, and SPDX has no in-band signature field.",
2167        },
2168        // Generic bucket for FSCT-run findings whose check site has no
2169        // specific registry mapping (SARIF fallback re-bucketing).
2170        "SBOM-FSCT-GENERAL" => RuleMeta {
2171            sarif_id: "SBOM-FSCT-GENERAL",
2172            name: "FsctGeneralRequirement",
2173            short_description: "CISA FSCT 3e: general baseline-attribute requirement",
2174            default_severity: ViolationSeverity::Warning,
2175            refs: &[(K::CisaFsct, "Baseline Attributes")],
2176            remediation: REMEDIATION_GENERIC_FSCT,
2177        },
2178        _ => return None,
2179    };
2180    Some(meta)
2181}
2182
2183/// Every stable internal rule key with a `rule_meta` match arm, in match-arm
2184/// order. Kept adjacent to [`rule_meta`]; the `all_rule_ids_matches_the_registry`
2185/// test asserts the list and the match arms stay in lockstep.
2186const ALL_RULE_IDS: &[&str] = &[
2187    "SBOM-CRA-ART-13-2",
2188    "SBOM-CRA-SBOM-FRESHNESS",
2189    "SBOM-CRA-MACHINE-READABLE",
2190    "SBOM-CRA-ART-13-5",
2191    "SBOM-CRA-ART-13-17-CONTACT",
2192    "SBOM-CRA-VULN-METADATA",
2193    "SBOM-CRA-CVD-POLICY",
2194    "SBOM-CRA-ART-13-8",
2195    "SBOM-CRA-VULN-STATEMENT",
2196    "SBOM-CRA-LIFECYCLE",
2197    "SBOM-CRA-ART-13-15-PRODUCT",
2198    "SBOM-CRA-COMPONENT-VERSION",
2199    "SBOM-CRA-ART-24-SUPPLIER",
2200    "SBOM-CRA-ART-13-16",
2201    "SBOM-CRA-ART-13-16-EMAIL",
2202    "SBOM-CRA-COMPONENT-SUPPLIER",
2203    "SBOM-CRA-ART-14",
2204    "SBOM-CRA-ART-24",
2205    "SBOM-CRA-ANNEX-I",
2206    "SBOM-CRA-ANNEX-I-IDENTIFIER",
2207    "SBOM-CRA-ANNEX-I-TRACEABILITY",
2208    "SBOM-CRA-ANNEX-I-SUPPLY-CHAIN",
2209    "SBOM-CRA-ANNEX-I-INTEGRITY",
2210    "SBOM-CRA-ANNEX-I-DEPENDENCY",
2211    "SBOM-CRA-ANNEX-I-PRIMARY",
2212    "SBOM-CRA-ANNEX-I-CONTROLS",
2213    "SBOM-CRA-DOC-INTEGRITY",
2214    "SBOM-CRA-ANNEX-IV",
2215    "SBOM-CRA-ANNEX-V",
2216    "SBOM-CRA-CYCLES",
2217    "SBOM-CRA-ANNEX-VIII",
2218    "SBOM-CRA-PRE-8-RQ-02",
2219    "SBOM-CRA-PRE-7-RQ-07-RE",
2220    "SBOM-CRA-GENERAL",
2221    "SBOM-QUALITY-GENERAL",
2222    "SBOM-EUCC-PP",
2223    "SBOM-EUCC-TOE",
2224    "SBOM-EUCC-ITSEF",
2225    "SBOM-EUCC-VALIDITY",
2226    "SBOM-EUCC-CERTREF",
2227    "SBOM-EUCC-GENERAL",
2228    "SBOM-AIACT-ANNEX-IV-1",
2229    "SBOM-AIACT-ANNEX-IV-2D",
2230    "SBOM-AIACT-ANNEX-IV-2G",
2231    "SBOM-AIACT-ANNEX-IV-2C",
2232    "SBOM-AIACT-ANNEX-IV-3",
2233    "SBOM-AIACT-NA",
2234    "SBOM-AIACT-ANNEX-IV-1-DESCRIPTION",
2235    "SBOM-AIACT-ANNEX-IV-1-PURPOSE",
2236    "SBOM-AIACT-ANNEX-IV-2D-DATASETS",
2237    "SBOM-AIACT-ANNEX-IV-2D-SENSITIVITY",
2238    "SBOM-AIACT-ANNEX-IV-2D-PERSONAL-DATA",
2239    "SBOM-AIACT-ANNEX-IV-2G-METRICS",
2240    "SBOM-AIACT-ANNEX-IV-2C-ENERGY",
2241    "SBOM-AIACT-ANNEX-IV-3-LIMITATIONS",
2242    "SBOM-AIACT-UNTYPED-ML",
2243    "SBOM-AIACT-GENERAL",
2244    "SBOM-BSIAI-META",
2245    "SBOM-BSIAI-SYS",
2246    "SBOM-BSIAI-MODEL",
2247    "SBOM-BSIAI-DATASET",
2248    "SBOM-BSIAI-INFRA",
2249    "SBOM-BSIAI-SEC",
2250    "SBOM-BSIAI-NA",
2251    "SBOM-BSIAI-UNTYPED-ML",
2252    "SBOM-BSIAI-META-AUTHOR",
2253    "SBOM-BSIAI-META-FORMAT",
2254    "SBOM-BSIAI-META-TIMESTAMP",
2255    "SBOM-BSIAI-META-TOOL",
2256    "SBOM-BSIAI-META-SIGNATURE",
2257    "SBOM-BSIAI-SYS-PRIMARY",
2258    "SBOM-BSIAI-SYS-PRODUCER",
2259    "SBOM-BSIAI-SYS-DATAFLOW",
2260    "SBOM-BSIAI-MODEL-NAME",
2261    "SBOM-BSIAI-MODEL-VERSION",
2262    "SBOM-BSIAI-MODEL-IDENTIFIER",
2263    "SBOM-BSIAI-MODEL-HASH",
2264    "SBOM-BSIAI-MODEL-HASH-ALGO",
2265    "SBOM-BSIAI-MODEL-CARD",
2266    "SBOM-BSIAI-MODEL-ARCHITECTURE",
2267    "SBOM-BSIAI-MODEL-DATASETS",
2268    "SBOM-BSIAI-MODEL-LIMITATIONS",
2269    "SBOM-BSIAI-MODEL-LICENSE",
2270    "SBOM-BSIAI-DATASET-NAME",
2271    "SBOM-BSIAI-DATASET-IDENTIFIER",
2272    "SBOM-BSIAI-DATASET-HASH",
2273    "SBOM-BSIAI-DATASET-LICENSE",
2274    "SBOM-BSIAI-DATASET-SENSITIVITY",
2275    "SBOM-BSIAI-DATASET-PROVENANCE",
2276    "SBOM-BSIAI-INFRA-RUNTIME",
2277    "SBOM-BSIAI-SEC-CONTROLS",
2278    "SBOM-BSIAI-SEC-EXPLOITABILITY",
2279    "SBOM-BSIAI-GENERAL",
2280    "SBOM-NTIA-VERSION",
2281    "SBOM-NTIA-TIMESTAMP",
2282    "SBOM-NTIA-SUPPLIER",
2283    "SBOM-NTIA-DEPENDENCY",
2284    "SBOM-FDA-SUPPLIER",
2285    "SBOM-FDA-SUPPORT",
2286    "SBOM-FDA-NAME",
2287    "SBOM-FDA-VERSION",
2288    "SBOM-FDA-IDENTIFIER",
2289    "SBOM-FDA-HASH",
2290    "SBOM-FDA-CREATOR",
2291    "SBOM-FDA-NAMESPACE",
2292    "SBOM-FDA-DEPENDENCY",
2293    "SBOM-FDA-SECURITY",
2294    "SBOM-FDA-GENERAL",
2295    "SBOM-NTIA-AUTHOR",
2296    "SBOM-NTIA-NAME",
2297    "SBOM-NTIA-IDENTIFIER",
2298    "SBOM-NTIA-GENERAL",
2299    "SBOM-SSDF-GENERAL",
2300    "SBOM-EO14028-GENERAL",
2301    "SBOM-SSDF-PS1",
2302    "SBOM-SSDF-PS2",
2303    "SBOM-SSDF-PS3",
2304    "SBOM-SSDF-PO1",
2305    "SBOM-SSDF-PO3",
2306    "SBOM-SSDF-PW4",
2307    "SBOM-SSDF-PW6",
2308    "SBOM-SSDF-RV1",
2309    "SBOM-EO14028-FORMAT",
2310    "SBOM-EO14028-AUTOGEN",
2311    "SBOM-EO14028-CREATOR",
2312    "SBOM-EO14028-IDENTIFIER",
2313    "SBOM-EO14028-DEPENDENCY",
2314    "SBOM-EO14028-VERSION",
2315    "SBOM-EO14028-INTEGRITY",
2316    "SBOM-EO14028-DISCLOSURE",
2317    "SBOM-EO14028-SUPPLIER",
2318    "SBOM-EO14028-TIMESTAMP",
2319    "SBOM-EO14028-NAME",
2320    "SBOM-BSI-TR-03183-2-4",
2321    "SBOM-BSI-TR-03183-2-5-1",
2322    "SBOM-BSI-TR-03183-2-5-1-CONTACT",
2323    "SBOM-BSI-TR-03183-2-5-2",
2324    "SBOM-BSI-TR-03183-2-5-3",
2325    "SBOM-BSI-TR-03183-2-VERSION",
2326    "SBOM-BSI-TR-03183-2-LICENSE",
2327    "SBOM-BSI-TR-03183-2-LICENSE-SPDX",
2328    "SBOM-BSI-TR-03183-2-CREATOR",
2329    "SBOM-BSI-TR-03183-2-5-4",
2330    "SBOM-BSI-TR-03183-2-5-4-MISSING",
2331    "SBOM-BSI-TR-03183-2-5-5",
2332    "SBOM-BSI-TR-03183-2-5-5-COMPLETENESS",
2333    "SBOM-BSI-TR-03183-2-5-2-4",
2334    "SBOM-BSI-TR-03183-2-3-1",
2335    "SBOM-BSI-TR-03183-2-GENERAL",
2336    "SBOM-CNSA2-000",
2337    "SBOM-CNSA2-ALG-001",
2338    "SBOM-CNSA2-ALG-002",
2339    "SBOM-CNSA2-ALG-003",
2340    "SBOM-CNSA2-ALG-004",
2341    "SBOM-CNSA2-ALG-006",
2342    "SBOM-CNSA2-ALG-007",
2343    "SBOM-CNSA2-ALG-005",
2344    "SBOM-CNSA2-ALG-008",
2345    "SBOM-CNSA2-ALG-UNKNOWN",
2346    "SBOM-CNSA2-CERT-001",
2347    "SBOM-CNSA2-CERT-UNKNOWN",
2348    "SBOM-CNSA2-PROTO-001",
2349    "SBOM-CNSA2-PROTO-002",
2350    "SBOM-CNSA2-PROTO-UNKNOWN",
2351    "SBOM-CNSA2-GENERAL",
2352    "SBOM-PQC-000",
2353    "SBOM-PQC-001",
2354    "SBOM-PQC-012",
2355    "SBOM-PQC-010",
2356    "SBOM-PQC-005",
2357    "SBOM-PQC-008",
2358    "SBOM-PQC-009",
2359    "SBOM-PQC-KEY-001",
2360    "SBOM-PQC-CERT-001",
2361    "SBOM-PQC-CERT-UNKNOWN",
2362    "SBOM-PQC-PROTO-001",
2363    "SBOM-PQC-PROTO-002",
2364    "SBOM-PQC-PROTO-UNKNOWN",
2365    "SBOM-PQC-GENERAL",
2366    "SBOM-CISA2026-AUTHOR",
2367    "SBOM-CISA2026-SIGNATURE",
2368    "SBOM-CISA2026-FORMAT",
2369    "SBOM-CISA2026-GENERATION-CONTEXT",
2370    "SBOM-CISA2026-TIMESTAMP",
2371    "SBOM-CISA2026-TOOL",
2372    "SBOM-CISA2026-TOOL-VERSION",
2373    "SBOM-CISA2026-SBOM-VERSION",
2374    "SBOM-CISA2026-PRODUCER",
2375    "SBOM-CISA2026-NAME",
2376    "SBOM-CISA2026-VERSION",
2377    "SBOM-CISA2026-IDENTIFIER",
2378    "SBOM-CISA2026-HASH",
2379    "SBOM-CISA2026-HASH-ALGO",
2380    "SBOM-CISA2026-LICENSE",
2381    "SBOM-CISA2026-DEPENDENCY",
2382    "SBOM-CISA2026-COVERAGE",
2383    "SBOM-CISA2026-GENERAL",
2384    "SBOM-PCI-6-3-2-INVENTORY",
2385    "SBOM-PCI-6-3-2-NAME",
2386    "SBOM-PCI-6-3-2-VERSION",
2387    "SBOM-PCI-6-3-2-SUPPLIER",
2388    "SBOM-PCI-6-3-2-IDENTIFIER",
2389    "SBOM-PCI-6-3-2-THIRD-PARTY",
2390    "SBOM-PCI-6-3-2-COMPLETENESS",
2391    "SBOM-PCI-6-3-2-FRESHNESS",
2392    "SBOM-PCI-6-3-2-VULN-EVIDENCE",
2393    "SBOM-PCI-11-3-1-1-SEVERITY",
2394    "SBOM-PCI-GENERAL",
2395    "SBOM-FSCT-AUTHOR",
2396    "SBOM-FSCT-AUTHOR-TOOL",
2397    "SBOM-FSCT-TIMESTAMP",
2398    "SBOM-FSCT-SBOM-TYPE",
2399    "SBOM-FSCT-PRIMARY",
2400    "SBOM-FSCT-DIRECT-DEPS",
2401    "SBOM-FSCT-TRANSITIVE-DEPS",
2402    "SBOM-FSCT-DYNAMIC-DEPS",
2403    "SBOM-FSCT-COMPONENT-NAME",
2404    "SBOM-FSCT-VERSION",
2405    "SBOM-FSCT-SUPPLIER",
2406    "SBOM-FSCT-IDENTIFIER",
2407    "SBOM-FSCT-IDENTIFIER-MULTI",
2408    "SBOM-FSCT-HASH",
2409    "SBOM-FSCT-HASH-PRIMARY-SHA2",
2410    "SBOM-FSCT-RELATIONSHIP",
2411    "SBOM-FSCT-RELATIONSHIP-ALL",
2412    "SBOM-FSCT-COMPLETENESS",
2413    "SBOM-FSCT-LICENSE-PRIMARY",
2414    "SBOM-FSCT-LICENSE-COVERAGE",
2415    "SBOM-FSCT-LICENSE-ALL",
2416    "SBOM-FSCT-COPYRIGHT-PRIMARY",
2417    "SBOM-FSCT-COPYRIGHT-COVERAGE",
2418    "SBOM-FSCT-COPYRIGHT-ALL",
2419    "SBOM-FSCT-NOASSERTION",
2420    "SBOM-FSCT-UPSTREAM-SBOM",
2421    "SBOM-FSCT-SIGNATURE",
2422    "SBOM-FSCT-GENERAL",
2423];
2424
2425/// Enumerate every registered internal rule key, in registry order.
2426#[must_use]
2427pub fn all_rule_ids() -> &'static [&'static str] {
2428    ALL_RULE_IDS
2429}
2430
2431/// Map an owned rule-id string back to the registry's `&'static str` key.
2432/// Used when deserializing payloads that carry a serialized `rule_id`, so a
2433/// round-tripped violation keeps its registry identity instead of collapsing
2434/// to the generic default.
2435#[must_use]
2436pub(crate) fn lookup_static_rule_id(rule_id: &str) -> Option<&'static str> {
2437    ALL_RULE_IDS.iter().find(|k| **k == rule_id).copied()
2438}
2439
2440// ---------------------------------------------------------------------------
2441// Per-standard SARIF rule catalogues.
2442//
2443// Each slice lists, in display order, the externally-visible SARIF rule ids a
2444// standard's report declares as reportingDescriptors. Every id must be a
2445// registry key whose `sarif_id` equals the key itself (a self-descriptor);
2446// the `sarif_rule_slices_are_self_descriptors` test enforces this. The SARIF
2447// generator (src/reports/sarif.rs) renders these through `rule_meta`, so
2448// name / shortDescription / defaultConfiguration.level can no longer drift
2449// from the registry.
2450// ---------------------------------------------------------------------------
2451
2452/// NTIA Minimum Elements SARIF rule catalogue.
2453pub const NTIA_SARIF_RULE_IDS: &[&str] = &[
2454    "SBOM-NTIA-AUTHOR",
2455    "SBOM-NTIA-NAME",
2456    "SBOM-NTIA-VERSION",
2457    "SBOM-NTIA-SUPPLIER",
2458    "SBOM-NTIA-IDENTIFIER",
2459    "SBOM-NTIA-DEPENDENCY",
2460    "SBOM-NTIA-TIMESTAMP",
2461    "SBOM-NTIA-GENERAL",
2462];
2463
2464/// FDA premarket SARIF rule catalogue. The FDA baseline check reuses the
2465/// NTIA timestamp rule id (the FDA guidance incorporates the NTIA minimum
2466/// elements).
2467pub const FDA_SARIF_RULE_IDS: &[&str] = &[
2468    "SBOM-NTIA-TIMESTAMP",
2469    "SBOM-FDA-CREATOR",
2470    "SBOM-FDA-NAMESPACE",
2471    "SBOM-FDA-SUPPLIER",
2472    "SBOM-FDA-HASH",
2473    "SBOM-FDA-IDENTIFIER",
2474    "SBOM-FDA-VERSION",
2475    "SBOM-FDA-DEPENDENCY",
2476    "SBOM-FDA-SUPPORT",
2477    "SBOM-FDA-SECURITY",
2478    "SBOM-FDA-GENERAL",
2479];
2480
2481/// NIST SSDF (SP 800-218) SARIF rule catalogue.
2482pub const SSDF_SARIF_RULE_IDS: &[&str] = &[
2483    "SBOM-SSDF-PS1",
2484    "SBOM-SSDF-PS2",
2485    "SBOM-SSDF-PS3",
2486    "SBOM-SSDF-PO1",
2487    "SBOM-SSDF-PO3",
2488    "SBOM-SSDF-PW4",
2489    "SBOM-SSDF-PW6",
2490    "SBOM-SSDF-RV1",
2491    "SBOM-SSDF-GENERAL",
2492];
2493
2494/// EO 14028 Section 4 SARIF rule catalogue.
2495pub const EO14028_SARIF_RULE_IDS: &[&str] = &[
2496    "SBOM-EO14028-TIMESTAMP",
2497    "SBOM-EO14028-NAME",
2498    "SBOM-EO14028-FORMAT",
2499    "SBOM-EO14028-AUTOGEN",
2500    "SBOM-EO14028-CREATOR",
2501    "SBOM-EO14028-IDENTIFIER",
2502    "SBOM-EO14028-DEPENDENCY",
2503    "SBOM-EO14028-VERSION",
2504    "SBOM-EO14028-INTEGRITY",
2505    "SBOM-EO14028-DISCLOSURE",
2506    "SBOM-EO14028-SUPPLIER",
2507    "SBOM-EO14028-GENERAL",
2508];
2509
2510/// Shared CRA / EUCC / BSI TR-03183-2 / EU AI Act / BSI-G7 SBOM-for-AI SARIF
2511/// rule catalogue (the default for CRA-family and readiness profiles).
2512pub const COMPLIANCE_SARIF_RULE_IDS: &[&str] = &[
2513    "SBOM-CRA-SBOM-FRESHNESS",
2514    "SBOM-CRA-MACHINE-READABLE",
2515    "SBOM-CRA-ART-13-17-CONTACT",
2516    "SBOM-CRA-VULN-METADATA",
2517    "SBOM-CRA-ART-13-5",
2518    "SBOM-CRA-CVD-POLICY",
2519    "SBOM-CRA-ART-13-8",
2520    "SBOM-CRA-LIFECYCLE",
2521    "SBOM-CRA-ART-13-15-PRODUCT",
2522    "SBOM-CRA-COMPONENT-VERSION",
2523    "SBOM-CRA-ART-13-16",
2524    "SBOM-CRA-ART-13-16-EMAIL",
2525    "SBOM-CRA-COMPONENT-SUPPLIER",
2526    "SBOM-CRA-VULN-STATEMENT",
2527    "SBOM-CRA-ANNEX-I",
2528    "SBOM-CRA-DOC-INTEGRITY",
2529    "SBOM-CRA-ANNEX-V",
2530    "SBOM-CRA-GENERAL",
2531    "SBOM-CRA-PRE-8-RQ-02",
2532    "SBOM-CRA-PRE-7-RQ-07-RE",
2533    "SBOM-EUCC-PP",
2534    "SBOM-EUCC-TOE",
2535    "SBOM-EUCC-ITSEF",
2536    "SBOM-EUCC-VALIDITY",
2537    "SBOM-EUCC-CERTREF",
2538    "SBOM-BSI-TR-03183-2-4",
2539    "SBOM-BSI-TR-03183-2-5-1",
2540    "SBOM-BSI-TR-03183-2-5-1-CONTACT",
2541    "SBOM-BSI-TR-03183-2-5-2",
2542    "SBOM-BSI-TR-03183-2-5-3",
2543    "SBOM-BSI-TR-03183-2-VERSION",
2544    "SBOM-BSI-TR-03183-2-LICENSE",
2545    "SBOM-BSI-TR-03183-2-LICENSE-SPDX",
2546    "SBOM-BSI-TR-03183-2-CREATOR",
2547    "SBOM-BSI-TR-03183-2-5-4",
2548    "SBOM-BSI-TR-03183-2-5-4-MISSING",
2549    "SBOM-BSI-TR-03183-2-5-5",
2550    "SBOM-BSI-TR-03183-2-5-5-COMPLETENESS",
2551    "SBOM-BSI-TR-03183-2-5-2-4",
2552    "SBOM-BSI-TR-03183-2-3-1",
2553    "SBOM-BSI-TR-03183-2-GENERAL",
2554    "SBOM-AIACT-NA",
2555    "SBOM-AIACT-ANNEX-IV-1",
2556    "SBOM-AIACT-ANNEX-IV-2D",
2557    "SBOM-AIACT-ANNEX-IV-2G",
2558    "SBOM-AIACT-ANNEX-IV-2C",
2559    "SBOM-AIACT-ANNEX-IV-3",
2560    "SBOM-AIACT-UNTYPED-ML",
2561    "SBOM-BSIAI-NA",
2562    "SBOM-BSIAI-META",
2563    "SBOM-BSIAI-SYS",
2564    "SBOM-BSIAI-MODEL",
2565    "SBOM-BSIAI-DATASET",
2566    "SBOM-BSIAI-INFRA",
2567    "SBOM-BSIAI-SEC",
2568    "SBOM-BSIAI-UNTYPED-ML",
2569];
2570
2571/// NSA CNSA 2.0 SARIF rule catalogue: every `SBOM-CNSA2-*` self-descriptor
2572/// in the registry. The `cnsa2_and_pqc_slices_cover_their_rule_families`
2573/// test keeps this slice in lockstep with the registry, so a CNSA 2.0 run
2574/// declares its own rule family instead of the CRA-family catalogue.
2575pub const CNSA2_SARIF_RULE_IDS: &[&str] = &[
2576    "SBOM-CNSA2-000",
2577    "SBOM-CNSA2-ALG-001",
2578    "SBOM-CNSA2-ALG-002",
2579    "SBOM-CNSA2-ALG-003",
2580    "SBOM-CNSA2-ALG-004",
2581    "SBOM-CNSA2-ALG-005",
2582    "SBOM-CNSA2-ALG-006",
2583    "SBOM-CNSA2-ALG-007",
2584    "SBOM-CNSA2-ALG-008",
2585    "SBOM-CNSA2-ALG-UNKNOWN",
2586    "SBOM-CNSA2-CERT-001",
2587    "SBOM-CNSA2-CERT-UNKNOWN",
2588    "SBOM-CNSA2-PROTO-001",
2589    "SBOM-CNSA2-PROTO-002",
2590    "SBOM-CNSA2-PROTO-UNKNOWN",
2591    "SBOM-CNSA2-GENERAL",
2592];
2593
2594/// NIST PQC readiness SARIF rule catalogue: every `SBOM-PQC-*`
2595/// self-descriptor in the registry. Same lockstep guarantee as
2596/// [`CNSA2_SARIF_RULE_IDS`].
2597pub const PQC_SARIF_RULE_IDS: &[&str] = &[
2598    "SBOM-PQC-000",
2599    "SBOM-PQC-001",
2600    "SBOM-PQC-005",
2601    "SBOM-PQC-008",
2602    "SBOM-PQC-009",
2603    "SBOM-PQC-010",
2604    "SBOM-PQC-012",
2605    "SBOM-PQC-KEY-001",
2606    "SBOM-PQC-CERT-001",
2607    "SBOM-PQC-CERT-UNKNOWN",
2608    "SBOM-PQC-PROTO-001",
2609    "SBOM-PQC-PROTO-002",
2610    "SBOM-PQC-PROTO-UNKNOWN",
2611    "SBOM-PQC-GENERAL",
2612];
2613
2614/// CISA 2026 Minimum Elements SARIF rule catalogue: every `SBOM-CISA2026-*`
2615/// self-descriptor in the registry. The
2616/// `p4_profile_slices_cover_their_rule_families` test keeps this slice in
2617/// lockstep with the registry.
2618pub const CISA2026_SARIF_RULE_IDS: &[&str] = &[
2619    "SBOM-CISA2026-AUTHOR",
2620    "SBOM-CISA2026-SIGNATURE",
2621    "SBOM-CISA2026-FORMAT",
2622    "SBOM-CISA2026-GENERATION-CONTEXT",
2623    "SBOM-CISA2026-TIMESTAMP",
2624    "SBOM-CISA2026-TOOL",
2625    "SBOM-CISA2026-TOOL-VERSION",
2626    "SBOM-CISA2026-SBOM-VERSION",
2627    "SBOM-CISA2026-PRODUCER",
2628    "SBOM-CISA2026-NAME",
2629    "SBOM-CISA2026-VERSION",
2630    "SBOM-CISA2026-IDENTIFIER",
2631    "SBOM-CISA2026-HASH",
2632    "SBOM-CISA2026-HASH-ALGO",
2633    "SBOM-CISA2026-LICENSE",
2634    "SBOM-CISA2026-DEPENDENCY",
2635    "SBOM-CISA2026-COVERAGE",
2636    "SBOM-CISA2026-GENERAL",
2637];
2638
2639/// PCI DSS v4.0.1 Req. 6.3.2 SARIF rule catalogue: every `SBOM-PCI-*`
2640/// self-descriptor in the registry. Same lockstep guarantee as
2641/// [`CISA2026_SARIF_RULE_IDS`].
2642pub const PCIDSS_SARIF_RULE_IDS: &[&str] = &[
2643    "SBOM-PCI-6-3-2-INVENTORY",
2644    "SBOM-PCI-6-3-2-NAME",
2645    "SBOM-PCI-6-3-2-VERSION",
2646    "SBOM-PCI-6-3-2-SUPPLIER",
2647    "SBOM-PCI-6-3-2-IDENTIFIER",
2648    "SBOM-PCI-6-3-2-THIRD-PARTY",
2649    "SBOM-PCI-6-3-2-COMPLETENESS",
2650    "SBOM-PCI-6-3-2-FRESHNESS",
2651    "SBOM-PCI-6-3-2-VULN-EVIDENCE",
2652    "SBOM-PCI-11-3-1-1-SEVERITY",
2653    "SBOM-PCI-GENERAL",
2654];
2655
2656/// CISA FSCT 3rd-edition SARIF rule catalogue: every `SBOM-FSCT-*`
2657/// self-descriptor in the registry. Same lockstep guarantee as
2658/// [`CISA2026_SARIF_RULE_IDS`].
2659pub const FSCT_SARIF_RULE_IDS: &[&str] = &[
2660    "SBOM-FSCT-AUTHOR",
2661    "SBOM-FSCT-AUTHOR-TOOL",
2662    "SBOM-FSCT-TIMESTAMP",
2663    "SBOM-FSCT-SBOM-TYPE",
2664    "SBOM-FSCT-PRIMARY",
2665    "SBOM-FSCT-DIRECT-DEPS",
2666    "SBOM-FSCT-TRANSITIVE-DEPS",
2667    "SBOM-FSCT-DYNAMIC-DEPS",
2668    "SBOM-FSCT-COMPONENT-NAME",
2669    "SBOM-FSCT-VERSION",
2670    "SBOM-FSCT-SUPPLIER",
2671    "SBOM-FSCT-IDENTIFIER",
2672    "SBOM-FSCT-IDENTIFIER-MULTI",
2673    "SBOM-FSCT-HASH",
2674    "SBOM-FSCT-HASH-PRIMARY-SHA2",
2675    "SBOM-FSCT-RELATIONSHIP",
2676    "SBOM-FSCT-RELATIONSHIP-ALL",
2677    "SBOM-FSCT-COMPLETENESS",
2678    "SBOM-FSCT-LICENSE-PRIMARY",
2679    "SBOM-FSCT-LICENSE-COVERAGE",
2680    "SBOM-FSCT-LICENSE-ALL",
2681    "SBOM-FSCT-COPYRIGHT-PRIMARY",
2682    "SBOM-FSCT-COPYRIGHT-COVERAGE",
2683    "SBOM-FSCT-COPYRIGHT-ALL",
2684    "SBOM-FSCT-NOASSERTION",
2685    "SBOM-FSCT-UPSTREAM-SBOM",
2686    "SBOM-FSCT-SIGNATURE",
2687    "SBOM-FSCT-GENERAL",
2688];
2689
2690#[cfg(test)]
2691mod tests {
2692    use super::*;
2693
2694    /// `ALL_RULE_IDS` and the `rule_meta` match arms must stay in lockstep:
2695    /// every listed id resolves, every match arm is listed, no duplicates.
2696    /// The match arms are recovered from the source text of this file, so a
2697    /// new arm cannot land without being enumerated.
2698    #[test]
2699    fn all_rule_ids_matches_the_registry() {
2700        let src = include_str!("registry.rs");
2701        let mut match_arms = Vec::new();
2702        for line in src.lines() {
2703            let t = line.trim();
2704            if let Some(rest) = t.strip_prefix('"')
2705                && let Some(id) = rest.strip_suffix("\" => RuleMeta {")
2706            {
2707                match_arms.push(id.to_string());
2708            }
2709        }
2710        let listed: Vec<&str> = all_rule_ids().to_vec();
2711        let listed_set: std::collections::BTreeSet<&str> = listed.iter().copied().collect();
2712        assert_eq!(
2713            listed.len(),
2714            listed_set.len(),
2715            "ALL_RULE_IDS contains duplicates"
2716        );
2717        let arm_set: std::collections::BTreeSet<&str> =
2718            match_arms.iter().map(String::as_str).collect();
2719        assert_eq!(
2720            arm_set, listed_set,
2721            "rule_meta match arms and ALL_RULE_IDS drifted apart"
2722        );
2723        for id in listed {
2724            assert!(rule_meta(id).is_some(), "listed id {id:?} does not resolve");
2725        }
2726    }
2727
2728    /// CNSA 2.0 rules must remediate with CNSA 2.0 guidance, not the generic
2729    /// fallback: SBOM-CNSA2-ALG-001..004/006/007, -000 and -CERT-001 used
2730    /// `REMEDIATION_GENERIC`, which cites the EU CRA (EU 2024/2847) — a US
2731    /// NSA CNSA 2.0 finding pointed the user at an EU regulation.
2732    #[test]
2733    fn cnsa2_rules_cite_cnsa_not_eu_cra() {
2734        for id in all_rule_ids().iter().filter(|id| id.contains("CNSA2")) {
2735            let meta = rule_meta(id).expect("listed id resolves");
2736            assert!(
2737                !meta.remediation.contains("EU CRA"),
2738                "{id}: a CNSA 2.0 rule must not cite the EU CRA as its remediation source"
2739            );
2740            assert!(
2741                meta.remediation.contains("CNSA 2.0"),
2742                "{id}: CNSA 2.0 rules should carry CNSA 2.0 migration guidance"
2743            );
2744        }
2745    }
2746
2747    /// Non-CRA standards must not fall back to the CRA-citing generic
2748    /// remediation: an NTIA/FDA/AI-Act/BSI finding that points the user at
2749    /// EU 2024/2847 cites the wrong regulation (same defect class as the
2750    /// CNSA case above). CRA rules keep the CRA citation.
2751    #[test]
2752    fn non_cra_rules_do_not_cite_eu_cra_as_generic_fallback() {
2753        for id in all_rule_ids()
2754            .iter()
2755            .filter(|id| id.contains("NTIA") || id.contains("FDA") || id.contains("AIACT"))
2756        {
2757            let meta = rule_meta(id).expect("listed id resolves");
2758            assert!(
2759                !meta.remediation.contains("EU CRA regulation"),
2760                "{id}: a non-CRA rule must not cite the EU CRA as its generic remediation"
2761            );
2762        }
2763        let bsi = rule_meta("SBOM-BSI-TR-03183-2-GENERAL").expect("BSI general rule resolves");
2764        assert!(
2765            bsi.remediation.contains("TR-03183-2"),
2766            "BSI general rule should cite TR-03183-2, got: {}",
2767            bsi.remediation
2768        );
2769    }
2770
2771    /// Every id in a per-standard SARIF slice must be a self-descriptor:
2772    /// a registry key whose `sarif_id` is the key itself. The SARIF
2773    /// generator relies on this to render descriptors without aliasing.
2774    #[test]
2775    fn sarif_rule_slices_are_self_descriptors() {
2776        for (label, slice) in [
2777            ("ntia", NTIA_SARIF_RULE_IDS),
2778            ("fda", FDA_SARIF_RULE_IDS),
2779            ("ssdf", SSDF_SARIF_RULE_IDS),
2780            ("eo14028", EO14028_SARIF_RULE_IDS),
2781            ("compliance", COMPLIANCE_SARIF_RULE_IDS),
2782            ("cnsa2", CNSA2_SARIF_RULE_IDS),
2783            ("pqc", PQC_SARIF_RULE_IDS),
2784            ("cisa2026", CISA2026_SARIF_RULE_IDS),
2785            ("pci-dss", PCIDSS_SARIF_RULE_IDS),
2786            ("fsct", FSCT_SARIF_RULE_IDS),
2787        ] {
2788            let mut seen = std::collections::BTreeSet::new();
2789            for id in slice {
2790                assert!(seen.insert(*id), "[{label}] duplicate slice id {id}");
2791                let meta = rule_meta(id)
2792                    .unwrap_or_else(|| panic!("[{label}] slice id {id} not in registry"));
2793                assert_eq!(
2794                    meta.sarif_id, *id,
2795                    "[{label}] slice id {id} aliases to {}; slices must list self-descriptors",
2796                    meta.sarif_id
2797                );
2798            }
2799        }
2800    }
2801
2802    /// The CNSA 2.0 / PQC catalogues must enumerate their entire rule
2803    /// family: a registry rule missing from its slice would only surface via
2804    /// the catalogue-completion backfill when it happens to fire, putting
2805    /// SARIF consumers' suppressions/baselines back on an incomplete
2806    /// catalogue.
2807    #[test]
2808    fn cnsa2_and_pqc_slices_cover_their_rule_families() {
2809        for (prefix, slice) in [
2810            ("SBOM-CNSA2-", CNSA2_SARIF_RULE_IDS),
2811            ("SBOM-PQC-", PQC_SARIF_RULE_IDS),
2812        ] {
2813            let expected: std::collections::BTreeSet<&str> = all_rule_ids()
2814                .iter()
2815                .copied()
2816                .filter(|id| id.starts_with(prefix))
2817                .collect();
2818            let actual: std::collections::BTreeSet<&str> = slice.iter().copied().collect();
2819            assert_eq!(
2820                actual, expected,
2821                "{prefix}* SARIF slice drifted from the registry"
2822            );
2823        }
2824    }
2825
2826    /// The CISA 2026 / PCI DSS / FSCT catalogues must enumerate their entire
2827    /// rule family (same guarantee as the CNSA 2.0 / PQC test above): the
2828    /// parallel checker wave adds check sites but may not touch this file,
2829    /// so a family rule missing from its slice would silently fall off the
2830    /// upfront-declared SARIF catalogue.
2831    #[test]
2832    fn p4_profile_slices_cover_their_rule_families() {
2833        for (prefix, slice) in [
2834            ("SBOM-CISA2026-", CISA2026_SARIF_RULE_IDS),
2835            ("SBOM-PCI-", PCIDSS_SARIF_RULE_IDS),
2836            ("SBOM-FSCT-", FSCT_SARIF_RULE_IDS),
2837        ] {
2838            let expected: std::collections::BTreeSet<&str> = all_rule_ids()
2839                .iter()
2840                .copied()
2841                .filter(|id| id.starts_with(prefix))
2842                .collect();
2843            let actual: std::collections::BTreeSet<&str> = slice.iter().copied().collect();
2844            assert_eq!(
2845                actual, expected,
2846                "{prefix}* SARIF slice drifted from the registry"
2847            );
2848        }
2849    }
2850
2851    /// Aliased keys (key != sarif_id) must carry the canonical descriptor's
2852    /// name and short description, so every surface renders the shared SARIF
2853    /// rule identically.
2854    #[test]
2855    fn aliased_keys_share_the_canonical_descriptor_text() {
2856        for id in all_rule_ids() {
2857            let meta = rule_meta(id).expect("listed id resolves");
2858            if meta.sarif_id == *id {
2859                continue;
2860            }
2861            let canonical = rule_meta(meta.sarif_id).unwrap_or_else(|| {
2862                panic!(
2863                    "{id} aliases to {} which has no self-descriptor",
2864                    meta.sarif_id
2865                )
2866            });
2867            assert_eq!(
2868                meta.name, canonical.name,
2869                "{id} name differs from its canonical descriptor {}",
2870                meta.sarif_id
2871            );
2872            assert_eq!(
2873                meta.short_description, canonical.short_description,
2874                "{id} short_description differs from its canonical descriptor {}",
2875                meta.sarif_id
2876            );
2877        }
2878    }
2879}