import { ClientAccordionContent } from "@/components/compliance/compliance-accordion/client-accordion-content"; import { ComplianceAccordionRequirementTitle } from "@/components/compliance/compliance-accordion/compliance-accordion-requeriment-title"; import { ComplianceAccordionTitle } from "@/components/compliance/compliance-accordion/compliance-accordion-title"; import { AccordionItemProps } from "@/components/shadcn/accordion/Accordion"; import { FindingStatus } from "@/components/shadcn/table/status-finding-badge"; import { AttributesData, CISAttributesMetadata, Framework, Requirement, REQUIREMENT_STATUS, RequirementsData, RequirementStatus, } from "@/types/compliance"; import { calculateFrameworkCounters, createRequirementsMap, findOrCreateCategory, findOrCreateFramework, updateCounters, } from "./commons"; export const mapComplianceData = ( attributesData: AttributesData, requirementsData: RequirementsData, filter?: string, // "Level 1" or "Level 2" or undefined (show all) ): Framework[] => { const attributes = attributesData?.data || []; const requirementsMap = createRequirementsMap(requirementsData); const frameworks: Framework[] = []; // Process attributes and merge with requirements data for (const attributeItem of attributes) { const id = attributeItem.id; const metadataArray = attributeItem.attributes?.attributes ?.metadata as unknown as CISAttributesMetadata[]; const attrs = metadataArray?.[0]; if (!attrs) continue; // Apply profile filter. // Most CIS benchmarks use "Level 1"/"Level 2" as the Profile, but some // (e.g. M365) prefix it with the license tier: "E3 Level 1", "E5 Level 2". // Match by suffix so the Level 1 filter works across all CIS variants. if (filter === "Level 1" && !attrs.Profile?.endsWith("Level 1")) { continue; // Skip Level 2 requirements when Level 1 is selected } // Get corresponding requirement data const requirementData = requirementsMap.get(id); if (!requirementData) continue; const frameworkName = attributeItem.attributes.framework; const sectionName = attrs.Section; const description = attributeItem.attributes.description; const status = requirementData.attributes.status || ""; const checks = attributeItem.attributes.attributes.check_ids || []; const requirementName = id; // Find or create framework using common helper const framework = findOrCreateFramework(frameworks, frameworkName); const normalizedSectionName = sectionName.replace(/^(\d+)\s/, "$1. "); const category = findOrCreateCategory( framework.categories, normalizedSectionName, ); // Create a control for this requirement (each requirement is its own control) const controlLabel = `${id} - ${description}`; const control = { label: controlLabel, pass: 0, fail: 0, manual: 0, requirements: [] as Requirement[], }; // Create requirement const finalStatus: RequirementStatus = status as RequirementStatus; const requirement: Requirement = { name: requirementName, description: attrs.Description, status: finalStatus, check_ids: checks, invalid_config: requirementData.attributes.invalid_config || false, pass: finalStatus === REQUIREMENT_STATUS.PASS ? 1 : 0, fail: finalStatus === REQUIREMENT_STATUS.FAIL ? 1 : 0, manual: finalStatus === REQUIREMENT_STATUS.MANUAL ? 1 : 0, profile: attrs.Profile, subsection: attrs.SubSection || "", assessment_status: attrs.AssessmentStatus, rationale_statement: attrs.RationaleStatement, impact_statement: attrs.ImpactStatement, remediation_procedure: attrs.RemediationProcedure, audit_procedure: attrs.AuditProcedure, additional_information: attrs.AdditionalInformation, default_value: attrs.DefaultValue || "", references: attrs.References, }; control.requirements.push(requirement); // Update control counters using common helper updateCounters(control, requirement.status); category.controls.push(control); } // Calculate counters using common helper calculateFrameworkCounters(frameworks); return frameworks; }; export const toAccordionItems = ( data: Framework[], scanId: string | undefined, ): AccordionItemProps[] => { return data.flatMap((framework) => framework.categories.map((category) => { return { key: `${framework.name}-${category.name}`, title: ( ), content: "", items: category.controls.map((control, i: number) => { const requirement = control.requirements[0]; // Each control has one requirement const itemKey = `${framework.name}-${category.name}-control-${i}`; return { key: itemKey, title: ( ), content: ( ), items: [], }; }), }; }), ); };