HH/e2e/tests/security/input-validation.spec.ts
ismail c5f76b3855
Some checks are pending
Build and Push Docker Image / build (push) Waiting to run
updates
2026-05-11 14:45:30 +03:00

178 lines
7.3 KiB
TypeScript

import { test, expect } from '@playwright/test';
import { RoleAuthHelper, PublicFormHelper } from '../../helpers/helpers';
test.describe('XSS & Input Validation', () => {
test('script tag in complaint form is escaped', async ({ page }) => {
const helper = new PublicFormHelper(page);
await helper.goToPublicLanding();
await helper.selectFormType('complaint');
const xssPayload = '<script>alert("xss")</script>';
await page.fill('#id_complainant_name', `XSS Test ${Date.now()}`);
await page.selectOption('#id_relation_to_patient', 'patient');
await page.fill('#id_mobile_number', '0501234567');
await page.fill('#id_patient_name', xssPayload);
await page.fill('#id_national_id', '1234567890');
await page.fill('#id_incident_date', '2025-01-15');
await page.fill('#id_complaint_details', `Test XSS payload: ${xssPayload}`);
await page.click('#submit_btn');
await page.waitForLoadState('domcontentloaded');
const bodyText = await page.textContent('body');
expect(bodyText).not.toContain('<script>alert("xss")</script>');
expect(bodyText).not.toContain('alert("xss")');
});
test('script tag in inquiry form is escaped', async ({ page }) => {
const helper = new PublicFormHelper(page);
await helper.goToPublicLanding();
await helper.selectFormType('inquiry');
const nameInput = page.locator('input[name="name"]');
await nameInput.waitFor({ state: 'visible', timeout: 15000 }).catch(() => {});
if (!(await nameInput.isVisible().catch(() => false))) {
test.skip();
return;
}
const xssPayload = '<img src=x onerror=alert(1)>';
await page.fill('input[name="name"]', `XSS Inquiry ${Date.now()}`);
await page.fill('input[name="email"]', 'xss@test.com');
await page.fill('input[name="phone"]', '0501234567');
const hospitalSelect = page.locator('select[name="hospital"]');
const opts = hospitalSelect.locator('option');
if (await opts.count() > 1) {
await hospitalSelect.selectOption({ index: 1 });
}
await page.selectOption('select[name="category"]', 'general');
await page.fill('input[name="subject"]', xssPayload);
await page.fill('textarea[name="message"]', `Body: ${xssPayload}`);
await page.click('#inqSubmitBtn');
await page.waitForLoadState('domcontentloaded');
const bodyText = await page.textContent('body');
expect(bodyText).not.toContain('onerror=alert(1)');
});
test('script tag in observation form is escaped', async ({ page }) => {
const helper = new PublicFormHelper(page);
await helper.goToPublicLanding();
await helper.selectFormType('observation');
const xssPayload = '<script>document.cookie</script>';
await page.fill('input[name="title"]', xssPayload);
await page.fill('textarea[name="description"]', `XSS test: ${xssPayload}`);
await page.fill('input[name="reporter_name"]', 'XSS Observer');
await page.fill('input[name="reporter_phone"]', '0501234567');
await page.click('button[type="submit"]');
await page.waitForLoadState('domcontentloaded');
const bodyText = await page.textContent('body');
expect(bodyText).not.toContain('<script>document.cookie</script>');
});
test('SQL injection in complaint list search does not crash', async ({ page }) => {
const auth = new RoleAuthHelper(page);
await auth.login('hospital_admin');
await page.goto('/complaints/');
await page.waitForLoadState('domcontentloaded');
const sqlPayload = "'; DROP TABLE complaints; --";
const searchInput = page.locator('input[name="search"], #search, input[type="search"]');
await searchInput.waitFor({ state: 'visible', timeout: 20000 }).catch(() => {});
if (!(await searchInput.isVisible().catch(() => false))) {
test.skip();
return;
}
await page.fill('input[name="search"]', sqlPayload);
await page.waitForTimeout(500);
const bodyText = await page.textContent('body');
expect(bodyText).not.toContain('Server Error');
expect(bodyText).not.toContain('Traceback');
expect(bodyText).not.toContain('DROP TABLE');
});
test('oversized text input does not crash complaint form', async ({ page }) => {
const helper = new PublicFormHelper(page);
await helper.goToPublicLanding();
await helper.selectFormType('complaint');
const longText = 'A'.repeat(10000);
await page.fill('#id_complainant_name', `Long Text ${Date.now()}`);
await page.selectOption('#id_relation_to_patient', 'patient');
await page.fill('#id_mobile_number', '0501234567');
await page.fill('#id_patient_name', 'Long Patient');
await page.fill('#id_national_id', '1234567890');
await page.fill('#id_incident_date', '2025-01-15');
await page.fill('#id_complaint_details', longText);
await page.click('#submit_btn');
await page.waitForLoadState('domcontentloaded');
const bodyText = await page.textContent('body');
expect(bodyText).not.toContain('Server Error');
expect(bodyText).not.toContain('Traceback');
});
test('Arabic text and emoji in complaint form works', async ({ page }) => {
const helper = new PublicFormHelper(page);
await helper.goToPublicLanding();
await helper.selectFormType('complaint');
await page.fill('#id_complainant_name', `مريض تجريبي ${Date.now()}`);
await page.selectOption('#id_relation_to_patient', 'patient');
await page.fill('#id_mobile_number', '0501234567');
await page.fill('#id_patient_name', 'اسم المريض 😊');
await page.fill('#id_national_id', '1234567890');
await page.fill('#id_incident_date', '2025-01-15');
await page.fill('#id_complaint_details', 'شكوى تجريبية من المريض 🏥');
await page.click('#submit_btn');
await page.waitForLoadState('domcontentloaded');
const bodyText = await page.textContent('body');
expect(bodyText).not.toContain('Server Error');
expect(bodyText).not.toContain('Traceback');
});
test('email injection in patient field is handled safely', async ({ page }) => {
const helper = new PublicFormHelper(page);
await helper.goToPublicLanding();
await helper.selectFormType('complaint');
const emailInjection = 'test@test.com\nBCC:evil@evil.com\nSubject:phishing';
await page.fill('#id_complainant_name', `Email Inject ${Date.now()}`);
await page.selectOption('#id_relation_to_patient', 'patient');
await page.fill('#id_email', emailInjection);
await page.fill('#id_mobile_number', '0501234567');
await page.fill('#id_patient_name', 'Email Inject Patient');
await page.fill('#id_national_id', '1234567890');
await page.fill('#id_incident_date', '2025-01-15');
await page.fill('#id_complaint_details', 'Email injection test');
await page.click('#submit_btn');
await page.waitForLoadState('domcontentloaded');
const bodyText = await page.textContent('body');
expect(bodyText).not.toContain('Server Error');
expect(bodyText).not.toContain('Traceback');
});
test('CSRF token required for POST to complaint create', async ({ request }) => {
const resp = await request.fetch('http://localhost:8000/api/v1/complaints/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: {
title: 'CSRF test',
description: 'testing',
},
});
expect([200, 201, 403, 401, 404, 405]).toContain(resp.status());
});
});