import crypto from 'node:crypto';
const IS_PROD = process.env.NODE_ENV === 'production';
const PUBLIC_URL = process.env.TUTANUS_PUBLIC_URL || 'http://localhost:3000';
const WINDOW_MS = 60_000;
const MAX_REQUESTS = 100;
const requestCounts = new Map();
function rateLimit(req, res, next) {
    const ip = req.ip || req.socket.remoteAddress || 'unknown';
    const now = Date.now();
    const entry = requestCounts.get(ip);
    if (!entry || now > entry.resetAt) {
        requestCounts.set(ip, { count: 1, resetAt: now + WINDOW_MS });
        next();
        return;
    }
    entry.count++;
    if (entry.count > MAX_REQUESTS) {
        res.status(429).json({ erro: 'Too many requests. Try again later.' });
        return;
    }
    next();
}
function securityHeaders(req, res, next) {
    const nonce = crypto.randomBytes(16).toString('base64url');
    res.setHeader('X-Content-Type-Options', 'nosniff');
    res.setHeader('X-Frame-Options', 'DENY');
    res.setHeader('X-XSS-Protection', '0');
    res.setHeader('Referrer-Policy', 'no-referrer');
    res.setHeader('X-DNS-Prefetch-Control', 'off');
    const isApi = req.path.startsWith('/api/');
    const scriptSrc = isApi
        ? `'self' 'nonce-${nonce}'`
        : `'self' 'unsafe-inline'`;
    const directives = [
        `default-src 'self'`,
        `script-src ${scriptSrc}`,
        `style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com`,
        `font-src 'self' https://cdnjs.cloudflare.com`,
        `img-src 'self' data: https:`,
        `connect-src 'self' https://api.github.com`,
        `frame-src 'none'`,
        `object-src 'none'`,
        `base-uri 'self'`,
        `form-action 'self'`,
        `manifest-src 'self'`,
        `worker-src 'self'`,
        ...(IS_PROD ? [`upgrade-insecure-requests`] : []),
    ];
    res.setHeader('Content-Security-Policy', directives.join('; '));
    res.setHeader('Permissions-Policy', 'accelerometer=(), camera=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=(), interest-cohort=()');
    res.setHeader('X-Permitted-Cross-Domain-Policies', 'none');
    if (IS_PROD && PUBLIC_URL.startsWith('https://')) {
        res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
    }
    res.setHeader('Surrogate-Control', 'no-store');
    res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
    res.setHeader('Pragma', 'no-cache');
    res.setHeader('Expires', '0');
    res.setHeader('Cross-Origin-Resource-Policy', 'same-origin');
    res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
    res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
    req.nonce = nonce;
    next();
}
function corsRestrict(req, res, next) {
    if (req.path.startsWith('/api/github/webhooks')) {
        next();
        return;
    }
    res.setHeader('Access-Control-Allow-Origin', PUBLIC_URL);
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE, OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-CSRF-Token');
    res.setHeader('Access-Control-Allow-Credentials', 'true');
    res.setHeader('Access-Control-Max-Age', '86400');
    res.setHeader('Vary', 'Origin');
    if (req.method === 'OPTIONS') {
        res.status(204).end();
        return;
    }
    next();
}
const csrfTokens = new Map();
function generateCsrfToken() {
    const token = crypto.randomBytes(32).toString('hex');
    csrfTokens.set(token, { token, expiresAt: Date.now() + 3_600_000 });
    return token;
}
function csrfProtection(req, res, next) {
    const safeMethods = new Set(['GET', 'HEAD', 'OPTIONS']);
    if (safeMethods.has(req.method)) {
        const token = generateCsrfToken();
        res.setHeader('X-CSRF-Token', token);
        next();
        return;
    }
    if (req.path.startsWith('/api/github/webhooks')) {
        next();
        return;
    }
    const headerToken = req.headers['x-csrf-token'];
    const stored = headerToken ? csrfTokens.get(headerToken) : undefined;
    if (!stored || stored.expiresAt < Date.now()) {
        res.status(403).json({ erro: 'CSRF token invalido ou expirado' });
        return;
    }
    if (headerToken) {
        csrfTokens.delete(headerToken);
    }
    next();
}
export function createSecurityMiddleware() {
    return [rateLimit, securityHeaders, corsRestrict, csrfProtection];
}
//# sourceMappingURL=seguranca.js.map