diff --git a/.github/workflows/ui-e2e-tests.yml b/.github/workflows/ui-e2e-tests.yml index db35056879..d20e181ccd 100644 --- a/.github/workflows/ui-e2e-tests.yml +++ b/.github/workflows/ui-e2e-tests.yml @@ -51,6 +51,10 @@ jobs: E2E_OCI_KEY_CONTENT: ${{ secrets.E2E_OCI_KEY_CONTENT }} E2E_OCI_REGION: ${{ secrets.E2E_OCI_REGION }} E2E_NEW_USER_PASSWORD: ${{ secrets.E2E_NEW_USER_PASSWORD }} + E2E_ALIBABACLOUD_ACCOUNT_ID: ${{ secrets.E2E_ALIBABACLOUD_ACCOUNT_ID }} + E2E_ALIBABACLOUD_ACCESS_KEY_ID: ${{ secrets.E2E_ALIBABACLOUD_ACCESS_KEY_ID }} + E2E_ALIBABACLOUD_ACCESS_KEY_SECRET: ${{ secrets.E2E_ALIBABACLOUD_ACCESS_KEY_SECRET }} + E2E_ALIBABACLOUD_ROLE_ARN: ${{ secrets.E2E_ALIBABACLOUD_ROLE_ARN }} steps: - name: Checkout repository diff --git a/ui/package.json b/ui/package.json index 1f9675fae9..5bea99207a 100644 --- a/ui/package.json +++ b/ui/package.json @@ -154,5 +154,6 @@ "@react-aria/interactions>react": "19.2.2" } }, - "version": "0.0.1" + "version": "0.0.1", + "packageManager": "pnpm@10.24.0+sha512.01ff8ae71b4419903b65c60fb2dc9d34cf8bb6e06d03bde112ef38f7a34d6904c424ba66bea5cdcf12890230bf39f9580473140ed9c946fef328b6e5238a345a" } diff --git a/ui/tests/providers/providers-page.ts b/ui/tests/providers/providers-page.ts index 817e28bdb1..2d0f48f18c 100644 --- a/ui/tests/providers/providers-page.ts +++ b/ui/tests/providers/providers-page.ts @@ -43,6 +43,12 @@ export interface OCIProviderData { alias?: string; } +// AlibabaCloud provider data +export interface AlibabaCloudProviderData { + accountId: string; + alias?: string; +} + // AWS credential options export const AWS_CREDENTIAL_OPTIONS = { AWS_ROLE_ARN: "role", @@ -167,6 +173,25 @@ export interface OCIProviderCredential { region?: string; } +// AlibabaCloud credential options +export const ALIBABACLOUD_CREDENTIAL_OPTIONS = { + ALIBABACLOUD_CREDENTIALS: "credentials", + ALIBABACLOUD_ROLE: "role", +} as const; + +// AlibabaCloud credential type +type AlibabaCloudCredentialType = + (typeof ALIBABACLOUD_CREDENTIAL_OPTIONS)[keyof typeof ALIBABACLOUD_CREDENTIAL_OPTIONS]; + +// AlibabaCloud provider credential +export interface AlibabaCloudProviderCredential { + type: AlibabaCloudCredentialType; + accessKeyId: string; + accessKeySecret: string; + roleArn?: string; + roleSessionName?: string; +} + // Providers page export class ProvidersPage extends BasePage { // Alias input @@ -184,6 +209,7 @@ export class ProvidersPage extends BasePage { readonly kubernetesProviderRadio: Locator; readonly githubProviderRadio: Locator; readonly ociProviderRadio: Locator; + readonly alibabacloudProviderRadio: Locator; // AWS provider form elements readonly accountIdInput: Locator; @@ -247,6 +273,15 @@ export class ProvidersPage extends BasePage { readonly ociKeyContentInput: Locator; readonly ociRegionInput: Locator; + // AlibabaCloud provider form elements + readonly alibabacloudAccountIdInput: Locator; + readonly alibabacloudAccessKeyIdInput: Locator; + readonly alibabacloudAccessKeySecretInput: Locator; + readonly alibabacloudRoleArnInput: Locator; + readonly alibabacloudRoleSessionNameInput: Locator; + readonly alibabacloudStaticCredentialsRadio: Locator; + readonly alibabacloudRoleCredentialsRadio: Locator; + // Delete button readonly deleteProviderConfirmationButton: Locator; @@ -290,6 +325,10 @@ export class ProvidersPage extends BasePage { this.ociProviderRadio = page.getByRole("option", { name: /Oracle Cloud Infrastructure/i, }); + // Alibaba Cloud + this.alibabacloudProviderRadio = page.getByRole("option", { + name: /Alibaba Cloud/i, + }); // AWS provider form inputs this.accountIdInput = page.getByRole("textbox", { name: "Account ID" }); @@ -354,6 +393,30 @@ export class ProvidersPage extends BasePage { }); this.ociRegionInput = page.getByRole("textbox", { name: /Region/i }); + // AlibabaCloud provider form inputs + this.alibabacloudAccountIdInput = page.getByRole("textbox", { + name: "Account ID", + }); + this.alibabacloudAccessKeyIdInput = page.getByRole("textbox", { + name: "Access Key ID", + }); + this.alibabacloudAccessKeySecretInput = page.getByRole("textbox", { + name: "Access Key Secret", + }); + this.alibabacloudRoleArnInput = page.getByRole("textbox", { + name: "Role ARN", + }); + this.alibabacloudRoleSessionNameInput = page.getByRole("textbox", { + name: "Role Session Name", + }); + // Radios for selecting AlibabaCloud credentials method + this.alibabacloudStaticCredentialsRadio = page.getByRole("radio", { + name: /Connect via Access Keys/i, + }); + this.alibabacloudRoleCredentialsRadio = page.getByRole("radio", { + name: /Connect assuming RAM Role/i, + }); + // Alias input this.aliasInput = page.getByRole("textbox", { name: "Provider alias (optional)", @@ -857,6 +920,101 @@ export class ProvidersPage extends BasePage { await expect(this.ociRegionInput).toBeVisible(); } + async selectAlibabaCloudProvider(): Promise { + await this.selectProviderRadio(this.alibabacloudProviderRadio); + } + + async fillAlibabaCloudProviderDetails( + data: AlibabaCloudProviderData, + ): Promise { + // Fill the AlibabaCloud provider details + + await this.alibabacloudAccountIdInput.fill(data.accountId); + + if (data.alias) { + await this.aliasInput.fill(data.alias); + } + } + + async selectAlibabaCloudCredentialsType( + type: AlibabaCloudCredentialType, + ): Promise { + // Ensure we are on the add-credentials page where the selector exists + + await expect(this.page).toHaveURL(/\/providers\/add-credentials/); + + if (type === ALIBABACLOUD_CREDENTIAL_OPTIONS.ALIBABACLOUD_CREDENTIALS) { + await this.alibabacloudStaticCredentialsRadio.click({ force: true }); + } else if (type === ALIBABACLOUD_CREDENTIAL_OPTIONS.ALIBABACLOUD_ROLE) { + await this.alibabacloudRoleCredentialsRadio.click({ force: true }); + } else { + throw new Error(`Invalid AlibabaCloud credential type: ${type}`); + } + } + + async fillAlibabaCloudStaticCredentials( + credentials: AlibabaCloudProviderCredential, + ): Promise { + // Fill the AlibabaCloud static credentials form + + if (credentials.accessKeyId) { + await this.alibabacloudAccessKeyIdInput.fill(credentials.accessKeyId); + } + if (credentials.accessKeySecret) { + await this.alibabacloudAccessKeySecretInput.fill( + credentials.accessKeySecret, + ); + } + } + + async fillAlibabaCloudRoleCredentials( + credentials: AlibabaCloudProviderCredential, + ): Promise { + // Fill the AlibabaCloud RAM Role credentials form + + if (credentials.roleArn) { + await this.alibabacloudRoleArnInput.fill(credentials.roleArn); + } + if (credentials.accessKeyId) { + await this.alibabacloudAccessKeyIdInput.fill(credentials.accessKeyId); + } + if (credentials.accessKeySecret) { + await this.alibabacloudAccessKeySecretInput.fill( + credentials.accessKeySecret, + ); + } + if (credentials.roleSessionName) { + await this.alibabacloudRoleSessionNameInput.fill( + credentials.roleSessionName, + ); + } + } + + async verifyAlibabaCloudCredentialsPageLoaded(): Promise { + // Verify the AlibabaCloud credentials page is loaded + + await this.verifyPageHasProwlerTitle(); + await expect(this.alibabacloudStaticCredentialsRadio).toBeVisible(); + await expect(this.alibabacloudRoleCredentialsRadio).toBeVisible(); + } + + async verifyAlibabaCloudStaticCredentialsPageLoaded(): Promise { + // Verify the AlibabaCloud static credentials page is loaded + + await this.verifyPageHasProwlerTitle(); + await expect(this.alibabacloudAccessKeyIdInput).toBeVisible(); + await expect(this.alibabacloudAccessKeySecretInput).toBeVisible(); + } + + async verifyAlibabaCloudRoleCredentialsPageLoaded(): Promise { + // Verify the AlibabaCloud RAM Role credentials page is loaded + + await this.verifyPageHasProwlerTitle(); + await expect(this.alibabacloudRoleArnInput).toBeVisible(); + await expect(this.alibabacloudAccessKeyIdInput).toBeVisible(); + await expect(this.alibabacloudAccessKeySecretInput).toBeVisible(); + } + async verifyPageLoaded(): Promise { // Verify the providers page is loaded @@ -875,6 +1033,7 @@ export class ProvidersPage extends BasePage { await expect(this.m365ProviderRadio).toBeVisible(); await expect(this.kubernetesProviderRadio).toBeVisible(); await expect(this.githubProviderRadio).toBeVisible(); + await expect(this.alibabacloudProviderRadio).toBeVisible(); } async verifyCredentialsPageLoaded(): Promise { diff --git a/ui/tests/providers/providers.md b/ui/tests/providers/providers.md index 67977b61e4..7346628df8 100644 --- a/ui/tests/providers/providers.md +++ b/ui/tests/providers/providers.md @@ -708,3 +708,128 @@ - Provider cleanup performed before each test to ensure clean state - Requires valid OCI account with API Key set up - API Key credential type is automatically used for OCI providers + +--- + +## Test Case: `PROVIDER-E2E-013` - Add AlibabaCloud Provider with Static Credentials + +**Priority:** `critical` + +**Tags:** + +- type → @e2e, @serial +- feature → @providers +- provider → @alibabacloud + +**Description/Objective:** Validates the complete flow of adding a new Alibaba Cloud provider using static credentials (Access Key ID and Access Key Secret) + +**Preconditions:** + +- Admin user authentication required (admin.auth.setup setup) +- Environment variables configured: E2E_ALIBABACLOUD_ACCOUNT_ID, E2E_ALIBABACLOUD_ACCESS_KEY_ID, E2E_ALIBABACLOUD_ACCESS_KEY_SECRET +- Remove any existing provider with the same Account ID before starting the test +- This test must be run serially and never in parallel with other tests, as it requires the Account ID not to be already registered beforehand. + +### Flow Steps: + +1. Navigate to providers page +2. Click "Add Provider" button +3. Select AlibabaCloud provider type +4. Fill provider details (account ID and alias) +5. Verify AlibabaCloud credentials page is loaded +6. Select static credentials type +7. Verify static credentials page is loaded +8. Fill AlibabaCloud credentials (access key ID and access key secret) +9. Launch initial scan +10. Verify redirect to Scans page +11. Verify scheduled scan status in Scans table (provider exists and scan name is "scheduled scan") + +### Expected Result: + +- AlibabaCloud provider successfully added with static credentials +- Initial scan launched successfully +- User redirected to Scans page +- Scheduled scan appears in Scans table with correct provider and scan name + +### Key verification points: + +- Provider page loads correctly +- Connect account page displays AlibabaCloud option +- Provider details form accepts account ID and alias +- Credentials page loads with credential type selection +- Static credentials page loads with access key ID and access key secret fields +- Static credentials are properly filled in the correct fields +- Launch scan page appears +- Successful redirect to Scans page after scan launch +- Provider exists in Scans table (verified by account ID) +- Scan name field contains "scheduled scan" + +### Notes: + +- Test uses environment variables for AlibabaCloud credentials +- Provider cleanup performed before each test to ensure clean state +- Requires valid Alibaba Cloud account with appropriate permissions +- Static credentials must have sufficient permissions for security scanning + +--- + +## Test Case: `PROVIDER-E2E-014` - Add AlibabaCloud Provider with RAM Role Credentials + +**Priority:** `critical` + +**Tags:** + +- type → @e2e, @serial +- feature → @providers +- provider → @alibabacloud + +**Description/Objective:** Validates the complete flow of adding a new Alibaba Cloud provider using RAM Role credentials (Access Key ID, Access Key Secret, and Role ARN) + +**Preconditions:** + +- Admin user authentication required (admin.auth.setup setup) +- Environment variables configured: E2E_ALIBABACLOUD_ACCOUNT_ID, E2E_ALIBABACLOUD_ACCESS_KEY_ID, E2E_ALIBABACLOUD_ACCESS_KEY_SECRET, E2E_ALIBABACLOUD_ROLE_ARN +- Remove any existing provider with the same Account ID before starting the test +- This test must be run serially and never in parallel with other tests, as it requires the Account ID not to be already registered beforehand. + +### Flow Steps: + +1. Navigate to providers page +2. Click "Add Provider" button +3. Select AlibabaCloud provider type +4. Fill provider details (account ID and alias) +5. Verify AlibabaCloud credentials page is loaded +6. Select RAM Role credentials type +7. Verify RAM Role credentials page is loaded +8. Fill AlibabaCloud RAM Role credentials (access key ID, access key secret, and role ARN) +9. Launch initial scan +10. Verify redirect to Scans page +11. Verify scheduled scan status in Scans table (provider exists and scan name is "scheduled scan") + +### Expected Result: + +- AlibabaCloud provider successfully added with RAM Role credentials +- Initial scan launched successfully +- User redirected to Scans page +- Scheduled scan appears in Scans table with correct provider and scan name + +### Key verification points: + +- Provider page loads correctly +- Connect account page displays AlibabaCloud option +- Provider details form accepts account ID and alias +- Credentials page loads with credential type selection +- RAM Role credentials page loads with access key ID, access key secret, and role ARN fields +- RAM Role credentials are properly filled in the correct fields +- Launch scan page appears +- Successful redirect to Scans page after scan launch +- Provider exists in Scans table (verified by account ID) +- Scan name field contains "scheduled scan" + +### Notes: + +- Test uses environment variables for AlibabaCloud RAM Role credentials +- Provider cleanup performed before each test to ensure clean state +- Requires valid Alibaba Cloud account with RAM Role configured +- RAM Role must have sufficient permissions for security scanning +- Role ARN must be properly configured and assumable diff --git a/ui/tests/providers/providers.spec.ts b/ui/tests/providers/providers.spec.ts index 865ee0cc4d..24917d51be 100644 --- a/ui/tests/providers/providers.spec.ts +++ b/ui/tests/providers/providers.spec.ts @@ -22,6 +22,9 @@ import { OCIProviderData, OCIProviderCredential, OCI_CREDENTIAL_OPTIONS, + AlibabaCloudProviderData, + AlibabaCloudProviderCredential, + ALIBABACLOUD_CREDENTIAL_OPTIONS, } from "./providers-page"; import { ScansPage } from "../scans/scans-page"; import fs from "fs"; @@ -1138,4 +1141,190 @@ test.describe("Add Provider", () => { }, ); }); + + test.describe.serial("Add AlibabaCloud Provider", () => { + // Providers page object + let providersPage: ProvidersPage; + let scansPage: ScansPage; + + // Test data from environment variables + const accountId = process.env.E2E_ALIBABACLOUD_ACCOUNT_ID; + const accessKeyId = process.env.E2E_ALIBABACLOUD_ACCESS_KEY_ID; + const accessKeySecret = process.env.E2E_ALIBABACLOUD_ACCESS_KEY_SECRET; + const roleArn = process.env.E2E_ALIBABACLOUD_ROLE_ARN; + + // Validate required environment variable for beforeEach + if (!accountId) { + throw new Error( + "E2E_ALIBABACLOUD_ACCOUNT_ID environment variable is not set", + ); + } + + // Setup before each test + test.beforeEach(async ({ page }) => { + providersPage = new ProvidersPage(page); + // Clean up existing provider to ensure clean test state + await deleteProviderIfExists(providersPage, accountId); + }); + + // Use admin authentication for provider management + test.use({ storageState: "playwright/.auth/admin_user.json" }); + + test( + "should add a new AlibabaCloud provider with static credentials", + { + tag: [ + "@critical", + "@e2e", + "@providers", + "@alibabacloud", + "@serial", + "@PROVIDER-E2E-013", + ], + }, + async ({ page }) => { + // Validate required environment variables + if (!accessKeyId || !accessKeySecret) { + throw new Error( + "E2E_ALIBABACLOUD_ACCESS_KEY_ID and E2E_ALIBABACLOUD_ACCESS_KEY_SECRET environment variables are not set", + ); + } + + // Prepare test data for AlibabaCloud provider + const alibabacloudProviderData: AlibabaCloudProviderData = { + accountId: accountId, + alias: "Test E2E AlibabaCloud Account - Static Credentials", + }; + + // Prepare static credentials + const staticCredentials: AlibabaCloudProviderCredential = { + type: ALIBABACLOUD_CREDENTIAL_OPTIONS.ALIBABACLOUD_CREDENTIALS, + accessKeyId: accessKeyId, + accessKeySecret: accessKeySecret, + }; + + // Navigate to providers page + await providersPage.goto(); + await providersPage.verifyPageLoaded(); + + // Start adding new provider + await providersPage.clickAddProvider(); + await providersPage.verifyConnectAccountPageLoaded(); + + // Select AlibabaCloud provider + await providersPage.selectAlibabaCloudProvider(); + + // Fill provider details + await providersPage.fillAlibabaCloudProviderDetails( + alibabacloudProviderData, + ); + await providersPage.clickNext(); + + // Verify credentials page is loaded + await providersPage.verifyAlibabaCloudCredentialsPageLoaded(); + + // Select static credentials type + await providersPage.selectAlibabaCloudCredentialsType( + ALIBABACLOUD_CREDENTIAL_OPTIONS.ALIBABACLOUD_CREDENTIALS, + ); + + // Verify static credentials page is loaded + await providersPage.verifyAlibabaCloudStaticCredentialsPageLoaded(); + + // Fill static credentials + await providersPage.fillAlibabaCloudStaticCredentials(staticCredentials); + await providersPage.clickNext(); + + // Launch scan + await providersPage.verifyLaunchScanPageLoaded(); + await providersPage.clickNext(); + + // Wait for redirect to scan page + scansPage = new ScansPage(page); + await scansPage.verifyPageLoaded(); + + // Verify scan status is "Scheduled scan" + await scansPage.verifyScheduledScanStatus(accountId); + }, + ); + + test( + "should add a new AlibabaCloud provider with RAM Role credentials", + { + tag: [ + "@critical", + "@e2e", + "@providers", + "@alibabacloud", + "@serial", + "@PROVIDER-E2E-014", + ], + }, + async ({ page }) => { + // Validate required environment variables + if (!accessKeyId || !accessKeySecret || !roleArn) { + throw new Error( + "E2E_ALIBABACLOUD_ACCESS_KEY_ID, E2E_ALIBABACLOUD_ACCESS_KEY_SECRET, and E2E_ALIBABACLOUD_ROLE_ARN environment variables are not set", + ); + } + + // Prepare test data for AlibabaCloud provider + const alibabacloudProviderData: AlibabaCloudProviderData = { + accountId: accountId, + alias: "Test E2E AlibabaCloud Account - RAM Role Credentials", + }; + + // Prepare RAM Role credentials + const roleCredentials: AlibabaCloudProviderCredential = { + type: ALIBABACLOUD_CREDENTIAL_OPTIONS.ALIBABACLOUD_ROLE, + accessKeyId: accessKeyId, + accessKeySecret: accessKeySecret, + roleArn: roleArn, + }; + + // Navigate to providers page + await providersPage.goto(); + await providersPage.verifyPageLoaded(); + + // Start adding new provider + await providersPage.clickAddProvider(); + await providersPage.verifyConnectAccountPageLoaded(); + + // Select AlibabaCloud provider + await providersPage.selectAlibabaCloudProvider(); + + // Fill provider details + await providersPage.fillAlibabaCloudProviderDetails( + alibabacloudProviderData, + ); + await providersPage.clickNext(); + + // Verify credentials page is loaded + await providersPage.verifyAlibabaCloudCredentialsPageLoaded(); + + // Select RAM Role credentials type + await providersPage.selectAlibabaCloudCredentialsType( + ALIBABACLOUD_CREDENTIAL_OPTIONS.ALIBABACLOUD_ROLE, + ); + + // Verify RAM Role credentials page is loaded + await providersPage.verifyAlibabaCloudRoleCredentialsPageLoaded(); + + // Fill RAM Role credentials + await providersPage.fillAlibabaCloudRoleCredentials(roleCredentials); + await providersPage.clickNext(); + + // Launch scan + await providersPage.verifyLaunchScanPageLoaded(); + await providersPage.clickNext(); + + // Wait for redirect to scan page + scansPage = new ScansPage(page); + await scansPage.verifyPageLoaded(); + + // Verify scan status is "Scheduled scan" + await scansPage.verifyScheduledScanStatus(accountId); + }, + ); + }); });