mirror of
https://github.com/prowler-cloud/prowler.git
synced 2025-12-19 05:17:47 +00:00
Compare commits
14 Commits
ed3fd72e70
...
PRWLR-7606
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14a8ab3525 | ||
|
|
15400abbbd | ||
|
|
cf48ae5234 | ||
|
|
2e6c8ca892 | ||
|
|
9a52c5125d | ||
|
|
a5f8f5ea60 | ||
|
|
e08b003605 | ||
|
|
556a7b84a5 | ||
|
|
f1277e868c | ||
|
|
aef7876fed | ||
|
|
83f4d237c9 | ||
|
|
3c947061c8 | ||
|
|
900645f79b | ||
|
|
afd89cfb2c |
@@ -8,6 +8,7 @@ All notable changes to the **Prowler UI** are documented in this file.
|
||||
|
||||
- Mutelist configuration form [(#8190)](https://github.com/prowler-cloud/prowler/pull/8190)
|
||||
- SAML login integration [(#8203)](https://github.com/prowler-cloud/prowler/pull/8203)
|
||||
- Github provider support [(#8304)](https://github.com/prowler-cloud/prowler/pull/8304)
|
||||
- Resource view [(#7760)](https://github.com/prowler-cloud/prowler/pull/7760)
|
||||
- Navigation link in Scans view to access Compliance Overview [(#8251)](https://github.com/prowler-cloud/prowler/pull/8251)
|
||||
- Status column for findings table in the Compliance Detail view [(#8244)](https://github.com/prowler-cloud/prowler/pull/8244)
|
||||
|
||||
@@ -9,37 +9,60 @@ import {
|
||||
AddViaServiceAccountForm,
|
||||
SelectViaGCP,
|
||||
} from "@/components/providers/workflow/forms/select-credentials-type/gcp";
|
||||
import { SelectViaGitHub } from "@/components/providers/workflow/forms/select-credentials-type/github";
|
||||
import { ProviderType } from "@/types/providers";
|
||||
|
||||
interface Props {
|
||||
searchParams: { type: ProviderType; id: string; via?: string };
|
||||
}
|
||||
|
||||
// Helper function to determine if the credentials form should be shown
|
||||
const shouldShowCredentialsForm = (
|
||||
type: ProviderType,
|
||||
via?: string,
|
||||
): boolean => {
|
||||
const credentialsConfig = {
|
||||
aws: ["credentials"],
|
||||
gcp: ["credentials"],
|
||||
github: ["personal_access_token", "oauth_app_token", "github_app"],
|
||||
};
|
||||
|
||||
// If the type is in the configuration, check if the 'via' method is allowed
|
||||
if (credentialsConfig[type as keyof typeof credentialsConfig]) {
|
||||
return credentialsConfig[type as keyof typeof credentialsConfig].includes(
|
||||
via || "",
|
||||
);
|
||||
}
|
||||
|
||||
// For unspecified types, show the default form
|
||||
return !["aws", "gcp", "github"].includes(type);
|
||||
};
|
||||
|
||||
export default function AddCredentialsPage({ searchParams }: Props) {
|
||||
const { type, via } = searchParams;
|
||||
|
||||
return (
|
||||
<>
|
||||
{searchParams.type === "aws" && !searchParams.via && (
|
||||
<SelectViaAWS initialVia={searchParams.via} />
|
||||
)}
|
||||
{/* Selectors for authentication methods */}
|
||||
{type === "aws" && !via && <SelectViaAWS initialVia={via} />}
|
||||
|
||||
{searchParams.type === "gcp" && !searchParams.via && (
|
||||
<SelectViaGCP initialVia={searchParams.via} />
|
||||
)}
|
||||
{type === "gcp" && !via && <SelectViaGCP initialVia={via} />}
|
||||
|
||||
{((searchParams.type === "aws" && searchParams.via === "credentials") ||
|
||||
(searchParams.type === "gcp" && searchParams.via === "credentials") ||
|
||||
(searchParams.type !== "aws" && searchParams.type !== "gcp")) && (
|
||||
{type === "github" && !via && <SelectViaGitHub initialVia={via} />}
|
||||
|
||||
{/* Credentials form */}
|
||||
{shouldShowCredentialsForm(type, via) && (
|
||||
<AddViaCredentialsForm searchParams={searchParams} />
|
||||
)}
|
||||
|
||||
{searchParams.type === "aws" && searchParams.via === "role" && (
|
||||
{/* Specific forms */}
|
||||
{type === "aws" && via === "role" && (
|
||||
<AddViaRoleForm searchParams={searchParams} />
|
||||
)}
|
||||
|
||||
{searchParams.type === "gcp" &&
|
||||
searchParams.via === "service-account" && (
|
||||
<AddViaServiceAccountForm searchParams={searchParams} />
|
||||
)}
|
||||
{type === "gcp" && via === "service-account" && (
|
||||
<AddViaServiceAccountForm searchParams={searchParams} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
import { CredentialsUpdateInfo } from "@/components/providers";
|
||||
import { CredentialsUpdateInfo } from "@/components/providers/credentials-update-info";
|
||||
import {
|
||||
UpdateViaCredentialsForm,
|
||||
UpdateViaRoleForm,
|
||||
@@ -17,31 +17,51 @@ interface Props {
|
||||
};
|
||||
}
|
||||
|
||||
// Helper function to determine if the credentials form should be shown
|
||||
const shouldShowCredentialsForm = (
|
||||
type: ProviderType,
|
||||
via?: string,
|
||||
): boolean => {
|
||||
const credentialsConfig = {
|
||||
aws: ["credentials"],
|
||||
gcp: ["credentials"],
|
||||
github: ["personal_access_token", "oauth_app_token", "github_app"],
|
||||
};
|
||||
|
||||
// If the type is in the configuration, check if the 'via' method is allowed
|
||||
if (credentialsConfig[type as keyof typeof credentialsConfig]) {
|
||||
return credentialsConfig[type as keyof typeof credentialsConfig].includes(
|
||||
via || "",
|
||||
);
|
||||
}
|
||||
|
||||
// For unspecified types, show the default form
|
||||
return !["aws", "gcp", "github"].includes(type);
|
||||
};
|
||||
|
||||
export default function UpdateCredentialsPage({ searchParams }: Props) {
|
||||
const { type, via } = searchParams;
|
||||
|
||||
return (
|
||||
<>
|
||||
{(searchParams.type === "aws" || searchParams.type === "gcp") &&
|
||||
!searchParams.via && (
|
||||
<CredentialsUpdateInfo
|
||||
providerType={searchParams.type}
|
||||
initialVia={searchParams.via}
|
||||
/>
|
||||
)}
|
||||
{/* Credentials update info for supported providers */}
|
||||
{(type === "aws" || type === "gcp" || type === "github") && !via && (
|
||||
<CredentialsUpdateInfo providerType={type} initialVia={via} />
|
||||
)}
|
||||
|
||||
{((searchParams.type === "aws" && searchParams.via === "credentials") ||
|
||||
(searchParams.type === "gcp" && searchParams.via === "credentials") ||
|
||||
(searchParams.type !== "aws" && searchParams.type !== "gcp")) && (
|
||||
{/* Credentials form */}
|
||||
{shouldShowCredentialsForm(type, via) && (
|
||||
<UpdateViaCredentialsForm searchParams={searchParams} />
|
||||
)}
|
||||
|
||||
{searchParams.type === "aws" && searchParams.via === "role" && (
|
||||
{/* Specific forms */}
|
||||
{type === "aws" && via === "role" && (
|
||||
<UpdateViaRoleForm searchParams={searchParams} />
|
||||
)}
|
||||
|
||||
{searchParams.type === "gcp" &&
|
||||
searchParams.via === "service-account" && (
|
||||
<UpdateViaServiceAccountForm searchParams={searchParams} />
|
||||
)}
|
||||
{type === "gcp" && via === "service-account" && (
|
||||
<UpdateViaServiceAccountForm searchParams={searchParams} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { IconSvgProps } from "@/types";
|
||||
|
||||
export const GitHubProviderBadge: 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 98 96"
|
||||
width={size || width}
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -1,5 +1,6 @@
|
||||
export * from "./aws-provider-badge";
|
||||
export * from "./azure-provider-badge";
|
||||
export * from "./gcp-provider-badge";
|
||||
export * from "./github-provider-badge";
|
||||
export * from "./ks8-provider-badge";
|
||||
export * from "./m365-provider-badge";
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { SelectViaAWS } from "@/components/providers/workflow/forms/select-credentials-type/aws";
|
||||
import { SelectViaGCP } from "@/components/providers/workflow/forms/select-credentials-type/gcp";
|
||||
import { SelectViaGitHub } from "@/components/providers/workflow/forms/select-credentials-type/github";
|
||||
import { ProviderType } from "@/types/providers";
|
||||
|
||||
interface UpdateCredentialsInfoProps {
|
||||
@@ -20,6 +21,9 @@ export const CredentialsUpdateInfo = ({
|
||||
if (providerType === "gcp") {
|
||||
return <SelectViaGCP initialVia={initialVia} />;
|
||||
}
|
||||
if (providerType === "github") {
|
||||
return <SelectViaGitHub initialVia={initialVia} />;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,17 +5,17 @@ import React from "react";
|
||||
import { Control, Controller } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
|
||||
import { addProviderFormSchema } from "@/types";
|
||||
|
||||
import {
|
||||
AWSProviderBadge,
|
||||
AzureProviderBadge,
|
||||
GCPProviderBadge,
|
||||
GitHubProviderBadge,
|
||||
KS8ProviderBadge,
|
||||
M365ProviderBadge,
|
||||
} from "../icons/providers-badge";
|
||||
import { CustomRadio } from "../ui/custom";
|
||||
import { FormMessage } from "../ui/form";
|
||||
} from "@/components/icons/providers-badge";
|
||||
import { CustomRadio } from "@/components/ui/custom";
|
||||
import { FormMessage } from "@/components/ui/form";
|
||||
import { addProviderFormSchema, IconSvgProps } from "@/types";
|
||||
|
||||
interface RadioGroupProviderProps {
|
||||
control: Control<z.infer<typeof addProviderFormSchema>>;
|
||||
@@ -23,6 +23,64 @@ interface RadioGroupProviderProps {
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
const PROVIDERS_CONFIG = [
|
||||
{
|
||||
value: "aws",
|
||||
name: "Amazon Web Services",
|
||||
description: "Amazon Web Services",
|
||||
BadgeComponent: AWSProviderBadge,
|
||||
},
|
||||
{
|
||||
value: "gcp",
|
||||
name: "Google Cloud Platform",
|
||||
description: "Google Cloud Platform",
|
||||
BadgeComponent: GCPProviderBadge,
|
||||
},
|
||||
{
|
||||
value: "azure",
|
||||
name: "Microsoft Azure",
|
||||
description: "Microsoft Azure",
|
||||
BadgeComponent: AzureProviderBadge,
|
||||
},
|
||||
{
|
||||
value: "m365",
|
||||
name: "Microsoft 365",
|
||||
description: "Microsoft 365",
|
||||
BadgeComponent: M365ProviderBadge,
|
||||
},
|
||||
{
|
||||
value: "kubernetes",
|
||||
name: "Kubernetes",
|
||||
description: "Kubernetes",
|
||||
BadgeComponent: KS8ProviderBadge,
|
||||
},
|
||||
{
|
||||
value: "github",
|
||||
name: "GitHub",
|
||||
description: "GitHub",
|
||||
BadgeComponent: GitHubProviderBadge,
|
||||
},
|
||||
] as const;
|
||||
|
||||
const ProviderRadio = ({
|
||||
value,
|
||||
name,
|
||||
description,
|
||||
BadgeComponent,
|
||||
}: {
|
||||
value: string;
|
||||
name: string;
|
||||
description: string;
|
||||
BadgeComponent: React.FC<IconSvgProps>;
|
||||
}) => (
|
||||
<CustomRadio description={description} value={value}>
|
||||
<div className="flex items-center">
|
||||
<BadgeComponent size={26} />
|
||||
<span className="ml-2">{name}</span>
|
||||
</div>
|
||||
</CustomRadio>
|
||||
);
|
||||
|
||||
export const RadioGroupProvider: React.FC<RadioGroupProviderProps> = ({
|
||||
control,
|
||||
isInvalid,
|
||||
@@ -41,36 +99,15 @@ export const RadioGroupProvider: React.FC<RadioGroupProviderProps> = ({
|
||||
value={field.value || ""}
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
<CustomRadio description="Amazon Web Services" value="aws">
|
||||
<div className="flex items-center">
|
||||
<AWSProviderBadge size={26} />
|
||||
<span className="ml-2">Amazon Web Services</span>
|
||||
</div>
|
||||
</CustomRadio>
|
||||
<CustomRadio description="Google Cloud Platform" value="gcp">
|
||||
<div className="flex items-center">
|
||||
<GCPProviderBadge size={26} />
|
||||
<span className="ml-2">Google Cloud Platform</span>
|
||||
</div>
|
||||
</CustomRadio>
|
||||
<CustomRadio description="Microsoft Azure" value="azure">
|
||||
<div className="flex items-center">
|
||||
<AzureProviderBadge size={26} />
|
||||
<span className="ml-2">Microsoft Azure</span>
|
||||
</div>
|
||||
</CustomRadio>
|
||||
<CustomRadio description="Microsoft 365" value="m365">
|
||||
<div className="flex items-center">
|
||||
<M365ProviderBadge size={26} />
|
||||
<span className="ml-2">Microsoft 365</span>
|
||||
</div>
|
||||
</CustomRadio>
|
||||
<CustomRadio description="Kubernetes" value="kubernetes">
|
||||
<div className="flex items-center">
|
||||
<KS8ProviderBadge size={26} />
|
||||
<span className="ml-2">Kubernetes</span>
|
||||
</div>
|
||||
</CustomRadio>
|
||||
{PROVIDERS_CONFIG.map((provider) => (
|
||||
<ProviderRadio
|
||||
key={provider.value}
|
||||
value={provider.value}
|
||||
name={provider.name}
|
||||
description={provider.description}
|
||||
BadgeComponent={provider.BadgeComponent}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</RadioGroup>
|
||||
{errorMessage && (
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
AzureCredentials,
|
||||
GCPDefaultCredentials,
|
||||
GCPServiceAccountKey,
|
||||
GitHubCredentials,
|
||||
KubernetesCredentials,
|
||||
M365Credentials,
|
||||
ProviderType,
|
||||
@@ -25,9 +26,19 @@ import { AWSRoleCredentialsForm } from "./select-credentials-type/aws/credential
|
||||
import { GCPDefaultCredentialsForm } from "./select-credentials-type/gcp/credentials-type";
|
||||
import { GCPServiceAccountKeyForm } from "./select-credentials-type/gcp/credentials-type/gcp-service-account-key-form";
|
||||
import { AzureCredentialsForm } from "./via-credentials/azure-credentials-form";
|
||||
import { GitHubCredentialsForm } from "./via-credentials/github-credentials-form";
|
||||
import { KubernetesCredentialsForm } from "./via-credentials/k8s-credentials-form";
|
||||
import { M365CredentialsForm } from "./via-credentials/m365-credentials-form";
|
||||
|
||||
const VIA_VALUES_WITH_BACK_BUTTON = [
|
||||
"credentials",
|
||||
"role",
|
||||
"service-account",
|
||||
"personal_access_token",
|
||||
"oauth_app_token",
|
||||
"github_app",
|
||||
] as const;
|
||||
|
||||
type BaseCredentialsFormProps = {
|
||||
providerType: ProviderType;
|
||||
providerId: string;
|
||||
@@ -59,6 +70,71 @@ export const BaseCredentialsForm = ({
|
||||
successNavigationUrl,
|
||||
});
|
||||
|
||||
const currentVia = searchParamsObj.get("via");
|
||||
|
||||
const shouldShowBackButton =
|
||||
showBackButton && VIA_VALUES_WITH_BACK_BUTTON.includes(currentVia as any);
|
||||
|
||||
const renderCredentialsForm = () => {
|
||||
switch (providerType) {
|
||||
case "aws":
|
||||
return currentVia === "role" ? (
|
||||
<AWSRoleCredentialsForm
|
||||
control={form.control as unknown as Control<AWSCredentialsRole>}
|
||||
setValue={form.setValue as any}
|
||||
externalId={externalId}
|
||||
/>
|
||||
) : (
|
||||
<AWSStaticCredentialsForm
|
||||
control={form.control as unknown as Control<AWSCredentials>}
|
||||
/>
|
||||
);
|
||||
|
||||
case "azure":
|
||||
return (
|
||||
<AzureCredentialsForm
|
||||
control={form.control as unknown as Control<AzureCredentials>}
|
||||
/>
|
||||
);
|
||||
|
||||
case "m365":
|
||||
return (
|
||||
<M365CredentialsForm
|
||||
control={form.control as unknown as Control<M365Credentials>}
|
||||
/>
|
||||
);
|
||||
|
||||
case "gcp":
|
||||
return currentVia === "service-account" ? (
|
||||
<GCPServiceAccountKeyForm
|
||||
control={form.control as unknown as Control<GCPServiceAccountKey>}
|
||||
/>
|
||||
) : (
|
||||
<GCPDefaultCredentialsForm
|
||||
control={form.control as unknown as Control<GCPDefaultCredentials>}
|
||||
/>
|
||||
);
|
||||
|
||||
case "kubernetes":
|
||||
return (
|
||||
<KubernetesCredentialsForm
|
||||
control={form.control as unknown as Control<KubernetesCredentials>}
|
||||
/>
|
||||
);
|
||||
|
||||
case "github":
|
||||
return (
|
||||
<GitHubCredentialsForm
|
||||
control={form.control as unknown as Control<GitHubCredentials>}
|
||||
via={currentVia || undefined}
|
||||
/>
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form
|
||||
@@ -80,67 +156,24 @@ export const BaseCredentialsForm = ({
|
||||
|
||||
<Divider />
|
||||
|
||||
{providerType === "aws" && searchParamsObj.get("via") === "role" && (
|
||||
<AWSRoleCredentialsForm
|
||||
control={form.control as unknown as Control<AWSCredentialsRole>}
|
||||
setValue={form.setValue as any}
|
||||
externalId={externalId}
|
||||
/>
|
||||
)}
|
||||
{providerType === "aws" && searchParamsObj.get("via") !== "role" && (
|
||||
<AWSStaticCredentialsForm
|
||||
control={form.control as unknown as Control<AWSCredentials>}
|
||||
/>
|
||||
)}
|
||||
{providerType === "azure" && (
|
||||
<AzureCredentialsForm
|
||||
control={form.control as unknown as Control<AzureCredentials>}
|
||||
/>
|
||||
)}
|
||||
{providerType === "m365" && (
|
||||
<M365CredentialsForm
|
||||
control={form.control as unknown as Control<M365Credentials>}
|
||||
/>
|
||||
)}
|
||||
{providerType === "gcp" &&
|
||||
searchParamsObj.get("via") === "service-account" && (
|
||||
<GCPServiceAccountKeyForm
|
||||
control={form.control as unknown as Control<GCPServiceAccountKey>}
|
||||
/>
|
||||
)}
|
||||
{providerType === "gcp" &&
|
||||
searchParamsObj.get("via") !== "service-account" && (
|
||||
<GCPDefaultCredentialsForm
|
||||
control={
|
||||
form.control as unknown as Control<GCPDefaultCredentials>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{providerType === "kubernetes" && (
|
||||
<KubernetesCredentialsForm
|
||||
control={form.control as unknown as Control<KubernetesCredentials>}
|
||||
/>
|
||||
)}
|
||||
{renderCredentialsForm()}
|
||||
|
||||
<div className="flex w-full justify-end sm:space-x-6">
|
||||
{showBackButton &&
|
||||
(searchParamsObj.get("via") === "credentials" ||
|
||||
searchParamsObj.get("via") === "role" ||
|
||||
searchParamsObj.get("via") === "service-account") && (
|
||||
<CustomButton
|
||||
type="button"
|
||||
ariaLabel="Back"
|
||||
className="w-1/2 bg-transparent"
|
||||
variant="faded"
|
||||
size="lg"
|
||||
radius="lg"
|
||||
onPress={handleBackStep}
|
||||
startContent={!isLoading && <ChevronLeftIcon size={24} />}
|
||||
isDisabled={isLoading}
|
||||
>
|
||||
<span>Back</span>
|
||||
</CustomButton>
|
||||
)}
|
||||
{shouldShowBackButton && (
|
||||
<CustomButton
|
||||
type="button"
|
||||
ariaLabel="Back"
|
||||
className="w-1/2 bg-transparent"
|
||||
variant="faded"
|
||||
size="lg"
|
||||
radius="lg"
|
||||
onPress={handleBackStep}
|
||||
startContent={!isLoading && <ChevronLeftIcon size={24} />}
|
||||
isDisabled={isLoading}
|
||||
>
|
||||
<span>Back</span>
|
||||
</CustomButton>
|
||||
)}
|
||||
<CustomButton
|
||||
type="submit"
|
||||
ariaLabel="Save"
|
||||
|
||||
@@ -7,14 +7,14 @@ import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import * as z from "zod";
|
||||
|
||||
import { addProvider } from "@/actions/providers/providers";
|
||||
import { RadioGroupProvider } from "@/components/providers/radio-group-provider";
|
||||
import { ProviderTitleDocs } from "@/components/providers/workflow/provider-title-docs";
|
||||
import { useToast } from "@/components/ui";
|
||||
import { CustomButton, CustomInput } from "@/components/ui/custom";
|
||||
import { Form } from "@/components/ui/form";
|
||||
import { addProviderFormSchema, ApiError } from "@/types";
|
||||
|
||||
import { addProvider } from "../../../../actions/providers/providers";
|
||||
import { addProviderFormSchema, ApiError } from "../../../../types";
|
||||
import { RadioGroupProvider } from "../../radio-group-provider";
|
||||
import { ProviderTitleDocs } from "../provider-title-docs";
|
||||
export type FormValues = z.infer<typeof addProviderFormSchema>;
|
||||
|
||||
// Helper function for labels and placeholders
|
||||
@@ -45,6 +45,11 @@ const getProviderFieldDetails = (providerType?: string) => {
|
||||
label: "Domain ID",
|
||||
placeholder: "e.g. your-domain.onmicrosoft.com",
|
||||
};
|
||||
case "github":
|
||||
return {
|
||||
label: "Username",
|
||||
placeholder: "e.g. your-github-username",
|
||||
};
|
||||
default:
|
||||
return {
|
||||
label: "Provider UID",
|
||||
@@ -142,7 +147,12 @@ export const ConnectAccountForm = () => {
|
||||
|
||||
const handleBackStep = () => {
|
||||
setPrevStep((prev) => prev - 1);
|
||||
// Reset the providerUid and providerAlias fields when going back
|
||||
|
||||
//Deselect the providerType if the user is going back to the first step
|
||||
if (prevStep === 2) {
|
||||
form.setValue("providerType", undefined as any);
|
||||
}
|
||||
|
||||
form.setValue("providerUid", "");
|
||||
form.setValue("providerAlias", "");
|
||||
};
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { Control } from "react-hook-form";
|
||||
|
||||
import { CustomInput, CustomTextarea } from "@/components/ui/custom";
|
||||
import { ProviderCredentialFields } from "@/lib/provider-credentials/provider-credential-fields";
|
||||
import { GitHubCredentials } from "@/types";
|
||||
|
||||
export const GitHubAppForm = ({
|
||||
control,
|
||||
}: {
|
||||
control: Control<GitHubCredentials>;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col">
|
||||
<div className="text-md font-bold leading-9 text-default-foreground">
|
||||
GitHub App
|
||||
</div>
|
||||
<div className="text-sm text-default-500">
|
||||
Use GitHub App credentials for advanced integration. This requires
|
||||
both the App ID and Private Key.
|
||||
</div>
|
||||
</div>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name={ProviderCredentialFields.GITHUB_APP_ID}
|
||||
label="GitHub App ID"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the GitHub App ID"
|
||||
variant="bordered"
|
||||
isRequired={true}
|
||||
isInvalid={
|
||||
!!control._formState.errors[ProviderCredentialFields.GITHUB_APP_ID]
|
||||
}
|
||||
/>
|
||||
<CustomTextarea
|
||||
control={control}
|
||||
name={ProviderCredentialFields.GITHUB_APP_KEY}
|
||||
label="GitHub App Private Key"
|
||||
labelPlacement="inside"
|
||||
placeholder="Paste your GitHub App Private Key content here"
|
||||
variant="bordered"
|
||||
minRows={8}
|
||||
isRequired={true}
|
||||
isInvalid={
|
||||
!!control._formState.errors[ProviderCredentialFields.GITHUB_APP_KEY]
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Control } from "react-hook-form";
|
||||
|
||||
import { CustomInput } from "@/components/ui/custom";
|
||||
import { ProviderCredentialFields } from "@/lib/provider-credentials/provider-credential-fields";
|
||||
import { GitHubCredentials } from "@/types";
|
||||
|
||||
export const GitHubOAuthAppTokenForm = ({
|
||||
control,
|
||||
}: {
|
||||
control: Control<GitHubCredentials>;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col">
|
||||
<div className="text-md font-bold leading-9 text-default-foreground">
|
||||
OAuth App Token
|
||||
</div>
|
||||
<div className="text-sm text-default-500">
|
||||
Use an OAuth app token for application-level authentication. This is
|
||||
suitable for applications that need broader access.
|
||||
</div>
|
||||
</div>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name={ProviderCredentialFields.OAUTH_APP_TOKEN}
|
||||
type="password"
|
||||
label="OAuth App Token"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the OAuth App Token"
|
||||
variant="bordered"
|
||||
isRequired={true}
|
||||
isInvalid={
|
||||
!!control._formState.errors[ProviderCredentialFields.OAUTH_APP_TOKEN]
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Control } from "react-hook-form";
|
||||
|
||||
import { CustomInput } from "@/components/ui/custom";
|
||||
import { ProviderCredentialFields } from "@/lib/provider-credentials/provider-credential-fields";
|
||||
import { GitHubCredentials } from "@/types";
|
||||
|
||||
export const GitHubPersonalAccessTokenForm = ({
|
||||
control,
|
||||
}: {
|
||||
control: Control<GitHubCredentials>;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col">
|
||||
<div className="text-md font-bold leading-9 text-default-foreground">
|
||||
Personal Access Token
|
||||
</div>
|
||||
<div className="text-sm text-default-500">
|
||||
Use a personal access token for individual user authentication. This
|
||||
is the simplest method for personal use.
|
||||
</div>
|
||||
</div>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name={ProviderCredentialFields.PERSONAL_ACCESS_TOKEN}
|
||||
type="password"
|
||||
label="Personal Access Token"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the Personal Access Token"
|
||||
variant="bordered"
|
||||
isRequired={true}
|
||||
isInvalid={
|
||||
!!control._formState.errors[
|
||||
ProviderCredentialFields.PERSONAL_ACCESS_TOKEN
|
||||
]
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from "./github-app-form";
|
||||
export * from "./github-oauth-app-token-form";
|
||||
export * from "./github-personal-access-token-form";
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./radio-group-github-via-credentials-type-form";
|
||||
export * from "./select-via-github";
|
||||
@@ -0,0 +1,80 @@
|
||||
"use client";
|
||||
|
||||
import { RadioGroup } from "@nextui-org/react";
|
||||
import React from "react";
|
||||
import { Control, Controller } from "react-hook-form";
|
||||
|
||||
import { CustomRadio } from "@/components/ui/custom";
|
||||
import { FormMessage } from "@/components/ui/form";
|
||||
|
||||
type RadioGroupGitHubViaCredentialsFormProps = {
|
||||
control: Control<any>;
|
||||
isInvalid: boolean;
|
||||
errorMessage?: string;
|
||||
onChange?: (value: string) => void;
|
||||
};
|
||||
|
||||
export const RadioGroupGitHubViaCredentialsTypeForm = ({
|
||||
control,
|
||||
isInvalid,
|
||||
errorMessage,
|
||||
onChange,
|
||||
}: RadioGroupGitHubViaCredentialsFormProps) => {
|
||||
return (
|
||||
<Controller
|
||||
name="githubCredentialsType"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<RadioGroup
|
||||
className="flex flex-wrap"
|
||||
isInvalid={isInvalid}
|
||||
{...field}
|
||||
value={field.value || ""}
|
||||
onValueChange={(value) => {
|
||||
field.onChange(value);
|
||||
if (onChange) {
|
||||
onChange(value);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
<span className="text-sm text-default-500">
|
||||
Authentication Methods
|
||||
</span>
|
||||
<CustomRadio
|
||||
description="Use a personal access token for individual user authentication"
|
||||
value="personal_access_token"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<span className="ml-2">Personal Access Token</span>
|
||||
</div>
|
||||
</CustomRadio>
|
||||
<CustomRadio
|
||||
description="Use an OAuth app token for application-level authentication"
|
||||
value="oauth_app_token"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<span className="ml-2">OAuth App Token</span>
|
||||
</div>
|
||||
</CustomRadio>
|
||||
<CustomRadio
|
||||
description="Use GitHub App credentials (requires App ID and Private Key)"
|
||||
value="github_app"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<span className="ml-2">GitHub App</span>
|
||||
</div>
|
||||
</CustomRadio>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
{errorMessage && (
|
||||
<FormMessage className="text-system-error dark:text-system-error">
|
||||
{errorMessage}
|
||||
</FormMessage>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
"use client";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
import { Form } from "@/components/ui/form";
|
||||
|
||||
import { RadioGroupGitHubViaCredentialsTypeForm } from "./radio-group-github-via-credentials-type-form";
|
||||
|
||||
interface SelectViaGitHubProps {
|
||||
initialVia?: string;
|
||||
}
|
||||
|
||||
export const SelectViaGitHub = ({ initialVia }: SelectViaGitHubProps) => {
|
||||
const router = useRouter();
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
githubCredentialsType: initialVia || "",
|
||||
},
|
||||
});
|
||||
|
||||
const handleSelectionChange = (value: string) => {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set("via", value);
|
||||
router.push(url.toString());
|
||||
};
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<RadioGroupGitHubViaCredentialsTypeForm
|
||||
control={form.control}
|
||||
isInvalid={!!form.formState.errors.githubCredentialsType}
|
||||
errorMessage={form.formState.errors.githubCredentialsType?.message}
|
||||
onChange={handleSelectionChange}
|
||||
/>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,232 @@
|
||||
import { Control } from "react-hook-form";
|
||||
|
||||
import { CustomInput, CustomTextarea } from "@/components/ui/custom";
|
||||
import { ProviderCredentialFields } from "@/lib/provider-credentials/provider-credential-fields";
|
||||
import { GitHubCredentials } from "@/types";
|
||||
|
||||
export const GitHubCredentialsForm = ({
|
||||
control,
|
||||
via,
|
||||
}: {
|
||||
control: Control<GitHubCredentials>;
|
||||
via?: string;
|
||||
}) => {
|
||||
const renderHeader = (description: string) => (
|
||||
<div className="flex flex-col">
|
||||
<h2 className="text-md font-bold leading-9 text-default-foreground">
|
||||
Connect via Credentials
|
||||
</h2>
|
||||
<div className="text-sm text-default-500">{description}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderPersonalAccessTokenFields = () => (
|
||||
<div className="space-y-3">
|
||||
<div className="border-b border-divider pb-2">
|
||||
<h3 className="text-sm font-semibold text-default-foreground">
|
||||
Personal Access Token
|
||||
</h3>
|
||||
<p className="text-sm text-default-500">
|
||||
Use a personal access token for individual user authentication
|
||||
</p>
|
||||
</div>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name={ProviderCredentialFields.PERSONAL_ACCESS_TOKEN}
|
||||
type="password"
|
||||
label="Personal Access Token"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the Personal Access Token"
|
||||
variant="bordered"
|
||||
isRequired
|
||||
isInvalid={
|
||||
!!control._formState.errors[
|
||||
ProviderCredentialFields.PERSONAL_ACCESS_TOKEN
|
||||
]
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderOAuthAppTokenFields = () => (
|
||||
<div className="space-y-3">
|
||||
<div className="border-b border-divider pb-2">
|
||||
<h3 className="text-sm font-semibold text-default-foreground">
|
||||
OAuth App Token
|
||||
</h3>
|
||||
<p className="text-sm text-default-500">
|
||||
Use an OAuth app token for application-level authentication
|
||||
</p>
|
||||
</div>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name={ProviderCredentialFields.OAUTH_APP_TOKEN}
|
||||
type="password"
|
||||
label="OAuth App Token"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the OAuth App Token"
|
||||
variant="bordered"
|
||||
isRequired
|
||||
isInvalid={
|
||||
!!control._formState.errors[ProviderCredentialFields.OAUTH_APP_TOKEN]
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderGitHubAppFields = () => (
|
||||
<div className="space-y-3">
|
||||
<div className="border-b border-divider pb-2">
|
||||
<h3 className="text-sm font-semibold text-default-foreground">
|
||||
GitHub App
|
||||
</h3>
|
||||
<p className="text-sm text-default-500">
|
||||
Use GitHub App credentials (both App ID and Private Key are required)
|
||||
</p>
|
||||
</div>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name={ProviderCredentialFields.GITHUB_APP_ID}
|
||||
label="GitHub App ID"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the GitHub App ID"
|
||||
variant="bordered"
|
||||
isRequired
|
||||
isInvalid={
|
||||
!!control._formState.errors[ProviderCredentialFields.GITHUB_APP_ID]
|
||||
}
|
||||
/>
|
||||
<CustomTextarea
|
||||
control={control}
|
||||
name={ProviderCredentialFields.GITHUB_APP_KEY}
|
||||
label="GitHub App Private Key"
|
||||
labelPlacement="inside"
|
||||
placeholder="Paste your GitHub App Private Key content here"
|
||||
variant="bordered"
|
||||
minRows={10}
|
||||
isRequired
|
||||
isInvalid={
|
||||
!!control._formState.errors[ProviderCredentialFields.GITHUB_APP_KEY]
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderAllOptions = () => (
|
||||
<>
|
||||
{renderHeader(
|
||||
"Choose one of the following authentication methods for your GitHub credentials:",
|
||||
)}
|
||||
|
||||
{/* Option 1: Personal Access Token */}
|
||||
<div className="space-y-3">
|
||||
<div className="border-b border-divider pb-2">
|
||||
<h3 className="text-sm font-semibold text-default-foreground">
|
||||
Option 1: Personal Access Token
|
||||
</h3>
|
||||
<p className="text-sm text-default-500">
|
||||
Use a personal access token for individual user authentication
|
||||
</p>
|
||||
</div>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name={ProviderCredentialFields.PERSONAL_ACCESS_TOKEN}
|
||||
type="password"
|
||||
label="Personal Access Token"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the Personal Access Token"
|
||||
variant="bordered"
|
||||
isRequired={false}
|
||||
isInvalid={
|
||||
!!control._formState.errors[
|
||||
ProviderCredentialFields.PERSONAL_ACCESS_TOKEN
|
||||
]
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Option 2: OAuth App Token */}
|
||||
<div className="space-y-3">
|
||||
<div className="border-b border-divider pb-2">
|
||||
<h3 className="text-sm font-semibold text-default-foreground">
|
||||
Option 2: OAuth App Token
|
||||
</h3>
|
||||
<p className="text-sm text-default-500">
|
||||
Use an OAuth app token for application-level authentication
|
||||
</p>
|
||||
</div>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name={ProviderCredentialFields.OAUTH_APP_TOKEN}
|
||||
type="password"
|
||||
label="OAuth App Token"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the OAuth App Token"
|
||||
variant="bordered"
|
||||
isRequired={false}
|
||||
isInvalid={
|
||||
!!control._formState.errors[
|
||||
ProviderCredentialFields.OAUTH_APP_TOKEN
|
||||
]
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Option 3: GitHub App */}
|
||||
<div className="space-y-3">
|
||||
<div className="border-b border-divider pb-2">
|
||||
<h3 className="text-sm font-semibold text-default-foreground">
|
||||
Option 3: GitHub App
|
||||
</h3>
|
||||
<p className="text-sm text-default-500">
|
||||
Use GitHub App credentials (both App ID and Private Key are
|
||||
required)
|
||||
</p>
|
||||
</div>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name={ProviderCredentialFields.GITHUB_APP_ID}
|
||||
label="GitHub App ID"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the GitHub App ID"
|
||||
variant="bordered"
|
||||
isRequired={false}
|
||||
isInvalid={
|
||||
!!control._formState.errors[ProviderCredentialFields.GITHUB_APP_ID]
|
||||
}
|
||||
/>
|
||||
<CustomTextarea
|
||||
control={control}
|
||||
name={ProviderCredentialFields.GITHUB_APP_KEY}
|
||||
label="GitHub App Private Key"
|
||||
labelPlacement="inside"
|
||||
placeholder="Paste your GitHub App Private Key content here"
|
||||
variant="bordered"
|
||||
minRows={8}
|
||||
isRequired={false}
|
||||
isInvalid={
|
||||
!!control._formState.errors[ProviderCredentialFields.GITHUB_APP_KEY]
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
// If via parameter is provided, show only the selected method
|
||||
if (via) {
|
||||
return (
|
||||
<>
|
||||
{renderHeader(
|
||||
"Enter your GitHub credentials for the selected authentication method",
|
||||
)}
|
||||
|
||||
{via === "personal_access_token" && renderPersonalAccessTokenFields()}
|
||||
{via === "oauth_app_token" && renderOAuthAppTokenFields()}
|
||||
{via === "github_app" && renderGitHubAppFields()}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// If no via parameter, show all options (fallback behavior)
|
||||
return renderAllOptions();
|
||||
};
|
||||
@@ -22,7 +22,7 @@ export const ProviderTitleDocs = ({
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-end gap-x-2">
|
||||
<p className="text-sm text-default-500">
|
||||
<p className="whitespace-nowrap text-sm text-default-500">
|
||||
{getProviderHelpText(providerType as string).text}
|
||||
</p>
|
||||
<CustomLink
|
||||
|
||||
@@ -66,6 +66,7 @@ export const SelectScanProvider = <
|
||||
| "azure"
|
||||
| "gcp"
|
||||
| "kubernetes"
|
||||
| "github"
|
||||
}
|
||||
entityAlias={selectedItem.alias}
|
||||
entityId={selectedItem.uid}
|
||||
@@ -91,6 +92,7 @@ export const SelectScanProvider = <
|
||||
| "azure"
|
||||
| "gcp"
|
||||
| "kubernetes"
|
||||
| "github"
|
||||
}
|
||||
entityAlias={item.alias}
|
||||
entityId={item.uid}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
AWSProviderBadge,
|
||||
AzureProviderBadge,
|
||||
GCPProviderBadge,
|
||||
GitHubProviderBadge,
|
||||
KS8ProviderBadge,
|
||||
M365ProviderBadge,
|
||||
} from "@/components/icons/providers-badge";
|
||||
@@ -21,6 +22,8 @@ export const getProviderLogo = (provider: ProviderType) => {
|
||||
return <KS8ProviderBadge width={35} height={35} />;
|
||||
case "m365":
|
||||
return <M365ProviderBadge width={35} height={35} />;
|
||||
case "github":
|
||||
return <GitHubProviderBadge width={35} height={35} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -38,6 +41,8 @@ export const getProviderName = (provider: ProviderType): string => {
|
||||
return "Kubernetes";
|
||||
case "m365":
|
||||
return "Microsoft 365";
|
||||
case "github":
|
||||
return "GitHub";
|
||||
default:
|
||||
return "Unknown Provider";
|
||||
}
|
||||
|
||||
@@ -28,4 +28,9 @@ 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.PERSONAL_ACCESS_TOKEN]:
|
||||
ProviderCredentialFields.PERSONAL_ACCESS_TOKEN,
|
||||
[ErrorPointers.OAUTH_APP_TOKEN]: ProviderCredentialFields.OAUTH_APP_TOKEN,
|
||||
[ErrorPointers.GITHUB_APP_ID]: ProviderCredentialFields.GITHUB_APP_ID,
|
||||
[ErrorPointers.GITHUB_APP_KEY]: ProviderCredentialFields.GITHUB_APP_KEY,
|
||||
};
|
||||
|
||||
@@ -25,6 +25,11 @@ export const getProviderHelpText = (provider: string) => {
|
||||
text: "Need help connecting your Kubernetes cluster?",
|
||||
link: "https://goto.prowler.com/provider-k8s",
|
||||
};
|
||||
case "github":
|
||||
return {
|
||||
text: "Need help connecting your GitHub account?",
|
||||
link: "https://goto.prowler.com/provider-github",
|
||||
};
|
||||
default:
|
||||
return {
|
||||
text: "How to setup a provider?",
|
||||
|
||||
@@ -10,7 +10,7 @@ You use Prowler tool's capabilities to answer the user's query.
|
||||
## Prowler Capabilities
|
||||
|
||||
- Prowler is an Open Cloud Security tool
|
||||
- Prowler scans misconfigurations in AWS, Azure, Microsoft 365, GCP, and Kubernetes
|
||||
- Prowler scans misconfigurations in AWS, Azure, Microsoft 365, GCP, GitHub, and Kubernetes
|
||||
- Prowler helps with continuous monitoring, security assessments and audits, incident response, compliance, hardening, and forensics readiness
|
||||
- Supports multiple compliance frameworks including CIS, NIST 800, NIST CSF, CISA, FedRAMP, PCI-DSS, GDPR, HIPAA, FFIEC, SOC2, GXP, Well-Architected Security, ENS, and more. These compliance frameworks are not available for all providers.
|
||||
|
||||
@@ -279,7 +279,7 @@ const userInfoAgentPrompt = `You are Prowler's User Info Agent, specializing in
|
||||
- Mentioning all keys in the function call is mandatory. Don't skip any keys.
|
||||
- Don't add empty filters in the function call.`;
|
||||
|
||||
const providerAgentPrompt = `You are Prowler's Provider Agent, specializing in provider information within the Prowler tool. Prowler supports the following provider types: AWS, GCP, Azure, and other cloud platforms.
|
||||
const providerAgentPrompt = `You are Prowler's Provider Agent, specializing in provider information within the Prowler tool. Prowler supports the following provider types: AWS, GCP, Azure, GitHub, and other cloud platforms.
|
||||
|
||||
## Available Tools
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ export const getProviderChecksTool = tool(
|
||||
{
|
||||
name: "getProviderChecks",
|
||||
description:
|
||||
"Returns a list of available checks for a specific provider (aws, gcp, azure, kubernetes). Allows filtering by service, severity, and compliance framework ID. If no filters are provided, all checks will be returned.",
|
||||
"Returns a list of available checks for a specific provider (aws, gcp, azure, kubernetes, github). Allows filtering by service, severity, and compliance framework ID. If no filters are provided, all checks will be returned.",
|
||||
schema: checkSchema,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -147,6 +147,28 @@ export const buildKubernetesSecret = (formData: FormData) => {
|
||||
return filterEmptyValues(secret);
|
||||
};
|
||||
|
||||
export const buildGitHubSecret = (formData: FormData) => {
|
||||
const secret = {
|
||||
[ProviderCredentialFields.PERSONAL_ACCESS_TOKEN]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.PERSONAL_ACCESS_TOKEN,
|
||||
),
|
||||
[ProviderCredentialFields.OAUTH_APP_TOKEN]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.OAUTH_APP_TOKEN,
|
||||
),
|
||||
[ProviderCredentialFields.GITHUB_APP_ID]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.GITHUB_APP_ID,
|
||||
),
|
||||
[ProviderCredentialFields.GITHUB_APP_KEY]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.GITHUB_APP_KEY,
|
||||
),
|
||||
};
|
||||
return filterEmptyValues(secret);
|
||||
};
|
||||
|
||||
// Main function to build secret configuration
|
||||
export const buildSecretConfig = (
|
||||
formData: FormData,
|
||||
@@ -177,6 +199,10 @@ export const buildSecretConfig = (
|
||||
secretType: "static",
|
||||
secret: buildKubernetesSecret(formData),
|
||||
}),
|
||||
github: () => ({
|
||||
secretType: "static",
|
||||
secret: buildGitHubSecret(formData),
|
||||
}),
|
||||
};
|
||||
|
||||
const builder = secretBuilders[providerType];
|
||||
|
||||
@@ -35,6 +35,12 @@ export const ProviderCredentialFields = {
|
||||
|
||||
// Kubernetes fields
|
||||
KUBECONFIG_CONTENT: "kubeconfig_content",
|
||||
|
||||
// GitHub fields
|
||||
PERSONAL_ACCESS_TOKEN: "personal_access_token",
|
||||
OAUTH_APP_TOKEN: "oauth_app_token",
|
||||
GITHUB_APP_ID: "github_app_id",
|
||||
GITHUB_APP_KEY: "github_app_key_content",
|
||||
} as const;
|
||||
|
||||
// Type for credential field values
|
||||
@@ -59,6 +65,10 @@ export const ErrorPointers = {
|
||||
SESSION_DURATION: "/data/attributes/secret/session_duration",
|
||||
ROLE_SESSION_NAME: "/data/attributes/secret/role_session_name",
|
||||
SERVICE_ACCOUNT_KEY: "/data/attributes/secret/service_account_key",
|
||||
PERSONAL_ACCESS_TOKEN: "/data/attributes/secret/personal_access_token",
|
||||
OAUTH_APP_TOKEN: "/data/attributes/secret/oauth_app_token",
|
||||
GITHUB_APP_ID: "/data/attributes/secret/github_app_id",
|
||||
GITHUB_APP_KEY: "/data/attributes/secret/github_app_key_content",
|
||||
} as const;
|
||||
|
||||
export type ErrorPointer = (typeof ErrorPointers)[keyof typeof ErrorPointers];
|
||||
|
||||
@@ -235,13 +235,22 @@ export type KubernetesCredentials = {
|
||||
[ProviderCredentialFields.PROVIDER_ID]: string;
|
||||
};
|
||||
|
||||
export type GitHubCredentials = {
|
||||
[ProviderCredentialFields.PERSONAL_ACCESS_TOKEN]?: string;
|
||||
[ProviderCredentialFields.OAUTH_APP_TOKEN]?: string;
|
||||
[ProviderCredentialFields.GITHUB_APP_ID]?: string;
|
||||
[ProviderCredentialFields.GITHUB_APP_KEY]?: string;
|
||||
[ProviderCredentialFields.PROVIDER_ID]: string;
|
||||
};
|
||||
|
||||
export type CredentialsFormSchema =
|
||||
| AWSCredentials
|
||||
| AzureCredentials
|
||||
| GCPDefaultCredentials
|
||||
| GCPServiceAccountKey
|
||||
| KubernetesCredentials
|
||||
| M365Credentials;
|
||||
| M365Credentials
|
||||
| GitHubCredentials;
|
||||
|
||||
export interface SearchParamsProps {
|
||||
[key: string]: string | string[] | undefined;
|
||||
|
||||
@@ -71,9 +71,12 @@ export const awsCredentialsTypeSchema = z.object({
|
||||
|
||||
export const addProviderFormSchema = z
|
||||
.object({
|
||||
providerType: z.enum(["aws", "azure", "gcp", "kubernetes", "m365"], {
|
||||
required_error: "Please select a provider type",
|
||||
}),
|
||||
providerType: z.enum(
|
||||
["aws", "azure", "gcp", "kubernetes", "m365", "github"],
|
||||
{
|
||||
required_error: "Please select a provider type",
|
||||
},
|
||||
),
|
||||
})
|
||||
.and(
|
||||
z.discriminatedUnion("providerType", [
|
||||
@@ -105,6 +108,11 @@ export const addProviderFormSchema = z
|
||||
providerUid: z.string(),
|
||||
awsCredentialsType: z.string().optional(),
|
||||
}),
|
||||
z.object({
|
||||
providerType: z.literal("github"),
|
||||
[ProviderCredentialFields.PROVIDER_ALIAS]: z.string(),
|
||||
providerUid: z.string(),
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
@@ -167,7 +175,22 @@ export const addCredentialsFormSchema = (providerType: string) =>
|
||||
[ProviderCredentialFields.USER]: z.string().optional(),
|
||||
[ProviderCredentialFields.PASSWORD]: z.string().optional(),
|
||||
}
|
||||
: {}),
|
||||
: providerType === "github"
|
||||
? {
|
||||
[ProviderCredentialFields.PERSONAL_ACCESS_TOKEN]: z
|
||||
.string()
|
||||
.optional(),
|
||||
[ProviderCredentialFields.OAUTH_APP_TOKEN]: z
|
||||
.string()
|
||||
.optional(),
|
||||
[ProviderCredentialFields.GITHUB_APP_ID]: z
|
||||
.string()
|
||||
.optional(),
|
||||
[ProviderCredentialFields.GITHUB_APP_KEY]: z
|
||||
.string()
|
||||
.optional(),
|
||||
}
|
||||
: {}),
|
||||
})
|
||||
.superRefine((data: Record<string, any>, ctx) => {
|
||||
if (providerType === "m365") {
|
||||
@@ -190,6 +213,61 @@ export const addCredentialsFormSchema = (providerType: string) =>
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (providerType === "github") {
|
||||
const hasPersonalAccessToken = !!data[ProviderCredentialFields.PERSONAL_ACCESS_TOKEN];
|
||||
const hasOAuthAppToken = !!data[ProviderCredentialFields.OAUTH_APP_TOKEN];
|
||||
const hasGitHubAppId = !!data[ProviderCredentialFields.GITHUB_APP_ID];
|
||||
const hasGitHubAppKey = !!data[ProviderCredentialFields.GITHUB_APP_KEY];
|
||||
|
||||
// Validate Personal Access Token - show error if field is empty or undefined
|
||||
if (!data[ProviderCredentialFields.PERSONAL_ACCESS_TOKEN] || data[ProviderCredentialFields.PERSONAL_ACCESS_TOKEN].trim() === "") {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Personal Access Token cannot be empty",
|
||||
path: [ProviderCredentialFields.PERSONAL_ACCESS_TOKEN],
|
||||
});
|
||||
}
|
||||
|
||||
// Validate OAuth App Token - show error if field is empty or undefined
|
||||
if (!data[ProviderCredentialFields.OAUTH_APP_TOKEN] || data[ProviderCredentialFields.OAUTH_APP_TOKEN].trim() === "") {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "OAuth App Token cannot be empty",
|
||||
path: [ProviderCredentialFields.OAUTH_APP_TOKEN],
|
||||
});
|
||||
}
|
||||
|
||||
// Validate GitHub App ID - show error if field is empty or undefined
|
||||
if (!data[ProviderCredentialFields.GITHUB_APP_ID] || data[ProviderCredentialFields.GITHUB_APP_ID].trim() === "") {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "GitHub App ID cannot be empty",
|
||||
path: [ProviderCredentialFields.GITHUB_APP_ID],
|
||||
});
|
||||
}
|
||||
|
||||
// Validate GitHub App Key - show error if field is empty or undefined
|
||||
if (!data[ProviderCredentialFields.GITHUB_APP_KEY] || data[ProviderCredentialFields.GITHUB_APP_KEY].trim() === "") {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "GitHub App Key cannot be empty",
|
||||
path: [ProviderCredentialFields.GITHUB_APP_KEY],
|
||||
});
|
||||
}
|
||||
// Check if any field has been filled out
|
||||
const hasAnyValue = hasPersonalAccessToken || hasOAuthAppToken || (hasGitHubAppId && hasGitHubAppKey);
|
||||
|
||||
// If no field has been filled out, show the general message
|
||||
if (!hasAnyValue) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Please provide at least one authentication method",
|
||||
path: [ProviderCredentialFields.PERSONAL_ACCESS_TOKEN],
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
export const addCredentialsRoleFormSchema = (providerType: string) =>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const checkSchema = z.object({
|
||||
providerType: z.enum(["aws", "gcp", "azure", "kubernetes", "m365"]),
|
||||
providerType: z.enum(["aws", "gcp", "azure", "kubernetes", "m365", "github"]),
|
||||
service: z.array(z.string()).optional(),
|
||||
severity: z
|
||||
.array(z.enum(["informational", "low", "medium", "high", "critical"]))
|
||||
|
||||
@@ -89,7 +89,7 @@ export const getCompliancesOverviewSchema = z.object({
|
||||
|
||||
export const getComplianceFrameworksSchema = z.object({
|
||||
providerType: z
|
||||
.enum(["aws", "azure", "gcp", "kubernetes", "m365"])
|
||||
.enum(["aws", "azure", "gcp", "kubernetes", "m365", "github"])
|
||||
.describe("The provider type to get the compliance frameworks for."),
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,14 @@ import { z } from "zod";
|
||||
|
||||
// Get Providers Schema
|
||||
|
||||
const providerEnum = z.enum(["", "aws", "azure", "gcp", "kubernetes"]);
|
||||
const providerEnum = z.enum([
|
||||
"",
|
||||
"aws",
|
||||
"azure",
|
||||
"gcp",
|
||||
"kubernetes",
|
||||
"github",
|
||||
]);
|
||||
|
||||
const sortFieldsEnum = z.enum([
|
||||
"",
|
||||
|
||||
@@ -37,7 +37,14 @@ const resourceSortEnum = z.enum([
|
||||
"-updated_at",
|
||||
]);
|
||||
|
||||
const providerTypeEnum = z.enum(["", "aws", "gcp", "azure", "kubernetes"]);
|
||||
const providerTypeEnum = z.enum([
|
||||
"",
|
||||
"aws",
|
||||
"gcp",
|
||||
"azure",
|
||||
"kubernetes",
|
||||
"github",
|
||||
]);
|
||||
|
||||
export const getResourcesSchema = z.object({
|
||||
page: z.number().optional().describe("The page number to fetch."),
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import { z } from "zod";
|
||||
|
||||
const providerTypeEnum = z.enum(["", "aws", "azure", "gcp", "kubernetes"]);
|
||||
const providerTypeEnum = z.enum([
|
||||
"",
|
||||
"aws",
|
||||
"azure",
|
||||
"gcp",
|
||||
"kubernetes",
|
||||
"github",
|
||||
]);
|
||||
const stateEnum = z.enum([
|
||||
"",
|
||||
"available",
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
export type ProviderType = "aws" | "azure" | "m365" | "gcp" | "kubernetes";
|
||||
export type ProviderType =
|
||||
| "aws"
|
||||
| "azure"
|
||||
| "m365"
|
||||
| "gcp"
|
||||
| "kubernetes"
|
||||
| "github";
|
||||
|
||||
export interface ProviderProps {
|
||||
id: string;
|
||||
|
||||
Reference in New Issue
Block a user