canina/tests/e2e/security/xss-ratelimit.spec.ts

111 lines
4.8 KiB
TypeScript

import { test, expect } from '../../fixtures';
test.describe('سناریوی اعتبارسنجی امنیتی XSS و محدودیت نرخ پیامک (Security XSS & Rate-Limiting)', () => {
test('تزریق اسکریپت مخرب در فیلدهای فرم تماس و راستی‌آزمایی عدم اجرای XSS و پاکسازی داده‌ها', async ({ page }) => {
let capturedPayload: Record<string, string> = {};
await page.route('**/api/contact*', async (route) => {
if (route.request().method() === 'POST') {
capturedPayload = route.request().postDataJSON?.() || {};
await route.fulfill({
status: 201,
contentType: 'application/json',
body: JSON.stringify({
success: true,
message: 'پیام شما با موفقیت ثبت شد',
}),
});
} else {
await route.continue();
}
});
let dialogFired = false;
page.on('dialog', async (dialog) => {
dialogFired = true;
await dialog.dismiss();
});
// 1. ورود به صفحه تماس با ما
await page.goto('/contact', { waitUntil: 'domcontentloaded' });
await expect(page).toHaveURL(/\/contact/);
// 2. پر کردن فیلدها با پی‌لود خطرناک XSS
const xssPayload = '<img src=x onerror=alert("XSS_ATTACK_CANINA") />';
const nameInput = page.locator('#contact-name');
const phoneInput = page.locator('#contact-phone');
const subjectInput = page.locator('#contact-subject');
const messageInput = page.locator('#contact-message');
await expect(nameInput).toBeVisible({ timeout: 10000 });
await nameInput.fill(xssPayload);
await phoneInput.fill('09121112233');
await subjectInput.fill('XSS Test Subject');
await messageInput.fill('<script>alert("XSS")</script> متن درخواست تستی');
// 3. ارسال فرم تماس
const submitBtn = page.locator('button[type="submit"]').filter({ hasText: /ارسال پیام/i }).first();
await expect(submitBtn).toBeVisible();
await submitBtn.click();
// 4. راستی‌آزمایی نمایش پیام موفقیت بدون فعال شدن هیچ alert/dialog مخرب
const successBox = page.locator('h4').filter({ hasText: 'پیام شما با موفقیت ثبت شد' });
await expect(successBox).toBeVisible({ timeout: 10000 });
// راستی‌آزمایی قطعی عدم اجرای alert جاوااسکریپت در مرورگر
expect(dialogFired).toBe(false);
// راستی‌آزمایی ارسال امن داده‌ها
expect(capturedPayload.name).toBe(xssPayload);
expect(capturedPayload.phone).toBe('09121112233');
});
test('شبیه‌سازی محدودیت نرخ و تایمر معکوس OTP (Rate-Limiting & Cooldown Timer)', async ({ page }) => {
// Intercept send-otp to return 429 when rate limited
let sendOtpAttempts = 0;
await page.route('**/api/auth/send-otp*', async (route) => {
sendOtpAttempts++;
if (sendOtpAttempts === 1) {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true, code: '12345' }),
});
} else {
await route.fulfill({
status: 429,
contentType: 'application/json',
body: JSON.stringify({
success: false,
message: 'تعداد درخواست‌های شما بیش از حد مجاز است. لطفاً ۲ دقیقه صبر کنید.',
}),
});
}
});
// 1. باز کردن مودال ورود با OTP
await page.goto('/', { waitUntil: 'domcontentloaded' });
const headerLoginBtn = page.locator('[data-testid="header-login-btn"]').first();
await expect(headerLoginBtn).toBeVisible({ timeout: 15000 });
await headerLoginBtn.click();
// 2. ورود با کد پیامکی (OTP)
const otpOptionBtn = page.locator('button').filter({ hasText: /ورود با کد پیامکی \(OTP\)/i }).first();
await expect(otpOptionBtn).toBeVisible({ timeout: 10000 });
await otpOptionBtn.click();
// 3. وارد کردن شماره موبایل و ارسال اولین OTP
const phoneInput = page.locator('input[type="tel"]').first();
await expect(phoneInput).toBeVisible({ timeout: 10000 });
await phoneInput.fill('09129998877');
const sendOtpBtn = page.locator('button[type="submit"]').filter({ hasText: /ارسال کد تایید/i }).first();
await expect(sendOtpBtn).toBeVisible();
await sendOtpBtn.click();
// 4. راستی‌آزمایی فعال بودن متن تایمر معکوس (Cooldown Timer) و عدم امکان کلیک زودهنگام
const timerText = page.locator('span').filter({ hasText: /ارسال مجدد کد تا/i }).first();
await expect(timerText).toBeVisible({ timeout: 10000 });
});
});