From 5346222be2df86249db4a917579a55a37826fc1b Mon Sep 17 00:00:00 2001 From: Alejandro Bailo <59607668+alejandrobailo@users.noreply.github.com> Date: Thu, 12 Mar 2026 11:38:03 +0100 Subject: [PATCH] fix(ui): handle missing relationships in FindingDetail to prevent crash (#10314) --- ui/CHANGELOG.md | 4 + .../findings/table/column-findings.tsx | 14 +- .../findings/table/finding-detail.tsx | 253 ++++++++++-------- 3 files changed, 146 insertions(+), 125 deletions(-) diff --git a/ui/CHANGELOG.md b/ui/CHANGELOG.md index 67dcc83a39..9f7f977a2d 100644 --- a/ui/CHANGELOG.md +++ b/ui/CHANGELOG.md @@ -11,6 +11,10 @@ All notable changes to the **Prowler UI** are documented in this file. - Providers page redesigned with cloud organization hierarchy, HeroUI-to-shadcn migration, organization and account group filters, and row selection for bulk actions [(#10292)](https://github.com/prowler-cloud/prowler/pull/10292) - AWS Organizations onboarding now uses a clearer 3-step flow: deploy the ProwlerScan role in the management account via CloudFormation Stack, deploy to member accounts via StackSet with a copyable template URL, and confirm with the Role ARN [(#10274)](https://github.com/prowler-cloud/prowler/pull/10274) +### 🐞 Fixed + +- Finding detail drawer crashing when resource, scan, or provider relationships are missing from the API response [(#10314)](https://github.com/prowler-cloud/prowler/pull/10314) + ### 🔐 Security - npm transitive dependencies patched to resolve 11 Dependabot alerts (6 HIGH, 4 MEDIUM, 1 LOW): hono, @hono/node-server, fast-xml-parser, serialize-javascript, minimatch [(#10267)](https://github.com/prowler-cloud/prowler/pull/10267) diff --git a/ui/components/findings/table/column-findings.tsx b/ui/components/findings/table/column-findings.tsx index 486a0505df..ea2c8ae464 100644 --- a/ui/components/findings/table/column-findings.tsx +++ b/ui/components/findings/table/column-findings.tsx @@ -33,20 +33,14 @@ const getResourceData = ( row: { original: FindingProps }, field: keyof FindingProps["relationships"]["resource"]["attributes"], ) => { - return ( - row.original.relationships?.resource?.attributes?.[field] || - `No ${field} found in resource` - ); + return row.original.relationships?.resource?.attributes?.[field] || "-"; }; const getProviderData = ( row: { original: FindingProps }, field: keyof FindingProps["relationships"]["provider"]["attributes"], ) => { - return ( - row.original.relationships?.provider?.attributes?.[field] || - `No ${field} found in provider` - ); + return row.original.relationships?.provider?.attributes?.[field] || "-"; }; // Component for finding title that opens the detail drawer @@ -186,6 +180,10 @@ export function getColumnFindings( cell: ({ row }) => { const resourceName = getResourceData(row, "name"); + if (resourceName === "-") { + return

-

; + } + return ( { const finding = findingDetails; const attributes = finding.attributes; - const resource = finding.relationships.resource.attributes; - const scan = finding.relationships.scan.attributes; - const providerDetails = finding.relationships.provider.attributes; + const resource = finding.relationships?.resource?.attributes; + const scan = finding.relationships?.scan?.attributes; + const providerDetails = finding.relationships?.provider?.attributes; const pathname = usePathname(); const searchParams = useSearchParams(); @@ -97,7 +97,7 @@ export const FindingDetail = ({ // Build Git URL for IaC findings const gitUrl = - providerDetails.provider === "iac" + providerDetails?.provider === "iac" && resource ? buildGitFileUrl( providerDetails.uid, resource.name, @@ -160,23 +160,24 @@ export const FindingDetail = ({ Scans -

- Here is an overview of this finding: -

- {/* General Tab */} +

+ Here is an overview of this finding: +

- + {providerDetails && ( + + )} {attributes.check_metadata.servicename} - {resource.region} + {resource?.region ?? "-"}
@@ -305,119 +306,137 @@ export const FindingDetail = ({ {/* Resources Tab */} - {providerDetails.provider === "iac" && gitUrl && ( -
- - - - - View in Repository - - - - Go to Resource in the Repository - - -
- )} + {resource ? ( + <> + {providerDetails?.provider === "iac" && gitUrl && ( +
+ + + + + View in Repository + + + + Go to Resource in the Repository + + +
+ )} -
- - {renderValue(resource.name)} - - - {renderValue(resource.type)} - -
- -
- - {renderValue(resource.service)} - - {renderValue(resource.region)} -
- -
- - {renderValue(resource.partition)} - - - {renderValue(resource.details)} - -
- - - - - - {resource.tags && Object.entries(resource.tags).length > 0 && ( -
-

- Tags -

- {Object.entries(resource.tags).map(([key, value]) => ( - - {renderValue(value)} - - ))} + + {renderValue(resource.name)} + + + {renderValue(resource.type)} +
-
- )} -
- - - - - - -
+
+ + {renderValue(resource.service)} + + + {renderValue(resource.region)} + +
+ +
+ + {renderValue(resource.partition)} + + + {renderValue(resource.details)} + +
+ + + + + + {resource.tags && Object.entries(resource.tags).length > 0 && ( +
+

+ Tags +

+
+ {Object.entries(resource.tags).map(([key, value]) => ( + + {renderValue(value)} + + ))} +
+
+ )} + +
+ + + + + + +
+ + ) : ( +

+ Resource information is not available. +

+ )}
{/* Scans Tab */} -
- {scan.name || "N/A"} - - {scan.unique_resource_count} - - {scan.progress}% -
+ {scan ? ( + <> +
+ {scan.name || "N/A"} + + {scan.unique_resource_count} + + {scan.progress}% +
-
- {scan.trigger} - {scan.state} - - {formatDuration(scan.duration)} - -
+
+ {scan.trigger} + {scan.state} + + {formatDuration(scan.duration)} + +
-
- - - - - - -
+
+ + + + + + +
-
- - - - {scan.scheduled_at && ( - - - - )} -
+
+ + + + {scan.scheduled_at && ( + + + + )} +
+ + ) : ( +

+ Scan information is not available. +

+ )}