mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-25 21:04:19 +00:00
Co-authored-by: alejandrobailo <alejandrobailo94@gmail.com>
103 lines
3.4 KiB
TypeScript
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;
|
|
};
|