feat(ui): add MongoDB Atlas provider support (#9253)

Co-authored-by: Alan Buscaglia <gentlemanprogramming@gmail.com>
This commit is contained in:
Daniel Barranquero
2025-11-27 12:37:20 +01:00
committed by GitHub
parent 6e135abaa0
commit ed5f6b3af6
31 changed files with 256 additions and 179 deletions

View File

@@ -90,7 +90,7 @@ prowler dashboard
| 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, API |
| MongoDB Atlas | 10 | 3 | 0 | 0 | Official | UI, API, 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 |

View File

@@ -33,7 +33,7 @@ The supported providers right now are:
| [Github](/user-guide/providers/github/getting-started-github) | Official | UI, API, CLI |
| [Oracle Cloud](/user-guide/providers/oci/getting-started-oci) | Official | UI, API, CLI |
| [Infra as Code](/user-guide/providers/iac/getting-started-iac) | Official | UI, API, CLI |
| [MongoDB Atlas](/user-guide/providers/mongodbatlas/getting-started-mongodbatlas) | Official | CLI, API |
| [MongoDB Atlas](/user-guide/providers/mongodbatlas/getting-started-mongodbatlas) | Official | UI, API, CLI |
| [LLM](/user-guide/providers/llm/getting-started-llm) | Official | CLI |
| **NHN** | Unofficial | CLI |

View File

@@ -22,6 +22,7 @@ All notable changes to the **Prowler UI** are documented in this file.
- IaC (Infrastructure as Code) provider support for scanning remote repositories [(#8751)](https://github.com/prowler-cloud/prowler/pull/8751)
- PDF reporting for NIS2 compliance framework [(#9170)](https://github.com/prowler-cloud/prowler/pull/9170)
- External resource link to IaC findings for direct navigation to source code in Git repositories [(#9151)](https://github.com/prowler-cloud/prowler/pull/9151)
- MongoDB Atlas provider support [(#9253)](https://github.com/prowler-cloud/prowler/pull/9253)
- New Overview page and new app styles [(#9234)](https://github.com/prowler-cloud/prowler/pull/9234)
- Use branch name as region for IaC findings [(#9296)](https://github.com/prowler-cloud/prowler/pull/9296)

View File

@@ -4,62 +4,6 @@ import { redirect } from "next/navigation";
import { apiBaseUrl, getAuthHeaders } from "@/lib";
import { handleApiResponse } from "@/lib/server-actions-helper";
import { FindingsResponse } from "@/types";
interface IncludedItem {
type: string;
id: string;
attributes?: { provider?: string };
relationships?: { provider?: { data?: { id: string } } };
}
type FindingsApiResponse = FindingsResponse & {
included?: IncludedItem[];
};
const filterMongoFindings = <T extends FindingsApiResponse | null | undefined>(
result: T,
): T => {
if (!result?.data) return result;
const included = (result as FindingsApiResponse).included || [];
// Get IDs of providers containing "mongo" in included items
const mongoProviderIds = new Set(
included
.filter(
(item) =>
item.type === "providers" &&
item.attributes?.provider?.toLowerCase().includes("mongo"),
)
.map((item) => item.id),
);
// Filter out findings associated with mongo providers
result.data = result.data.filter((finding) => {
const scanId = finding.relationships?.scan?.data?.id;
// Find the scan in included items
const scan = included.find(
(item) => item.type === "scans" && item.id === scanId,
);
const providerId = scan?.relationships?.provider?.data?.id;
return !providerId || !mongoProviderIds.has(providerId);
});
// Filter out mongo-related included items
if ((result as FindingsApiResponse).included) {
(result as FindingsApiResponse).included = included.filter(
(item) =>
!(
item.type === "providers" &&
item.attributes?.provider?.toLowerCase().includes("mongo")
),
);
}
return result;
};
export const getFindings = async ({
page = 1,
pageSize = 10,
@@ -89,9 +33,7 @@ export const getFindings = async ({
headers,
});
const result = await handleApiResponse(findings);
return filterMongoFindings(result);
return handleApiResponse(findings);
} catch (error) {
console.error("Error fetching findings:", error);
return undefined;
@@ -129,9 +71,7 @@ export const getLatestFindings = async ({
headers,
});
const result = await handleApiResponse(findings);
return filterMongoFindings(result);
return handleApiResponse(findings);
} catch (error) {
console.error("Error fetching findings:", error);
return undefined;

View File

@@ -28,8 +28,6 @@ interface AggregatedProvider {
fail: number;
}
const EXCLUDED_PROVIDERS = new Set(["mongo", "mongodb", "mongodbatlas"]);
// API can return multiple entries for the same provider type, so we sum their findings
function aggregateProvidersByType(
providers: ProviderOverview[],
@@ -39,8 +37,6 @@ function aggregateProvidersByType(
for (const provider of providers) {
const { id, attributes } = provider;
if (EXCLUDED_PROVIDERS.has(id)) continue;
const existing = aggregated.get(id);
if (existing) {

View File

@@ -39,26 +39,9 @@ export const getProviders = async ({
headers,
});
const result = (await handleApiResponse(response)) as
return (await handleApiResponse(response)) as
| ProvidersApiResponse
| undefined;
if (result?.data) {
// Filter out providers with provider type containing "mongo"
result.data = result.data.filter(
(provider) =>
!provider.attributes?.provider?.toLowerCase().includes("mongo"),
);
// Also filter out mongo-related included items if present
if (result.included) {
result.included = result.included.filter(
(item) => !item.type.toLowerCase().includes("mongo"),
);
}
}
return result;
} catch (error) {
console.error("Error fetching providers:", error);
return undefined;

View File

@@ -9,47 +9,6 @@ import {
} from "@/lib/compliance/compliance-report-types";
import { addScanOperation } from "@/lib/sentry-breadcrumbs";
import { handleApiError, handleApiResponse } from "@/lib/server-actions-helper";
import { ScansApiResponse } from "@/types";
const filterMongoScans = (result: ScansApiResponse | undefined) => {
if (!result?.data) return result;
const included = result.included || [];
// Get IDs of providers containing "mongo"
const mongoProviderIds = new Set(
included
.filter(
(item) =>
item.type === "providers" &&
item.attributes?.provider?.toLowerCase().includes("mongo"),
)
.map((item) => item.id),
);
// If no mongo providers found, return as-is
if (mongoProviderIds.size === 0) return result;
// Filter out scans associated with mongo providers
result.data = result.data.filter((scan) => {
const providerId = scan.relationships?.provider?.data?.id;
return !providerId || !mongoProviderIds.has(providerId);
});
// Filter out mongo-related included items
if (result.included) {
result.included = included.filter(
(item) =>
!(
item.type === "providers" &&
item.attributes?.provider?.toLowerCase().includes("mongo")
),
);
}
return result;
};
export const getScans = async ({
page = 1,
query = "",
@@ -84,10 +43,7 @@ export const getScans = async ({
try {
const response = await fetch(url.toString(), { headers });
const result = await handleApiResponse(response);
// Filter out mongo-related scans when provider is included
return filterMongoScans(result);
return handleApiResponse(response);
} catch (error) {
console.error("Error fetching scans:", error);
return undefined;

View File

@@ -11,6 +11,7 @@ import {
IacProviderBadge,
KS8ProviderBadge,
M365ProviderBadge,
MongoDBAtlasProviderBadge,
OracleCloudProviderBadge,
} from "@/components/icons/providers-badge";
import {
@@ -31,6 +32,7 @@ const PROVIDER_ICON: Record<ProviderType, ReactNode> = {
github: <GitHubProviderBadge width={18} height={18} />,
iac: <IacProviderBadge width={18} height={18} />,
oraclecloud: <OracleCloudProviderBadge width={18} height={18} />,
mongodbatlas: <MongoDBAtlasProviderBadge width={18} height={18} />,
};
interface AccountsSelectorProps {

View File

@@ -52,6 +52,11 @@ const OracleCloudProviderBadge = lazy(() =>
default: m.OracleCloudProviderBadge,
})),
);
const MongoDBAtlasProviderBadge = lazy(() =>
import("@/components/icons/providers-badge").then((m) => ({
default: m.MongoDBAtlasProviderBadge,
})),
);
type IconProps = { width: number; height: number };
@@ -95,6 +100,10 @@ const PROVIDER_DATA: Record<
label: "Oracle Cloud Infrastructure",
icon: OracleCloudProviderBadge,
},
mongodbatlas: {
label: "MongoDB Atlas",
icon: MongoDBAtlasProviderBadge,
},
};
type ProviderTypeSelectorProps = {

View File

@@ -8,6 +8,7 @@ import {
IacProviderBadge,
KS8ProviderBadge,
M365ProviderBadge,
MongoDBAtlasProviderBadge,
OracleCloudProviderBadge,
} from "../icons/providers-badge";
@@ -38,6 +39,15 @@ export const CustomProviderInputM365 = () => {
);
};
export const CustomProviderInputMongoDBAtlas = () => {
return (
<div className="flex items-center gap-x-2">
<MongoDBAtlasProviderBadge width={25} height={25} />
<p className="text-sm">MongoDB Atlas</p>
</div>
);
};
export const CustomProviderInputGCP = () => {
return (
<div className="flex items-center gap-x-2">

View File

@@ -14,6 +14,7 @@ import {
CustomProviderInputIac,
CustomProviderInputKubernetes,
CustomProviderInputM365,
CustomProviderInputMongoDBAtlas,
CustomProviderInputOracleCloud,
} from "./custom-provider-inputs";
@@ -25,21 +26,13 @@ const providerDisplayData: Record<
label: "Amazon Web Services",
component: <CustomProviderInputAWS />,
},
gcp: {
label: "Google Cloud Platform",
component: <CustomProviderInputGCP />,
},
azure: {
label: "Microsoft Azure",
component: <CustomProviderInputAzure />,
},
m365: {
label: "Microsoft 365",
component: <CustomProviderInputM365 />,
},
kubernetes: {
label: "Kubernetes",
component: <CustomProviderInputKubernetes />,
gcp: {
label: "Google Cloud Platform",
component: <CustomProviderInputGCP />,
},
github: {
label: "GitHub",
@@ -49,6 +42,18 @@ const providerDisplayData: Record<
label: "Infrastructure as Code",
component: <CustomProviderInputIac />,
},
kubernetes: {
label: "Kubernetes",
component: <CustomProviderInputKubernetes />,
},
m365: {
label: "Microsoft 365",
component: <CustomProviderInputM365 />,
},
mongodbatlas: {
label: "MongoDB Atlas",
component: <CustomProviderInputMongoDBAtlas />,
},
oraclecloud: {
label: "Oracle Cloud Infrastructure",
component: <CustomProviderInputOracleCloud />,

View File

@@ -1,6 +1,19 @@
import { CONNECTION_STATUS_MAPPING } from "@/lib/helper-filters";
import { FilterOption, FilterType } from "@/types/filters";
import { PROVIDER_TYPES } from "@/types/providers";
import {
PROVIDER_DISPLAY_NAMES,
PROVIDER_TYPES,
ProviderType,
} from "@/types/providers";
// Create a mapping for provider types to display with icons and labels
const PROVIDER_TYPE_MAPPING = PROVIDER_TYPES.map((providerType) => ({
[providerType]: {
provider: providerType as ProviderType,
uid: "",
alias: PROVIDER_DISPLAY_NAMES[providerType],
},
}));
export const filterProviders: FilterOption[] = [
{
@@ -13,6 +26,7 @@ export const filterProviders: FilterOption[] = [
key: "provider__in",
labelCheckboxGroup: "Cloud Provider",
values: [...PROVIDER_TYPES],
valueLabelMapping: PROVIDER_TYPE_MAPPING,
},
// Add more filter categories as needed
];
@@ -22,6 +36,7 @@ export const filterScans = [
key: "provider_type__in",
labelCheckboxGroup: "Cloud Provider",
values: [...PROVIDER_TYPES],
valueLabelMapping: PROVIDER_TYPE_MAPPING,
index: 0,
},
{
@@ -64,6 +79,7 @@ export const filterFindings = [
key: FilterType.PROVIDER_TYPE,
labelCheckboxGroup: "Cloud Provider",
values: [...PROVIDER_TYPES],
valueLabelMapping: PROVIDER_TYPE_MAPPING,
index: 5,
},
{

View File

@@ -7,6 +7,7 @@ import { GitHubProviderBadge } from "./github-provider-badge";
import { IacProviderBadge } from "./iac-provider-badge";
import { KS8ProviderBadge } from "./ks8-provider-badge";
import { M365ProviderBadge } from "./m365-provider-badge";
import { MongoDBAtlasProviderBadge } from "./mongodbatlas-provider-badge";
import { OracleCloudProviderBadge } from "./oraclecloud-provider-badge";
export {
@@ -17,6 +18,7 @@ export {
IacProviderBadge,
KS8ProviderBadge,
M365ProviderBadge,
MongoDBAtlasProviderBadge,
OracleCloudProviderBadge,
};
@@ -30,4 +32,5 @@ export const PROVIDER_ICONS: Record<string, React.FC<IconSvgProps>> = {
GitHub: GitHubProviderBadge,
"Infrastructure as Code": IacProviderBadge,
"Oracle Cloud Infrastructure": OracleCloudProviderBadge,
"MongoDB Atlas": MongoDBAtlasProviderBadge,
};

View File

@@ -0,0 +1,40 @@
import * as React from "react";
import { IconSvgProps } from "@/types";
export const MongoDBAtlasProviderBadge: React.FC<IconSvgProps> = ({
size,
width,
height,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 256 256"
width={size || width}
{...props}
>
<g fill="none">
<rect width="256" height="256" fill="#e6f4ec" rx="60" />
<g transform="translate(128 128) scale(7.25) translate(-16 -16)">
<path
d="M15.9.087l.854 1.604c.192.296.4.558.645.802.715.715 1.394 1.464 2.004 2.266 1.447 1.9 2.423 4.01 3.12 6.292.418 1.394.645 2.824.662 4.27.07 4.323-1.412 8.035-4.4 11.12-.488.488-1.01.94-1.57 1.342-.296 0-.436-.227-.558-.436-.227-.383-.366-.82-.436-1.255-.105-.523-.174-1.046-.14-1.586v-.244C16.057 24.21 15.796.21 15.9.087z"
fill="#599636"
/>
<path
d="M15.9.034c-.035-.07-.07-.017-.105.017.017.35-.105.662-.296.96-.21.296-.488.523-.767.767-1.55 1.342-2.77 2.963-3.747 4.776-1.3 2.44-1.97 5.055-2.16 7.808-.087.993.314 4.497.627 5.508.854 2.684 2.388 4.933 4.375 6.885.488.47 1.01.906 1.55 1.325.157 0 .174-.14.21-.244a4.78 4.78 0 0 0 .157-.68l.35-2.614L15.9.034z"
fill="#6cac48"
/>
<path
d="M16.754 28.845c.035-.4.227-.732.436-1.063-.21-.087-.366-.26-.488-.453-.105-.174-.192-.383-.26-.575-.244-.732-.296-1.5-.366-2.248v-.453c-.087.07-.105.662-.105.75a17.37 17.37 0 0 1-.314 2.353c-.052.314-.087.627-.28.906 0 .035 0 .07.017.122.314.924.4 1.865.453 2.824v.35c0 .418-.017.33.33.47.14.052.296.07.436.174.105 0 .122-.087.122-.157l-.052-.575v-1.604c-.017-.28.035-.558.07-.82z"
fill="#c2bfbf"
/>
</g>
</g>
</svg>
);

View File

@@ -19,6 +19,7 @@ const providerTypeLabels: Record<ProviderType, string> = {
github: "GitHub",
iac: "Infrastructure as Code",
oraclecloud: "Oracle Cloud Infrastructure",
mongodbatlas: "MongoDB Atlas",
};
interface EnhancedProviderSelectorProps {

View File

@@ -1,10 +1,10 @@
import { Tooltip } from "@heroui/tooltip";
import React from "react";
import { ProviderType } from "@/types";
import { ConnectionFalse, ConnectionPending, ConnectionTrue } from "../icons";
import { getProviderLogo } from "../ui/entities";
interface ProviderInfoProps {
connected: boolean | null;
provider: ProviderType;
@@ -12,12 +12,12 @@ interface ProviderInfoProps {
providerUID?: string;
}
export const ProviderInfo: React.FC<ProviderInfoProps> = ({
export const ProviderInfo = ({
connected,
provider,
providerAlias,
providerUID,
}) => {
}: ProviderInfoProps) => {
const getIcon = () => {
switch (connected) {
case true:
@@ -31,8 +31,8 @@ export const ProviderInfo: React.FC<ProviderInfoProps> = ({
case false:
return (
<Tooltip content="Provider connection failed" className="text-xs">
<div className="rounded-medium border-danger bg-system-error-lighter flex items-center justify-center border-2 p-1">
<ConnectionFalse className="text-text-error" size={24} />
<div className="rounded-medium border-border-error flex items-center justify-center border-2 p-1">
<ConnectionFalse className="text-text-error-primary" size={24} />
</div>
</Tooltip>
);

View File

@@ -15,6 +15,7 @@ import {
IacProviderBadge,
KS8ProviderBadge,
M365ProviderBadge,
MongoDBAtlasProviderBadge,
OracleCloudProviderBadge,
} from "../icons/providers-badge";
import { CustomRadio } from "../ui/custom";
@@ -68,6 +69,12 @@ export const RadioGroupProvider: React.FC<RadioGroupProviderProps> = ({
<span className="ml-2">Microsoft 365</span>
</div>
</CustomRadio>
<CustomRadio description="MongoDB Atlas" value="mongodbatlas">
<div className="flex items-center">
<MongoDBAtlasProviderBadge size={26} />
<span className="ml-2">MongoDB Atlas</span>
</div>
</CustomRadio>
<CustomRadio description="Kubernetes" value="kubernetes">
<div className="flex items-center">
<KS8ProviderBadge size={26} />

View File

@@ -20,6 +20,7 @@ import {
KubernetesCredentials,
M365CertificateCredentials,
M365ClientSecretCredentials,
MongoDBAtlasCredentials,
OCICredentials,
ProviderType,
} from "@/types";
@@ -37,6 +38,7 @@ import { AzureCredentialsForm } from "./via-credentials/azure-credentials-form";
import { GitHubCredentialsForm } from "./via-credentials/github-credentials-form";
import { IacCredentialsForm } from "./via-credentials/iac-credentials-form";
import { KubernetesCredentialsForm } from "./via-credentials/k8s-credentials-form";
import { MongoDBAtlasCredentialsForm } from "./via-credentials/mongodbatlas-credentials-form";
import { OracleCloudCredentialsForm } from "./via-credentials/oraclecloud-credentials-form";
type BaseCredentialsFormProps = {
@@ -172,6 +174,13 @@ export const BaseCredentialsForm = ({
control={form.control as unknown as Control<OCICredentials>}
/>
)}
{providerType === "mongodbatlas" && (
<MongoDBAtlasCredentialsForm
control={
form.control as unknown as Control<MongoDBAtlasCredentials>
}
/>
)}
<div className="flex w-full justify-end gap-4">
{showBackButton && requiresBackButton(searchParamsObj.get("via")) && (

View File

@@ -62,6 +62,11 @@ const getProviderFieldDetails = (providerType?: ProviderType) => {
label: "Tenancy OCID",
placeholder: "e.g. ocid1.tenancy.oc1..aaaaaaa...",
};
case "mongodbatlas":
return {
label: "Organization ID",
placeholder: "e.g. 5f43a8c4e1234567890abcde",
};
default:
return {
label: "Provider UID",

View File

@@ -45,7 +45,7 @@ export const TestConnectionForm = ({
};
provider: ProviderType;
alias: string;
scanner_args: Record<string, any>;
scanner_args: Record<string, unknown>;
};
relationships: {
secret: {
@@ -239,27 +239,27 @@ export const TestConnectionForm = ({
</div>
{apiErrorMessage && (
<div className="text-text-error mt-4 rounded-md bg-red-100 p-3">
<div className="text-text-error-primary mt-4 rounded-md p-3">
<p>{`Provider ID ${apiErrorMessage?.toLowerCase()}. Please check and try again.`}</p>
</div>
)}
{connectionStatus && !connectionStatus.connected && (
<>
<div className="flex items-center gap-4 rounded-lg border border-red-200 bg-red-50 p-4">
<div className="flex items-center">
<div className="border-border-error flex items-start gap-4 rounded-lg border p-4">
<div className="flex shrink-0 items-center">
<Icon
icon="heroicons:exclamation-circle"
className="text-text-error h-5 w-5"
className="text-text-error-primary h-5 w-5"
/>
</div>
<div className="flex items-center">
<p className="text-small text-text-error">
<div className="min-w-0 flex-1">
<p className="text-small text-text-error-primary break-words">
{connectionStatus.error || "Unknown error"}
</p>
</div>
</div>
<p className="text-small text-text-error">
<p className="text-small text-text-error-primary">
It seems there was an issue with your credentials. Please review
your credentials and try again.
</p>

View File

@@ -2,3 +2,4 @@ export * from "./azure-credentials-form";
export * from "./github-credentials-form";
export * from "./iac-credentials-form";
export * from "./k8s-credentials-form";
export * from "./mongodbatlas-credentials-form";

View File

@@ -0,0 +1,49 @@
import { Control } from "react-hook-form";
import { CustomInput } from "@/components/ui/custom";
import { ProviderCredentialFields } from "@/lib/provider-credentials/provider-credential-fields";
import { MongoDBAtlasCredentials } from "@/types";
export const MongoDBAtlasCredentialsForm = ({
control,
}: {
control: Control<MongoDBAtlasCredentials>;
}) => {
return (
<>
<div className="flex flex-col">
<div className="text-md text-default-foreground leading-9 font-bold">
Connect via API Keys
</div>
<div className="text-default-500 text-sm">
Provide an organization-level MongoDB Atlas API public and private key
with read access to the resources you want Prowler to assess.
</div>
</div>
<CustomInput
control={control}
name={ProviderCredentialFields.ATLAS_PUBLIC_KEY}
type="text"
label="Atlas Public Key"
labelPlacement="inside"
placeholder="e.g. abcdefgh"
variant="bordered"
isRequired
/>
<CustomInput
control={control}
name={ProviderCredentialFields.ATLAS_PRIVATE_KEY}
type="password"
label="Atlas Private Key"
labelPlacement="inside"
placeholder="Enter the private key"
variant="bordered"
isRequired
/>
<div className="text-default-400 text-xs">
Keys never leave your browser unencrypted and are stored as secrets in
the backend. Rotate the key from MongoDB Atlas anytime if needed.
</div>
</>
);
};

View File

@@ -8,6 +8,7 @@ import {
IacProviderBadge,
KS8ProviderBadge,
M365ProviderBadge,
MongoDBAtlasProviderBadge,
OracleCloudProviderBadge,
} from "@/components/icons/providers-badge";
import { ProviderType } from "@/types";
@@ -30,6 +31,8 @@ export const getProviderLogo = (provider: ProviderType) => {
return <IacProviderBadge width={35} height={35} />;
case "oraclecloud":
return <OracleCloudProviderBadge width={35} height={35} />;
case "mongodbatlas":
return <MongoDBAtlasProviderBadge width={35} height={35} />;
default:
return null;
}
@@ -53,6 +56,8 @@ export const getProviderName = (provider: ProviderType): string => {
return "Infrastructure as Code";
case "oraclecloud":
return "Oracle Cloud Infrastructure";
case "mongodbatlas":
return "MongoDB Atlas";
default:
return "Unknown Provider";
}

View File

@@ -167,6 +167,12 @@ export const useCredentialsForm = ({
[ProviderCredentialFields.OCI_REGION]: "",
[ProviderCredentialFields.OCI_PASS_PHRASE]: "",
};
case "mongodbatlas":
return {
...baseDefaults,
[ProviderCredentialFields.ATLAS_PUBLIC_KEY]: "",
[ProviderCredentialFields.ATLAS_PRIVATE_KEY]: "",
};
default:
return baseDefaults;
}

View File

@@ -28,4 +28,6 @@ export const PROVIDER_CREDENTIALS_ERROR_MAPPING: Record<string, string> = {
[ErrorPointers.ROLE_SESSION_NAME]: ProviderCredentialFields.ROLE_SESSION_NAME,
[ErrorPointers.SERVICE_ACCOUNT_KEY]:
ProviderCredentialFields.SERVICE_ACCOUNT_KEY,
[ErrorPointers.ATLAS_PUBLIC_KEY]: ProviderCredentialFields.ATLAS_PUBLIC_KEY,
[ErrorPointers.ATLAS_PRIVATE_KEY]: ProviderCredentialFields.ATLAS_PRIVATE_KEY,
};

View File

@@ -42,6 +42,11 @@ export const getProviderHelpText = (provider: string) => {
text: "Need help connecting your Oracle Cloud account?",
link: "https://goto.prowler.com/provider-oraclecloud",
};
case "mongodbatlas":
return {
text: "Need help connecting your MongoDB Atlas organization?",
link: "https://goto.prowler.com/provider-mongodbatlas",
};
default:
return {
text: "How to setup a provider?",

View File

@@ -197,6 +197,20 @@ export const buildGitHubSecret = (formData: FormData) => {
return {};
};
export const buildMongoDBAtlasSecret = (formData: FormData) => {
const secret = {
[ProviderCredentialFields.ATLAS_PUBLIC_KEY]: getFormValue(
formData,
ProviderCredentialFields.ATLAS_PUBLIC_KEY,
),
[ProviderCredentialFields.ATLAS_PRIVATE_KEY]: getFormValue(
formData,
ProviderCredentialFields.ATLAS_PRIVATE_KEY,
),
};
return filterEmptyValues(secret);
};
export const buildIacSecret = (formData: FormData) => {
const secret = {
[ProviderCredentialFields.REPOSITORY_URL]: getFormValue(
@@ -308,6 +322,10 @@ export const buildSecretConfig = (
secretType: "static",
secret: buildOracleCloudSecret(formData, providerUid),
}),
mongodbatlas: () => ({
secretType: "static",
secret: buildMongoDBAtlasSecret(formData),
}),
};
const builder = secretBuilders[providerType];

View File

@@ -45,6 +45,10 @@ export const ProviderCredentialFields = {
GITHUB_APP_ID: "github_app_id",
GITHUB_APP_KEY: "github_app_key_content",
// MongoDB Atlas fields
ATLAS_PUBLIC_KEY: "atlas_public_key",
ATLAS_PRIVATE_KEY: "atlas_private_key",
// IaC fields
REPOSITORY_URL: "repository_url",
ACCESS_TOKEN: "access_token",
@@ -95,6 +99,8 @@ export const ErrorPointers = {
OCI_TENANCY: "/data/attributes/secret/tenancy",
OCI_REGION: "/data/attributes/secret/region",
OCI_PASS_PHRASE: "/data/attributes/secret/pass_phrase",
ATLAS_PUBLIC_KEY: "/data/attributes/secret/atlas_public_key",
ATLAS_PRIVATE_KEY: "/data/attributes/secret/atlas_private_key",
} as const;
export type ErrorPointer = (typeof ErrorPointers)[keyof typeof ErrorPointers];

View File

@@ -264,6 +264,12 @@ export type OCICredentials = {
[ProviderCredentialFields.PROVIDER_ID]: string;
};
export type MongoDBAtlasCredentials = {
[ProviderCredentialFields.ATLAS_PUBLIC_KEY]: string;
[ProviderCredentialFields.ATLAS_PRIVATE_KEY]: string;
[ProviderCredentialFields.PROVIDER_ID]: string;
};
export type CredentialsFormSchema =
| AWSCredentials
| AzureCredentials
@@ -272,7 +278,8 @@ export type CredentialsFormSchema =
| KubernetesCredentials
| IacCredentials
| M365Credentials
| OCICredentials;
| OCICredentials
| MongoDBAtlasCredentials;
export interface SearchParamsProps {
[key: string]: string | string[] | undefined;

View File

@@ -120,6 +120,11 @@ export const addProviderFormSchema = z
[ProviderCredentialFields.PROVIDER_ALIAS]: z.string(),
providerUid: z.string(),
}),
z.object({
providerType: z.literal("mongodbatlas"),
[ProviderCredentialFields.PROVIDER_ALIAS]: z.string(),
providerUid: z.string(),
}),
]),
);
@@ -231,6 +236,15 @@ export const addCredentialsFormSchema = (
.union([z.string(), z.literal("")])
.optional(),
}
: providerType === "mongodbatlas"
? {
[ProviderCredentialFields.ATLAS_PUBLIC_KEY]: z
.string()
.min(1, "Atlas Public Key is required"),
[ProviderCredentialFields.ATLAS_PRIVATE_KEY]: z
.string()
.min(1, "Atlas Private Key is required"),
}
: {}),
})
.superRefine((data: Record<string, any>, ctx) => {

View File

@@ -4,6 +4,7 @@ export const PROVIDER_TYPES = [
"gcp",
"kubernetes",
"m365",
"mongodbatlas",
"github",
"iac",
"oraclecloud",
@@ -17,6 +18,7 @@ export const PROVIDER_DISPLAY_NAMES: Record<ProviderType, string> = {
gcp: "Google Cloud",
kubernetes: "Kubernetes",
m365: "Microsoft 365",
mongodbatlas: "MongoDB Atlas",
github: "GitHub",
iac: "Infrastructure as Code",
oraclecloud: "Oracle Cloud Infrastructure",
@@ -85,27 +87,6 @@ export interface ProviderConnectionStatus {
value: string;
}
export interface ProviderOverviewProps {
data: {
type: "provider-overviews";
id: ProviderType;
attributes: {
findings: {
pass: number;
fail: number;
manual: number;
total: number;
};
resources: {
total: number;
};
};
}[];
meta: {
version: string;
};
}
export interface ProvidersApiResponse {
links: {
first: string;