/** * Security Headers Test * Tests that security headers are properly applied to responses */ import { expect, test } from '@playwright/test'; test.describe('Security Headers', () => { test('should return security headers on homepage', async ({ page }) => { const response = await page.goto('/'); expect(response).not.toBeNull(); const headers = response!.headers(); // Test CSP header expect(headers['content-security-policy']).toBeDefined(); expect(headers['content-security-policy']).toContain("default-src 'self'"); expect(headers['content-security-policy']).toContain('frame-ancestors \'none\''); // Test X-Frame-Options expect(headers['x-frame-options']).toBe('DENY'); // Test X-Content-Type-Options expect(headers['x-content-type-options']).toBe('nosniff'); // Test Referrer-Policy expect(headers['referrer-policy']).toBe('strict-origin-when-cross-origin'); // Test Permissions-Policy expect(headers['permissions-policy']).toBeDefined(); expect(headers['permissions-policy']).toContain('geolocation=()'); }); test('should return CORS headers on API endpoint', async ({ request }) => { const response = await request.get('/api/health.json'); expect(response.ok()).toBeTruthy(); const headers = response.headers(); // Test CORS headers expect(headers['access-control-allow-origin']).toBe('https://workroot.in'); expect(headers['access-control-allow-methods']).toBe('GET'); // Test content type expect(headers['content-type']).toBe('application/json'); // Test cache control expect(headers['cache-control']).toBe('no-cache, no-store, must-revalidate'); // Verify response body contains correct domain const body = await response.json(); expect(body.domain).toBe('workroot.in'); }); test('should apply security headers to all pages', async ({ page }) => { const pages = ['/', '/about', '/services', '/portfolio', '/contact', '/blog']; for (const pagePath of pages) { const response = await page.goto(pagePath); const headers = response!.headers(); expect(headers['x-frame-options']).toBe('DENY'); expect(headers['x-content-type-options']).toBe('nosniff'); expect(headers['content-security-policy']).toBeDefined(); } }); }); test.describe('Domain Security', () => { test('should include workroot.in in structured data', async ({ page }) => { await page.goto('/'); // Check JSON-LD schema includes correct domain const schemaScripts = await page.locator('script[type="application/ld+json"]').all(); expect(schemaScripts.length).toBeGreaterThan(0); for (const script of schemaScripts) { const content = await script.textContent(); if (content && content.includes('workroot')) { expect(content).toContain('workroot.in'); expect(content).not.toContain('workroot.com'); } } }); test('should have canonical URL with workroot.in', async ({ page }) => { await page.goto('/'); const canonical = page.locator('link[rel="canonical"]'); const href = await canonical.getAttribute('href'); expect(href).toContain('workroot.in'); expect(href).not.toContain('workroot.com'); }); test('should have correct domain in Open Graph tags', async ({ page }) => { await page.goto('/'); const ogUrl = page.locator('meta[property="og:url"]'); const content = await ogUrl.getAttribute('content'); expect(content).toContain('workroot.in'); expect(content).not.toContain('workroot.com'); }); }); test.describe('External Resources', () => { test('should only load resources from whitelisted domains', async ({ page }) => { // Track all requests const requests: string[] = []; page.on('request', (request) => { requests.push(request.url()); }); await page.goto('/'); await page.waitForLoadState('networkidle'); // Check that external resources are from allowed domains only const externalRequests = requests.filter(url => !url.startsWith('http://localhost') && !url.startsWith('http://127.0.0.1') && !url.startsWith('http://0.0.0.0') ); const allowedDomains = [ 'workroot.in', 'images.unsplash.com', 'fonts.googleapis.com', 'fonts.gstatic.com', ]; for (const requestUrl of externalRequests) { const url = new URL(requestUrl); const isAllowed = allowedDomains.some(domain => url.hostname.includes(domain)); expect(isAllowed).toBeTruthy(); } }); test('should use crossorigin for external resources', async ({ page }) => { await page.goto('/'); // Check preconnect links have crossorigin attribute const preconnects = page.locator('link[rel="preconnect"]'); const count = await preconnects.count(); for (let i = 0; i < count; i++) { const href = await preconnects.nth(i).getAttribute('href'); if (href && !href.startsWith('/')) { // External resource should have crossorigin const crossorigin = await preconnects.nth(i).getAttribute('crossorigin'); expect(crossorigin).toBeDefined(); } } }); });