Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Playwright to XFD #126

Merged
merged 11 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
name: Playwright Tests
on:
deployment_status:
paths:
- playwright/**
- .github/workflows/playwright.yml
defaults:
run:
working-directory: ./playwright
env:
PW_XFD_2FA_ISSUER: ${{ secrets._PW_XFD_2FA_ISSUER }}
PW_XFD_2FA_SECRET: ${{ secrets.PW_XFD_2FA_SECRET }}
PW_XFD_PASSWORD: ${{ secrets.PW_XFD_PASSWORD }}
PW_XFD_URL: ${{ vars.PW_XFD_URL }}
PW_XFD_USER_ROLE: ${{ vars.PW_XFD_USER_ROLE }}
PW_XFD_USERNAME: ${{ secrets.PW_XFD_USERNAME }}

jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.41.2-jammy
if: github.event.deployment_status.state == 'success'
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
run: npm ci
- name: Run your tests
run: npx playwright test
env:
HOME: /root
6 changes: 6 additions & 0 deletions playwright/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/blob-report/
/playwright-report/
/playwright/.cache/
/test-results/
node_modules/
storageState.json
66 changes: 66 additions & 0 deletions playwright/e2e/global-admin/home.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { test, expect, Page } from '@playwright/test';
import exp from 'constants';

test.describe.configure({ mode: 'serial' });
let page: Page;

test.beforeAll(async ({ browser }) => {
page = await browser.newPage();
await page.goto('/');
});

test.afterAll(async () => {
await page.close();
});
test('home', async () => {
// Expect home page to show Latest Vulnerabilities.
await expect(
page.getByRole('heading', { name: 'Latest Vulnerabilities' })
).toBeVisible();
await expect(
page.getByRole('heading', { name: 'Open Vulnerabilities by Domain' })
).toBeVisible();
await expect(
page.getByRole('heading', { name: 'Most Common Ports' })
).toBeVisible();
await expect(
page.getByRole('heading', { name: 'Most Common Vulnerabilities' })
).toBeVisible();
await expect(
page.getByRole('heading', { name: 'Severity Levels' })
).toBeVisible();
await expect(
page.getByPlaceholder('Search a domain, vuln, port, service, IP')
).toBeVisible();
await expect(page.getByRole('link', { name: 'Inventory' })).toBeVisible();
await page.screenshot({ path: 'test-results/img/global-admin/home.png' });
});

test('Open Vulnerabilities by Domain', async () => {
await page.getByRole('button', { name: 'All' }).click();
await page.screenshot({
path: 'test-results/img/global-admin/open_vuln_all.png'
});
if (
(await page.getByRole('button', { name: 'Medium' }).isDisabled()) == false
) {
await page.getByRole('button', { name: 'Medium' }).click();
await page.screenshot({
path: 'test-results/img/global-admin/open_vuln_medium.png'
});
}
if (
(await page.getByRole('button', { name: 'High' }).isDisabled()) == false
) {
await page.getByRole('button', { name: 'High' }).click();
await page.screenshot({
path: 'test-results/img/global-admin/open_vuln_high.png'
});
}
if ((await page.getByLabel('Go to next page').isDisabled()) == false) {
await page.getByLabel('Go to next page').click();
}
if ((await page.getByLabel('Go to previous page').isDisabled()) == false) {
await page.getByLabel('Go to previous page').click();
}
});
72 changes: 72 additions & 0 deletions playwright/e2e/global-admin/inventory.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { test, expect, chromium, Page } from '@playwright/test';

test.describe.configure({ mode: 'serial' });
let page: Page;

test.beforeAll(async ({ browser }) => {
page = await browser.newPage();
await page.goto('/');
});

test.afterAll(async () => {
await page.close();
});
test('Inventory', async () => {
await page.getByRole('link', { name: 'Inventory' }).click();
await expect(page).toHaveURL('/inventory');
await page.getByRole('button', { name: 'IP(s)' }).click();
await page.getByRole('button', { name: 'Severity' }).click();
await page.getByLabel('Sort by:').first().click();
await page.getByRole('option', { name: 'Domain Name' }).click();
await page.getByLabel('Sort by:').first().click();
await page.getByRole('option', { name: 'IP' }).click();
await page.getByLabel('Sort by:').first().click();
await page.getByRole('option', { name: 'Last Seen' }).click();
await page.getByLabel('Sort by:').first().click();
await page.getByRole('option', { name: 'First Seen' }).click();
await page.screenshot({
path: 'test-results/img/global-admin/inventory.png'
});
});

test('Domains', async () => {
await page.goto('/inventory');
await page.getByRole('link', { name: 'All Domains' }).click();
await expect(page).toHaveURL('/inventory/domains');
if ((await page.getByLabel('Go to next page').isDisabled()) == false) {
await page.getByLabel('Go to next page').click();
}
if ((await page.getByLabel('Go to previous page').isDisabled()) == false) {
await page.getByLabel('Go to previous page').click();
}
await page.screenshot({ path: 'test-results/img/global-admin/domains.png' });
});

test('Domain details', async () => {
await page.goto('/inventory/domains');
await page.getByRole('row').nth(2).getByRole('link').click();
await expect(page).toHaveURL(new RegExp('/inventory/domain/'));
await expect(page.getByText('IP:')).toBeVisible();
await expect(page.getByText('First Seen:')).toBeVisible();
await expect(page.getByText('Last Seen:')).toBeVisible();
await expect(page.getByText('Organization:')).toBeVisible();
await page.screenshot({
path: 'test-results/img/global-admin/domain_details.png'
});
});

test('Domains filter', async () => {
await page.goto('/inventory/domains');
await page.locator('#organizationName').click();
await page.locator('#organizationName').fill('Homeland');
await page.locator('#organizationName').press('Enter');
let rowCount = await page.getByRole('row').count();
for (let it = 2; it < rowCount; it++) {
await expect(
page.getByRole('row').nth(it).getByRole('cell').nth(1)
).toContainText('Homeland');
}
await page.screenshot({
path: 'test-results/img/global-admin/domain_filter.png'
});
});
59 changes: 59 additions & 0 deletions playwright/e2e/global-admin/vulnerabilities.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { test, expect, chromium, Page } from '@playwright/test';

test.describe.configure({ mode: 'serial' });
let page: Page;

test.beforeAll(async ({ browser }) => {
page = await browser.newPage();
await page.goto('/');
});

test.afterAll(async () => {
await page.close();
});

test('Vulnerabilities', async () => {
await page.getByRole('link', { name: 'Inventory' }).click();
await page.getByRole('link', { name: 'All Vulnerabilities' }).click();
await expect(page).toHaveURL('/inventory/vulnerabilities');
if ((await page.getByLabel('Go to next page').isDisabled()) == false) {
await page.getByLabel('Go to next page').click();
}
if ((await page.getByLabel('Go to previous page').isDisabled()) == false) {
await page.getByLabel('Go to previous page').click();
}
await page.screenshot({
path: 'test-results/img/global-admin/vulnerabilities.png'
});
});

test('Vulnerability details NIST', async () => {
await page.goto('/inventory/vulnerabilities');
const newTabPromise = page.waitForEvent('popup');
await page.getByRole('row').nth(2).getByRole('link').nth(0).click();
const newTab = await newTabPromise;
await newTab.waitForLoadState();
await expect(newTab).toHaveURL(
new RegExp('^https://nvd\\.nist\\.gov/vuln/detail/')
);
});

test('Domain details link', async () => {
await page.goto('/inventory/vulnerabilities');
await page.getByRole('row').nth(2).getByRole('link').nth(1).click();
await expect(page).toHaveURL(new RegExp('/inventory/domain/'));
});

test('Vulnerability details', async () => {
await page.goto('/inventory/vulnerabilities');
await page.getByRole('row').nth(2).getByRole('link').nth(2).click();
await expect(page).toHaveURL(new RegExp('/inventory/vulnerability/'));
await expect(page.getByRole('heading', { name: 'Overview' })).toBeVisible();
await expect(
page.getByRole('heading', { name: 'Installed (Known) Products' })
).toBeVisible();
await expect(page.getByRole('heading', { name: 'Provenance' })).toBeVisible();
await expect(
page.getByRole('heading', { name: 'Vulnerability Detection History' })
).toBeVisible();
});
42 changes: 42 additions & 0 deletions playwright/global-setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { chromium, FullConfig, test as setup } from '@playwright/test';
import * as OTPAuth from 'otpauth';
import * as dotenv from 'dotenv';

dotenv.config();

const authFile = './storageState.json';

let totp = new OTPAuth.TOTP({
issuer: process.env.PW_XFD_2FA_ISSUER,
label: 'Crossfeed',
algorithm: 'SHA1',
digits: 6,
period: 30,
secret: process.env.PW_XFD_2FA_SECRET
});

async function globalSetup(config: FullConfig) {
const { baseURL, storageState } = config.projects[0].use;
const browser = await chromium.launch();
const page = await browser.newPage();

//Log in with credentials.
await page.goto(String(process.env.PW_XFD_URL));
await page
.getByPlaceholder('Enter your email address')
.fill(String(process.env.PW_XFD_USERNAME));
await page
.getByPlaceholder('Enter your password')
.fill(String(process.env.PW_XFD_PASSWORD));
await page.getByRole('button', { name: 'Sign in' }).click();
await page
.getByPlaceholder('Enter code from your authenticator app')
.fill(totp.generate());
await page.getByRole('button', { name: 'Confirm' }).click();
//Wait for storageState to write to json file for other tests to use.
await page.waitForTimeout(1000);
await page.context().storageState({ path: authFile });
await page.close();
}

export default globalSetup;
Loading
Loading