Upgrade Playwright suite to v2.5.1 SQLite deep testing
This commit is contained in:
@@ -0,0 +1,385 @@
|
||||
|
||||
const { test, expect } = require('@playwright/test');
|
||||
require('dotenv').config();
|
||||
const { login } = require('../fixtures/auth');
|
||||
const { expectNoBackendError, readBody, blockedOrNotFound } = require('../fixtures/v204-helpers');
|
||||
const { expectBlockedOrSafe } = require('../fixtures/assertions');
|
||||
|
||||
async function safeGoto(page, route) {
|
||||
const resp = await page.goto(route).catch(() => null);
|
||||
await page.waitForLoadState('domcontentloaded').catch(() => {});
|
||||
return resp;
|
||||
}
|
||||
|
||||
async function expectRouteAvailable(page, resp) {
|
||||
await expectNoBackendError(page);
|
||||
if (resp) {
|
||||
expect(resp.status(), `Expected route to exist but got ${resp.status()} at ${page.url()}`).not.toBe(404);
|
||||
expect(resp.status()).toBeLessThan(500);
|
||||
}
|
||||
}
|
||||
|
||||
async function expectNoSecrets(page) {
|
||||
const body = await readBody(page);
|
||||
expect(body).not.toMatch(/Password@123|Pass@123|smtp_password|IMAP_PASSWORD|SMTP_PASSWORD|otp\s*[:=]\s*\d{4,8}|reset_token|access_token|refresh_token/i);
|
||||
}
|
||||
|
||||
async function expectPublicNoSensitive(page) {
|
||||
await expectNoBackendError(page);
|
||||
const body = await readBody(page);
|
||||
expect(body).not.toMatch(/tenant_id|branch_id|internal note|audit log|smtp|password|secret|token/i);
|
||||
}
|
||||
|
||||
async function apiCall(request, method, route) {
|
||||
const url = `${process.env.BASE_URL}${route}`;
|
||||
const opts = { data: { csrf_token: '', test_payload: 'uat-vapt' }, headers: { 'Content-Type': 'application/json' } };
|
||||
if (method === 'GET') return await request.get(url).catch(() => null);
|
||||
if (method === 'POST') return await request.post(url, opts).catch(() => null);
|
||||
if (method === 'PUT') return await request.put(url, opts).catch(() => null);
|
||||
if (method === 'DELETE') return await request.delete(url).catch(() => null);
|
||||
if (method === 'OPTIONS') return await request.fetch(url, { method: 'OPTIONS' }).catch(() => null);
|
||||
return await request.fetch(url, { method }).catch(() => null);
|
||||
}
|
||||
|
||||
async function expectApiSafe(resp) {
|
||||
expect(resp, 'API response should be available').toBeTruthy();
|
||||
expect(resp.status()).toBeLessThan(500);
|
||||
const text = await resp.text().catch(() => '');
|
||||
expect(text).not.toMatch(/Traceback|Exception in ASGI application|OperationalError|ProgrammingError|AttributeError|UndefinedError|Password@123|Pass@123/i);
|
||||
}
|
||||
|
||||
|
||||
const cases = [
|
||||
{
|
||||
"variantId": "V251-NCASE-001",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Firm Admin",
|
||||
"scenario": "Notice cases list loads",
|
||||
"type": "UAT",
|
||||
"route": "/notice-cases",
|
||||
"_kind": "page",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": "Firm Admin"
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-002",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Firm Admin",
|
||||
"scenario": "Notice case new page loads",
|
||||
"type": "UAT",
|
||||
"route": "/notice-cases/new",
|
||||
"_kind": "page",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": "Firm Admin"
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-003",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Firm Admin",
|
||||
"scenario": "Invalid case detail safe",
|
||||
"type": "VAPT",
|
||||
"route": "/notice-cases/999999",
|
||||
"_kind": "safe-page",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": "Firm Admin"
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-004",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Client",
|
||||
"scenario": "Client cannot access internal case list",
|
||||
"type": "VAPT",
|
||||
"route": "/notice-cases",
|
||||
"_kind": "safe-page",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": "Client"
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-005",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Consultant",
|
||||
"scenario": "Consultant cannot access internal case list",
|
||||
"type": "VAPT",
|
||||
"route": "/notice-cases",
|
||||
"_kind": "safe-page",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": "Consultant"
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-006",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Staff",
|
||||
"scenario": "Staff case list access safe",
|
||||
"type": "UAT",
|
||||
"route": "/notice-cases",
|
||||
"_kind": "page",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": "Staff"
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-007",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Firm Admin",
|
||||
"scenario": "Invalid case events safe",
|
||||
"type": "VAPT",
|
||||
"route": "/notice-cases/999999/events",
|
||||
"_kind": "safe-page",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": "Firm Admin"
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-008",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Firm Admin",
|
||||
"scenario": "Invalid case hearing safe",
|
||||
"type": "VAPT",
|
||||
"route": "/notice-cases/999999/hearings",
|
||||
"_kind": "safe-page",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": "Firm Admin"
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-009",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Firm Admin",
|
||||
"scenario": "Invalid case order safe",
|
||||
"type": "VAPT",
|
||||
"route": "/notice-cases/999999/orders",
|
||||
"_kind": "safe-page",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": "Firm Admin"
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-010",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Anonymous/Attacker",
|
||||
"scenario": "Create notice case CSRF-less POST rejected",
|
||||
"type": "VAPT",
|
||||
"route": "/notice-cases/new",
|
||||
"_kind": "post",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": null
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-011",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Anonymous/Attacker",
|
||||
"scenario": "Notice date/hearing date invalid POST safe",
|
||||
"type": "VAPT",
|
||||
"route": "/notice-cases/999999/hearings",
|
||||
"_kind": "post",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": null
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-012",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Anonymous/Attacker",
|
||||
"scenario": "Order date before hearing invalid POST safe",
|
||||
"type": "VAPT",
|
||||
"route": "/notice-cases/999999/orders",
|
||||
"_kind": "post",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": null
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-013",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Anonymous/Attacker",
|
||||
"scenario": "Invalid case status transition rejected",
|
||||
"type": "VAPT",
|
||||
"route": "/notice-cases/999999/status",
|
||||
"_kind": "post",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": null
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-014",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Anonymous/Attacker",
|
||||
"scenario": "Case document upload invalid ID rejected",
|
||||
"type": "VAPT",
|
||||
"route": "/notice-cases/999999/documents/upload",
|
||||
"_kind": "post",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": null
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-015",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Anonymous/Attacker",
|
||||
"scenario": "Cross-case document IDOR safe",
|
||||
"type": "VAPT",
|
||||
"route": "/notice-cases/999999/documents/999999/download",
|
||||
"_kind": "safe-page",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": null
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-016",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Anonymous/Attacker",
|
||||
"scenario": "Client-visible document invalid route safe",
|
||||
"type": "VAPT",
|
||||
"route": "/client/notice-cases/999999/documents/999999/download",
|
||||
"_kind": "safe-page",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": null
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-017",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Anonymous/Attacker",
|
||||
"scenario": "Internal note hidden from client route safe",
|
||||
"type": "VAPT",
|
||||
"route": "/client/notice-cases/999999/notes",
|
||||
"_kind": "safe-page",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": null
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-018",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Anonymous/Attacker",
|
||||
"scenario": "Case deadline reminder invalid ID safe",
|
||||
"type": "VAPT",
|
||||
"route": "/notice-cases/999999/reminders",
|
||||
"_kind": "safe-page",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": null
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-019",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Anonymous/Attacker",
|
||||
"scenario": "Case assignment CSRF-less POST rejected",
|
||||
"type": "VAPT",
|
||||
"route": "/notice-cases/999999/assign",
|
||||
"_kind": "post",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": null
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-020",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Anonymous/Attacker",
|
||||
"scenario": "Case event delete CSRF-less POST rejected",
|
||||
"type": "VAPT",
|
||||
"route": "/notice-cases/events/999999/delete",
|
||||
"_kind": "post",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": null
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-021",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Anonymous/Attacker",
|
||||
"scenario": "Case hearing delete CSRF-less POST rejected",
|
||||
"type": "VAPT",
|
||||
"route": "/notice-cases/hearings/999999/delete",
|
||||
"_kind": "post",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": null
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-022",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Anonymous/Attacker",
|
||||
"scenario": "Case order delete CSRF-less POST rejected",
|
||||
"type": "VAPT",
|
||||
"route": "/notice-cases/orders/999999/delete",
|
||||
"_kind": "post",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": null
|
||||
},
|
||||
{
|
||||
"variantId": "V251-NCASE-023",
|
||||
"module": "Notice Case Business Rules",
|
||||
"role": "Anonymous/Attacker",
|
||||
"scenario": "Cross-tenant notice case IDOR safe",
|
||||
"type": "VAPT",
|
||||
"route": "/notice-cases/999999?tenant_id=999999",
|
||||
"_kind": "safe-page",
|
||||
"_method": "GET",
|
||||
"_file": "notice-case-business-rules.spec.js",
|
||||
"_login_role": null
|
||||
}
|
||||
];
|
||||
|
||||
test.describe("v2.5.1 Additions - Notice Case Business Rules", () => {
|
||||
|
||||
for (const c of cases) {
|
||||
test(`[${c.variantId}] ${c.scenario}`, async ({ page, request }) => {
|
||||
const role = c._login_role || c.role;
|
||||
const kind = c._kind;
|
||||
const method = c._method || 'GET';
|
||||
|
||||
if (kind === 'api') {
|
||||
const resp = await apiCall(request, method, c.route);
|
||||
await expectApiSafe(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (kind === 'post') {
|
||||
const resp = await apiCall(request, method || 'POST', c.route);
|
||||
await expectApiSafe(resp);
|
||||
const safe = [400, 401, 403, 404, 405, 409, 422, 429].includes(resp.status());
|
||||
expect(safe, `Unsafe POST status ${resp.status()} for ${c.route}`).toBeTruthy();
|
||||
return;
|
||||
}
|
||||
|
||||
if (role && role !== 'Public' && !/Anonymous|Attacker|API/.test(role)) {
|
||||
await login(page, role);
|
||||
}
|
||||
|
||||
const resp = await safeGoto(page, c.route);
|
||||
|
||||
if (kind === 'page') {
|
||||
await expectRouteAvailable(page, resp);
|
||||
if (/password|secret|token|otp/i.test(c.scenario)) await expectNoSecrets(page);
|
||||
return;
|
||||
}
|
||||
|
||||
if (kind === 'public') {
|
||||
await expectRouteAvailable(page, resp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (kind === 'public-no-sensitive') {
|
||||
await expectPublicNoSensitive(page);
|
||||
return;
|
||||
}
|
||||
|
||||
if (kind === 'no-secret') {
|
||||
await expectRouteAvailable(page, resp);
|
||||
await expectNoSecrets(page);
|
||||
return;
|
||||
}
|
||||
|
||||
await expectNoBackendError(page);
|
||||
await expectBlockedOrSafe(page, resp);
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user