fix(oci): use home region for identity bootstrap (#12909)

Co-authored-by: Pedro Martín <pedromarting3@gmail.com>
This commit is contained in:
Prowler BotandPedro Martín authored and GitHub committed 2026-09-29 18:08:45 +02:00
1 parent f23b38a269
commit 01d7df6fce
26 files changed
+383 -60

No files matched your search

@@ -0,0 +1 @@
Required home region selector in the OCI credentials form, so tenancies not subscribed to us-ashburn-1 can connect
@@ -4,6 +4,9 @@ import {
WizardInputField,
WizardTextareaField,
} from "@/components/providers/workflow/forms/fields";
import { Combobox } from "@/components/shadcn/combobox";
import { FormControl, FormField, FormMessage } from "@/components/shadcn/form";
import { OCI_REGION_GROUPS } from "@/lib/provider-credentials/oci-regions";
import { ProviderCredentialFields } from "@/lib/provider-credentials/provider-credential-fields";
import { OCICredentials } from "@/types";
@@ -48,6 +51,34 @@ export const OracleCloudCredentialsForm = ({
variant="bordered"
isRequired
/>
<FormField
control={control}
name={ProviderCredentialFields.OCI_REGION}
render={({ field }) => (
<div className="flex flex-col gap-1.5">
<span className="text-text-neutral-tertiary text-xs font-light tracking-tight">
Home Region<span className="text-text-error-primary">*</span>
</span>
<FormControl>
<Combobox
aria-label="Home Region"
value={field.value ?? ""}
onValueChange={field.onChange}
groups={OCI_REGION_GROUPS}
placeholder="Select your tenancy home region"
searchPlaceholder="Search region..."
emptyMessage="No region found."
contentClassName="z-[60] sm:w-(--radix-popover-trigger-width) sm:max-w-none"
/>
</FormControl>
<span className="text-text-neutral-tertiary text-xs">
Shown in the OCI Console under Tenancy Details. Used only to
validate the credentials: all subscribed regions are scanned.
</span>
<FormMessage className="text-text-error-primary max-w-full text-xs" />
</div>
)}
/>
<WizardTextareaField
control={control}
name={ProviderCredentialFields.OCI_KEY_CONTENT}
+1
View File
@@ -181,6 +181,7 @@ export const useCredentialsForm = ({
[ProviderCredentialFields.OCI_FINGERPRINT]: "",
[ProviderCredentialFields.OCI_KEY_CONTENT]: "",
[ProviderCredentialFields.OCI_TENANCY]: providerUid || "",
[ProviderCredentialFields.OCI_REGION]: "",
[ProviderCredentialFields.OCI_PASS_PHRASE]: "",
};
case "mongodbatlas":
@@ -389,6 +389,10 @@ export const buildOracleCloudSecret = (
[ProviderCredentialFields.OCI_TENANCY]:
providerUid ||
getFormValue(formData, ProviderCredentialFields.OCI_TENANCY),
[ProviderCredentialFields.OCI_REGION]: getFormValue(
formData,
ProviderCredentialFields.OCI_REGION,
),
[ProviderCredentialFields.OCI_PASS_PHRASE]: getFormValue(
formData,
ProviderCredentialFields.OCI_PASS_PHRASE,
@@ -0,0 +1,72 @@
import type { ComboboxGroup } from "@/components/shadcn/combobox";
// Keep in sync with OCI_REGIONS in prowler/providers/oraclecloud/config.py
const OCI_COMMERCIAL_REGIONS = [
"af-casablanca-1",
"af-johannesburg-1",
"ap-batam-1",
"ap-chuncheon-1",
"ap-hyderabad-1",
"ap-kulai-2",
"ap-melbourne-1",
"ap-mumbai-1",
"ap-osaka-1",
"ap-seoul-1",
"ap-singapore-1",
"ap-singapore-2",
"ap-sydney-1",
"ap-tokyo-1",
"ca-montreal-1",
"ca-toronto-1",
"eu-amsterdam-1",
"eu-frankfurt-1",
"eu-madrid-1",
"eu-madrid-3",
"eu-marseille-1",
"eu-milan-1",
"eu-paris-1",
"eu-stockholm-1",
"eu-turin-1",
"eu-zurich-1",
"il-jerusalem-1",
"me-abudhabi-1",
"me-dubai-1",
"me-jeddah-1",
"me-riyadh-1",
"mx-monterrey-1",
"mx-queretaro-1",
"sa-bogota-1",
"sa-santiago-1",
"sa-saopaulo-1",
"sa-valparaiso-1",
"sa-vinhedo-1",
"uk-cardiff-1",
"uk-london-1",
"us-ashburn-1",
"us-chicago-1",
"us-phoenix-1",
"us-sanjose-1",
];
const OCI_GOVERNMENT_REGIONS = [
{ value: "us-langley-1", label: "us-langley-1 (US Gov West)" },
{ value: "us-luke-1", label: "us-luke-1 (US Gov East)" },
{ value: "us-gov-ashburn-1", label: "us-gov-ashburn-1 (US DoD East)" },
{ value: "us-gov-chicago-1", label: "us-gov-chicago-1 (US DoD North)" },
{ value: "us-gov-phoenix-1", label: "us-gov-phoenix-1 (US DoD West)" },
];
export const OCI_REGION_GROUPS: ComboboxGroup[] = [
{
heading: "Commercial",
options: OCI_COMMERCIAL_REGIONS.map((region) => ({
value: region,
label: region,
})),
},
{ heading: "Government", options: OCI_GOVERNMENT_REGIONS },
];
export const OCI_REGION_VALUES = OCI_REGION_GROUPS.flatMap((group) =>
group.options.map((option) => option.value),
);
+13
View File
@@ -224,6 +224,7 @@ export interface OCIProviderCredential {
userId?: string;
fingerprint?: string;
keyContent?: string;
homeRegion?: string;
}
// AlibabaCloud credential options
@@ -365,6 +366,7 @@ export class ProvidersPage extends BasePage {
readonly ociUserIdInput: Locator;
readonly ociFingerprintInput: Locator;
readonly ociKeyContentInput: Locator;
readonly ociHomeRegionCombobox: Locator;
// AlibabaCloud provider form elements
readonly alibabacloudAccountIdInput: Locator;
@@ -510,6 +512,9 @@ export class ProvidersPage extends BasePage {
this.ociKeyContentInput = page.getByRole("textbox", {
name: /Private Key Content/i,
});
this.ociHomeRegionCombobox = page.getByRole("combobox", {
name: /Home Region/i,
});
// AlibabaCloud provider form inputs
this.alibabacloudAccountIdInput = page.getByRole("textbox", {
@@ -1284,6 +1289,12 @@ export class ProvidersPage extends BasePage {
if (credentials.keyContent) {
await this.ociKeyContentInput.fill(credentials.keyContent);
}
if (credentials.homeRegion) {
await this.ociHomeRegionCombobox.click();
await this.page
.locator(`[role="option"][data-value="${credentials.homeRegion}"]`)
.click();
}
}
async verifyOCICredentialsPageLoaded(): Promise<void> {
@@ -1294,6 +1305,7 @@ export class ProvidersPage extends BasePage {
await expect(this.ociUserIdInput).toBeVisible();
await expect(this.ociFingerprintInput).toBeVisible();
await expect(this.ociKeyContentInput).toBeVisible();
await expect(this.ociHomeRegionCombobox).toBeVisible();
}
async verifyOCIUpdateCredentialsPageLoaded(): Promise<void> {
@@ -1304,6 +1316,7 @@ export class ProvidersPage extends BasePage {
await expect(this.ociUserIdInput).toBeVisible();
await expect(this.ociFingerprintInput).toBeVisible();
await expect(this.ociKeyContentInput).toBeVisible();
await expect(this.ociHomeRegionCombobox).toBeVisible();
}
async selectAlibabaCloudProvider(): Promise<void> {
+5 -5
View File
@@ -611,7 +611,7 @@
**Preconditions:**
- Admin user authentication required (admin.auth.setup setup)
- Environment variables configured: E2E_OCI_TENANCY_ID, E2E_OCI_USER_ID, E2E_OCI_FINGERPRINT, E2E_OCI_KEY_CONTENT
- Environment variables configured: E2E_OCI_TENANCY_ID, E2E_OCI_USER_ID, E2E_OCI_FINGERPRINT, E2E_OCI_KEY_CONTENT, E2E_OCI_REGION (optional, defaults to us-ashburn-1)
- Remove any existing provider with the same Tenancy ID before starting the test
- This test must be run serially and never in parallel with other tests, as it requires the Tenancy ID not to be already registered beforehand.
@@ -622,7 +622,7 @@
3. Select OCI provider type
4. Fill provider details (tenancy ID and alias)
5. Verify OCI credentials page is loaded
6. Fill OCI credentials (user ID, fingerprint, key content)
6. Fill OCI credentials (user ID, fingerprint, key content, home region)
7. Confirm provider connection without launching a scan
8. Verify return to Providers page
9. Verify provider exists in Providers table
@@ -640,7 +640,7 @@
- Connect account page displays OCI option
- Provider details form accepts tenancy ID and alias
- OCI credentials page loads
- Credentials form accepts all required fields (user ID, fingerprint, key content)
- Credentials form accepts all required fields (user ID, fingerprint, key content, home region)
- Launch step appears
- Successful return to Providers page after closing the launch step
- Provider exists in Providers table (verified by tenancy ID)
@@ -670,7 +670,7 @@
**Preconditions:**
- Admin user authentication required (admin.auth.setup setup)
- Environment variables configured: E2E_OCI_TENANCY_ID, E2E_OCI_USER_ID, E2E_OCI_FINGERPRINT, E2E_OCI_KEY_CONTENT
- Environment variables configured: E2E_OCI_TENANCY_ID, E2E_OCI_USER_ID, E2E_OCI_FINGERPRINT, E2E_OCI_KEY_CONTENT, E2E_OCI_REGION (optional, defaults to us-ashburn-1)
- An OCI provider with the specified Tenancy ID must already exist (run PROVIDER-E2E-012 first)
- This test must be run serially and never in parallel with other tests
@@ -682,7 +682,7 @@
4. Click "Update Credentials" option
5. Verify update credentials page is loaded
6. Verify OCI credentials form fields are visible (confirms providerUid is loaded)
7. Fill OCI credentials (user ID, fingerprint, key content)
7. Fill OCI credentials (user ID, fingerprint, key content, home region)
8. Click Next to submit
9. Verify successful navigation to test connection page
+4
View File
@@ -954,6 +954,7 @@ test.describe("Add Provider", () => {
const userId = process.env.E2E_OCI_USER_ID ?? "";
const fingerprint = process.env.E2E_OCI_FINGERPRINT ?? "";
const keyContent = process.env.E2E_OCI_KEY_CONTENT ?? "";
const homeRegion = process.env.E2E_OCI_REGION ?? "us-ashburn-1";
// Setup before each test
test.beforeEach(async ({ page }) => {
@@ -995,6 +996,7 @@ test.describe("Add Provider", () => {
userId: userId,
fingerprint: fingerprint,
keyContent: keyContent,
homeRegion: homeRegion,
};
// Navigate to providers page
@@ -1439,6 +1441,7 @@ test.describe("Update Provider Credentials", () => {
const userId = process.env.E2E_OCI_USER_ID ?? "";
const fingerprint = process.env.E2E_OCI_FINGERPRINT ?? "";
const keyContent = process.env.E2E_OCI_KEY_CONTENT ?? "";
const homeRegion = process.env.E2E_OCI_REGION ?? "us-ashburn-1";
// Setup before each test
test.beforeEach(async ({ page }) => {
@@ -1465,6 +1468,7 @@ test.describe("Update Provider Credentials", () => {
userId: userId,
fingerprint: fingerprint,
keyContent: keyContent,
homeRegion: homeRegion,
};
// Navigate to providers page
+1
View File
@@ -281,6 +281,7 @@ export type OCICredentials = {
[ProviderCredentialFields.OCI_FINGERPRINT]: string;
[ProviderCredentialFields.OCI_KEY_CONTENT]: string;
[ProviderCredentialFields.OCI_TENANCY]: string;
[ProviderCredentialFields.OCI_REGION]: string;
[ProviderCredentialFields.OCI_PASS_PHRASE]?: string;
[ProviderCredentialFields.PROVIDER_ID]: string;
};
+1
View File
@@ -151,6 +151,7 @@ declare global {
E2E_OCI_USER_ID?: string;
E2E_OCI_FINGERPRINT?: string;
E2E_OCI_KEY_CONTENT?: string;
E2E_OCI_REGION?: string;
// E2E Alibaba Cloud
E2E_ALIBABACLOUD_ACCOUNT_ID?: string;
+23 -1
View File
@@ -307,11 +307,33 @@ describe("addCredentialsFormSchema - oraclecloud", () => {
[ProviderCredentialFields.OCI_TENANCY]: "ocid1.tenancy.oc1..example",
} as const;
it("accepts OCI API key credentials without region", () => {
it("rejects OCI API key credentials without a home region", () => {
const schema = addCredentialsFormSchema("oraclecloud");
const result = schema.safeParse(BASE_OCI_VALUES);
expect(result.success).toBe(false);
});
it("rejects an unknown OCI home region", () => {
const schema = addCredentialsFormSchema("oraclecloud");
const result = schema.safeParse({
...BASE_OCI_VALUES,
[ProviderCredentialFields.OCI_REGION]: "mars-north-1",
});
expect(result.success).toBe(false);
});
it("accepts OCI API key credentials with a home region", () => {
const schema = addCredentialsFormSchema("oraclecloud");
const result = schema.safeParse({
...BASE_OCI_VALUES,
[ProviderCredentialFields.OCI_REGION]: "me-abudhabi-1",
});
expect(result.success).toBe(true);
});
});
+9
View File
@@ -1,6 +1,7 @@
import yaml from "js-yaml";
import { z } from "zod";
import { OCI_REGION_VALUES } from "@/lib/provider-credentials/oci-regions";
import { ProviderCredentialFields } from "@/lib/provider-credentials/provider-credential-fields";
import { validateMutelistYaml, validateYaml } from "@/lib/yaml";
import { MAX_SAML_ADDITIONAL_EMAIL_DOMAINS } from "@/types/saml";
@@ -325,6 +326,14 @@ export const addCredentialsFormSchema = (
[ProviderCredentialFields.OCI_TENANCY]: z
.string()
.min(1, "Tenancy OCID is required"),
[ProviderCredentialFields.OCI_REGION]: z
.string()
.refine(
(region) => OCI_REGION_VALUES.includes(region),
{
error: "Home region is required",
},
),
[ProviderCredentialFields.OCI_PASS_PHRASE]: z
.union([z.string(), z.literal("")])
.optional(),