Compare commits

...

42 Commits

Author SHA1 Message Date
Andoni A.
2cf2a791b8 chore(github): refactor following the pattern in finding-detail title 2025-11-04 11:01:51 +01:00
Andoni A.
00ce63484e chore(github): refactor following AGENTS.md 2025-11-04 10:44:26 +01:00
Andoni A.
719b59c203 Merge branch 'master' into add-links-to-iac-findings 2025-11-04 10:16:23 +01:00
Andoni A.
33ea05ab19 feat(github): add external resource link 2025-11-04 09:44:18 +01:00
Andoni A.
3b4a2c39cb fix(iac): solve migration order, yes, again, nowyes 2025-10-31 13:59:20 +01:00
Andoni A.
9b9af9bbc4 Merge branch 'master' into DEVREL-91-add-ia-c-to-the-app 2025-10-31 13:58:37 +01:00
Andoni A.
8e19c74ccc fix(iac): solve migration order, yes, again 2025-10-31 13:57:11 +01:00
Andoni A.
957342fdd4 fix(iac): solve indentation 2025-10-31 13:05:47 +01:00
Andoni A.
c3e395cd1c Merge branch 'master' into DEVREL-91-add-ia-c-to-the-app 2025-10-31 12:54:58 +01:00
Andoni A.
6a11df4434 fix(iac): solve migration order 2025-10-29 13:11:40 +01:00
Andoni A.
6204e1b409 fix(iac): solve next typecheck 2025-10-29 12:01:22 +01:00
Andoni A.
843c1ae360 docs: update providers table 2025-10-29 11:49:05 +01:00
Andoni A.
a997f0b438 Merge branch 'master' into DEVREL-91-add-ia-c-to-the-app 2025-10-29 11:25:26 +01:00
Andoni A.
782b698590 chore(iac): improve error handling flow 2025-10-29 10:46:31 +01:00
Andoni Alonso
59a7002c3e Merge branch 'master' into DEVREL-91-add-ia-c-to-the-app 2025-10-27 17:51:22 +01:00
Andoni A.
b5e13da454 fix(iac): filter findings by status 2025-10-27 16:19:36 +01:00
Andoni A.
5307bf4a8d chore: update CHANGELOG format 2025-10-27 14:40:33 +01:00
Andoni A.
113d1ce05d chore(iac): move version to top of the file 2025-10-27 14:38:34 +01:00
Andoni A.
6c97f6d9cc chore(iac): move lazy imports to top of the file 2025-10-27 14:37:28 +01:00
Andoni A.
a9e5068b6d Merge branch 'master' into DEVREL-91-add-ia-c-to-the-app 2025-10-27 12:16:13 +01:00
Andoni A.
c1213ac802 chore(iac): move lazy imports to top of the file 2025-10-27 12:02:39 +01:00
Andoni A.
1693f7a8d5 fix(iac): return IaC findings in batchs to avoid possible memory issues 2025-10-27 11:09:20 +01:00
Andoni A.
0a3b9c10fc fix(iac): remove duplicate repository URL input for IaC provider 2025-10-27 10:46:11 +01:00
Andoni A.
f249b54ce4 fix(iac) IaC logo now renders correctly on the overview page 2025-10-27 10:10:40 +01:00
Andoni A.
7b8558b837 chore(iac): hardcode risk value to avoid empty field 2025-10-27 09:43:29 +01:00
Andoni A.
2a87120229 chore: move load compliance frameworks into conditional 2025-10-23 10:49:45 +02:00
Andoni A.
5a13701cd8 chore: update CHANGELOG 2025-10-22 11:00:18 +02:00
Andoni A.
6349f20bb9 fix: solve linter issues 2025-10-22 10:52:35 +02:00
Andoni A.
4ce625aa15 fix: rename iac provider migration 2025-10-22 10:44:59 +02:00
Andoni A.
23a77b75fb Merge branch 'master' into DEVREL-91-add-ia-c-to-the-app 2025-10-22 10:38:36 +02:00
Andoni A.
861f9691be chore: update with new branch 2025-09-24 09:34:48 +02:00
Andoni A.
a5fe98b0d2 refactor: scan IaC skip validations 2025-09-24 09:12:19 +02:00
Andoni A.
d47ff104d6 remove: commit local dev changes, revert after sending PR 2025-09-12 10:58:07 +02:00
Andoni A.
d09adb3edd chore: update comments 2025-09-12 10:56:31 +02:00
Andoni A.
f76847d653 chore: update trivy 2025-09-12 10:38:01 +02:00
Andoni A.
06cf9ed0cc wip: set defaults 2025-09-12 10:38:01 +02:00
Andoni A.
75390c0979 wip: add IaC to UI 2025-09-12 10:37:49 +02:00
Andoni A.
27f5c9591b wip: fix IaC scan mapping 2025-09-12 10:36:42 +02:00
Andoni A.
dbff60576b wip: add IaC to API 2025-09-12 10:36:42 +02:00
Andoni A.
ff22d198d0 wip 2025-09-12 10:36:41 +02:00
Andoni A.
5c2b867546 wip: do not use progress bar in Celery workers 2025-09-12 10:36:41 +02:00
Andoni A.
ae2200131f wip 2025-09-12 10:36:41 +02:00
6 changed files with 270 additions and 17 deletions

View File

@@ -80,19 +80,19 @@ prowler dashboard
> For the most accurate and up-to-date information about checks, services, frameworks, and categories, visit [**Prowler Hub**](https://hub.prowler.com).
| Provider | Checks | Services | [Compliance Frameworks](https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/compliance/) | [Categories](https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/misc/#categories) | Support | Stage | Interface |
|---|---|---|---|---|---|---|---|
| AWS | 576 | 82 | 38 | 10 | Official | Stable | UI, API, CLI |
| GCP | 79 | 13 | 12 | 3 | Official | Stable | UI, API, CLI |
| Azure | 162 | 19 | 12 | 4 | Official | Stable | UI, API, CLI |
| Kubernetes | 83 | 7 | 5 | 7 | Official | Stable | UI, API, CLI |
| GitHub | 17 | 2 | 1 | 0 | Official | Stable | UI, API, CLI |
| M365 | 70 | 7 | 3 | 2 | Official | Stable | UI, API, CLI |
| OCI | 51 | 13 | 1 | 10 | Official | Stable | UI, API, CLI |
| IaC | [See `trivy` docs.](https://trivy.dev/latest/docs/coverage/iac/) | N/A | N/A | N/A | Official | Beta | CLI |
| MongoDB Atlas | 10 | 3 | 0 | 0 | Official | Beta | CLI |
| LLM | [See `promptfoo` docs.](https://www.promptfoo.dev/docs/red-team/plugins/) | N/A | N/A | N/A | Official | Beta | CLI |
| NHN | 6 | 2 | 1 | 0 | Unofficial | Beta | CLI |
| Provider | Checks | Services | [Compliance Frameworks](https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/compliance/) | [Categories](https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/misc/#categories) | Support | Interface |
|---|---|---|---|---|---|---|
| AWS | 576 | 82 | 38 | 10 | Official | UI, API, CLI |
| GCP | 79 | 13 | 11 | 3 | Official | UI, API, CLI |
| Azure | 162 | 19 | 12 | 4 | Official | UI, API, CLI |
| Kubernetes | 83 | 7 | 5 | 7 | Official | UI, API, CLI |
| GitHub | 17 | 2 | 1 | 0 | Official | UI, API, CLI |
| M365 | 70 | 7 | 3 | 2 | Official | UI, API, CLI |
| OCI | 51 | 13 | 1 | 10 | Official | UI, API, CLI |
| IaC | [See `trivy` docs.](https://trivy.dev/latest/docs/coverage/iac/) | N/A | N/A | N/A | Official | UI, API, CLI |
| MongoDB Atlas | 10 | 3 | 0 | 0 | Official | CLI |
| LLM | [See `promptfoo` docs.](https://www.promptfoo.dev/docs/red-team/plugins/) | N/A | N/A | N/A | Official | CLI |
| NHN | 6 | 2 | 1 | 0 | Unofficial | CLI |
> [!Note]
> The numbers in the table are updated periodically.

View File

@@ -52,6 +52,11 @@ All notable changes to the **Prowler SDK** are documented in this file.
---
### Changed
- Adapt IaC provider to be used in the Prowler App [(#8751)](https://github.com/prowler-cloud/prowler/pull/8751)
---
## [v5.13.0] (Prowler v5.13.0)
### Added

View File

@@ -17,6 +17,10 @@ SAMPLE_TRIVY_OUTPUT = {
"Severity": "LOW",
"PrimaryURL": "https://avd.aquasec.com/misconfig/aws/s3/avd-aws-0001",
"RuleID": "AVD-AWS-0001",
"CauseMetadata": {
"StartLine": 10,
"EndLine": 15,
},
},
{
"ID": "AVD-AWS-0002",
@@ -27,6 +31,10 @@ SAMPLE_TRIVY_OUTPUT = {
"Severity": "LOW",
"PrimaryURL": "https://avd.aquasec.com/misconfig/aws/s3/avd-aws-0002",
"RuleID": "AVD-AWS-0002",
"CauseMetadata": {
"StartLine": 20,
"EndLine": 25,
},
},
],
"Vulnerabilities": [],
@@ -46,6 +54,10 @@ SAMPLE_TRIVY_OUTPUT = {
"Severity": "LOW",
"PrimaryURL": "https://avd.aquasec.com/misconfig/aws/s3/avd-aws-0003",
"RuleID": "AVD-AWS-0003",
"CauseMetadata": {
"StartLine": 30,
"EndLine": 35,
},
}
],
"Vulnerabilities": [],
@@ -67,6 +79,10 @@ SAMPLE_FAILED_CHECK = {
"Severity": "low",
"PrimaryURL": "https://avd.aquasec.com/misconfig/aws/s3/avd-aws-0001",
"RuleID": "AVD-AWS-0001",
"CauseMetadata": {
"StartLine": 10,
"EndLine": 15,
},
}
SAMPLE_PASSED_CHECK = {
@@ -78,6 +94,10 @@ SAMPLE_PASSED_CHECK = {
"Severity": "low",
"PrimaryURL": "https://avd.aquasec.com/misconfig/aws/s3/avd-aws-0003",
"RuleID": "AVD-AWS-0003",
"CauseMetadata": {
"StartLine": 30,
"EndLine": 35,
},
}
# Additional sample checks

View File

@@ -1,6 +1,7 @@
"use client";
import { Snippet } from "@heroui/snippet";
import { ExternalLink } from "lucide-react";
import ReactMarkdown from "react-markdown";
import { CodeSnippet } from "@/components/ui/code-snippet/code-snippet";
@@ -17,6 +18,7 @@ import { FindingProps, ProviderType } from "@/types";
import { Muted } from "../muted";
import { DeltaIndicator } from "./delta-indicator";
import { buildGitFileUrl, extractLineRangeFromUid } from "./git-utils";
const MarkdownContainer = ({ children }: { children: string }) => {
return (
@@ -60,6 +62,16 @@ 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 =
providerDetails.provider === "iac"
? buildGitFileUrl(
providerDetails.uid,
resource.name,
extractLineRangeFromUid(attributes.uid) || "",
)
: null;
return (
<div className="flex flex-col gap-6 rounded-lg">
{/* Header */}
@@ -238,7 +250,33 @@ export const FindingDetail = ({
</CustomSection>
{/* Resource Details */}
<CustomSection title="Resource Details">
<CustomSection
title={
<span className="flex items-center gap-2">
Resource Details
{gitUrl && (
<a
href={gitUrl}
target="_blank"
rel="noopener noreferrer"
title="Go to Resource in the Repository"
className="text-blue-600 transition-colors hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300"
aria-label="Open resource in repository"
>
<ExternalLink size={16} />
</a>
)}
</span>
}
>
<InfoField label="Resource ID" variant="simple">
<Snippet className="bg-gray-50 py-1 dark:bg-slate-800" hideSymbol>
<span className="text-xs whitespace-pre-line">
{renderValue(resource.uid)}
</span>
</Snippet>
</InfoField>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<InfoField label="Resource Name">
{renderValue(resource.name)}

View File

@@ -0,0 +1,188 @@
/**
* Extracts line range from a Finding UID
* Finding UID format: {CheckID}-{resource_name}-{line_range}
* Example: "AVD-AWS-0001-main.tf-10:15" -> "10:15"
*
* @param findingUid - The finding UID
* @returns Line range string or null if not found
*/
export function extractLineRangeFromUid(findingUid: string): string | null {
if (!findingUid) {
return null;
}
// Split by dash and get the last part (line range)
const parts = findingUid.split("-");
const lastPart = parts[parts.length - 1];
// Check if the last part is a line range in format "number:number"
// This ensures we don't confuse numeric filenames with line ranges
if (/^\d+:\d+$/.test(lastPart)) {
return lastPart;
}
return null;
}
/**
* Builds a Git repository URL with file path and line numbers
* Supports GitHub, GitLab, Bitbucket, and generic Git URLs
*
* @param repoUrl - Repository URL (can be HTTPS or git@ format)
* @param filePath - Path to the file in the repository
* @param lineRange - Line range in format "10-15" or "10:15" or "10"
* @returns Complete URL to the file with line numbers, or null if URL cannot be built
*/
export function buildGitFileUrl(
repoUrl: string,
filePath: string,
lineRange: string,
): string | null {
if (!repoUrl || !filePath) {
return null;
}
try {
// Normalize the repository URL
let normalizedUrl = repoUrl.trim();
// Convert git@ format to HTTPS (best effort)
if (normalizedUrl.startsWith("git@")) {
// git@github.com:user/repo.git -> https://github.com/user/repo
normalizedUrl = normalizedUrl
.replace(/^git@/, "https://")
.replace(/\.git$/, "")
.replace(/:([^:]+)$/, "/$1"); // Replace last : with /
}
// Remove .git suffix if present
normalizedUrl = normalizedUrl.replace(/\.git$/, "");
// Parse URL to determine provider
const url = new URL(normalizedUrl);
const hostname = url.hostname.toLowerCase();
// Clean up file path (remove leading slashes)
const cleanFilePath = filePath.replace(/^\/+/, "");
// Parse line range
const { startLine, endLine } = parseLineRange(lineRange);
// Build URL based on Git provider
if (hostname.includes("github")) {
return buildGitHubUrl(normalizedUrl, cleanFilePath, startLine, endLine);
} else if (hostname.includes("gitlab")) {
return buildGitLabUrl(normalizedUrl, cleanFilePath, startLine, endLine);
} else if (hostname.includes("bitbucket")) {
return buildBitbucketUrl(
normalizedUrl,
cleanFilePath,
startLine,
endLine,
);
} else {
// Generic Git provider - try GitHub format as fallback
return buildGitHubUrl(normalizedUrl, cleanFilePath, startLine, endLine);
}
} catch (error) {
console.error("Error building Git file URL:", error);
return null;
}
}
/**
* Parses line range string into start and end line numbers
*/
function parseLineRange(lineRange: string): {
startLine: number | null;
endLine: number | null;
} {
if (!lineRange || lineRange === "file") {
return { startLine: null, endLine: null };
}
// Handle formats: "10-15", "10:15", "10"
// Safe regex: anchored pattern for line numbers only (no ReDoS risk)
// eslint-disable-next-line security/detect-unsafe-regex
const match = lineRange.match(/^(\d+)[-:]?(\d+)?$/);
if (match) {
const startLine = parseInt(match[1], 10);
const endLine = match[2] ? parseInt(match[2], 10) : startLine;
return { startLine, endLine };
}
return { startLine: null, endLine: null };
}
/**
* Builds GitHub-style URL
* Format: https://github.com/user/repo/blob/main/path/file.tf#L10-L15
*/
function buildGitHubUrl(
baseUrl: string,
filePath: string,
startLine: number | null,
endLine: number | null,
): string {
// Assume main/master branch for simplicity
const branch = "main";
let url = `${baseUrl}/blob/${branch}/${filePath}`;
if (startLine !== null) {
if (endLine !== null && endLine !== startLine) {
url += `#L${startLine}-L${endLine}`;
} else {
url += `#L${startLine}`;
}
}
return url;
}
/**
* Builds GitLab-style URL
* Format: https://gitlab.com/user/repo/-/blob/main/path/file.tf#L10-15
*/
function buildGitLabUrl(
baseUrl: string,
filePath: string,
startLine: number | null,
endLine: number | null,
): string {
const branch = "main";
let url = `${baseUrl}/-/blob/${branch}/${filePath}`;
if (startLine !== null) {
if (endLine !== null && endLine !== startLine) {
url += `#L${startLine}-${endLine}`;
} else {
url += `#L${startLine}`;
}
}
return url;
}
/**
* Builds Bitbucket-style URL
* Format: https://bitbucket.org/user/repo/src/main/path/file.tf#lines-10:15
*/
function buildBitbucketUrl(
baseUrl: string,
filePath: string,
startLine: number | null,
endLine: number | null,
): string {
const branch = "main";
let url = `${baseUrl}/src/${branch}/${filePath}`;
if (startLine !== null) {
if (endLine !== null && endLine !== startLine) {
url += `#lines-${startLine}:${endLine}`;
} else {
url += `#lines-${startLine}`;
}
}
return url;
}

View File

@@ -1,7 +1,9 @@
import { type ReactNode } from "react";
interface CustomSectionProps {
title: string;
children: React.ReactNode;
action?: React.ReactNode;
title: string | ReactNode;
children: ReactNode;
action?: ReactNode;
}
export const CustomSection = ({