Files
livedash-node/scripts/test-security-headers.ts
Kaj Kowalski 1eea2cc3e4 refactor: fix biome linting issues and update project documentation
- Fix 36+ biome linting issues reducing errors/warnings from 227 to 191
- Replace explicit 'any' types with proper TypeScript interfaces
- Fix React hooks dependencies and useCallback patterns
- Resolve unused variables and parameter assignment issues
- Improve accessibility with proper label associations
- Add comprehensive API documentation for admin and security features
- Update README.md with accurate PostgreSQL setup and current tech stack
- Create complete documentation for audit logging, CSP monitoring, and batch processing
- Fix outdated project information and missing developer workflows
2025-07-12 00:28:09 +02:00

259 lines
7.2 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env tsx
/**
* Security Headers Testing Script
*
* This script tests HTTP security headers on a running Next.js server.
* Run this against your development or production server to verify
* that security headers are properly configured.
*
* Usage:
* pnpm exec tsx scripts/test-security-headers.ts [url]
*
* Examples:
* pnpm exec tsx scripts/test-security-headers.ts http://localhost:3000
* pnpm exec tsx scripts/test-security-headers.ts https://your-domain.com
*/
interface SecurityHeader {
name: string;
expectedValue?: string;
description: string;
critical: boolean;
}
const SECURITY_HEADERS: SecurityHeader[] = [
{
name: "X-Content-Type-Options",
expectedValue: "nosniff",
description: "Prevents MIME type sniffing attacks",
critical: true,
},
{
name: "X-Frame-Options",
expectedValue: "DENY",
description: "Prevents clickjacking attacks",
critical: true,
},
{
name: "X-XSS-Protection",
expectedValue: "1; mode=block",
description: "Enables XSS protection in legacy browsers",
critical: false,
},
{
name: "Referrer-Policy",
expectedValue: "strict-origin-when-cross-origin",
description: "Controls referrer information sent with requests",
critical: false,
},
{
name: "X-DNS-Prefetch-Control",
expectedValue: "off",
description: "Prevents DNS rebinding attacks",
critical: false,
},
{
name: "Content-Security-Policy",
description: "Prevents code injection attacks",
critical: true,
},
{
name: "Permissions-Policy",
description: "Controls browser feature access",
critical: false,
},
{
name: "Strict-Transport-Security",
description: "Enforces HTTPS (production only)",
critical: false,
},
];
const CSP_DIRECTIVES = [
"default-src 'self'",
"script-src 'self' 'unsafe-eval' 'unsafe-inline'",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https:",
"font-src 'self' data:",
"connect-src 'self' https:",
"frame-ancestors 'none'",
"base-uri 'self'",
"form-action 'self'",
"object-src 'none'",
"upgrade-insecure-requests",
];
const PERMISSIONS_POLICIES = [
"camera=()",
"microphone=()",
"geolocation=()",
"interest-cohort=()",
"browsing-topics=()",
];
async function testSecurityHeaders(url: string): Promise<void> {
console.log(`🔍 Testing security headers for: ${url}\n`);
try {
const response = await fetch(url, {
method: "HEAD", // Use HEAD to avoid downloading the full response body
});
console.log(
`📊 Response Status: ${response.status} ${response.statusText}\n`
);
let criticalMissing = 0;
let warningCount = 0;
for (const header of SECURITY_HEADERS) {
const value = response.headers.get(header.name);
if (!value) {
const status = header.critical ? "❌ CRITICAL" : "⚠️ WARNING";
console.log(`${status} Missing: ${header.name}`);
console.log(` Description: ${header.description}\n`);
if (header.critical) criticalMissing++;
else warningCount++;
continue;
}
if (header.expectedValue && value !== header.expectedValue) {
const status = header.critical ? "❌ CRITICAL" : "⚠️ WARNING";
console.log(`${status} Incorrect: ${header.name}`);
console.log(` Expected: ${header.expectedValue}`);
console.log(` Actual: ${value}`);
console.log(` Description: ${header.description}\n`);
if (header.critical) criticalMissing++;
else warningCount++;
continue;
}
console.log(`✅ OK: ${header.name}`);
console.log(` Value: ${value}`);
console.log(` Description: ${header.description}\n`);
}
// Detailed CSP analysis
const csp = response.headers.get("Content-Security-Policy");
if (csp) {
console.log("🔒 Content Security Policy Analysis:");
let cspIssues = 0;
for (const directive of CSP_DIRECTIVES) {
if (csp.includes(directive)) {
console.log(`${directive}`);
} else {
console.log(` ❌ Missing: ${directive}`);
cspIssues++;
}
}
if (cspIssues > 0) {
console.log(
` ⚠️ ${cspIssues} CSP directive(s) missing or incorrect\n`
);
warningCount += cspIssues;
} else {
console.log(` ✅ All CSP directives present\n`);
}
}
// Detailed Permissions Policy analysis
const permissionsPolicy = response.headers.get("Permissions-Policy");
if (permissionsPolicy) {
console.log("🔐 Permissions Policy Analysis:");
let policyIssues = 0;
for (const policy of PERMISSIONS_POLICIES) {
if (permissionsPolicy.includes(policy)) {
console.log(`${policy}`);
} else {
console.log(` ❌ Missing: ${policy}`);
policyIssues++;
}
}
if (policyIssues > 0) {
console.log(` ⚠️ ${policyIssues} permission policy(ies) missing\n`);
warningCount += policyIssues;
} else {
console.log(` ✅ All permission policies present\n`);
}
}
// HSTS environment check
const hsts = response.headers.get("Strict-Transport-Security");
const isHttps = url.startsWith("https://");
if (isHttps && !hsts) {
console.log("⚠️ WARNING: HTTPS site missing HSTS header");
console.log(
" Consider adding Strict-Transport-Security for production\n"
);
warningCount++;
} else if (hsts && !isHttps) {
console.log(
" INFO: HSTS header present on HTTP site (will be ignored by browsers)\n"
);
}
// Summary
console.log("=".repeat(60));
console.log("📋 SECURITY HEADERS SUMMARY");
console.log("=".repeat(60));
if (criticalMissing === 0 && warningCount === 0) {
console.log(
"🎉 EXCELLENT: All security headers are properly configured!"
);
} else if (criticalMissing === 0) {
console.log(`✅ GOOD: No critical issues found`);
console.log(
`⚠️ ${warningCount} warning(s) - consider addressing these for optimal security`
);
} else {
console.log(`❌ ISSUES FOUND:`);
console.log(` Critical: ${criticalMissing}`);
console.log(` Warnings: ${warningCount}`);
console.log(
`\n🔧 Please address critical issues before deploying to production`
);
}
// Additional recommendations
console.log("\n💡 ADDITIONAL RECOMMENDATIONS:");
console.log(
"• Regularly test headers with online tools like securityheaders.com"
);
console.log("• Monitor CSP violations in production to fine-tune policies");
console.log(
"• Consider implementing HSTS preloading for production domains"
);
console.log("• Review and update security headers based on new threats");
} catch (error) {
console.error(`❌ Error testing headers: ${error}`);
process.exit(1);
}
}
// Main execution
async function main() {
const url = process.argv[2] || "http://localhost:3000";
console.log("🛡️ Security Headers Testing Tool");
console.log("=".repeat(60));
await testSecurityHeaders(url);
}
if (require.main === module) {
main().catch((error) => {
console.error("Script failed:", error);
process.exit(1);
});
}