mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-23 20:42:02 +00:00
545 lines
16 KiB
TypeScript
545 lines
16 KiB
TypeScript
import { filterEmptyValues, getFormValue } from "@/lib";
|
|
import { ProviderType } from "@/types";
|
|
import {
|
|
isConfigurableProvider,
|
|
type KnownProviderType,
|
|
} from "@/types/providers";
|
|
|
|
import { ProviderCredentialFields } from "./provider-credential-fields";
|
|
|
|
// Helper functions for each provider type
|
|
export const buildAWSSecret = (formData: FormData, isRole: boolean) => {
|
|
if (isRole) {
|
|
const secret = {
|
|
[ProviderCredentialFields.ROLE_ARN]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.ROLE_ARN,
|
|
),
|
|
[ProviderCredentialFields.EXTERNAL_ID]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.EXTERNAL_ID,
|
|
),
|
|
[ProviderCredentialFields.AWS_ACCESS_KEY_ID]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.AWS_ACCESS_KEY_ID,
|
|
),
|
|
[ProviderCredentialFields.AWS_SECRET_ACCESS_KEY]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.AWS_SECRET_ACCESS_KEY,
|
|
),
|
|
[ProviderCredentialFields.AWS_SESSION_TOKEN]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.AWS_SESSION_TOKEN,
|
|
),
|
|
session_duration:
|
|
parseInt(
|
|
getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.SESSION_DURATION,
|
|
) as string,
|
|
10,
|
|
) || 3600,
|
|
[ProviderCredentialFields.ROLE_SESSION_NAME]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.ROLE_SESSION_NAME,
|
|
),
|
|
};
|
|
return filterEmptyValues(secret);
|
|
}
|
|
|
|
const secret = {
|
|
[ProviderCredentialFields.AWS_ACCESS_KEY_ID]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.AWS_ACCESS_KEY_ID,
|
|
),
|
|
[ProviderCredentialFields.AWS_SECRET_ACCESS_KEY]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.AWS_SECRET_ACCESS_KEY,
|
|
),
|
|
[ProviderCredentialFields.AWS_SESSION_TOKEN]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.AWS_SESSION_TOKEN,
|
|
),
|
|
};
|
|
return filterEmptyValues(secret);
|
|
};
|
|
|
|
export const buildAzureSecret = (formData: FormData) => {
|
|
const secret = {
|
|
[ProviderCredentialFields.CLIENT_ID]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.CLIENT_ID,
|
|
),
|
|
[ProviderCredentialFields.CLIENT_SECRET]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.CLIENT_SECRET,
|
|
),
|
|
[ProviderCredentialFields.TENANT_ID]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.TENANT_ID,
|
|
),
|
|
};
|
|
return filterEmptyValues(secret);
|
|
};
|
|
|
|
export const buildM365Secret = (formData: FormData) => {
|
|
const secret = {
|
|
[ProviderCredentialFields.CLIENT_ID]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.CLIENT_ID,
|
|
),
|
|
[ProviderCredentialFields.TENANT_ID]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.TENANT_ID,
|
|
),
|
|
[ProviderCredentialFields.CLIENT_SECRET]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.CLIENT_SECRET,
|
|
),
|
|
[ProviderCredentialFields.CERTIFICATE_CONTENT]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.CERTIFICATE_CONTENT,
|
|
),
|
|
};
|
|
return filterEmptyValues(secret);
|
|
};
|
|
|
|
export const buildGCPSecret = (
|
|
formData: FormData,
|
|
isServiceAccount: boolean,
|
|
) => {
|
|
if (isServiceAccount) {
|
|
const serviceAccountKeyRaw = getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.SERVICE_ACCOUNT_KEY,
|
|
) as string;
|
|
|
|
try {
|
|
return {
|
|
service_account_key: JSON.parse(serviceAccountKeyRaw),
|
|
};
|
|
} catch (error) {
|
|
console.error("Invalid service account key JSON:", error);
|
|
throw new Error("Invalid service account key format");
|
|
}
|
|
}
|
|
|
|
const secret = {
|
|
[ProviderCredentialFields.CLIENT_ID]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.CLIENT_ID,
|
|
),
|
|
[ProviderCredentialFields.CLIENT_SECRET]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.CLIENT_SECRET,
|
|
),
|
|
[ProviderCredentialFields.REFRESH_TOKEN]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.REFRESH_TOKEN,
|
|
),
|
|
};
|
|
return filterEmptyValues(secret);
|
|
};
|
|
|
|
export const buildKubernetesSecret = (formData: FormData) => {
|
|
const secret = {
|
|
[ProviderCredentialFields.KUBECONFIG_CONTENT]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.KUBECONFIG_CONTENT,
|
|
),
|
|
};
|
|
return filterEmptyValues(secret);
|
|
};
|
|
|
|
export const buildGitHubSecret = (formData: FormData) => {
|
|
// Check which authentication method is being used
|
|
const hasPersonalToken =
|
|
formData.get(ProviderCredentialFields.PERSONAL_ACCESS_TOKEN) !== null &&
|
|
formData.get(ProviderCredentialFields.PERSONAL_ACCESS_TOKEN) !== "";
|
|
const hasOAuthToken =
|
|
formData.get(ProviderCredentialFields.OAUTH_APP_TOKEN) !== null &&
|
|
formData.get(ProviderCredentialFields.OAUTH_APP_TOKEN) !== "";
|
|
const hasGitHubApp =
|
|
formData.get(ProviderCredentialFields.GITHUB_APP_ID) !== null &&
|
|
formData.get(ProviderCredentialFields.GITHUB_APP_ID) !== "";
|
|
|
|
if (hasPersonalToken) {
|
|
const secret = {
|
|
[ProviderCredentialFields.PERSONAL_ACCESS_TOKEN]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.PERSONAL_ACCESS_TOKEN,
|
|
),
|
|
};
|
|
return filterEmptyValues(secret);
|
|
}
|
|
|
|
if (hasOAuthToken) {
|
|
const secret = {
|
|
[ProviderCredentialFields.OAUTH_APP_TOKEN]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.OAUTH_APP_TOKEN,
|
|
),
|
|
};
|
|
return filterEmptyValues(secret);
|
|
}
|
|
|
|
if (hasGitHubApp) {
|
|
const secret = {
|
|
[ProviderCredentialFields.GITHUB_APP_ID]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.GITHUB_APP_ID,
|
|
),
|
|
[ProviderCredentialFields.GITHUB_APP_KEY]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.GITHUB_APP_KEY,
|
|
),
|
|
};
|
|
return filterEmptyValues(secret);
|
|
}
|
|
|
|
// If no credentials are provided, return empty object
|
|
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 buildAlibabaCloudSecret = (
|
|
formData: FormData,
|
|
isRole: boolean,
|
|
) => {
|
|
if (isRole) {
|
|
const secret = {
|
|
[ProviderCredentialFields.ALIBABACLOUD_ROLE_ARN]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.ALIBABACLOUD_ROLE_ARN,
|
|
),
|
|
[ProviderCredentialFields.ALIBABACLOUD_ACCESS_KEY_ID]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.ALIBABACLOUD_ACCESS_KEY_ID,
|
|
),
|
|
[ProviderCredentialFields.ALIBABACLOUD_ACCESS_KEY_SECRET]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.ALIBABACLOUD_ACCESS_KEY_SECRET,
|
|
),
|
|
[ProviderCredentialFields.ALIBABACLOUD_ROLE_SESSION_NAME]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.ALIBABACLOUD_ROLE_SESSION_NAME,
|
|
),
|
|
};
|
|
return filterEmptyValues(secret);
|
|
}
|
|
|
|
const secret = {
|
|
[ProviderCredentialFields.ALIBABACLOUD_ACCESS_KEY_ID]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.ALIBABACLOUD_ACCESS_KEY_ID,
|
|
),
|
|
[ProviderCredentialFields.ALIBABACLOUD_ACCESS_KEY_SECRET]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.ALIBABACLOUD_ACCESS_KEY_SECRET,
|
|
),
|
|
};
|
|
return filterEmptyValues(secret);
|
|
};
|
|
|
|
export const buildVercelSecret = (formData: FormData) => {
|
|
const secret = {
|
|
[ProviderCredentialFields.VERCEL_API_TOKEN]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.VERCEL_API_TOKEN,
|
|
),
|
|
};
|
|
return filterEmptyValues(secret);
|
|
};
|
|
|
|
export const buildOktaSecret = (formData: FormData) => {
|
|
const secret = {
|
|
[ProviderCredentialFields.OKTA_CLIENT_ID]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.OKTA_CLIENT_ID,
|
|
),
|
|
[ProviderCredentialFields.OKTA_PRIVATE_KEY]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.OKTA_PRIVATE_KEY,
|
|
),
|
|
};
|
|
return filterEmptyValues(secret);
|
|
};
|
|
|
|
export const buildOpenStackSecret = (formData: FormData) => {
|
|
const secret = {
|
|
[ProviderCredentialFields.OPENSTACK_CLOUDS_YAML_CONTENT]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.OPENSTACK_CLOUDS_YAML_CONTENT,
|
|
),
|
|
[ProviderCredentialFields.OPENSTACK_CLOUDS_YAML_CLOUD]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.OPENSTACK_CLOUDS_YAML_CLOUD,
|
|
),
|
|
};
|
|
return filterEmptyValues(secret);
|
|
};
|
|
|
|
export const buildGoogleWorkspaceSecret = (formData: FormData) => {
|
|
const secret = {
|
|
[ProviderCredentialFields.GOOGLEWORKSPACE_CREDENTIALS_CONTENT]:
|
|
getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.GOOGLEWORKSPACE_CREDENTIALS_CONTENT,
|
|
),
|
|
[ProviderCredentialFields.GOOGLEWORKSPACE_DELEGATED_USER]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.GOOGLEWORKSPACE_DELEGATED_USER,
|
|
),
|
|
};
|
|
return filterEmptyValues(secret);
|
|
};
|
|
|
|
export const buildIacSecret = (formData: FormData) => {
|
|
const secret = {
|
|
[ProviderCredentialFields.REPOSITORY_URL]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.REPOSITORY_URL,
|
|
),
|
|
[ProviderCredentialFields.ACCESS_TOKEN]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.ACCESS_TOKEN,
|
|
),
|
|
};
|
|
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
|
|
*/
|
|
const base64Encode = (str: string): string => {
|
|
if (!str) return "";
|
|
// Convert string to UTF-8 bytes, then to base64
|
|
const utf8Bytes = new TextEncoder().encode(str);
|
|
// Convert Uint8Array to binary string without spread operator
|
|
let binaryString = "";
|
|
for (let i = 0; i < utf8Bytes.length; i++) {
|
|
binaryString += String.fromCharCode(utf8Bytes[i]);
|
|
}
|
|
return btoa(binaryString);
|
|
};
|
|
|
|
export const buildOracleCloudSecret = (
|
|
formData: FormData,
|
|
providerUid?: string,
|
|
) => {
|
|
const keyContent = getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.OCI_KEY_CONTENT,
|
|
) as string;
|
|
|
|
// Base64 encode the key content for the backend
|
|
// Uses modern TextEncoder API to properly handle UTF-8 characters
|
|
const encodedKeyContent = base64Encode(keyContent);
|
|
|
|
const secret = {
|
|
[ProviderCredentialFields.OCI_USER]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.OCI_USER,
|
|
),
|
|
[ProviderCredentialFields.OCI_FINGERPRINT]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.OCI_FINGERPRINT,
|
|
),
|
|
[ProviderCredentialFields.OCI_KEY_CONTENT]: encodedKeyContent,
|
|
[ProviderCredentialFields.OCI_TENANCY]:
|
|
providerUid ||
|
|
getFormValue(formData, ProviderCredentialFields.OCI_TENANCY),
|
|
[ProviderCredentialFields.OCI_PASS_PHRASE]: getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.OCI_PASS_PHRASE,
|
|
),
|
|
};
|
|
return filterEmptyValues(secret);
|
|
};
|
|
|
|
/**
|
|
* Clean a Cloudflare API token by removing common copy-paste issues:
|
|
* - Leading/trailing whitespace
|
|
* - "Bearer " prefix (if user copied the full header)
|
|
* - Tabs and other whitespace characters
|
|
*/
|
|
const cleanCloudflareToken = (token: string | null | undefined): string => {
|
|
if (!token) return "";
|
|
// Remove leading/trailing whitespace and tabs
|
|
let cleaned = token.trim().replace(/\t/g, "");
|
|
// Remove "Bearer " prefix if present (case-insensitive)
|
|
if (cleaned.toLowerCase().startsWith("bearer ")) {
|
|
cleaned = cleaned.slice(7).trim();
|
|
}
|
|
return cleaned;
|
|
};
|
|
|
|
export const buildCloudflareSecret = (formData: FormData) => {
|
|
// Check which authentication method is being used
|
|
const hasApiToken =
|
|
formData.get(ProviderCredentialFields.CLOUDFLARE_API_TOKEN) !== null &&
|
|
formData.get(ProviderCredentialFields.CLOUDFLARE_API_TOKEN) !== "";
|
|
const hasApiKey =
|
|
formData.get(ProviderCredentialFields.CLOUDFLARE_API_KEY) !== null &&
|
|
formData.get(ProviderCredentialFields.CLOUDFLARE_API_KEY) !== "";
|
|
|
|
if (hasApiToken) {
|
|
const apiToken = getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.CLOUDFLARE_API_TOKEN,
|
|
) as string;
|
|
return {
|
|
[ProviderCredentialFields.CLOUDFLARE_API_TOKEN]:
|
|
cleanCloudflareToken(apiToken),
|
|
};
|
|
}
|
|
|
|
if (hasApiKey) {
|
|
const apiKey = getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.CLOUDFLARE_API_KEY,
|
|
) as string;
|
|
const apiEmail = getFormValue(
|
|
formData,
|
|
ProviderCredentialFields.CLOUDFLARE_API_EMAIL,
|
|
) as string;
|
|
return filterEmptyValues({
|
|
[ProviderCredentialFields.CLOUDFLARE_API_KEY]: apiKey?.trim(),
|
|
[ProviderCredentialFields.CLOUDFLARE_API_EMAIL]: apiEmail?.trim(),
|
|
});
|
|
}
|
|
|
|
return {};
|
|
};
|
|
|
|
// Main function to build secret configuration
|
|
export const buildSecretConfig = (
|
|
formData: FormData,
|
|
providerType: ProviderType,
|
|
providerUid?: string,
|
|
) => {
|
|
const isRole = formData.get(ProviderCredentialFields.ROLE_ARN) !== null;
|
|
const isServiceAccount =
|
|
formData.get(ProviderCredentialFields.SERVICE_ACCOUNT_KEY) !== null;
|
|
|
|
const secretBuilders: Record<
|
|
KnownProviderType,
|
|
() => { secretType: string; secret: unknown }
|
|
> = {
|
|
aws: () => ({
|
|
secretType: isRole ? "role" : "static",
|
|
secret: buildAWSSecret(formData, isRole),
|
|
}),
|
|
azure: () => ({
|
|
secretType: "static",
|
|
secret: buildAzureSecret(formData),
|
|
}),
|
|
m365: () => ({
|
|
secretType: "static",
|
|
secret: buildM365Secret(formData),
|
|
}),
|
|
gcp: () => ({
|
|
secretType: isServiceAccount ? "service_account" : "static",
|
|
secret: buildGCPSecret(formData, isServiceAccount),
|
|
}),
|
|
kubernetes: () => ({
|
|
secretType: "static",
|
|
secret: buildKubernetesSecret(formData),
|
|
}),
|
|
github: () => ({
|
|
secretType: "static",
|
|
secret: buildGitHubSecret(formData),
|
|
}),
|
|
iac: () => ({
|
|
secretType: "static",
|
|
secret: buildIacSecret(formData),
|
|
}),
|
|
image: () => ({
|
|
secretType: "static",
|
|
secret: buildImageSecret(formData),
|
|
}),
|
|
oraclecloud: () => ({
|
|
secretType: "static",
|
|
secret: buildOracleCloudSecret(formData, providerUid),
|
|
}),
|
|
mongodbatlas: () => ({
|
|
secretType: "static",
|
|
secret: buildMongoDBAtlasSecret(formData),
|
|
}),
|
|
alibabacloud: () => {
|
|
const isRole =
|
|
formData.get(ProviderCredentialFields.ALIBABACLOUD_ROLE_ARN) !== null;
|
|
return {
|
|
secretType: isRole ? "role" : "static",
|
|
secret: buildAlibabaCloudSecret(formData, isRole),
|
|
};
|
|
},
|
|
cloudflare: () => ({
|
|
secretType: "static",
|
|
secret: buildCloudflareSecret(formData),
|
|
}),
|
|
openstack: () => ({
|
|
secretType: "static",
|
|
secret: buildOpenStackSecret(formData),
|
|
}),
|
|
googleworkspace: () => ({
|
|
secretType: "static",
|
|
secret: buildGoogleWorkspaceSecret(formData),
|
|
}),
|
|
vercel: () => ({
|
|
secretType: "static",
|
|
secret: buildVercelSecret(formData),
|
|
}),
|
|
okta: () => ({
|
|
secretType: "static",
|
|
secret: buildOktaSecret(formData),
|
|
}),
|
|
};
|
|
|
|
if (!isConfigurableProvider(providerType)) {
|
|
throw new Error(`Unsupported provider type: ${providerType}`);
|
|
}
|
|
|
|
return secretBuilders[providerType]();
|
|
};
|