mirror of
https://github.com/prowler-cloud/prowler.git
synced 2025-12-19 05:17:47 +00:00
Compare commits
1 Commits
d1d03ba421
...
DEVREL-109
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
271c1bcb2f |
@@ -10,6 +10,7 @@ All notable changes to the **Prowler UI** are documented in this file.
|
||||
- Customer Support menu item [(#9143)](https://github.com/prowler-cloud/prowler/pull/9143)
|
||||
- IaC (Infrastructure as Code) provider support for scanning remote repositories [(#8751)](https://github.com/prowler-cloud/prowler/pull/8751)
|
||||
- External resource link to IaC findings for direct navigation to source code in Git repositories [(#9151)](https://github.com/prowler-cloud/prowler/pull/9151)
|
||||
- External resource link to AWS findings for direct navigation to AWS Console using resource ARN
|
||||
|
||||
### 🔄 Changed
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { CustomLink } from "@/components/ui/custom/custom-link";
|
||||
import { EntityInfoShort, InfoField } from "@/components/ui/entities";
|
||||
import { DateWithTime } from "@/components/ui/entities/date-with-time";
|
||||
import { SeverityBadge } from "@/components/ui/table/severity-badge";
|
||||
import { buildAwsConsoleUrl } from "@/lib/aws-utils";
|
||||
import { buildGitFileUrl, extractLineRangeFromUid } from "@/lib/iac-utils";
|
||||
import { FindingProps, ProviderType } from "@/types";
|
||||
|
||||
@@ -59,15 +60,17 @@ export const FindingDetail = ({
|
||||
params.set("id", findingDetails.id);
|
||||
const url = `${window.location.origin}${currentUrl.pathname}?${params.toString()}`;
|
||||
|
||||
// Build Git URL for IaC findings
|
||||
const gitUrl =
|
||||
// Build external resource URL based on provider
|
||||
const externalUrl =
|
||||
providerDetails.provider === "iac"
|
||||
? buildGitFileUrl(
|
||||
providerDetails.uid,
|
||||
resource.name,
|
||||
extractLineRangeFromUid(attributes.uid) || "",
|
||||
)
|
||||
: null;
|
||||
: providerDetails.provider === "aws" && resource.uid
|
||||
? buildAwsConsoleUrl(resource.uid)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6 rounded-lg">
|
||||
@@ -257,22 +260,31 @@ export const FindingDetail = ({
|
||||
{/* Resource Details */}
|
||||
<CustomSection
|
||||
title={
|
||||
providerDetails.provider === "iac" ? (
|
||||
externalUrl ? (
|
||||
<span className="flex items-center gap-2">
|
||||
Resource Details
|
||||
{gitUrl && (
|
||||
<Tooltip content="Go to Resource in the Repository" size="sm">
|
||||
<a
|
||||
href={gitUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-bg-data-info inline-flex cursor-pointer"
|
||||
aria-label="Open resource in repository"
|
||||
>
|
||||
<ExternalLink size={16} className="inline" />
|
||||
</a>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Tooltip
|
||||
content={
|
||||
providerDetails.provider === "iac"
|
||||
? "Go to Resource in the Repository"
|
||||
: "Go to Resource in AWS Console"
|
||||
}
|
||||
size="sm"
|
||||
>
|
||||
<a
|
||||
href={externalUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-bg-data-info inline-flex cursor-pointer"
|
||||
aria-label={
|
||||
providerDetails.provider === "iac"
|
||||
? "Open resource in repository"
|
||||
: "Open resource in AWS Console"
|
||||
}
|
||||
>
|
||||
<ExternalLink size={16} className="inline" />
|
||||
</a>
|
||||
</Tooltip>
|
||||
</span>
|
||||
) : (
|
||||
"Resource Details"
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
} from "@/components/ui/entities";
|
||||
import { SeverityBadge, StatusFindingBadge } from "@/components/ui/table";
|
||||
import { createDict } from "@/lib";
|
||||
import { buildAwsConsoleUrl } from "@/lib/aws-utils";
|
||||
import { buildGitFileUrl } from "@/lib/iac-utils";
|
||||
import { FindingProps, ProviderType, ResourceProps } from "@/types";
|
||||
|
||||
@@ -164,11 +165,13 @@ export const ResourceDetail = ({
|
||||
const providerData = resource.relationships.provider.data.attributes;
|
||||
const allFindings = findingsData;
|
||||
|
||||
// Build Git URL for IaC resources
|
||||
const gitUrl =
|
||||
// Build external resource URL based on provider
|
||||
const externalUrl =
|
||||
providerData.provider === "iac"
|
||||
? buildGitFileUrl(providerData.uid, attributes.name, "")
|
||||
: null;
|
||||
: providerData.provider === "aws" && attributes.uid
|
||||
? buildAwsConsoleUrl(attributes.uid)
|
||||
: null;
|
||||
|
||||
if (selectedFindingId) {
|
||||
const findingTitle =
|
||||
@@ -196,22 +199,31 @@ export const ResourceDetail = ({
|
||||
{/* Resource Details section */}
|
||||
<CustomSection
|
||||
title={
|
||||
providerData.provider === "iac" ? (
|
||||
externalUrl ? (
|
||||
<span className="flex items-center gap-2">
|
||||
Resource Details
|
||||
{gitUrl && (
|
||||
<Tooltip content="Go to Resource in the Repository" size="sm">
|
||||
<a
|
||||
href={gitUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-bg-data-info inline-flex cursor-pointer"
|
||||
aria-label="Open resource in repository"
|
||||
>
|
||||
<ExternalLink size={16} className="inline" />
|
||||
</a>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Tooltip
|
||||
content={
|
||||
providerData.provider === "iac"
|
||||
? "Go to Resource in the Repository"
|
||||
: "Go to Resource in AWS Console"
|
||||
}
|
||||
size="sm"
|
||||
>
|
||||
<a
|
||||
href={externalUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-bg-data-info inline-flex cursor-pointer"
|
||||
aria-label={
|
||||
providerData.provider === "iac"
|
||||
? "Open resource in repository"
|
||||
: "Open resource in AWS Console"
|
||||
}
|
||||
>
|
||||
<ExternalLink size={16} className="inline" />
|
||||
</a>
|
||||
</Tooltip>
|
||||
</span>
|
||||
) : (
|
||||
"Resource Details"
|
||||
|
||||
72
ui/lib/aws-utils.ts
Normal file
72
ui/lib/aws-utils.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Builds an AWS Console URL for a given resource ARN
|
||||
* Uses AWS Console's go/view service which accepts ARNs directly
|
||||
*
|
||||
* @param resourceArn - The AWS resource ARN
|
||||
* @returns Complete URL to view the resource in AWS Console, or null if ARN is invalid
|
||||
*/
|
||||
export function buildAwsConsoleUrl(resourceArn: string): string | null {
|
||||
if (!resourceArn || !resourceArn.startsWith("arn:")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// AWS Console provides a universal URL that works with any ARN
|
||||
// Format: https://console.aws.amazon.com/go/view?arn=<ARN>
|
||||
const encodedArn = encodeURIComponent(resourceArn);
|
||||
return `https://console.aws.amazon.com/go/view?arn=${encodedArn}`;
|
||||
} catch (error) {
|
||||
console.error("Error building AWS Console URL:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the AWS region from an ARN
|
||||
* ARN format: arn:partition:service:region:account-id:resource
|
||||
*
|
||||
* @param resourceArn - The AWS resource ARN
|
||||
* @returns AWS region code or null if not found
|
||||
*/
|
||||
export function extractAwsRegionFromArn(resourceArn: string): string | null {
|
||||
if (!resourceArn || !resourceArn.startsWith("arn:")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const parts = resourceArn.split(":");
|
||||
if (parts.length >= 4) {
|
||||
const region = parts[3];
|
||||
return region || null;
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error("Error extracting region from ARN:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the AWS service from an ARN
|
||||
* ARN format: arn:partition:service:region:account-id:resource
|
||||
*
|
||||
* @param resourceArn - The AWS resource ARN
|
||||
* @returns AWS service name or null if not found
|
||||
*/
|
||||
export function extractAwsServiceFromArn(resourceArn: string): string | null {
|
||||
if (!resourceArn || !resourceArn.startsWith("arn:")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const parts = resourceArn.split(":");
|
||||
if (parts.length >= 3) {
|
||||
const service = parts[2];
|
||||
return service || null;
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error("Error extracting service from ARN:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user