localgreenchain/lib/security/headers.ts
Claude 5ea8bab5c3
Add production deployment infrastructure (Agent 4)
- Docker: Multi-stage Dockerfile with security hardening, docker-compose
  for production and development environments
- Environment: Comprehensive .env.example with all config options,
  lib/config/env.ts for typed environment validation
- Logging: Structured JSON logging with request/response middleware
- Monitoring: Prometheus metrics endpoint, Grafana dashboard, health
  checks (liveness/readiness probes)
- Security: Security headers, rate limiting, CORS middleware
- CI/CD: GitHub Actions workflows for CI, production deploy, and
  preview deployments
- Error tracking: Sentry integration foundation

Files created:
- Docker: Dockerfile, docker-compose.yml, docker-compose.dev.yml, .dockerignore
- Config: lib/config/env.ts, lib/config/index.ts
- Logging: lib/logging/logger.ts, lib/logging/middleware.ts
- Monitoring: lib/monitoring/sentry.ts, lib/monitoring/metrics.ts,
  lib/monitoring/health.ts
- Security: lib/security/headers.ts, lib/security/rateLimit.ts,
  lib/security/cors.ts
- API: pages/api/health/*, pages/api/metrics.ts
- Infra: infra/prometheus/prometheus.yml, infra/grafana/*
2025-11-23 03:54:03 +00:00

135 lines
3.3 KiB
TypeScript

/**
* Security Headers Middleware
* Agent 4: Production Deployment
*
* Adds security headers to all responses to protect against common attacks.
*/
import type { NextApiRequest, NextApiResponse, NextApiHandler } from 'next';
import { env } from '../config';
interface SecurityHeadersConfig {
contentSecurityPolicy?: string;
reportUri?: string;
enableHSTS?: boolean;
hstsMaxAge?: number;
frameOptions?: 'DENY' | 'SAMEORIGIN';
contentTypeOptions?: boolean;
xssProtection?: boolean;
referrerPolicy?: string;
permissionsPolicy?: string;
}
const DEFAULT_CONFIG: SecurityHeadersConfig = {
enableHSTS: true,
hstsMaxAge: 31536000, // 1 year
frameOptions: 'DENY',
contentTypeOptions: true,
xssProtection: true,
referrerPolicy: 'strict-origin-when-cross-origin',
};
/**
* Generate Content Security Policy header
*/
function generateCSP(reportUri?: string): string {
const directives = [
"default-src 'self'",
"script-src 'self' 'unsafe-inline' 'unsafe-eval'", // Needed for Next.js
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: blob: https:",
"font-src 'self' data:",
"connect-src 'self' https: wss:",
"media-src 'self'",
"object-src 'none'",
"frame-ancestors 'none'",
"base-uri 'self'",
"form-action 'self'",
"upgrade-insecure-requests",
];
if (reportUri) {
directives.push(`report-uri ${reportUri}`);
}
return directives.join('; ');
}
/**
* Generate Permissions Policy header
*/
function generatePermissionsPolicy(): string {
const policies = [
'accelerometer=()',
'camera=()',
'geolocation=(self)',
'gyroscope=()',
'magnetometer=()',
'microphone=()',
'payment=()',
'usb=()',
];
return policies.join(', ');
}
/**
* Apply security headers to response
*/
export function applySecurityHeaders(
res: NextApiResponse,
config: SecurityHeadersConfig = DEFAULT_CONFIG
): void {
// Content Security Policy
const csp = config.contentSecurityPolicy || generateCSP(config.reportUri || env.cspReportUri);
res.setHeader('Content-Security-Policy', csp);
// Strict Transport Security (HTTPS)
if (config.enableHSTS && env.isProduction) {
res.setHeader(
'Strict-Transport-Security',
`max-age=${config.hstsMaxAge}; includeSubDomains; preload`
);
}
// Prevent clickjacking
res.setHeader('X-Frame-Options', config.frameOptions || 'DENY');
// Prevent MIME type sniffing
if (config.contentTypeOptions) {
res.setHeader('X-Content-Type-Options', 'nosniff');
}
// XSS Protection (legacy, but still useful)
if (config.xssProtection) {
res.setHeader('X-XSS-Protection', '1; mode=block');
}
// Referrer Policy
res.setHeader('Referrer-Policy', config.referrerPolicy || 'strict-origin-when-cross-origin');
// Permissions Policy
res.setHeader(
'Permissions-Policy',
config.permissionsPolicy || generatePermissionsPolicy()
);
// Remove unnecessary headers
res.removeHeader('X-Powered-By');
}
/**
* Security headers middleware for API routes
*/
export function withSecurityHeaders(
handler: NextApiHandler,
config?: SecurityHeadersConfig
): NextApiHandler {
return async (req: NextApiRequest, res: NextApiResponse) => {
applySecurityHeaders(res, config);
return handler(req, res);
};
}
// Export types
export type { SecurityHeadersConfig };