mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-11-09 04:38:49 +00:00
194 lines
5.8 KiB
TypeScript
194 lines
5.8 KiB
TypeScript
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/ui/accordion/Accordion";
|
|
import { FindingStatus } from "@/components/ui/table/status-finding-badge";
|
|
import {
|
|
AttributesData,
|
|
Framework,
|
|
GenericAttributesMetadata,
|
|
Requirement,
|
|
RequirementItemData,
|
|
RequirementsData,
|
|
RequirementStatus,
|
|
} from "@/types/compliance";
|
|
|
|
export const mapComplianceData = (
|
|
attributesData: AttributesData,
|
|
requirementsData: RequirementsData,
|
|
): Framework[] => {
|
|
const attributes = attributesData?.data || [];
|
|
const requirements = requirementsData?.data || [];
|
|
|
|
// Create a map for quick lookup of requirements by id
|
|
const requirementsMap = new Map<string, RequirementItemData>();
|
|
requirements.forEach((req: RequirementItemData) => {
|
|
requirementsMap.set(req.id, req);
|
|
});
|
|
|
|
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 GenericAttributesMetadata[];
|
|
const attrs = metadataArray?.[0];
|
|
if (!attrs) continue;
|
|
|
|
// Get corresponding requirement data
|
|
const requirementData = requirementsMap.get(id);
|
|
if (!requirementData) continue;
|
|
|
|
const frameworkName = attributeItem.attributes.framework;
|
|
const sectionName = attrs.Section; // Level 1: Section -> Category
|
|
const requirementName = attributeItem.attributes.name || id; // Level 2: name -> Control
|
|
const description = attributeItem.attributes.description;
|
|
const status = requirementData.attributes.status || "";
|
|
const checks = attributeItem.attributes.attributes.check_ids || [];
|
|
|
|
if (!sectionName) {
|
|
continue;
|
|
}
|
|
|
|
// Find or create framework
|
|
let framework = frameworks.find((f) => f.name === frameworkName);
|
|
if (!framework) {
|
|
framework = {
|
|
name: frameworkName,
|
|
pass: 0,
|
|
fail: 0,
|
|
manual: 0,
|
|
categories: [],
|
|
};
|
|
frameworks.push(framework);
|
|
}
|
|
|
|
// Find or create category (Section)
|
|
let category = framework.categories.find((c) => c.name === sectionName);
|
|
if (!category) {
|
|
category = {
|
|
name: sectionName,
|
|
pass: 0,
|
|
fail: 0,
|
|
manual: 0,
|
|
controls: [],
|
|
};
|
|
framework.categories.push(category);
|
|
}
|
|
|
|
// Create a control for this requirement (each requirement is its own control in this generic approach)
|
|
const control = {
|
|
label: requirementName,
|
|
pass: 0,
|
|
fail: 0,
|
|
manual: 0,
|
|
requirements: [] as Requirement[],
|
|
};
|
|
|
|
// Create requirement
|
|
const finalStatus: RequirementStatus = status as RequirementStatus;
|
|
const requirement: Requirement = {
|
|
name: requirementName,
|
|
description: description,
|
|
status: finalStatus,
|
|
check_ids: checks,
|
|
pass: finalStatus === "PASS" ? 1 : 0,
|
|
fail: finalStatus === "FAIL" ? 1 : 0,
|
|
manual: finalStatus === "MANUAL" ? 1 : 0,
|
|
item_id: attrs.ItemId,
|
|
subsection: attrs.SubSection,
|
|
subgroup: attrs.SubGroup || undefined,
|
|
service: attrs.Service || undefined,
|
|
type: attrs.Type || undefined,
|
|
};
|
|
|
|
control.requirements.push(requirement);
|
|
|
|
// Update control counters
|
|
if (requirement.status === "MANUAL") {
|
|
control.manual++;
|
|
} else if (requirement.status === "PASS") {
|
|
control.pass++;
|
|
} else if (requirement.status === "FAIL") {
|
|
control.fail++;
|
|
}
|
|
|
|
category.controls.push(control);
|
|
}
|
|
|
|
// Calculate counters for categories and frameworks
|
|
frameworks.forEach((framework) => {
|
|
framework.pass = 0;
|
|
framework.fail = 0;
|
|
framework.manual = 0;
|
|
|
|
framework.categories.forEach((category) => {
|
|
category.pass = 0;
|
|
category.fail = 0;
|
|
category.manual = 0;
|
|
|
|
category.controls.forEach((control) => {
|
|
category.pass += control.pass;
|
|
category.fail += control.fail;
|
|
category.manual += control.manual;
|
|
});
|
|
|
|
framework.pass += category.pass;
|
|
framework.fail += category.fail;
|
|
framework.manual += category.manual;
|
|
});
|
|
});
|
|
|
|
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: (
|
|
<ComplianceAccordionTitle
|
|
label={category.name}
|
|
pass={category.pass}
|
|
fail={category.fail}
|
|
manual={category.manual}
|
|
isParentLevel={true}
|
|
/>
|
|
),
|
|
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: (
|
|
<ComplianceAccordionRequirementTitle
|
|
type=""
|
|
name={control.label}
|
|
status={requirement.status as FindingStatus}
|
|
/>
|
|
),
|
|
content: (
|
|
<ClientAccordionContent
|
|
requirement={requirement}
|
|
scanId={scanId || ""}
|
|
framework={framework.name}
|
|
disableFindings={
|
|
requirement.check_ids.length === 0 && requirement.manual === 0
|
|
}
|
|
/>
|
|
),
|
|
items: [],
|
|
};
|
|
}),
|
|
};
|
|
}),
|
|
);
|
|
};
|