mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-01-25 02:08:11 +00:00
fix(ui): minor bugs (#8898)
This commit is contained in:
@@ -31,15 +31,11 @@ All notable changes to the **Prowler UI** are documented in this file.
|
||||
|
||||
- SAML configuration errors are now properly caught and displayed [(#8880)](https://github.com/prowler-cloud/prowler/pull/8880)
|
||||
- ThreatScore for each pillar in Prowler ThreatScore specific view [(#8582)](https://github.com/prowler-cloud/prowler/pull/8582)
|
||||
- Remove maxTokens model param for GPT-5 models [(#8843)](https://github.com/prowler-cloud/prowler/pull/8843)
|
||||
- MITRE ATTACK compliance view now shows all requirements in charts [(#8886)](https://github.com/prowler-cloud/prowler/pull/8886)
|
||||
|
||||
---
|
||||
|
||||
## [1.12.4] (Prowler v5.12.4)
|
||||
|
||||
### 🐞 Fixed
|
||||
|
||||
- Remove maxTokens model param for GPT-5 models [(#8843)](https://github.com/prowler-cloud/prowler/pull/8843)
|
||||
|
||||
## [1.12.3] (Prowler v5.12.3)
|
||||
|
||||
### 🐞 Fixed
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Spacer } from "@heroui/spacer";
|
||||
import { Suspense } from "react";
|
||||
|
||||
import { getRoles } from "@/actions/roles/roles";
|
||||
import { getUsers } from "@/actions/users/users";
|
||||
import { FilterControls } from "@/components/filters";
|
||||
import { filterUsers } from "@/components/filters/data-filters";
|
||||
@@ -52,6 +53,7 @@ const SSRDataTable = async ({
|
||||
const query = (filters["filter[search]"] as string) || "";
|
||||
|
||||
const usersData = await getUsers({ query, page, sort, filters, pageSize });
|
||||
const rolesData = await getRoles({});
|
||||
|
||||
// Create a dictionary for roles by user ID
|
||||
const roleDict = (usersData?.included || []).reduce(
|
||||
@@ -67,7 +69,7 @@ const SSRDataTable = async ({
|
||||
// Generate the array of roles with all the roles available
|
||||
const roles = Array.from(
|
||||
new Map(
|
||||
(usersData?.included || []).map((role: Role) => [
|
||||
(rolesData?.data || []).map((role: Role) => [
|
||||
role.id,
|
||||
{ id: role.id, name: role.attributes?.name || "Unnamed Role" },
|
||||
]),
|
||||
|
||||
@@ -17,7 +17,7 @@ interface CISDetailsProps {
|
||||
|
||||
export const CISCustomDetails = ({ requirement }: CISDetailsProps) => {
|
||||
const processReferences = (
|
||||
references: string | number | string[] | object[] | undefined,
|
||||
references: string | number | boolean | string[] | object[] | undefined,
|
||||
): string[] => {
|
||||
if (typeof references !== "string") return [];
|
||||
|
||||
|
||||
@@ -119,7 +119,12 @@ export const EditForm = ({
|
||||
</div>
|
||||
<div className="text-small flex items-center text-gray-600">
|
||||
<ShieldIcon className="mr-2 h-4 w-4" />
|
||||
<span className="text-gray-500">Role:</span>
|
||||
<span className="text-gray-500">
|
||||
Role:
|
||||
<span className="ml-2 font-semibold text-gray-900">
|
||||
{currentRole ? currentRole : "No role"}
|
||||
</span>
|
||||
</span>
|
||||
<span className="ml-2 font-semibold text-gray-900">
|
||||
{currentRole}
|
||||
</span>
|
||||
|
||||
@@ -25,16 +25,15 @@ export const mapComplianceData = (
|
||||
): Framework[] => {
|
||||
const attributes = attributesData?.data || [];
|
||||
const requirementsMap = createRequirementsMap(requirementsData);
|
||||
|
||||
const frameworks: Framework[] = [];
|
||||
|
||||
// Process attributes and merge with requirements data
|
||||
// Process ALL attributes to ensure consistent counters for charts
|
||||
for (const attributeItem of attributes) {
|
||||
const id = attributeItem.id;
|
||||
const metadataArray = attributeItem.attributes?.attributes
|
||||
?.metadata as unknown as MITREAttributesMetadata[];
|
||||
|
||||
if (!metadataArray || metadataArray.length === 0) continue;
|
||||
|
||||
// Get corresponding requirement data
|
||||
const requirementData = requirementsMap.get(id);
|
||||
if (!requirementData) continue;
|
||||
@@ -56,6 +55,7 @@ export const mapComplianceData = (
|
||||
const framework = findOrCreateFramework(frameworks, frameworkName);
|
||||
|
||||
// Create requirement directly (flat structure - no categories)
|
||||
// Include ALL requirements, even those without metadata (for accurate chart counts)
|
||||
const finalStatus: RequirementStatus = status as RequirementStatus;
|
||||
const requirement: Requirement = {
|
||||
name: requirementName,
|
||||
@@ -72,20 +72,23 @@ export const mapComplianceData = (
|
||||
subtechniques: subtechniques,
|
||||
platforms: platforms,
|
||||
technique_url: techniqueUrl,
|
||||
cloud_services: metadataArray.map((m) => {
|
||||
// Dynamically find the service field (AWSService, GCPService, AzureService, etc.)
|
||||
const serviceKey = Object.keys(m).find((key) =>
|
||||
key.toLowerCase().includes("service"),
|
||||
);
|
||||
const serviceName = serviceKey ? m[serviceKey] : "Unknown Service";
|
||||
// Mark items without metadata so accordion can filter them out
|
||||
hasMetadata: !!(metadataArray && metadataArray.length > 0),
|
||||
cloud_services:
|
||||
metadataArray?.map((m) => {
|
||||
// Dynamically find the service field (AWSService, GCPService, AzureService, etc.)
|
||||
const serviceKey = Object.keys(m).find((key) =>
|
||||
key.toLowerCase().includes("service"),
|
||||
);
|
||||
const serviceName = serviceKey ? m[serviceKey] : "Unknown Service";
|
||||
|
||||
return {
|
||||
service: serviceName,
|
||||
category: m.Category,
|
||||
value: m.Value,
|
||||
comment: m.Comment,
|
||||
};
|
||||
}),
|
||||
return {
|
||||
service: serviceName,
|
||||
category: m.Category,
|
||||
value: m.Value,
|
||||
comment: m.Comment,
|
||||
};
|
||||
}) || [],
|
||||
};
|
||||
|
||||
// Add requirement directly to framework (store in a special property)
|
||||
@@ -106,31 +109,38 @@ export const toAccordionItems = (
|
||||
return data.flatMap((framework) => {
|
||||
const requirements = (framework as any).requirements || [];
|
||||
|
||||
return requirements.map((requirement: Requirement, i: number) => {
|
||||
const itemKey = `${framework.name}-req-${i}`;
|
||||
// Filter out requirements without metadata (can't be displayed in accordion)
|
||||
const displayableRequirements = requirements.filter(
|
||||
(requirement: Requirement) => requirement.hasMetadata !== false,
|
||||
);
|
||||
|
||||
return {
|
||||
key: itemKey,
|
||||
title: (
|
||||
<ComplianceAccordionRequirementTitle
|
||||
type=""
|
||||
name={requirement.name}
|
||||
status={requirement.status as FindingStatus}
|
||||
/>
|
||||
),
|
||||
content: (
|
||||
<ClientAccordionContent
|
||||
requirement={requirement}
|
||||
scanId={scanId || ""}
|
||||
framework={framework.name}
|
||||
disableFindings={
|
||||
requirement.check_ids.length === 0 && requirement.manual === 0
|
||||
}
|
||||
/>
|
||||
),
|
||||
items: [],
|
||||
};
|
||||
});
|
||||
return displayableRequirements.map(
|
||||
(requirement: Requirement, i: number) => {
|
||||
const itemKey = `${framework.name}-req-${i}`;
|
||||
|
||||
return {
|
||||
key: itemKey,
|
||||
title: (
|
||||
<ComplianceAccordionRequirementTitle
|
||||
type=""
|
||||
name={requirement.name}
|
||||
status={requirement.status as FindingStatus}
|
||||
/>
|
||||
),
|
||||
content: (
|
||||
<ClientAccordionContent
|
||||
requirement={requirement}
|
||||
scanId={scanId || ""}
|
||||
framework={framework.name}
|
||||
disableFindings={
|
||||
requirement.check_ids.length === 0 && requirement.manual === 0
|
||||
}
|
||||
/>
|
||||
),
|
||||
items: [],
|
||||
};
|
||||
},
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ export interface Requirement {
|
||||
check_ids: 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 | object[] | undefined;
|
||||
[key: string]: string | string[] | number | boolean | object[] | undefined;
|
||||
}
|
||||
|
||||
export interface Control {
|
||||
|
||||
Reference in New Issue
Block a user