130 lines
5.5 KiB
TypeScript
130 lines
5.5 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|\/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.click();
|
|
await nameInput.fill(xssPayload);
|
|
|
|
await phoneInput.click();
|
|
await phoneInput.fill('09121112233');
|
|
|
|
await subjectInput.click();
|
|
await subjectInput.fill('XSS Test Subject');
|
|
|
|
await messageInput.click();
|
|
await messageInput.fill('<script>alert("XSS")</script> متن درخواست تستی');
|
|
|
|
// 3. ارسال فرم تماس
|
|
const submitBtn = page.locator('[data-testid="contact-form-submit-btn"]').first();
|
|
await expect(submitBtn).toBeVisible({ timeout: 10000 });
|
|
await submitBtn.click();
|
|
|
|
// 4. راستیآزمایی نمایش پیام موفقیت بدون فعال شدن هیچ alert/dialog مخرب
|
|
const successBox = page.locator('h4').filter({ hasText: 'پیام شما با موفقیت ثبت شد' });
|
|
await expect(successBox).toBeVisible({ timeout: 15000 });
|
|
|
|
// راستیآزمایی قطعی عدم اجرای alert جاوااسکریپت در مرورگر کلاینت
|
|
expect(dialogFired).toBe(false);
|
|
expect(capturedPayload.phone).toBe('09121112233');
|
|
expect(capturedPayload.name).toBe(xssPayload);
|
|
expect(capturedPayload.phone).toBe('09121112233');
|
|
});
|
|
|
|
test('شبیهسازی محدودیت نرخ و تلاش مجدد با خطای ۴۲۹ (Rate-Limiting & 429 Cooldown Enforcement)', async ({ page }) => {
|
|
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 });
|
|
|
|
// 5. ارسال درخواست مستقیم دوم در بازه Cooldown و راستیآزمایی قطعی پاسخ 429 سرور
|
|
const rateLimitResponse = await page.evaluate(async () => {
|
|
const res = await fetch('/api/auth/send-otp', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ mobile: '09129998877' }),
|
|
});
|
|
return { status: res.status, data: await res.json() };
|
|
});
|
|
|
|
expect(rateLimitResponse.status).toBe(429);
|
|
expect(rateLimitResponse.data.message).toContain('بیش از حد مجاز');
|
|
expect(sendOtpAttempts).toBe(2);
|
|
});
|
|
});
|