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
This commit is contained in:
2025-07-11 21:50:53 +02:00
committed by Kaj Kowalski
parent 3e9e75e854
commit 1eea2cc3e4
121 changed files with 28687 additions and 4895 deletions

View File

@ -0,0 +1,479 @@
#!/usr/bin/env tsx
import {
buildCSP,
validateCSP,
generateNonce,
detectCSPBypass,
testCSPImplementation,
} from "../lib/csp";
interface CSPTestResult {
test: string;
passed: boolean;
details?: string;
recommendations?: string[];
}
class CSPValidator {
private results: CSPTestResult[] = [];
private addResult(
test: string,
passed: boolean,
details?: string,
recommendations?: string[]
) {
this.results.push({ test, passed, details, recommendations });
}
async validateNonceGeneration() {
console.log("🎲 Testing Nonce Generation...");
// Test uniqueness
const nonces = new Set();
const iterations = 1000;
let duplicateFound = false;
for (let i = 0; i < iterations; i++) {
const nonce = generateNonce();
if (nonces.has(nonce)) {
duplicateFound = true;
break;
}
nonces.add(nonce);
}
this.addResult(
"Nonce Uniqueness",
!duplicateFound,
duplicateFound
? "Duplicate nonce detected"
: `${iterations} unique nonces generated`,
duplicateFound
? ["Check entropy source", "Verify crypto.randomBytes"]
: undefined
);
// Test format
const testNonce = generateNonce();
const validFormat = /^[A-Za-z0-9+/]+=*$/.test(testNonce);
this.addResult(
"Nonce Format",
validFormat,
`Generated nonce: ${testNonce}`,
!validFormat ? ["Ensure proper base64 encoding"] : undefined
);
// Test length
const decodedLength = Buffer.from(testNonce, "base64").length;
const correctLength = decodedLength === 16;
this.addResult(
"Nonce Length",
correctLength,
`Decoded length: ${decodedLength} bytes`,
!correctLength ? ["Use 16 bytes (128 bits) for security"] : undefined
);
}
async validateProductionCSP() {
console.log("🛡️ Testing Production CSP...");
const nonce = generateNonce();
const productionCSP = buildCSP({
nonce,
isDevelopment: false,
reportUri: "/api/csp-report",
enforceMode: true,
});
console.log("Production CSP:", productionCSP);
// Validate overall structure
const validation = validateCSP(productionCSP);
this.addResult(
"CSP Validation",
validation.isValid,
`Errors: ${validation.errors.length}, Warnings: ${validation.warnings.length}`,
validation.errors.length > 0 ? validation.errors : undefined
);
// Check for secure directives
const securityTests = [
{
name: "No unsafe-inline in scripts",
test:
!productionCSP.includes("script-src") ||
!productionCSP.match(/script-src[^;]*'unsafe-inline'/),
critical: true,
},
{
name: "No unsafe-eval in scripts",
test: !productionCSP.includes("'unsafe-eval'"),
critical: true,
},
{
name: "Nonce-based script execution",
test: productionCSP.includes(`'nonce-${nonce}'`),
critical: true,
},
{
name: "Strict dynamic enabled",
test: productionCSP.includes("'strict-dynamic'"),
critical: false,
},
{
name: "Object sources blocked",
test: productionCSP.includes("object-src 'none'"),
critical: true,
},
{
name: "Base URI restricted",
test: productionCSP.includes("base-uri 'self'"),
critical: true,
},
{
name: "Frame ancestors blocked",
test: productionCSP.includes("frame-ancestors 'none'"),
critical: true,
},
{
name: "HTTPS upgrade enabled",
test: productionCSP.includes("upgrade-insecure-requests"),
critical: false,
},
{
name: "Report URI configured",
test: productionCSP.includes("report-uri /api/csp-report"),
critical: false,
},
];
for (const secTest of securityTests) {
this.addResult(
secTest.name,
secTest.test,
undefined,
!secTest.test && secTest.critical
? ["This is a critical security requirement"]
: undefined
);
}
}
async validateDevelopmentCSP() {
console.log("🔧 Testing Development CSP...");
const devCSP = buildCSP({
isDevelopment: true,
reportUri: "/api/csp-report",
});
console.log("Development CSP:", devCSP);
// Development should be more permissive but still secure
const devTests = [
{
name: "Allows unsafe-eval for dev tools",
test: devCSP.includes("'unsafe-eval'"),
},
{
name: "Allows unsafe-inline for hot reload",
test: devCSP.includes("'unsafe-inline'"),
},
{
name: "Allows WebSocket connections",
test: devCSP.includes("wss:") || devCSP.includes("ws:"),
},
{
name: "Still blocks objects",
test: devCSP.includes("object-src 'none'"),
},
{
name: "Still restricts base URI",
test: devCSP.includes("base-uri 'self'"),
},
];
for (const devTest of devTests) {
this.addResult(devTest.name, devTest.test);
}
}
async validateBypassDetection() {
console.log("🕵️ Testing Bypass Detection...");
const bypassTests = [
{
name: "Detects javascript: protocol",
content: "window.location.href = 'javascript:alert(1)'",
shouldDetect: true,
},
{
name: "Detects data: HTML injection",
content: "iframe.src = 'data:text/html,<script>alert(1)</script>'",
shouldDetect: true,
},
{
name: "Detects eval injection",
content: "eval('malicious code')",
shouldDetect: true,
},
{
name: "Detects Function constructor",
content: "new Function('alert(1)')()",
shouldDetect: true,
},
{
name: "Detects setTimeout string",
content: "setTimeout('alert(1)', 1000)",
shouldDetect: true,
},
{
name: "Ignores legitimate content",
content: "This is normal text with no dangerous patterns",
shouldDetect: false,
},
{
name: "Ignores safe JavaScript",
content: "function safeFunction() { return 'hello'; }",
shouldDetect: false,
},
];
for (const bypassTest of bypassTests) {
const detection = detectCSPBypass(bypassTest.content);
const passed = detection.isDetected === bypassTest.shouldDetect;
this.addResult(
bypassTest.name,
passed,
`Detected: ${detection.isDetected}, Risk: ${detection.riskLevel}`,
!passed ? ["Review bypass detection patterns"] : undefined
);
}
}
async validateContentSources() {
console.log("🌐 Testing Content Source Restrictions...");
const nonce = generateNonce();
const csp = buildCSP({
nonce,
isDevelopment: false,
reportUri: "/api/csp-report",
});
// Check specific content source restrictions
const sourceTests = [
{
name: "Script sources are restrictive",
test: () => {
const scriptMatch = csp.match(/script-src ([^;]+)/);
if (!scriptMatch) return false;
const sources = scriptMatch[1];
return (
sources.includes("'self'") &&
sources.includes(`'nonce-${nonce}'`) &&
!sources.includes("'unsafe-inline'") &&
!sources.includes("*")
);
},
},
{
name: "Style sources use nonce",
test: () => {
const styleMatch = csp.match(/style-src ([^;]+)/);
if (!styleMatch) return false;
const sources = styleMatch[1];
return (
sources.includes("'self'") && sources.includes(`'nonce-${nonce}'`)
);
},
},
{
name: "Image sources are limited",
test: () => {
const imgMatch = csp.match(/img-src ([^;]+)/);
if (!imgMatch) return false;
const sources = imgMatch[1];
return (
sources.includes("'self'") &&
sources.includes("data:") &&
!sources.includes("*")
);
},
},
{
name: "Connect sources are specific",
test: () => {
const connectMatch = csp.match(/connect-src ([^;]+)/);
if (!connectMatch) return false;
const sources = connectMatch[1];
return (
sources.includes("'self'") &&
sources.includes("https://api.openai.com") &&
!sources.includes("ws:") &&
!sources.includes("wss:")
);
},
},
{
name: "Font sources are restricted",
test: () => {
const fontMatch = csp.match(/font-src ([^;]+)/);
if (!fontMatch) return false;
const sources = fontMatch[1];
return (
sources.includes("'self'") &&
sources.includes("data:") &&
!sources.includes("*")
);
},
},
];
for (const sourceTest of sourceTests) {
this.addResult(
sourceTest.name,
sourceTest.test(),
undefined,
!sourceTest.test()
? ["Review and tighten content source restrictions"]
: undefined
);
}
}
async validateCompatibility() {
console.log("🔄 Testing Framework Compatibility...");
// Test that CSP works with Next.js requirements
const compatibilityTests = [
{
name: "Next.js development compatibility",
test: () => {
const devCSP = buildCSP({ isDevelopment: true });
return devCSP.includes("'unsafe-eval'"); // Required for Next.js dev
},
},
{
name: "TailwindCSS compatibility",
test: () => {
const csp = buildCSP({ isDevelopment: false });
// Should either have nonce or unsafe-inline for styles
return (
csp.includes("'nonce-") ||
csp.includes("style-src 'self' 'unsafe-inline'")
);
},
},
{
name: "JSON-LD support",
test: () => {
const nonce = generateNonce();
const csp = buildCSP({ nonce, isDevelopment: false });
// Should allow nonce-based inline scripts
return csp.includes(`'nonce-${nonce}'`);
},
},
];
for (const compatTest of compatibilityTests) {
this.addResult(
compatTest.name,
compatTest.test(),
undefined,
!compatTest.test() ? ["Ensure framework compatibility"] : undefined
);
}
}
generateReport() {
console.log("\n📊 CSP Validation Report");
console.log("=".repeat(50));
const passed = this.results.filter((r) => r.passed).length;
const failed = this.results.filter((r) => r.passed === false).length;
const critical = this.results.filter(
(r) =>
!r.passed && r.recommendations?.some((rec) => rec.includes("critical"))
).length;
console.log(`\n📈 Summary: ${passed} passed, ${failed} failed`);
if (critical > 0) {
console.log(`⚠️ Critical issues: ${critical}`);
}
console.log("\n📋 Detailed Results:");
for (const result of this.results) {
const status = result.passed ? "✅" : "❌";
console.log(`${status} ${result.test}`);
if (result.details) {
console.log(` ${result.details}`);
}
if (result.recommendations) {
for (const rec of result.recommendations) {
console.log(` 💡 ${rec}`);
}
}
}
// Security score
const securityScore = Math.round((passed / this.results.length) * 100);
console.log(`\n🛡 Security Score: ${securityScore}%`);
if (securityScore >= 90) {
console.log("🎉 Excellent CSP implementation!");
} else if (securityScore >= 80) {
console.log("🔧 Good CSP implementation with room for improvement");
} else if (securityScore >= 70) {
console.log("⚠️ CSP implementation needs attention");
} else {
console.log("🚨 CSP implementation has serious security issues");
}
return {
passed,
failed,
critical,
securityScore,
success: failed === 0 && critical === 0,
};
}
async run() {
console.log("🔒 Enhanced CSP Implementation Validation");
console.log("=".repeat(50));
await this.validateNonceGeneration();
await this.validateProductionCSP();
await this.validateDevelopmentCSP();
await this.validateBypassDetection();
await this.validateContentSources();
await this.validateCompatibility();
return this.generateReport();
}
}
// Run validation if this script is called directly
if (import.meta.url === `file://${process.argv[1]}`) {
const validator = new CSPValidator();
validator
.run()
.then((report) => {
if (!report.success) {
process.exit(1);
}
})
.catch((error) => {
console.error("❌ Validation failed:", error);
process.exit(1);
});
}