Automated CSP Testing Frameworks
Automated CSP Testing Frameworks
Implementing automated testing ensures CSP policies remain effective as applications evolve:
// CSP Testing Framework
class CSPTestFramework {
constructor(baseUrl) {
this.baseUrl = baseUrl;
this.testResults = [];
}
async runTestSuite() {
const tests = [
this.testHeaderPresence,
this.testPolicyStrength,
this.testResourceLoading,
this.testViolationReporting,
this.testBrowserCompatibility,
this.testPerformanceImpact
];
for (const test of tests) {
try {
const result = await test.call(this);
this.testResults.push(result);
} catch (error) {
this.testResults.push({
name: test.name,
passed: false,
error: error.message
});
}
}
return this.generateReport();
}
async testHeaderPresence() {
const response = await fetch(this.baseUrl);
const cspHeader = response.headers.get('Content-Security-Policy');
const reportOnlyHeader = response.headers.get('Content-Security-Policy-Report-Only');
return {
name: 'CSP Header Presence',
passed: !!(cspHeader || reportOnlyHeader),
details: {
enforcing: !!cspHeader,
reportOnly: !!reportOnlyHeader,
policy: cspHeader || reportOnlyHeader
}
};
}
async testPolicyStrength() {
const response = await fetch(this.baseUrl);
const policy = response.headers.get('Content-Security-Policy') || '';
const weaknesses = [];
if (policy.includes("'unsafe-inline'")) {
weaknesses.push("Uses 'unsafe-inline'");
}
if (policy.includes("'unsafe-eval'")) {
weaknesses.push("Uses 'unsafe-eval'");
}
if (policy.includes('*')) {
weaknesses.push('Uses wildcard sources');
}
if (!policy.includes('default-src')) {
weaknesses.push('Missing default-src directive');
}
return {
name: 'Policy Strength Analysis',
passed: weaknesses.length === 0,
details: {
weaknesses,
score: Math.max(0, 100 - (weaknesses.length * 25))
}
};
}
async testResourceLoading() {
const page = await this.launchBrowser();
const violations = [];
page.on('console', msg => {
if (msg.type() === 'error' && msg.text().includes('Content Security Policy')) {
violations.push(msg.text());
}
});
await page.goto(this.baseUrl);
await page.waitForTimeout(3000);
// Test specific resources
const resources = await page.evaluate(() => {
return {
scripts: Array.from(document.scripts).map(s => s.src).filter(Boolean),
styles: Array.from(document.styleSheets).map(s => s.href).filter(Boolean),
images: Array.from(document.images).map(i => i.src).filter(Boolean)
};
});
await page.close();
return {
name: 'Resource Loading Test',
passed: violations.length === 0,
details: {
violations,
loadedResources: resources
}
};
}
async testViolationReporting() {
// Set up a mock report endpoint
const reports = [];
const reportServer = this.createReportServer(reports);
// Test with intentional violation
const response = await fetch(this.baseUrl, {
headers: {
'X-Test-CSP-Violation': 'true'
}
});
// Wait for report
await new Promise(resolve => setTimeout(resolve, 2000));
reportServer.close();
return {
name: 'Violation Reporting Test',
passed: reports.length > 0,
details: {
reportsReceived: reports.length,
reportContent: reports[0]
}
};
}
createReportServer(reports) {
const express = require('express');
const app = express();
app.post('/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
reports.push(req.body);
res.status(204).end();
});
return app.listen(3001);
}
}
// Usage
const tester = new CSPTestFramework('https://example.com');
tester.runTestSuite().then(report => {
console.log('CSP Test Report:', report);
});