mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-19 18:04:17 +00:00
feat(ui): add image provider support to UI
This commit is contained in:
1 parent
18bac5a890
commit
1ec79831d7
17 files changed
+265
-25
No files matched your search
@@ -10,6 +10,7 @@ import {
|
||||
GCPProviderBadge,
|
||||
GitHubProviderBadge,
|
||||
IacProviderBadge,
|
||||
ImageProviderBadge,
|
||||
KS8ProviderBadge,
|
||||
M365ProviderBadge,
|
||||
MongoDBAtlasProviderBadge,
|
||||
@@ -34,6 +35,7 @@ const PROVIDER_ICON: Record<ProviderType, ReactNode> = {
|
||||
m365: <M365ProviderBadge width={18} height={18} />,
|
||||
github: <GitHubProviderBadge width={18} height={18} />,
|
||||
iac: <IacProviderBadge width={18} height={18} />,
|
||||
image: <ImageProviderBadge width={18} height={18} />,
|
||||
oraclecloud: <OracleCloudProviderBadge width={18} height={18} />,
|
||||
mongodbatlas: <MongoDBAtlasProviderBadge width={18} height={18} />,
|
||||
alibabacloud: <AlibabaCloudProviderBadge width={18} height={18} />,
|
||||
|
||||
@@ -48,6 +48,11 @@ const IacProviderBadge = lazy(() =>
|
||||
default: m.IacProviderBadge,
|
||||
})),
|
||||
);
|
||||
const ImageProviderBadge = lazy(() =>
|
||||
import("@/components/icons/providers-badge").then((m) => ({
|
||||
default: m.ImageProviderBadge,
|
||||
})),
|
||||
);
|
||||
const OracleCloudProviderBadge = lazy(() =>
|
||||
import("@/components/icons/providers-badge").then((m) => ({
|
||||
default: m.OracleCloudProviderBadge,
|
||||
@@ -107,6 +112,10 @@ const PROVIDER_DATA: Record<
|
||||
label: "Infrastructure as Code",
|
||||
icon: IacProviderBadge,
|
||||
},
|
||||
image: {
|
||||
label: "Container Registry",
|
||||
icon: ImageProviderBadge,
|
||||
},
|
||||
oraclecloud: {
|
||||
label: "Oracle Cloud Infrastructure",
|
||||
icon: OracleCloudProviderBadge,
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
GCPProviderBadge,
|
||||
GitHubProviderBadge,
|
||||
IacProviderBadge,
|
||||
ImageProviderBadge,
|
||||
KS8ProviderBadge,
|
||||
M365ProviderBadge,
|
||||
MongoDBAtlasProviderBadge,
|
||||
@@ -21,6 +22,7 @@ export const PROVIDER_ICONS = {
|
||||
m365: M365ProviderBadge,
|
||||
github: GitHubProviderBadge,
|
||||
iac: IacProviderBadge,
|
||||
image: ImageProviderBadge,
|
||||
oraclecloud: OracleCloudProviderBadge,
|
||||
mongodbatlas: MongoDBAtlasProviderBadge,
|
||||
alibabacloud: AlibabaCloudProviderBadge,
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { IconSvgProps } from "@/types";
|
||||
|
||||
export const ImageProviderBadge: 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 24 24"
|
||||
width={size || width}
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M12.89 1.45L21 5.75V18.25L12.89 22.55C12.33 22.84 11.67 22.84 11.11 22.55L3 18.25V5.75L11.11 1.45C11.67 1.16 12.33 1.16 12.89 1.45Z"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M3.5 6L12 10.5L20.5 6"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M12 22.5V10.5"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -8,6 +8,7 @@ import { AzureProviderBadge } from "./azure-provider-badge";
|
||||
import { GCPProviderBadge } from "./gcp-provider-badge";
|
||||
import { GitHubProviderBadge } from "./github-provider-badge";
|
||||
import { IacProviderBadge } from "./iac-provider-badge";
|
||||
import { ImageProviderBadge } from "./image-provider-badge";
|
||||
import { KS8ProviderBadge } from "./ks8-provider-badge";
|
||||
import { M365ProviderBadge } from "./m365-provider-badge";
|
||||
import { MongoDBAtlasProviderBadge } from "./mongodbatlas-provider-badge";
|
||||
@@ -21,6 +22,7 @@ export {
|
||||
GCPProviderBadge,
|
||||
GitHubProviderBadge,
|
||||
IacProviderBadge,
|
||||
ImageProviderBadge,
|
||||
KS8ProviderBadge,
|
||||
M365ProviderBadge,
|
||||
MongoDBAtlasProviderBadge,
|
||||
@@ -37,6 +39,7 @@ export const PROVIDER_ICONS: Record<string, FC<IconSvgProps>> = {
|
||||
"Microsoft 365": M365ProviderBadge,
|
||||
GitHub: GitHubProviderBadge,
|
||||
"Infrastructure as Code": IacProviderBadge,
|
||||
"Container Registry": ImageProviderBadge,
|
||||
"Oracle Cloud Infrastructure": OracleCloudProviderBadge,
|
||||
"MongoDB Atlas": MongoDBAtlasProviderBadge,
|
||||
"Alibaba Cloud": AlibabaCloudProviderBadge,
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
GCPProviderBadge,
|
||||
GitHubProviderBadge,
|
||||
IacProviderBadge,
|
||||
ImageProviderBadge,
|
||||
KS8ProviderBadge,
|
||||
M365ProviderBadge,
|
||||
MongoDBAtlasProviderBadge,
|
||||
@@ -64,6 +65,11 @@ const PROVIDERS = [
|
||||
label: "Infrastructure as Code",
|
||||
badge: IacProviderBadge,
|
||||
},
|
||||
{
|
||||
value: "image",
|
||||
label: "Container Registry",
|
||||
badge: ImageProviderBadge,
|
||||
},
|
||||
{
|
||||
value: "oraclecloud",
|
||||
label: "Oracle Cloud Infrastructure",
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
GCPDefaultCredentials,
|
||||
GCPServiceAccountKey,
|
||||
IacCredentials,
|
||||
ImageCredentials,
|
||||
KubernetesCredentials,
|
||||
M365CertificateCredentials,
|
||||
M365ClientSecretCredentials,
|
||||
@@ -45,6 +46,7 @@ import {
|
||||
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 { ImageCredentialsForm } from "./via-credentials/image-credentials-form";
|
||||
import { KubernetesCredentialsForm } from "./via-credentials/k8s-credentials-form";
|
||||
import { MongoDBAtlasCredentialsForm } from "./via-credentials/mongodbatlas-credentials-form";
|
||||
import { OpenStackCredentialsForm } from "./via-credentials/openstack-credentials-form";
|
||||
@@ -180,6 +182,11 @@ export const BaseCredentialsForm = ({
|
||||
control={form.control as unknown as Control<IacCredentials>}
|
||||
/>
|
||||
)}
|
||||
{providerType === "image" && (
|
||||
<ImageCredentialsForm
|
||||
control={form.control as unknown as Control<ImageCredentials>}
|
||||
/>
|
||||
)}
|
||||
{providerType === "oraclecloud" && (
|
||||
<OracleCloudCredentialsForm
|
||||
control={form.control as unknown as Control<OCICredentials>}
|
||||
|
||||
@@ -57,6 +57,11 @@ const getProviderFieldDetails = (providerType?: ProviderType) => {
|
||||
label: "Repository URL",
|
||||
placeholder: "e.g. https://github.com/user/repo",
|
||||
};
|
||||
case "image":
|
||||
return {
|
||||
label: "Registry URL",
|
||||
placeholder: "e.g. https://registry.example.com",
|
||||
};
|
||||
case "oraclecloud":
|
||||
return {
|
||||
label: "Tenancy OCID",
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import { Control } from "react-hook-form";
|
||||
|
||||
import { CustomInput } from "@/components/ui/custom";
|
||||
import { ImageCredentials } from "@/types";
|
||||
|
||||
export const ImageCredentialsForm = ({
|
||||
control,
|
||||
}: {
|
||||
control: Control<ImageCredentials>;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col">
|
||||
<div className="text-md text-default-foreground leading-9 font-bold">
|
||||
Connect via Registry Credentials
|
||||
</div>
|
||||
<div className="text-default-500 text-sm">
|
||||
Provide registry credentials to authenticate with your container
|
||||
registry (all fields are optional).
|
||||
</div>
|
||||
</div>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="registry_username"
|
||||
label="Registry Username (Optional)"
|
||||
labelPlacement="inside"
|
||||
placeholder="Username for registry authentication"
|
||||
variant="bordered"
|
||||
type="text"
|
||||
isRequired={false}
|
||||
/>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="registry_password"
|
||||
label="Registry Password (Optional)"
|
||||
labelPlacement="inside"
|
||||
placeholder="Password for registry authentication"
|
||||
variant="bordered"
|
||||
type="password"
|
||||
isRequired={false}
|
||||
/>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="registry_token"
|
||||
label="Registry Token (Optional)"
|
||||
labelPlacement="inside"
|
||||
placeholder="Token for registry authentication"
|
||||
variant="bordered"
|
||||
type="password"
|
||||
isRequired={false}
|
||||
/>
|
||||
|
||||
<div className="flex flex-col pt-2">
|
||||
<div className="text-md text-default-foreground leading-9 font-bold">
|
||||
Scan Scope
|
||||
</div>
|
||||
<div className="text-default-500 text-sm">
|
||||
Limit which repositories and tags are scanned using regex patterns.
|
||||
</div>
|
||||
</div>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="image_filter"
|
||||
label="Image Filter (Optional)"
|
||||
labelPlacement="inside"
|
||||
placeholder="e.g. ^prod/.*"
|
||||
variant="bordered"
|
||||
type="text"
|
||||
isRequired={false}
|
||||
/>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="tag_filter"
|
||||
label="Tag Filter (Optional)"
|
||||
labelPlacement="inside"
|
||||
placeholder="e.g. ^(latest|v\d+\.\d+\.\d+)$"
|
||||
variant="bordered"
|
||||
type="text"
|
||||
isRequired={false}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from "./azure-credentials-form";
|
||||
export * from "./github-credentials-form";
|
||||
export * from "./iac-credentials-form";
|
||||
export * from "./image-credentials-form";
|
||||
export * from "./k8s-credentials-form";
|
||||
export * from "./mongodbatlas-credentials-form";
|
||||
export * from "./openstack-credentials-form";
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
GCPProviderBadge,
|
||||
GitHubProviderBadge,
|
||||
IacProviderBadge,
|
||||
ImageProviderBadge,
|
||||
KS8ProviderBadge,
|
||||
M365ProviderBadge,
|
||||
MongoDBAtlasProviderBadge,
|
||||
@@ -29,6 +30,8 @@ export const getProviderLogo = (provider: ProviderType) => {
|
||||
return <GitHubProviderBadge width={35} height={35} />;
|
||||
case "iac":
|
||||
return <IacProviderBadge width={35} height={35} />;
|
||||
case "image":
|
||||
return <ImageProviderBadge width={35} height={35} />;
|
||||
case "oraclecloud":
|
||||
return <OracleCloudProviderBadge width={35} height={35} />;
|
||||
case "mongodbatlas":
|
||||
@@ -58,6 +61,8 @@ export const getProviderName = (provider: ProviderType): string => {
|
||||
return "GitHub";
|
||||
case "iac":
|
||||
return "Infrastructure as Code";
|
||||
case "image":
|
||||
return "Container Registry";
|
||||
case "oraclecloud":
|
||||
return "Oracle Cloud Infrastructure";
|
||||
case "mongodbatlas":
|
||||
|
||||
@@ -43,6 +43,11 @@ export const getProviderHelpText = (provider: string) => {
|
||||
text: "Need help scanning your Infrastructure as Code repository?",
|
||||
link: "https://goto.prowler.com/provider-iac",
|
||||
};
|
||||
case "image":
|
||||
return {
|
||||
text: "Need help scanning your container registry?",
|
||||
link: "https://goto.prowler.com/provider-image",
|
||||
};
|
||||
case "oraclecloud":
|
||||
return {
|
||||
text: "Need help connecting your Oracle Cloud account?",
|
||||
|
||||
@@ -278,6 +278,32 @@ export const buildIacSecret = (formData: FormData) => {
|
||||
return filterEmptyValues(secret);
|
||||
};
|
||||
|
||||
export const buildImageSecret = (formData: FormData) => {
|
||||
const secret = {
|
||||
[ProviderCredentialFields.REGISTRY_USERNAME]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.REGISTRY_USERNAME,
|
||||
),
|
||||
[ProviderCredentialFields.REGISTRY_PASSWORD]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.REGISTRY_PASSWORD,
|
||||
),
|
||||
[ProviderCredentialFields.REGISTRY_TOKEN]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.REGISTRY_TOKEN,
|
||||
),
|
||||
[ProviderCredentialFields.IMAGE_FILTER]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.IMAGE_FILTER,
|
||||
),
|
||||
[ProviderCredentialFields.TAG_FILTER]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.TAG_FILTER,
|
||||
),
|
||||
};
|
||||
return filterEmptyValues(secret);
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility function to safely encode a string to base64
|
||||
* Handles UTF-8 characters properly without using deprecated APIs
|
||||
@@ -371,6 +397,10 @@ export const buildSecretConfig = (
|
||||
secretType: "static",
|
||||
secret: buildIacSecret(formData),
|
||||
}),
|
||||
image: () => ({
|
||||
secretType: "static",
|
||||
secret: buildImageSecret(formData),
|
||||
}),
|
||||
oraclecloud: () => ({
|
||||
secretType: "static",
|
||||
secret: buildOracleCloudSecret(formData, providerUid),
|
||||
|
||||
@@ -53,6 +53,13 @@ export const ProviderCredentialFields = {
|
||||
REPOSITORY_URL: "repository_url",
|
||||
ACCESS_TOKEN: "access_token",
|
||||
|
||||
// Image (Container Registry) fields
|
||||
REGISTRY_USERNAME: "registry_username",
|
||||
REGISTRY_PASSWORD: "registry_password",
|
||||
REGISTRY_TOKEN: "registry_token",
|
||||
IMAGE_FILTER: "image_filter",
|
||||
TAG_FILTER: "tag_filter",
|
||||
|
||||
// OCI fields
|
||||
OCI_USER: "user",
|
||||
OCI_FINGERPRINT: "fingerprint",
|
||||
@@ -101,6 +108,11 @@ export const ErrorPointers = {
|
||||
GITHUB_APP_KEY: "/data/attributes/secret/github_app_key_content",
|
||||
REPOSITORY_URL: "/data/attributes/secret/repository_url",
|
||||
ACCESS_TOKEN: "/data/attributes/secret/access_token",
|
||||
REGISTRY_USERNAME: "/data/attributes/secret/registry_username",
|
||||
REGISTRY_PASSWORD: "/data/attributes/secret/registry_password",
|
||||
REGISTRY_TOKEN: "/data/attributes/secret/registry_token",
|
||||
IMAGE_FILTER: "/data/attributes/secret/image_filter",
|
||||
TAG_FILTER: "/data/attributes/secret/tag_filter",
|
||||
CERTIFICATE_CONTENT: "/data/attributes/secret/certificate_content",
|
||||
OCI_USER: "/data/attributes/secret/user",
|
||||
OCI_FINGERPRINT: "/data/attributes/secret/fingerprint",
|
||||
|
||||
@@ -304,6 +304,15 @@ export type IacCredentials = {
|
||||
[ProviderCredentialFields.PROVIDER_ID]: string;
|
||||
};
|
||||
|
||||
export type ImageCredentials = {
|
||||
[ProviderCredentialFields.REGISTRY_USERNAME]?: string;
|
||||
[ProviderCredentialFields.REGISTRY_PASSWORD]?: string;
|
||||
[ProviderCredentialFields.REGISTRY_TOKEN]?: string;
|
||||
[ProviderCredentialFields.IMAGE_FILTER]?: string;
|
||||
[ProviderCredentialFields.TAG_FILTER]?: string;
|
||||
[ProviderCredentialFields.PROVIDER_ID]: string;
|
||||
};
|
||||
|
||||
export type OCICredentials = {
|
||||
[ProviderCredentialFields.OCI_USER]: string;
|
||||
[ProviderCredentialFields.OCI_FINGERPRINT]: string;
|
||||
@@ -348,6 +357,7 @@ export type CredentialsFormSchema =
|
||||
| GCPServiceAccountKey
|
||||
| KubernetesCredentials
|
||||
| IacCredentials
|
||||
| ImageCredentials
|
||||
| M365Credentials
|
||||
| OCICredentials
|
||||
| MongoDBAtlasCredentials
|
||||
|
||||
+39
-25
@@ -115,6 +115,11 @@ export const addProviderFormSchema = z
|
||||
[ProviderCredentialFields.PROVIDER_ALIAS]: z.string(),
|
||||
providerUid: z.string(),
|
||||
}),
|
||||
z.object({
|
||||
providerType: z.literal("image"),
|
||||
[ProviderCredentialFields.PROVIDER_ALIAS]: z.string(),
|
||||
providerUid: z.string(),
|
||||
}),
|
||||
z.object({
|
||||
providerType: z.literal("oraclecloud"),
|
||||
[ProviderCredentialFields.PROVIDER_ALIAS]: z.string(),
|
||||
@@ -225,44 +230,53 @@ export const addCredentialsFormSchema = (
|
||||
.string()
|
||||
.optional(),
|
||||
}
|
||||
: providerType === "oraclecloud"
|
||||
: providerType === "image"
|
||||
? {
|
||||
[ProviderCredentialFields.OCI_USER]: z
|
||||
[ProviderCredentialFields.REGISTRY_USERNAME]: z
|
||||
.string()
|
||||
.min(1, "User OCID is required"),
|
||||
[ProviderCredentialFields.OCI_FINGERPRINT]: z
|
||||
.optional(),
|
||||
[ProviderCredentialFields.REGISTRY_PASSWORD]: z
|
||||
.string()
|
||||
.min(1, "Fingerprint is required"),
|
||||
[ProviderCredentialFields.OCI_KEY_CONTENT]: z
|
||||
.optional(),
|
||||
[ProviderCredentialFields.REGISTRY_TOKEN]: z
|
||||
.string()
|
||||
.min(1, "Private Key Content is required"),
|
||||
[ProviderCredentialFields.OCI_TENANCY]: z
|
||||
.optional(),
|
||||
[ProviderCredentialFields.IMAGE_FILTER]: z
|
||||
.string()
|
||||
.min(1, "Tenancy OCID is required"),
|
||||
[ProviderCredentialFields.OCI_REGION]: z
|
||||
.optional(),
|
||||
[ProviderCredentialFields.TAG_FILTER]: z
|
||||
.string()
|
||||
.min(1, "Region is required"),
|
||||
[ProviderCredentialFields.OCI_PASS_PHRASE]: z
|
||||
.union([z.string(), z.literal("")])
|
||||
.optional(),
|
||||
}
|
||||
: providerType === "mongodbatlas"
|
||||
: providerType === "oraclecloud"
|
||||
? {
|
||||
[ProviderCredentialFields.ATLAS_PUBLIC_KEY]: z
|
||||
[ProviderCredentialFields.OCI_USER]: z
|
||||
.string()
|
||||
.min(1, "Atlas Public Key is required"),
|
||||
[ProviderCredentialFields.ATLAS_PRIVATE_KEY]: z
|
||||
.min(1, "User OCID is required"),
|
||||
[ProviderCredentialFields.OCI_FINGERPRINT]: z
|
||||
.string()
|
||||
.min(1, "Atlas Private Key is required"),
|
||||
.min(1, "Fingerprint is required"),
|
||||
[ProviderCredentialFields.OCI_KEY_CONTENT]: z
|
||||
.string()
|
||||
.min(1, "Private Key Content is required"),
|
||||
[ProviderCredentialFields.OCI_TENANCY]: z
|
||||
.string()
|
||||
.min(1, "Tenancy OCID is required"),
|
||||
[ProviderCredentialFields.OCI_REGION]: z
|
||||
.string()
|
||||
.min(1, "Region is required"),
|
||||
[ProviderCredentialFields.OCI_PASS_PHRASE]: z
|
||||
.union([z.string(), z.literal("")])
|
||||
.optional(),
|
||||
}
|
||||
: providerType === "alibabacloud"
|
||||
: providerType === "mongodbatlas"
|
||||
? {
|
||||
[ProviderCredentialFields.ALIBABACLOUD_ACCESS_KEY_ID]:
|
||||
z.string().min(1, "Access Key ID is required"),
|
||||
[ProviderCredentialFields.ALIBABACLOUD_ACCESS_KEY_SECRET]:
|
||||
z
|
||||
.string()
|
||||
.min(1, "Access Key Secret is required"),
|
||||
[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"),
|
||||
}
|
||||
: providerType === "openstack"
|
||||
? {
|
||||
|
||||
@@ -7,6 +7,7 @@ export const PROVIDER_TYPES = [
|
||||
"mongodbatlas",
|
||||
"github",
|
||||
"iac",
|
||||
"image",
|
||||
"oraclecloud",
|
||||
"alibabacloud",
|
||||
"openstack",
|
||||
@@ -23,6 +24,7 @@ export const PROVIDER_DISPLAY_NAMES: Record<ProviderType, string> = {
|
||||
mongodbatlas: "MongoDB Atlas",
|
||||
github: "GitHub",
|
||||
iac: "Infrastructure as Code",
|
||||
image: "Container Registry",
|
||||
oraclecloud: "Oracle Cloud Infrastructure",
|
||||
alibabacloud: "Alibaba Cloud",
|
||||
openstack: "OpenStack",
|
||||
|
||||
Reference in new issue
Block a user