diff --git a/ui/CHANGELOG.md b/ui/CHANGELOG.md index dba82a9d13..1cb3bc30de 100644 --- a/ui/CHANGELOG.md +++ b/ui/CHANGELOG.md @@ -10,6 +10,8 @@ All notable changes to the **Prowler UI** are documented in this file. - Improved `SnippetChip` component and show resource name in new findings table. [(#7813)](https://github.com/prowler-cloud/prowler/pull/7813) - Possibility to edit the organization name. [(#7829)](https://github.com/prowler-cloud/prowler/pull/7829) - Add GCP credential method (Account Service Key). [(#7872)](https://github.com/prowler-cloud/prowler/pull/7872) +- Add compliance detail view: ENS [(#7853)](https://github.com/prowler-cloud/prowler/pull/7853) +- Add compliance detail view: ISO [(#7897)](https://github.com/prowler-cloud/prowler/pull/7897) ### 🔄 Changed diff --git a/ui/app/(prowler)/compliance/[compliancetitle]/page.tsx b/ui/app/(prowler)/compliance/[compliancetitle]/page.tsx index eeb3f1a588..b32781e482 100644 --- a/ui/app/(prowler)/compliance/[compliancetitle]/page.tsx +++ b/ui/app/(prowler)/compliance/[compliancetitle]/page.tsx @@ -16,14 +16,11 @@ import { FailedSectionsChart } from "@/components/compliance/failed-sections-cha import { FailedSectionsChartSkeleton } from "@/components/compliance/failed-sections-chart-skeleton"; import { RequirementsChart } from "@/components/compliance/requirements-chart"; import { RequirementsChartSkeleton } from "@/components/compliance/requirements-chart-skeleton"; +import { getComplianceIcon } from "@/components/icons/compliance/IconCompliance"; import { ContentLayout } from "@/components/ui"; -import { mapComplianceData, toAccordionItems } from "@/lib/compliance/ens"; +import { getComplianceMapper } from "@/lib/compliance/commons"; import { ScanProps } from "@/types"; -import { - FailedSection, - MappedComplianceData, - RequirementsTotals, -} from "@/types/compliance"; +import { Framework, RequirementsTotals } from "@/types/compliance"; interface ComplianceDetailSearchParams { complianceId: string; @@ -32,7 +29,7 @@ interface ComplianceDetailSearchParams { "filter[region__in]"?: string; } -const Logo = ({ logoPath }: { logoPath: string }) => { +const ComplianceLogo = ({ logoPath }: { logoPath: string }) => { return (
{ return (
{children}
- {logoPath && } + {logoPath && }
); }; @@ -73,8 +70,7 @@ export default async function ComplianceDetail({ const { compliancetitle } = params; const { complianceId, version, scanId } = searchParams; const regionFilter = searchParams["filter[region__in]"]; - - const logoPath = `/${compliancetitle.toLowerCase()}.png`; + const logoPath = getComplianceIcon(compliancetitle); // Create a key that includes region filter for Suspense const searchParamsKey = JSON.stringify(searchParams || {}); @@ -164,7 +160,7 @@ const getComplianceData = async ( complianceId: string, scanId: string, region?: string, -): Promise => { +): Promise => { const [attributesData, requirementsData] = await Promise.all([ getComplianceAttributes(complianceId), getComplianceRequirements({ @@ -174,44 +170,16 @@ const getComplianceData = async ( }), ]); - const mappedData = mapComplianceData(attributesData, requirementsData); + // Determine framework from the first attribute item + const framework = attributesData?.data?.[0]?.attributes?.framework; + + // Get the appropriate mapper for this framework + const mapper = getComplianceMapper(framework); + const mappedData = mapper.mapComplianceData(attributesData, requirementsData); + return mappedData; }; -const getTopFailedSections = ( - mappedData: MappedComplianceData, -): FailedSection[] => { - const failedSectionMap = new Map(); - - mappedData.forEach((framework) => { - framework.categories.forEach((category) => { - category.controls.forEach((control) => { - control.requirements.forEach((requirement) => { - if (requirement.status === "FAIL") { - const sectionName = category.name; - - if (!failedSectionMap.has(sectionName)) { - failedSectionMap.set(sectionName, { total: 0, types: {} }); - } - - const sectionData = failedSectionMap.get(sectionName); - sectionData.total += 1; - - const type = requirement.type; - sectionData.types[type] = (sectionData.types[type] || 0) + 1; - } - }); - }); - }); - }); - - // Convert in descending order and slice top 5 - return Array.from(failedSectionMap.entries()) - .map(([name, data]) => ({ name, ...data })) - .sort((a, b) => b.total - a.total) - .slice(0, 5); // Top 5 -}; - const SSRComplianceContent = async ({ complianceId, scanId, @@ -221,7 +189,7 @@ const SSRComplianceContent = async ({ complianceId: string; scanId: string; region?: string; - logoPath: string; + logoPath?: string; }) => { if (!scanId) { return ( @@ -244,9 +212,19 @@ const SSRComplianceContent = async ({ }), { pass: 0, fail: 0, manual: 0 }, ); - const topFailedSections = getTopFailedSections(data); - const accordionItems = toAccordionItems(data, scanId); - const defaultKeys = accordionItems.slice(0, 2).map((item) => item.key); + + // Get the framework to determine which mapper to use + const attributesData = await getComplianceAttributes(complianceId); + const framework = attributesData?.data?.[0]?.attributes?.framework; + + // Get the appropriate mapper for this framework + const mapper = getComplianceMapper(framework); + const accordionItems = mapper.toAccordionItems(data, scanId); + const topFailedSections = mapper.getTopFailedSections(data); + + // Todo: rethink as every compliance has a different number of items + // const defaultKeys = accordionItems.slice(0, 2).map((item) => item.key); + const defaultKeys = [""]; return (
diff --git a/ui/components/compliance/compliance-accordion/client-accordion-content.tsx b/ui/components/compliance/compliance-accordion/client-accordion-content.tsx index 06bb394fa0..b79a1c1e8e 100644 --- a/ui/components/compliance/compliance-accordion/client-accordion-content.tsx +++ b/ui/components/compliance/compliance-accordion/client-accordion-content.tsx @@ -14,7 +14,8 @@ import { createDict } from "@/lib"; import { ComplianceId, Requirement } from "@/types/compliance"; import { FindingProps, FindingsResponse } from "@/types/components"; -import { ComplianceCustomDetails } from "../compliance-custom-details/ens-details"; +import { ENSCustomDetails } from "../compliance-custom-details/ens-details"; +import { ISOCustomDetails } from "../compliance-custom-details/iso-details"; interface ClientAccordionContentProps { requirement: Requirement; @@ -150,7 +151,14 @@ export const ClientAccordionContent = ({ case "ens_rd2022_aws": return (
- + +
+ ); + case "iso27001_2013_aws": + case "iso27001_2022_aws": + return ( +
+
); default: diff --git a/ui/components/compliance/compliance-accordion/compliance-accordion-requeriment-title.tsx b/ui/components/compliance/compliance-accordion/compliance-accordion-requeriment-title.tsx index 92f79e032d..1c62e87d29 100644 --- a/ui/components/compliance/compliance-accordion/compliance-accordion-requeriment-title.tsx +++ b/ui/components/compliance/compliance-accordion/compliance-accordion-requeriment-title.tsx @@ -1,5 +1,4 @@ import { FindingStatus, StatusFindingBadge } from "@/components/ui/table"; -import { translateType } from "@/lib/compliance/ens"; interface ComplianceAccordionRequirementTitleProps { type: string; @@ -8,16 +7,12 @@ interface ComplianceAccordionRequirementTitleProps { } export const ComplianceAccordionRequirementTitle = ({ - type, name, status, }: ComplianceAccordionRequirementTitleProps) => { return (
- - {translateType(type)}: - {name}
diff --git a/ui/components/compliance/compliance-accordion/compliance-accordion-title.tsx b/ui/components/compliance/compliance-accordion/compliance-accordion-title.tsx index e458cef948..0f3f681b59 100644 --- a/ui/components/compliance/compliance-accordion/compliance-accordion-title.tsx +++ b/ui/components/compliance/compliance-accordion/compliance-accordion-title.tsx @@ -5,6 +5,7 @@ interface ComplianceAccordionTitleProps { pass: number; fail: number; manual?: number; + isParentLevel?: boolean; } export const ComplianceAccordionTitle = ({ @@ -12,6 +13,7 @@ export const ComplianceAccordionTitle = ({ pass, fail, manual = 0, + isParentLevel = false, }: ComplianceAccordionTitleProps) => { const total = pass + fail + manual; const passPercentage = (pass / total) * 100; @@ -30,7 +32,7 @@ export const ComplianceAccordionTitle = ({
- {total > 0 && ( + {total > 0 && isParentLevel && ( Requirements: diff --git a/ui/components/compliance/compliance-card.tsx b/ui/components/compliance/compliance-card.tsx index db7539401f..c7a745348e 100644 --- a/ui/components/compliance/compliance-card.tsx +++ b/ui/components/compliance/compliance-card.tsx @@ -73,7 +73,7 @@ export const ComplianceCard: React.FC = ({ const navigateToDetail = () => { // We will unlock this while developing the rest of complainces. - if (!id.includes("ens") && !id.includes("cis")) { + if (!id.includes("ens") && !id.includes("iso")) { return; } diff --git a/ui/components/compliance/compliance-custom-details/ens-details.tsx b/ui/components/compliance/compliance-custom-details/ens-details.tsx index db0652c770..2c133f7e11 100644 --- a/ui/components/compliance/compliance-custom-details/ens-details.tsx +++ b/ui/components/compliance/compliance-custom-details/ens-details.tsx @@ -1,6 +1,7 @@ +import { translateType } from "@/lib/compliance/ens"; import { Requirement } from "@/types/compliance"; -export const ComplianceCustomDetails = ({ +export const ENSCustomDetails = ({ requirement, }: { requirement: Requirement; @@ -11,27 +12,35 @@ export const ComplianceCustomDetails = ({ {requirement.description}
+
+ Type: + + {translateType(requirement.type as string)} + +
Level: {requirement.nivel}
- {requirement.dimensiones && requirement.dimensiones.length > 0 && ( -
- Dimensions: -
- {requirement.dimensiones.map( - (dimension: string, index: number) => ( - - {dimension} - - ), - )} + {requirement.dimensiones && + Array.isArray(requirement.dimensiones) && + requirement.dimensiones.length > 0 && ( +
+ Dimensions: +
+ {requirement.dimensiones.map( + (dimension: string, index: number) => ( + + {dimension} + + ), + )} +
-
- )} + )}
); diff --git a/ui/components/compliance/compliance-custom-details/iso-details.tsx b/ui/components/compliance/compliance-custom-details/iso-details.tsx new file mode 100644 index 0000000000..1bcf687b08 --- /dev/null +++ b/ui/components/compliance/compliance-custom-details/iso-details.tsx @@ -0,0 +1,23 @@ +import { Requirement } from "@/types/compliance"; + +export const ISOCustomDetails = ({ + requirement, +}: { + requirement: Requirement; +}) => { + return ( +
+
+ {requirement.description} +
+
+ {requirement.objetive_name && ( +
+ Objective: + {requirement.objetive_name} +
+ )} +
+
+ ); +}; diff --git a/ui/components/compliance/failed-sections-chart.tsx b/ui/components/compliance/failed-sections-chart.tsx index 32076f1b66..d27e961737 100644 --- a/ui/components/compliance/failed-sections-chart.tsx +++ b/ui/components/compliance/failed-sections-chart.tsx @@ -12,17 +12,10 @@ import { } from "recharts"; import { translateType } from "@/lib/compliance/ens"; - -type FailedSectionItem = { - name: string; - total: number; - types: { - [key: string]: number; - }; -}; +import { FailedSection } from "@/types/compliance"; interface FailedSectionsListProps { - sections: FailedSectionItem[]; + sections: FailedSection[]; } const title = ( @@ -43,7 +36,7 @@ export const FailedSectionsChart = ({ sections }: FailedSectionsListProps) => { case "refuerzo": return "#7FB5FF"; // Increased contrast from #B5D7FF default: - return "#868994"; + return "#ff5356"; } }; @@ -56,9 +49,28 @@ export const FailedSectionsChart = ({ sections }: FailedSectionsListProps) => { })); const allTypes = Array.from( - new Set(sections.flatMap((section) => Object.keys(section.types))), + new Set(sections.flatMap((section) => Object.keys(section.types || {}))), ); + // Add empty bars to complete up to 5 bars for better distribution + while (chartData.length < 5) { + const emptyBar: any = { name: "" }; + allTypes.forEach((type) => { + emptyBar[type] = 0; + }); + chartData.push(emptyBar); + } + + // Calculate the maximum value to ensure proper scaling + const maxValue = Math.max( + ...chartData.map((item) => + allTypes.reduce((sum, type) => sum + ((item as any)[type] || 0), 0), + ), + ); + + // Set minimum domain to ensure bars are always visible + const domainMax = Math.max(maxValue, 1); + // Check if there are no failed sections if (!sections || sections.length === 0) { return ( @@ -73,14 +85,14 @@ export const FailedSectionsChart = ({ sections }: FailedSectionsListProps) => { return (
- {title} +
{title}
{ fontSize={12} axisLine={false} tickLine={false} + allowDecimals={false} + hide={true} + domain={[0, domainMax]} tick={{ fontSize: 12, fill: theme === "dark" ? "#94a3b8" : "#374151", @@ -105,34 +120,59 @@ export const FailedSectionsChart = ({ sections }: FailedSectionsListProps) => { tickLine={false} /> { + if (!props.active || !props.payload || !props.payload.length) { + return null; + } + + const data = props.payload[0].payload; + if (!data.name || data.name === "") { + return null; + } + + const hasValues = allTypes.some((type) => data[type] > 0); + if (!hasValues) { + return null; + } + + return ( +
+ {props.payload.map((entry: any, index: number) => ( +
+ {translateType(entry.dataKey)}: {entry.value} +
+ ))} +
+ ); }} - formatter={(value: number, name: string) => [ - value, - translateType(name), - ]} cursor={false} /> - translateType(value)} - wrapperStyle={{ - fontSize: "10px", - display: "flex", - justifyContent: "center", - width: "100%", - }} - iconType="circle" - layout="horizontal" - verticalAlign="bottom" - height={36} - /> + {allTypes.length > 1 && ( + translateType(value)} + wrapperStyle={{ + fontSize: "10px", + display: "flex", + justifyContent: "center", + width: "100%", + paddingTop: "16px", + marginBottom: "4px", + }} + iconType="circle" + layout="horizontal" + verticalAlign="bottom" + /> + )} {allTypes.map((type, i) => ( } classNames={{ - base: index === 0 || index === 1 ? "my-2" : "my-1", + base: index === 0 || index === 1 ? "my-1" : "my-1", title: "text-sm font-medium max-w-full overflow-hidden truncate", subtitle: "text-xs text-gray-500", trigger: - "p-2 rounded-lg data-[hover=true]:bg-gray-50 dark:data-[hover=true]:bg-gray-800/50 w-full flex items-center", - content: "p-2", + "py-2 px-2 rounded-lg data-[hover=true]:bg-gray-50 dark:data-[hover=true]:bg-gray-800/50 w-full flex items-center", + content: "px-0 py-1", }} > Framework[]; + toAccordionItems: ( + data: Framework[], + scanId: string | undefined, + ) => AccordionItemProps[]; + getTopFailedSections: (mappedData: Framework[]) => FailedSection[]; +} + +// Common function for getting top failed sections +export const getTopFailedSections = ( + mappedData: Framework[], +): FailedSection[] => { + const failedSectionMap = new Map(); + + mappedData.forEach((framework) => { + framework.categories.forEach((category) => { + category.controls.forEach((control) => { + control.requirements.forEach((requirement) => { + if (requirement.status === "FAIL") { + const sectionName = category.name; + + if (!failedSectionMap.has(sectionName)) { + failedSectionMap.set(sectionName, { total: 0, types: {} }); + } + + const sectionData = failedSectionMap.get(sectionName); + sectionData.total += 1; + + const type = requirement.type || "Fails"; + + sectionData.types[type as string] = + (sectionData.types[type as string] || 0) + 1; + } + }); + }); + }); + }); + + // Convert in descending order and slice top 5 + return Array.from(failedSectionMap.entries()) + .map(([name, data]) => ({ name, ...data })) + .sort((a, b) => b.total - a.total) + .slice(0, 5); // Top 5 +}; + +// Registry of compliance mappers +const complianceMappers: Record = { + ENS: { + mapComplianceData: mapENSComplianceData, + toAccordionItems: toENSAccordionItems, + getTopFailedSections, + }, + ISO27001: { + mapComplianceData: mapISOComplianceData, + toAccordionItems: toISOAccordionItems, + getTopFailedSections, + }, +}; + +// Default mapper (fallback to ENS for backward compatibility) +const defaultMapper: ComplianceMapper = complianceMappers.ENS; + +/** + * Get the appropriate compliance mapper based on the framework name + * @param framework - The framework name (e.g., "ENS", "ISO27001") + * @returns ComplianceMapper object with specific functions for the framework + */ +export const getComplianceMapper = (framework?: string): ComplianceMapper => { + if (!framework) { + return defaultMapper; + } + + return complianceMappers[framework] || defaultMapper; +}; diff --git a/ui/lib/compliance/ens.tsx b/ui/lib/compliance/ens.tsx index bab025dd06..c46c659ecd 100644 --- a/ui/lib/compliance/ens.tsx +++ b/ui/lib/compliance/ens.tsx @@ -5,8 +5,8 @@ import { AccordionItemProps } from "@/components/ui/accordion/Accordion"; import { FindingStatus } from "@/components/ui/table/status-finding-badge"; import { AttributesData, + ENSAttributesMetadata, Framework, - MappedComplianceData, Requirement, RequirementItemData, RequirementsData, @@ -31,7 +31,7 @@ export const translateType = (type: string) => { export const mapComplianceData = ( attributesData: AttributesData, requirementsData: RequirementsData, -): MappedComplianceData => { +): Framework[] => { const attributes = attributesData?.data || []; const requirements = requirementsData?.data || []; @@ -46,7 +46,9 @@ export const mapComplianceData = ( // Process attributes and merge with requirements data for (const attributeItem of attributes) { const id = attributeItem.id; - const attrs = attributeItem.attributes?.attributes?.metadata?.[0]; + const attrs = attributeItem.attributes?.attributes + ?.metadata?.[0] as ENSAttributesMetadata; + if (!attrs) continue; // Get corresponding requirement data @@ -96,7 +98,6 @@ export const mapComplianceData = ( if (!control) { control = { label: groupControlLabel, - type, pass: 0, fail: 0, manual: 0, @@ -166,7 +167,7 @@ export const mapComplianceData = ( }; export const toAccordionItems = ( - data: MappedComplianceData, + data: Framework[], scanId: string | undefined, ): AccordionItemProps[] => { return data.map((framework) => { @@ -178,6 +179,7 @@ export const toAccordionItems = ( pass={framework.pass} fail={framework.fail} manual={framework.manual} + isParentLevel={true} /> ), content: "", @@ -212,7 +214,7 @@ export const toAccordionItems = ( key: itemKey, title: ( diff --git a/ui/lib/compliance/iso.tsx b/ui/lib/compliance/iso.tsx new file mode 100644 index 0000000000..92e5cb999d --- /dev/null +++ b/ui/lib/compliance/iso.tsx @@ -0,0 +1,210 @@ +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, + ISO27001AttributesMetadata, + 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(); + 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 ISO27001AttributesMetadata[]; + 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 categoryName = attrs.Category; + const controlLabel = `${attrs.Objetive_ID} - ${attrs.Objetive_Name}`; + const description = attributeItem.attributes.description; + const status = requirementData.attributes.status || ""; + const checks = attributeItem.attributes.attributes.check_ids || []; + const requirementName = id; + const objetiveName = attrs.Objetive_Name; + const checkSummary = attrs.Check_Summary; + + // 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 + let category = framework.categories.find((c) => c.name === categoryName); + if (!category) { + category = { + name: categoryName, + pass: 0, + fail: 0, + manual: 0, + controls: [], + }; + framework.categories.push(category); + } + + // Find or create control + let control = category.controls.find((c) => c.label === controlLabel); + if (!control) { + control = { + label: controlLabel, + pass: 0, + fail: 0, + manual: 0, + requirements: [], + }; + category.controls.push(control); + } + + // 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, + objetive_name: objetiveName, + check_summary: checkSummary, + }; + + control.requirements.push(requirement); + } + + // Calculate counters + 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) => { + control.pass = 0; + control.fail = 0; + control.manual = 0; + + control.requirements.forEach((requirement) => { + if (requirement.status === "MANUAL") { + control.manual++; + } else if (requirement.status === "PASS") { + control.pass++; + } else if (requirement.status === "FAIL") { + control.fail++; + } + }); + + 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: ( + + ), + content: "", + items: category.controls.map((control, i: number) => { + return { + key: `${framework.name}-${category.name}-control-${i}`, + title: ( + + ), + content: "", + items: control.requirements.map((requirement, j: number) => { + const itemKey = `${framework.name}-${category.name}-control-${i}-req-${j}`; + + return { + key: itemKey, + title: ( + + ), + content: ( + + ), + items: [], + isDisabled: + requirement.check_ids.length === 0 && + requirement.manual === 0, + }; + }), + isDisabled: + control.pass === 0 && control.fail === 0 && control.manual === 0, + }; + }), + }; + }), + ); +}; diff --git a/ui/types/compliance.ts b/ui/types/compliance.ts index beeafcd836..ce563f4214 100644 --- a/ui/types/compliance.ts +++ b/ui/types/compliance.ts @@ -1,6 +1,9 @@ export type RequirementStatus = "PASS" | "FAIL" | "MANUAL" | "No findings"; -export type ComplianceId = "ens_rd2022_aws"; +export type ComplianceId = + | "ens_rd2022_aws" + | "iso27001_2013_aws" + | "iso27001_2022_aws"; export interface CompliancesOverview { data: ComplianceOverviewData[]; @@ -23,19 +26,17 @@ export interface Requirement { name: string; description: string; status: RequirementStatus; - type: string; pass: number; fail: number; manual: number; check_ids: string[]; - // ENS - nivel?: string; - dimensiones?: string[]; + // This is to allow any key to be added to the requirement object + // because each compliance has different keys + [key: string]: string | string[] | number | undefined; } export interface Control { label: string; - type: string; pass: number; fail: number; manual: number; @@ -58,12 +59,10 @@ export interface Framework { categories: Category[]; } -export type MappedComplianceData = Framework[]; - export interface FailedSection { name: string; total: number; - types: { [key: string]: number }; + types?: { [key: string]: number }; } export interface RequirementsTotals { @@ -73,7 +72,7 @@ export interface RequirementsTotals { } // API Responses types: -export interface AttributesMetadata { +export interface ENSAttributesMetadata { IdGrupoControl: string; Marco: string; Categoria: string; @@ -85,6 +84,13 @@ export interface AttributesMetadata { Dependencias: any[]; } +export interface ISO27001AttributesMetadata { + Category: string; + Objetive_ID: string; + Objetive_Name: string; + Check_Summary: string; +} + export interface AttributesItemData { type: "compliance-requirements-attributes"; id: string; @@ -93,7 +99,7 @@ export interface AttributesItemData { version: string; description: string; attributes: { - metadata: AttributesMetadata[]; + metadata: ENSAttributesMetadata[] | ISO27001AttributesMetadata[]; check_ids: string[]; }; };