85 lines
3.6 KiB
JavaScript
85 lines
3.6 KiB
JavaScript
const { test, expect } = require('@playwright/test');
|
|
|
|
const PRODUCTION_URL = 'https://colinknapp.com';
|
|
const LOCAL_URL = 'http://localhost:8080';
|
|
|
|
async function getPageUrl(page) {
|
|
try {
|
|
// Try production first
|
|
await page.goto(PRODUCTION_URL, { timeout: 60000 });
|
|
return PRODUCTION_URL;
|
|
} catch (error) {
|
|
console.log('Production site not available, falling back to local');
|
|
await page.goto(LOCAL_URL, { timeout: 60000 });
|
|
return LOCAL_URL;
|
|
}
|
|
}
|
|
|
|
test.describe('Security Headers Tests', () => {
|
|
test('should have all required security headers', async ({ page, request }) => {
|
|
const url = await getPageUrl(page);
|
|
console.log(`Running security header tests against ${url}`);
|
|
|
|
// Get headers directly from the main page
|
|
const response = await request.get(url);
|
|
const headers = response.headers();
|
|
|
|
// Check Content Security Policy
|
|
const csp = headers['content-security-policy'];
|
|
expect(csp).toBeTruthy();
|
|
expect(csp).toContain("default-src 'none'");
|
|
expect(csp).toContain("script-src 'self'");
|
|
expect(csp).toContain("style-src 'self'");
|
|
expect(csp).toContain("img-src 'self' data:");
|
|
expect(csp).toContain("font-src 'self' data:");
|
|
expect(csp).toContain("connect-src 'self'");
|
|
expect(csp).toContain("object-src 'none'");
|
|
expect(csp).toContain("frame-ancestors 'none'");
|
|
expect(csp).toContain("base-uri 'none'");
|
|
expect(csp).toContain("form-action 'none'");
|
|
|
|
// Check other security headers
|
|
expect(headers['x-content-type-options']).toBe('nosniff');
|
|
expect(headers['x-frame-options']).toBe('DENY');
|
|
expect(headers['referrer-policy']).toBe('strict-origin-when-cross-origin');
|
|
});
|
|
|
|
test('should have correct Subresource Integrity (SRI) attributes', async ({ page }) => {
|
|
const url = await getPageUrl(page);
|
|
console.log(`Running SRI tests against ${url}`);
|
|
await page.goto(url, { timeout: 60000 });
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Check stylesheet
|
|
const stylesheet = await page.locator('link[rel="stylesheet"]').first();
|
|
const stylesheetIntegrity = await stylesheet.getAttribute('integrity');
|
|
const stylesheetCrossorigin = await stylesheet.getAttribute('crossorigin');
|
|
expect(stylesheetIntegrity).toBeTruthy();
|
|
expect(stylesheetCrossorigin).toBe('anonymous');
|
|
|
|
// Check script
|
|
const script = await page.locator('script[src]').first();
|
|
const scriptIntegrity = await script.getAttribute('integrity');
|
|
const scriptCrossorigin = await script.getAttribute('crossorigin');
|
|
expect(scriptIntegrity).toBeTruthy();
|
|
expect(scriptCrossorigin).toBe('anonymous');
|
|
});
|
|
|
|
test('should have correct caching headers for static assets', async ({ request }) => {
|
|
const url = await getPageUrl({ goto: async () => {} });
|
|
console.log(`Running caching header tests against ${url}`);
|
|
const baseUrl = url.replace(/\/$/, '');
|
|
|
|
// Check styles.css
|
|
const stylesResponse = await request.get(`${baseUrl}/styles.css`);
|
|
const stylesCacheControl = stylesResponse.headers()['cache-control'];
|
|
expect(stylesCacheControl).toContain('public');
|
|
expect(stylesCacheControl).toContain('max-age=');
|
|
|
|
// Check theme.js
|
|
const scriptResponse = await request.get(`${baseUrl}/theme.js`);
|
|
const scriptCacheControl = scriptResponse.headers()['cache-control'];
|
|
expect(scriptCacheControl).toContain('public');
|
|
expect(scriptCacheControl).toContain('max-age=');
|
|
});
|
|
});
|