Files
prowler/ui/lib/compliance/cmmc.ts
T

103 lines
3.4 KiB
TypeScript

import type {
AttributesData,
CMMCAttributesMetadata,
Framework,
Requirement,
RequirementsData,
RequirementStatus,
} from "@/types/compliance";
import {
calculateFrameworkCounters,
createRequirementsMap,
findOrCreateCategory,
findOrCreateControl,
findOrCreateFramework,
getStatusCounters,
} from "./commons";
export { toGroupedAccordionItems as toAccordionItems } from "./grouped-accordion";
// Canonical NIST SP 800-171 family order for the 14 CMMC domains, so the
// accordion always reads in the same order regardless of the API response.
export const CMMC_DOMAIN_ORDER: readonly string[] = [
"Access Control",
"Awareness and Training",
"Audit and Accountability",
"Configuration Management",
"Identification and Authentication",
"Incident Response",
"Maintenance",
"Media Protection",
"Personnel Security",
"Physical Protection",
"Risk Assessment",
"Security Assessment",
"System and Communications Protection",
"System and Information Integrity",
];
export const mapComplianceData = (
attributesData: AttributesData,
requirementsData: RequirementsData,
): Framework[] => {
const attributes = attributesData?.data || [];
const requirementsMap = createRequirementsMap(requirementsData);
const frameworks: Framework[] = [];
for (const attributeItem of attributes) {
const id = attributeItem.id;
const metadataArray = attributeItem.attributes?.attributes
?.metadata as unknown as CMMCAttributesMetadata[];
const attrs = metadataArray?.[0];
if (!attrs) continue;
const requirementData = requirementsMap.get(id);
if (!requirementData) continue;
const frameworkName = attributeItem.attributes.framework;
// Group by Domain. Level and SourceRequirement live inside the requirement
// so they show up on the detail drawer.
const categoryName = attrs.Domain;
const requirementName = attributeItem.attributes.name || "";
const description = attributeItem.attributes.description;
const status = requirementData.attributes.status || "";
const checks = attributeItem.attributes.attributes.check_ids || [];
const framework = findOrCreateFramework(frameworks, frameworkName);
const category = findOrCreateCategory(framework.categories, categoryName);
// Flat 2-level structure: domain → requirements (no intermediate control).
const control = findOrCreateControl(category.controls, categoryName);
const finalStatus: RequirementStatus = status as RequirementStatus;
const requirement: Requirement = {
name: requirementName ? `${id} - ${requirementName}` : id,
description,
status: finalStatus,
check_ids: checks,
invalid_config: requirementData.attributes.invalid_config || false,
...getStatusCounters(finalStatus),
domain: attrs.Domain,
level: attrs.Level,
source_requirement: attrs.SourceRequirement,
};
control.requirements.push(requirement);
}
// Sort domains by the canonical NIST 800-171 family order.
for (const framework of frameworks) {
framework.categories.sort((a, b) => {
const ia = CMMC_DOMAIN_ORDER.indexOf(a.name);
const ib = CMMC_DOMAIN_ORDER.indexOf(b.name);
const orderA = ia === -1 ? CMMC_DOMAIN_ORDER.length : ia;
const orderB = ib === -1 ? CMMC_DOMAIN_ORDER.length : ib;
return orderA - orderB;
});
}
calculateFrameworkCounters(frameworks);
return frameworks;
};