mirror of
https://github.com/kjanat/livedash-node.git
synced 2026-01-16 14:52:08 +01:00
- 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
179 lines
6.4 KiB
TypeScript
179 lines
6.4 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
|
|
describe("Security Headers Configuration", () => {
|
|
describe("Next.js Config Validation", () => {
|
|
it("should have valid security headers configuration", async () => {
|
|
// Import the Next.js config
|
|
const nextConfig = await import("../../next.config.js");
|
|
|
|
expect(nextConfig.default).toBeDefined();
|
|
expect(nextConfig.default.headers).toBeDefined();
|
|
expect(typeof nextConfig.default.headers).toBe("function");
|
|
});
|
|
|
|
it("should generate expected headers structure", async () => {
|
|
const nextConfig = await import("../../next.config.js");
|
|
const headers = await nextConfig.default.headers();
|
|
|
|
expect(Array.isArray(headers)).toBe(true);
|
|
expect(headers.length).toBeGreaterThan(0);
|
|
|
|
// Find the main security headers configuration
|
|
const securityConfig = headers.find(
|
|
(h) => h.source === "/(.*)" && h.headers.length > 1
|
|
);
|
|
expect(securityConfig).toBeDefined();
|
|
|
|
if (securityConfig) {
|
|
const headerNames = securityConfig.headers.map((h) => h.key);
|
|
|
|
// Check required security headers are present
|
|
expect(headerNames).toContain("X-Content-Type-Options");
|
|
expect(headerNames).toContain("X-Frame-Options");
|
|
expect(headerNames).toContain("X-XSS-Protection");
|
|
expect(headerNames).toContain("Referrer-Policy");
|
|
expect(headerNames).toContain("Content-Security-Policy");
|
|
expect(headerNames).toContain("Permissions-Policy");
|
|
expect(headerNames).toContain("X-DNS-Prefetch-Control");
|
|
}
|
|
});
|
|
|
|
it("should have correct security header values", async () => {
|
|
const nextConfig = await import("../../next.config.js");
|
|
const headers = await nextConfig.default.headers();
|
|
|
|
const securityConfig = headers.find(
|
|
(h) => h.source === "/(.*)" && h.headers.length > 1
|
|
);
|
|
|
|
if (securityConfig) {
|
|
const headerMap = new Map(
|
|
securityConfig.headers.map((h) => [h.key, h.value])
|
|
);
|
|
|
|
expect(headerMap.get("X-Content-Type-Options")).toBe("nosniff");
|
|
expect(headerMap.get("X-Frame-Options")).toBe("DENY");
|
|
expect(headerMap.get("X-XSS-Protection")).toBe("1; mode=block");
|
|
expect(headerMap.get("Referrer-Policy")).toBe(
|
|
"strict-origin-when-cross-origin"
|
|
);
|
|
expect(headerMap.get("X-DNS-Prefetch-Control")).toBe("off");
|
|
|
|
// CSP should contain essential directives
|
|
const csp = headerMap.get("Content-Security-Policy");
|
|
expect(csp).toContain("default-src 'self'");
|
|
expect(csp).toContain("frame-ancestors 'none'");
|
|
expect(csp).toContain("object-src 'none'");
|
|
|
|
// Permissions Policy should restrict dangerous features
|
|
const permissions = headerMap.get("Permissions-Policy");
|
|
expect(permissions).toContain("camera=()");
|
|
expect(permissions).toContain("microphone=()");
|
|
expect(permissions).toContain("geolocation=()");
|
|
}
|
|
});
|
|
|
|
it("should handle HSTS header based on environment", async () => {
|
|
const nextConfig = await import("../../next.config.js");
|
|
|
|
// Test production environment
|
|
const originalEnv = process.env.NODE_ENV;
|
|
process.env.NODE_ENV = "production";
|
|
|
|
const prodHeaders = await nextConfig.default.headers();
|
|
const hstsConfig = prodHeaders.find((h) =>
|
|
h.headers.some((header) => header.key === "Strict-Transport-Security")
|
|
);
|
|
|
|
if (hstsConfig) {
|
|
const hstsHeader = hstsConfig.headers.find(
|
|
(h) => h.key === "Strict-Transport-Security"
|
|
);
|
|
expect(hstsHeader?.value).toBe(
|
|
"max-age=31536000; includeSubDomains; preload"
|
|
);
|
|
}
|
|
|
|
// Test development environment
|
|
process.env.NODE_ENV = "development";
|
|
|
|
const devHeaders = await nextConfig.default.headers();
|
|
const devHstsConfig = devHeaders.find((h) =>
|
|
h.headers.some((header) => header.key === "Strict-Transport-Security")
|
|
);
|
|
|
|
// In development, HSTS header array should be empty
|
|
if (devHstsConfig) {
|
|
expect(devHstsConfig.headers.length).toBe(0);
|
|
}
|
|
|
|
// Restore original environment
|
|
process.env.NODE_ENV = originalEnv;
|
|
});
|
|
});
|
|
|
|
describe("CSP Directive Validation", () => {
|
|
it("should have comprehensive CSP directives", async () => {
|
|
const nextConfig = await import("../../next.config.js");
|
|
const headers = await nextConfig.default.headers();
|
|
|
|
const securityConfig = headers.find(
|
|
(h) => h.source === "/(.*)" && h.headers.length > 1
|
|
);
|
|
const cspHeader = securityConfig?.headers.find(
|
|
(h) => h.key === "Content-Security-Policy"
|
|
);
|
|
|
|
expect(cspHeader).toBeDefined();
|
|
|
|
if (cspHeader) {
|
|
const csp = cspHeader.value;
|
|
|
|
// Essential security directives
|
|
expect(csp).toContain("default-src 'self'");
|
|
expect(csp).toContain("object-src 'none'");
|
|
expect(csp).toContain("base-uri 'self'");
|
|
expect(csp).toContain("form-action 'self'");
|
|
expect(csp).toContain("frame-ancestors 'none'");
|
|
expect(csp).toContain("upgrade-insecure-requests");
|
|
|
|
// Next.js compatibility directives
|
|
expect(csp).toContain(
|
|
"script-src 'self' 'unsafe-eval' 'unsafe-inline'"
|
|
);
|
|
expect(csp).toContain("style-src 'self' 'unsafe-inline'");
|
|
expect(csp).toContain("img-src 'self' data: https:");
|
|
expect(csp).toContain("font-src 'self' data:");
|
|
expect(csp).toContain("connect-src 'self' https:");
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("Permissions Policy Validation", () => {
|
|
it("should restrict dangerous browser features", async () => {
|
|
const nextConfig = await import("../../next.config.js");
|
|
const headers = await nextConfig.default.headers();
|
|
|
|
const securityConfig = headers.find(
|
|
(h) => h.source === "/(.*)" && h.headers.length > 1
|
|
);
|
|
const permissionsHeader = securityConfig?.headers.find(
|
|
(h) => h.key === "Permissions-Policy"
|
|
);
|
|
|
|
expect(permissionsHeader).toBeDefined();
|
|
|
|
if (permissionsHeader) {
|
|
const permissions = permissionsHeader.value;
|
|
|
|
// Should disable privacy-sensitive features
|
|
expect(permissions).toContain("camera=()");
|
|
expect(permissions).toContain("microphone=()");
|
|
expect(permissions).toContain("geolocation=()");
|
|
expect(permissions).toContain("interest-cohort=()");
|
|
expect(permissions).toContain("browsing-topics=()");
|
|
}
|
|
});
|
|
});
|
|
});
|