import type { IntegrationType } from "../types/integrations"; import { PROVIDER_WIZARD_STEP, type ProviderWizardStep, } from "../types/provider-wizard"; // Documentation URLs export const DOCS_URLS = { FINDINGS_ANALYSIS: "https://docs.prowler.com/user-guide/tutorials/prowler-app#step-8:-analyze-the-findings", FINDINGS_INGESTION: "https://docs.prowler.com/user-guide/tutorials/prowler-app-import-findings", FINDINGS_TRIAGE: "https://docs.prowler.com/user-guide/tutorials/prowler-app-findings-triage", AWS_ORGANIZATIONS: "https://docs.prowler.com/user-guide/tutorials/prowler-cloud-aws-organizations", AZURE_ORGANIZATIONS: "https://docs.prowler.com/user-guide/tutorials/prowler-cloud-azure-management-groups", GCP_ORGANIZATIONS: "https://docs.prowler.com/user-guide/tutorials/prowler-cloud-gcp-organizations", ALERTS: "https://docs.prowler.com/user-guide/tutorials/prowler-app-alerts", SLACK_INTEGRATION: "https://docs.prowler.com/user-guide/tutorials/prowler-app-slack-integration", SLACK_INTEGRATION_PRIVATE_CHANNELS: "https://docs.prowler.com/user-guide/tutorials/prowler-app-slack-integration#why-a-private-channel-is-missing-from-the-channel-list", SCAN_CONFIGURATION: "https://docs.prowler.com/user-guide/tutorials/prowler-app-scan-configuration", ATTACK_PATHS_CUSTOM_QUERIES: "https://docs.prowler.com/user-guide/tutorials/prowler-app-attack-paths#writing-custom-opencypher-queries", AI_AGENTS: "https://docs.prowler.com/user-guide/ai-agents/", } as const; // Prowler Hub — the public catalog of Prowler artifacts (checks, compliance, // attack paths). Every built-in Attack Paths query has a page keyed by its // query id, e.g. https://hub.prowler.com/attack-paths/aws-sts-privesc-assume-role export const PROWLER_HUB_URL = "https://hub.prowler.com"; export const getAttackPathHubUrl = (queryId: string): string => `${PROWLER_HUB_URL}/attack-paths/${encodeURIComponent(queryId)}`; // CloudFormation template URL for the ProwlerScan role. // Also used (URL-encoded) as the templateURL param in the quick-create links // built by getAWSCredentialsTemplateLinks and getAWSOrgDeploymentQuickLink below. export const PROWLER_CF_TEMPLATE_URL = "https://prowler-cloud-public.s3.eu-west-1.amazonaws.com/permissions/templates/aws/cloudformation/prowler-scan-role.yml"; // Prowler Cloud billing/subscription management page. export const BILLING_URL = "https://cloud.prowler.com/billing"; // Base URL for the CloudFormation "quick create stack" console flow. // Hardcoded to us-east-1 because the public template is hosted for that flow. const CF_QUICKCREATE_BASE_URL = "https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/quickcreate"; // Deep links that open each provider's cloud console with the credential // creation form pre-filled with the exact permissions, scopes and name // Prowler needs. The full 16-provider audit (which providers support this and // which do not) lives in the PROWLER-2187 PR description; keep both in sync // when adding or removing entries here. // AWS has its own CloudFormation quick-create link built in // `getAWSCredentialsTemplateLinks` below. export const PRECONFIGURED_CREDENTIAL_URLS = { // Opens the Cloudflare "Create Custom Token" form under the user profile // pre-filled with the four read-only scopes Prowler needs // (`Account Settings`, `Zone`, `Zone Settings`, `DNS`) and the token name. // Kept in sync with the "User API Token" URL published in // docs/user-guide/providers/cloudflare/authentication.mdx. CLOUDFLARE_API_TOKEN_USER: "https://dash.cloudflare.com/profile/api-tokens?permissionGroupKeys=%5B%7B%22key%22%3A%22account_settings%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22zone%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22zone_settings%22%2C%22type%22%3A%22read%22%7D%2C%7B%22key%22%3A%22dns%22%2C%22type%22%3A%22read%22%7D%5D&accountId=%2A&zoneId=all&name=Prowler%20Security%20Scanner", // Opens the GitHub fine-grained PAT creation form pre-filled with the four // read-only permissions Prowler needs to scan a user's own repositories. // Kept in sync with the "user repositories" URL published in // docs/user-guide/providers/github/authentication.mdx. GITHUB_PERSONAL_ACCESS_TOKEN_USER: "https://github.com/settings/personal-access-tokens/new?name=Prowler+Security+Scanner&description=Fine-grained+PAT+for+Prowler+security+scanning&expires_in=90&administration=read&contents=read&vulnerability_alerts=read&emails=read", } as const; // Builds the account-owned Cloudflare API Token URL for the given account. // Uses the dashboard router pattern (`?to=//api-tokens&...`) // published in docs/user-guide/providers/cloudflare/authentication.mdx, with // the account id substituted in place of the docs' `:account` placeholder to // avoid ambiguity when the user is signed into multiple accounts. Navigating // directly to `//api-tokens/create` does NOT pre-fill the form — // Cloudflare only reads the pre-fill params when they arrive via the router. // Same four read-only scopes as the user token URL. export const buildCloudflareAccountOwnedApiTokenUrl = ( accountId: string, ): string => { // Cloudflare's router expects the `to=` value with unencoded slashes; using // URLSearchParams would percent-encode them and break the redirect, so we // assemble the query string manually and only encode the pieces that need // it. const encodedAccountId = encodeURIComponent(accountId); const permissionGroupKeys = encodeURIComponent( JSON.stringify([ { key: "account_settings", type: "read" }, { key: "zone", type: "read" }, { key: "zone_settings", type: "read" }, { key: "dns", type: "read" }, ]), ); const name = encodeURIComponent("Prowler Security Scanner"); return `https://dash.cloudflare.com/?to=/${encodedAccountId}/api-tokens&permissionGroupKeys=${permissionGroupKeys}&name=${name}`; }; // Builds the organization-scoped GitHub fine-grained PAT URL. GitHub validates // permissions against the token's Resource Owner and only surfaces // `organization_administration` and `members` when it is an organization, so // we pin the owner via `target_name` and skip account-only permissions // (`emails`) that the docs' org template does not request. Kept in sync with // the "organization scanning" URL published in // docs/user-guide/providers/github/authentication.mdx. export const buildGitHubPersonalAccessTokenOrgUrl = ( targetName: string, ): string => { const params = new URLSearchParams({ name: "Prowler Security Scanner", description: "Fine-grained PAT for Prowler organization security scanning", expires_in: "90", target_name: targetName, administration: "read", contents: "read", vulnerability_alerts: "read", organization_administration: "read", members: "read", }); return `https://github.com/settings/personal-access-tokens/new?${params.toString()}`; }; export interface AWSOrgDeploymentQuickLinkParams { externalId: string; organizationalUnitId: string; deployFromDelegatedAdmin?: boolean; } const buildCloudFormationQuickCreateLink = ( parameters: Record, ): string => { const searchParams = new URLSearchParams({ templateURL: PROWLER_CF_TEMPLATE_URL, stackName: "Prowler", ...parameters, }); return `${CF_QUICKCREATE_BASE_URL}?${searchParams.toString()}`; }; // Shortlinks are used for all wizard steps except credentials so link // ownership stays with the docs/marketing team: they can retarget // destinations from the shortener panel without a UI PR. const PROVIDER_DOCS_SHORTLINK: Record = { aws: "https://goto.prowler.com/provider-aws", azure: "https://goto.prowler.com/provider-azure", m365: "https://goto.prowler.com/provider-m365", gcp: "https://goto.prowler.com/provider-gcp", kubernetes: "https://goto.prowler.com/provider-k8s", github: "https://goto.prowler.com/provider-github", iac: "https://goto.prowler.com/provider-iac", image: "https://goto.prowler.com/provider-image", oraclecloud: "https://goto.prowler.com/provider-oraclecloud", mongodbatlas: "https://goto.prowler.com/provider-mongodbatlas", alibabacloud: "https://goto.prowler.com/provider-alibabacloud", cloudflare: "https://goto.prowler.com/provider-cloudflare", openstack: "https://goto.prowler.com/provider-openstack", googleworkspace: "https://goto.prowler.com/provider-googleworkspace", vercel: "https://goto.prowler.com/provider-vercel", okta: "https://goto.prowler.com/provider-okta", }; // Default target for the credentials step: the section of the provider's // getting-started page that introduces the credentials flow. The getting- // started page keeps the user in the same mental model as the wizard, and // each section already links to `authentication.mdx` for readers who need // deeper detail. That indirection is intentional — we do NOT deep-link into // `authentication.mdx` from the wizard, otherwise the user is jumped into // low-level docs before they have the context to make sense of them. const PROVIDER_CREDENTIALS_STEP_DOCS_URL: Record = { aws: "https://docs.prowler.com/user-guide/providers/aws/getting-started-aws#step-3-set-up-aws-authentication", azure: "https://docs.prowler.com/user-guide/providers/azure/getting-started-azure#step-3-add-credentials-to-prowler-cloud", m365: "https://docs.prowler.com/user-guide/providers/microsoft365/getting-started-m365#step-3-choose-and-provide-authentication", gcp: "https://docs.prowler.com/user-guide/providers/gcp/getting-started-gcp#step-3-set-up-gcp-authentication", kubernetes: "https://docs.prowler.com/user-guide/providers/kubernetes/getting-started-k8s#step-2-configure-kubernetes-authentication", github: "https://docs.prowler.com/user-guide/providers/github/getting-started-github#step-3-choose-authentication-method", iac: "https://docs.prowler.com/user-guide/providers/iac/getting-started-iac#step-2-enter-authentication-details", image: "https://docs.prowler.com/user-guide/providers/image/getting-started-image#step-2-enter-authentication-and-scan-filters", oraclecloud: "https://docs.prowler.com/user-guide/providers/oci/getting-started-oci#step-3-add-oci-api-key-credentials", mongodbatlas: "https://docs.prowler.com/user-guide/providers/mongodbatlas/getting-started-mongodbatlas#step-2-provide-api-credentials", alibabacloud: "https://docs.prowler.com/user-guide/providers/alibabacloud/getting-started-alibabacloud#step-3-choose-and-provide-authentication", cloudflare: "https://docs.prowler.com/user-guide/providers/cloudflare/getting-started-cloudflare#step-3-choose-and-provide-authentication", openstack: "https://docs.prowler.com/user-guide/providers/openstack/getting-started-openstack#step-2-provide-credentials", googleworkspace: "https://docs.prowler.com/user-guide/providers/googleworkspace/getting-started-googleworkspace#step-3-provide-credentials", vercel: "https://docs.prowler.com/user-guide/providers/vercel/getting-started-vercel#step-2-provide-credentials", okta: "https://docs.prowler.com/user-guide/providers/okta/getting-started-okta#step-2-provide-credentials", }; // When the user has picked a specific auth method inside the credentials // step, jump directly to that method's subsection in the getting-started // page. Only providers whose docs have a heading per method are listed — // GCP and GitHub render their methods inside a Mintlify `` component // so a per-method anchor isn't available today; they fall back to the // general step URL above. const PROVIDER_CREDENTIALS_METHOD_DOCS_URL: Record< string, Record > = { aws: { role: "https://docs.prowler.com/user-guide/providers/aws/getting-started-aws#assume-role-recommended", credentials: "https://docs.prowler.com/user-guide/providers/aws/getting-started-aws#credentials-static-access-keys", }, m365: { app_certificate: "https://docs.prowler.com/user-guide/providers/microsoft365/getting-started-m365#application-certificate-authentication-recommended", app_client_secret: "https://docs.prowler.com/user-guide/providers/microsoft365/getting-started-m365#application-client-secret-authentication", }, alibabacloud: { role: "https://docs.prowler.com/user-guide/providers/alibabacloud/getting-started-alibabacloud#ram-role-assumption-recommended", credentials: "https://docs.prowler.com/user-guide/providers/alibabacloud/getting-started-alibabacloud#credentials-static-access-keys", }, cloudflare: { api_token: "https://docs.prowler.com/user-guide/providers/cloudflare/getting-started-cloudflare#user-api-token-authentication-recommended", api_key: "https://docs.prowler.com/user-guide/providers/cloudflare/getting-started-cloudflare#api-key-and-email-authentication-legacy", }, }; const PROVIDER_HELP_FALLBACK_URL = "https://goto.prowler.com/provider-help"; const getOwnRecordValue = ( record: Readonly>, key: string, ): T | undefined => (Object.hasOwn(record, key) ? record[key] : undefined); const resolveDocsLink = ( provider: string, step?: ProviderWizardStep, credentialsMethod?: string | null, ) => { const shortlink = getOwnRecordValue(PROVIDER_DOCS_SHORTLINK, provider); if (step === PROVIDER_WIZARD_STEP.CREDENTIALS) { if (credentialsMethod) { const methodDocs = getOwnRecordValue( PROVIDER_CREDENTIALS_METHOD_DOCS_URL, provider, ); const methodUrl = methodDocs ? getOwnRecordValue(methodDocs, credentialsMethod) : undefined; if (methodUrl) return methodUrl; } const stepUrl = getOwnRecordValue( PROVIDER_CREDENTIALS_STEP_DOCS_URL, provider, ); if (stepUrl) return stepUrl; } return shortlink; }; const PROVIDER_HELP_TEXT: Record = { aws: "Need help connecting your AWS account?", azure: "Need help connecting your Azure subscription?", m365: "Need help connecting your Microsoft 365 account?", gcp: "Need help connecting your GCP project?", kubernetes: "Need help connecting your Kubernetes cluster?", github: "Need help connecting your GitHub account?", iac: "Need help scanning your Infrastructure as Code repository?", image: "Need help scanning your container registry?", oraclecloud: "Need help connecting your Oracle Cloud account?", mongodbatlas: "Need help connecting your MongoDB Atlas organization?", alibabacloud: "Need help connecting your Alibaba Cloud account?", cloudflare: "Need help connecting your Cloudflare account?", openstack: "Need help connecting your OpenStack cloud?", googleworkspace: "Need help connecting your Google Workspace account?", vercel: "Need help connecting your Vercel team?", okta: "Need help connecting your Okta organization?", }; export const getProviderHelpText = ( provider: string, step?: ProviderWizardStep, credentialsMethod?: string | null, ) => { const link = resolveDocsLink(provider, step, credentialsMethod); if (!link) { // Unknown provider: hand off to the generic help shortlink instead of // deep-linking into a page that may not exist. return { text: "How to setup a provider?", link: PROVIDER_HELP_FALLBACK_URL, }; } return { text: getOwnRecordValue(PROVIDER_HELP_TEXT, provider) ?? "Need help connecting your provider?", link, }; }; export const getAWSCredentialsTemplateLinks = ( externalId: string, bucketName?: string, integrationType?: IntegrationType, bucketAccountId?: string, ): { cloudformation: string; terraform: string; cloudformationQuickLink: string; } => { let links = {}; if (integrationType === undefined || integrationType === "aws_security_hub") { links = { cloudformation: "https://github.com/prowler-cloud/prowler/blob/master/permissions/templates/cloudformation/prowler-scan-role.yml", terraform: "https://github.com/prowler-cloud/prowler/tree/master/permissions/templates/terraform", }; } if (integrationType === "amazon_s3") { links = { cloudformation: "https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/prowler-app-s3-integration/", terraform: "https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/prowler-app-s3-integration/#terraform", }; } // The template requires S3IntegrationBucketAccountId (owner account of the // bucket) whenever EnableS3Integration is true. Only enable S3 when both the // bucket name and its account id are known, otherwise an incomplete link // would fail stack validation on the quick-create flow (reachable from the // edit-credentials flow, where the account id can resolve to an empty value). const parameters: Record = { param_ExternalId: externalId, }; if (bucketName && bucketAccountId) { parameters.param_EnableS3Integration = "true"; parameters.param_S3IntegrationBucketName = bucketName; parameters.param_S3IntegrationBucketAccountId = bucketAccountId; } return { ...(links as { cloudformation: string; terraform: string; }), cloudformationQuickLink: buildCloudFormationQuickCreateLink(parameters), }; }; // Builds the CloudFormation quick-create link that onboards an entire AWS // Organization in a single stack: it creates the ProwlerScan role in the // account launching the stack (DeployLocalRole) and a service-managed StackSet // that rolls the role out to the member accounts under the given OU/root // (DeployStackSet). By default the stack is launched from the management // account; set deployFromDelegatedAdmin when launching from a delegated // administrator account instead, where the local role lands in that account. export const getAWSOrgDeploymentQuickLink = ({ externalId, organizationalUnitId, deployFromDelegatedAdmin = false, }: AWSOrgDeploymentQuickLinkParams): string => { const parameters: Record = { param_ExternalId: externalId, param_EnableOrganizations: "true", param_DeployLocalRole: "true", param_DeployStackSet: "true", param_AWSOrganizationalUnitId: organizationalUnitId, }; if (deployFromDelegatedAdmin) { parameters.param_DeployFromDelegatedAdmin = "true"; } return buildCloudFormationQuickCreateLink(parameters); };