547 lines
17 KiB
JavaScript
547 lines
17 KiB
JavaScript
|
|
const { test, expect } = require('@playwright/test');
|
|
require('dotenv').config();
|
|
const { BASE_URL, absoluteUrl } = require('../fixtures/url');
|
|
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 = absoluteUrl(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-SYS-001",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "System Admin",
|
|
"scenario": "Tenants page loads",
|
|
"type": "UAT",
|
|
"route": "/system-settings/tenants",
|
|
"_kind": "page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": "System Admin"
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-002",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "System Admin",
|
|
"scenario": "Branches page loads",
|
|
"type": "UAT",
|
|
"route": "/system-settings/branches",
|
|
"_kind": "page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": "System Admin"
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-003",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "System Admin",
|
|
"scenario": "Branding page loads",
|
|
"type": "UAT",
|
|
"route": "/system-settings/branding",
|
|
"_kind": "page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": "System Admin"
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-004",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "System Admin",
|
|
"scenario": "Financial years page loads",
|
|
"type": "UAT",
|
|
"route": "/system-settings/financial-years",
|
|
"_kind": "page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": "System Admin"
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-005",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "System Admin",
|
|
"scenario": "RBAC page loads",
|
|
"type": "UAT",
|
|
"route": "/system-settings/rbac",
|
|
"_kind": "page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": "System Admin"
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-006",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "System Admin",
|
|
"scenario": "Roles page loads",
|
|
"type": "UAT",
|
|
"route": "/system-settings/rbac/roles",
|
|
"_kind": "page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": "System Admin"
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-007",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "System Admin",
|
|
"scenario": "Permissions page loads",
|
|
"type": "UAT",
|
|
"route": "/system-settings/rbac/permissions",
|
|
"_kind": "page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": "System Admin"
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-008",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "System Admin",
|
|
"scenario": "Audit logs page loads",
|
|
"type": "UAT",
|
|
"route": "/system-settings/audit-logs",
|
|
"_kind": "page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": "System Admin"
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-009",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "System Admin",
|
|
"scenario": "Invalid tenant context switch safe",
|
|
"type": "UAT",
|
|
"route": "/system-settings/context/tenant/999999",
|
|
"_kind": "safe-page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": "System Admin"
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-010",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "System Admin",
|
|
"scenario": "Invalid branch context switch safe",
|
|
"type": "UAT",
|
|
"route": "/system-settings/context/branch/999999",
|
|
"_kind": "safe-page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": "System Admin"
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-011",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "System Admin",
|
|
"scenario": "Invalid financial year context switch safe",
|
|
"type": "UAT",
|
|
"route": "/system-settings/context/financial-year/INVALID",
|
|
"_kind": "safe-page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": "System Admin"
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-012",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "System Admin",
|
|
"scenario": "FY backup export page/action safe",
|
|
"type": "UAT",
|
|
"route": "/system-settings/financial-years/backup-export",
|
|
"_kind": "safe-page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": "System Admin"
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-013",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Staff",
|
|
"scenario": "Staff cannot access tenants",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/tenants",
|
|
"_kind": "safe-page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": "Staff"
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-014",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Client",
|
|
"scenario": "Client cannot access branches",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/branches",
|
|
"_kind": "safe-page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": "Client"
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-015",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Consultant",
|
|
"scenario": "Consultant cannot access financial years",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/financial-years",
|
|
"_kind": "safe-page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": "Consultant"
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-016",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Partner",
|
|
"scenario": "Partner cannot manage RBAC roles unless permitted",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/rbac/roles",
|
|
"_kind": "safe-page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": "Partner"
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-017",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Manager",
|
|
"scenario": "Manager cannot switch unauthorized tenant",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/context/tenant/999999",
|
|
"_kind": "safe-page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": "Manager"
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-018",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Firm Admin",
|
|
"scenario": "Firm Admin cannot switch to unauthorized branch",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/context/branch/999999",
|
|
"_kind": "safe-page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": "Firm Admin"
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-019",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Anonymous/Attacker",
|
|
"scenario": "Tenant create CSRF-less POST rejected",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/tenants",
|
|
"_kind": "post",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": null
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-020",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Anonymous/Attacker",
|
|
"scenario": "Tenant update invalid ID CSRF-less POST rejected",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/tenants/999999",
|
|
"_kind": "post",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": null
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-021",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Anonymous/Attacker",
|
|
"scenario": "Branch create CSRF-less POST rejected",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/branches",
|
|
"_kind": "post",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": null
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-022",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Anonymous/Attacker",
|
|
"scenario": "Branch update invalid ID CSRF-less POST rejected",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/branches/999999",
|
|
"_kind": "post",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": null
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-023",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Anonymous/Attacker",
|
|
"scenario": "Branding save CSRF-less POST rejected",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/branding",
|
|
"_kind": "post",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": null
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-024",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Anonymous/Attacker",
|
|
"scenario": "Financial year create CSRF-less POST rejected",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/financial-years",
|
|
"_kind": "post",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": null
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-025",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Anonymous/Attacker",
|
|
"scenario": "Make current FY CSRF-less POST rejected",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/financial-years/999999/make-current",
|
|
"_kind": "post",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": null
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-026",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Anonymous/Attacker",
|
|
"scenario": "Lock FY CSRF-less POST rejected",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/financial-years/999999/lock",
|
|
"_kind": "post",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": null
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-027",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Anonymous/Attacker",
|
|
"scenario": "Unlock FY CSRF-less POST rejected",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/financial-years/999999/unlock",
|
|
"_kind": "post",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": null
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-028",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Anonymous/Attacker",
|
|
"scenario": "FY backup export CSRF-less POST rejected",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/financial-years/999999/backup-export",
|
|
"_kind": "post",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": null
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-029",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Anonymous/Attacker",
|
|
"scenario": "FY backup download invalid ID safe",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/financial-years/backups/999999/download",
|
|
"_kind": "safe-page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": null
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-030",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Anonymous/Attacker",
|
|
"scenario": "Role create CSRF-less POST rejected",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/rbac/roles",
|
|
"_kind": "post",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": null
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-031",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Anonymous/Attacker",
|
|
"scenario": "Role update invalid ID CSRF-less POST rejected",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/rbac/roles/999999",
|
|
"_kind": "post",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": null
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-032",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Anonymous/Attacker",
|
|
"scenario": "Permission assign CSRF-less POST rejected",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/rbac/roles/999999/permissions",
|
|
"_kind": "post",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": null
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-033",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Anonymous/Attacker",
|
|
"scenario": "Permission remove CSRF-less POST rejected",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/rbac/roles/999999/permissions/999999/remove",
|
|
"_kind": "post",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": null
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-034",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Anonymous/Attacker",
|
|
"scenario": "Duplicate role invalid POST safe",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/rbac/roles",
|
|
"_kind": "post",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": null
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-035",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Anonymous/Attacker",
|
|
"scenario": "Unauthorized branch context query safe",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/context/branch/999999?tenant_id=999999",
|
|
"_kind": "safe-page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": null
|
|
},
|
|
{
|
|
"variantId": "V251-SYS-036",
|
|
"module": "System Settings / Tenancy / FY",
|
|
"role": "Anonymous/Attacker",
|
|
"scenario": "Audit log export requires authorization",
|
|
"type": "VAPT",
|
|
"route": "/system-settings/audit-logs/export",
|
|
"_kind": "safe-page",
|
|
"_method": "GET",
|
|
"_file": "system-settings-tenancy.spec.js",
|
|
"_login_role": null
|
|
}
|
|
];
|
|
|
|
test.describe("v2.5.1 Additions - System Settings / Tenancy / FY", () => {
|
|
|
|
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') {
|
|
// Some generated VAPT rows accidentally carry _method: 'GET' even when
|
|
// the scenario is explicitly CSRF-less POST. Force POST unless the row
|
|
// intentionally specifies another unsafe verb.
|
|
const unsafeMethod = (!method || method === 'GET') ? 'POST' : method;
|
|
const resp = await apiCall(request, unsafeMethod, c.route);
|
|
await expectApiSafe(resp);
|
|
const safe = [400, 401, 403, 404, 405, 409, 422, 429].includes(resp.status());
|
|
expect(safe, `Unsafe ${unsafeMethod} 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);
|
|
});
|
|
}
|
|
});
|