import { traverse } from '../../core/config/index.js';
import { messages } from '../../core/messages/index.js';
import { detectarContextoProjeto } from '../../shared/index.js';
import { agruparPor, criarErroAnalise, detectarSegredosHardcoded, filtrarOcorrenciasSuprimidas, splitLines } from '../../shared/helpers/index.js';
import { criarOcorrencia } from '../../types/index.js';
export const analistaSeguranca = {
    nome: 'seguranca',
    categoria: 'seguranca',
    descricao: 'Detecta vulnerabilidades e práticas inseguras no código',
    test: (relPath) => {
        return /\.(js|jsx|ts|tsx|mjs|cjs)$/.test(relPath);
    },
    aplicar: (src, relPath, ast) => {
        if (!src)
            return [];
        const contextoArquivo = detectarContextoProjeto({
            arquivo: relPath,
            conteudo: src,
            relPath
        });
        const problemas = [];
        try {
            detectarPadroesPerigosos(src, relPath, problemas);
            if (ast) {
                detectarProblemasAST(ast, problemas);
            }
            const ocorrencias = [];
            const porSeveridade = agruparPor(problemas, caminho => caminho.severidade);
            for (const [severidade, items] of Object.entries(porSeveridade)) {
                if (items.length > 0) {
                    const nivel = mapearSeveridadeParaNivel(severidade);
                    const nivelAjustado = contextoArquivo.isTest && nivel === 'aviso' ? 'info' : nivel;
                    const resumo = items.slice(0, 3).map(caminho => caminho.tipo).join(', ');
                    ocorrencias.push(criarOcorrencia({
                        tipo: 'vulnerabilidade-seguranca',
                        nivel: nivelAjustado,
                        mensagem: messages.DetectorAgregadosMensagens.problemasSegurancaResumo(severidade, resumo, items.length),
                        relPath,
                        linha: items[0].linha
                    }));
                }
            }
            return filtrarOcorrenciasSuprimidas(ocorrencias, 'seguranca', src);
        }
        catch (erro) {
            return [criarErroAnalise(relPath, messages.DetectorAgregadosMensagens.erroAnalisarSeguranca(erro))];
        }
    }
};
function detectarPadroesPerigosos(src, relPath, problemas) {
    const linhas = splitLines(src);
    function isLikelyHttpHeaderName(value) {
        const headerValue = String(value || '').trim();
        if (!headerValue)
            return false;
        if (headerValue.length < 4 || headerValue.length > 80)
            return false;
        if (!/^[A-Za-z0-9-]+$/.test(headerValue))
            return false;
        if (headerValue.startsWith('-') || headerValue.endsWith('-'))
            return false;
        if (!/-/.test(headerValue) && !/^X[A-Za-z]?/.test(headerValue))
            return false;
        return true;
    }
    function isHttpHeadersKeyValueContext(index) {
        const start = Math.max(0, index - 12);
        const end = Math.min(linhas.length, index + 6);
        const ctx = linhas.slice(start, end).join('\n');
        const hasHeaders = /\bheaders\b\s*[:=]/i.test(ctx) || /\bheader\b/i.test(ctx);
        const hasValorProp = /\bvalue\b\s*[:=]/i.test(ctx);
        return hasHeaders && hasValorProp;
    }
    const segredos = detectarSegredosHardcoded(src, relPath);
    segredos.forEach(string => {
        if (string.campo === 'key' && isLikelyHttpHeaderName(string.valor) && isHttpHeadersKeyValueContext(string.linha - 1)) {
            return;
        }
        problemas.push({
            tipo: 'hardcoded-secrets',
            descricao: `Credencial hardcoded (${string.campo}) no código pode ser exposta`,
            severidade: string.confianca === 'alta' ? 'critica' : 'alta',
            linha: string.linha,
            sugestao: 'Use variáveis de ambiente ou arquivo de configuração seguro'
        });
    });
    linhas.forEach((linha, index) => {
        const numeroLinha = index + 1;
        const linhaSemComentarios = linha.replace(/\/\/.*$/gm, '').replace(/\/\*.*?\*\//gs, '');
        const linhaSemStrings = linhaSemComentarios.replace(/'[^']*'/g, '').replace(/"[^"]*"/g, '').replace(/`[^`]*`/g, '').replace(/\/(?:[^\/\\]|\\.)*\//g, '');
        if (/\beval\s*\(/.test(linhaSemStrings)) {
            problemas.push({
                tipo: 'eval-usage',
                descricao: 'Uso de eval() pode executar código malicioso',
                severidade: 'critica',
                linha: numeroLinha,
                sugestao: 'Use JSON.parse() ou funções específicas ao invés de eval()'
            });
        }
        if (/dangerouslySetInnerHTML\s*[:=]/.test(linhaSemStrings)) {
            problemas.push({
                tipo: 'dangerously-set-inner-html',
                descricao: 'Uso de dangerouslySetInnerHTML pode causar XSS se o conteúdo não for sanitizado',
                severidade: 'alta',
                linha: numeroLinha,
                sugestao: 'Use sanitize-html ou DOMPurify para limpar o conteúdo antes de renderizar'
            });
        }
        if (/\.(innerHTML|outerHTML)\s*=\s*[^"']/.test(linha)) {
            problemas.push({
                tipo: 'dangerous-html',
                descricao: `${linha.includes('outerHTML') ? 'outerHTML' : 'innerHTML'} com variáveis pode causar XSS`,
                severidade: 'alta',
                linha: numeroLinha,
                sugestao: 'Use textContent ou sanitize o HTML antes de inserir'
            });
        }
        if (/\bdocument\.write(ln)?\s*\(/.test(linhaSemStrings)) {
            problemas.push({
                tipo: 'xss-vulnerability',
                descricao: 'Uso de document.write() é perigoso e pode causar XSS',
                severidade: 'alta',
                linha: numeroLinha,
                sugestao: 'Use manipulação de DOM segura (createElement, appendChild, etc.)'
            });
        }
        if (/\.(query|execute|raw|queryRaw|executeRaw)\s*\(/.test(linha) && (/req\.|params\.|query\.|body\./.test(linha) || /\$\{.*\}/.test(linha) || /\s\+\s/.test(linha))) {
            const temParametros = /['"`].*(\?|\$\d|:\w+).*['"`]/.test(linha);
            if (!temParametros) {
                problemas.push({
                    tipo: 'sql-injection',
                    descricao: 'Possível SQL Injection ao concatenar variáveis em query',
                    severidade: 'critica',
                    linha: numeroLinha,
                    sugestao: 'Use queries parametrizadas ou ORM com suporte a escaping'
                });
            }
        }
        if (/\b(exec|execSync|spawn|spawnSync)\s*\(/.test(linha) && /req\.|params\.|query\.|body\./.test(linha)) {
            problemas.push({
                tipo: 'command-injection',
                descricao: 'Execução de comandos com input do usuário pode causar Command Injection',
                severidade: 'critica',
                linha: numeroLinha,
                sugestao: 'Evite executar comandos shell com input do usuário; use APIs específicas ou valide rigorosamente'
            });
        }
        if (/\.cookie\s*\(/.test(linha) && !/req\.cookie|request\.cookie/.test(linha) && !/httpOnly\s*:\s*true/.test(linha)) {
            problemas.push({
                tipo: 'insecure-cookie',
                descricao: 'Cookie configurado sem a flag httpOnly (detectado via padrão de texto)',
                severidade: 'media',
                linha: numeroLinha,
                sugestao: 'Sempre use httpOnly: true para evitar acesso via JavaScript (XSS). Se estiver usando configuração multi-line, o detector AST cuidará da validação.'
            });
        }
        if (/\.(assign|replace|href)\s*=\s*.*(req\.|params\.|query\.|body\.)/.test(linha)) {
            problemas.push({
                tipo: 'xss-vulnerability',
                descricao: 'Redirecionamento dinâmico com input do usuário pode causar Open Redirect ou XSS',
                severidade: 'media',
                linha: numeroLinha,
                sugestao: 'Valide a URL de destino contra uma whitelist antes de redirecionar'
            });
        }
        if (/Math\.random\(\)/.test(linha) && /crypto|password|token|secret/i.test(linha)) {
            problemas.push({
                tipo: 'weak-crypto',
                descricao: 'Math.random() não é seguro para criptografia',
                severidade: 'alta',
                linha: numeroLinha,
                sugestao: 'Use crypto.randomBytes() ou crypto.getRandomValues()'
            });
        }
        if (/\b(console\.log|console\.debug|console\.info|res\.send|res\.json|res\.write|reply\.send)\s*\(.*(process\.env\.(PASSWORD|SECRET|TOKEN|KEY|API|PWD|AUTH|CREDENTIALS|DB|PASS|STRIPE|AWS|PRIVATE))/i.test(linha)) {
            problemas.push({
                tipo: 'sensitive-env-exposure',
                descricao: 'Possível exposição de variável de ambiente sensível em log ou resposta',
                severidade: 'critica',
                linha: numeroLinha,
                sugestao: 'Nunca exponha variáveis de ambiente que contenham segredos em logs ou respostas'
            });
        }
        if (/createHash\s*\(\s*['"`](md5|md4|sha1)['"`]\s*\)/.test(linha)) {
            const algoritmo = /createHash\s*\(\s*['"`](md5|md4|sha1)['"`]\s*\)/.exec(linha)?.[1];
            const linhaAnterior = index > 0 ? linhas[index - 1] : '';
            const linha2Atras = index > 1 ? linhas[index - 2] : '';
            const comentarioContexto = linhaAnterior + linha2Atras;
            const temJustificativa = /fingerprint|cache|baseline|perf|não.*segurança|not.*security|não.*criptograf/i.test(comentarioContexto) || /apenas.*identifica|only.*identif|deduplica/i.test(comentarioContexto);
            if (!temJustificativa) {
                problemas.push({
                    tipo: 'weak-crypto',
                    descricao: `Algoritmo de hash ${algoritmo?.toUpperCase()} é considerado fraco`,
                    severidade: 'alta',
                    linha: numeroLinha,
                    sugestao: 'Use SHA-256 ou superior: createHash("sha256") - ou adicione comentário se for apenas fingerprinting'
                });
            }
        }
        if (/new RegExp\s*\([^)]*\)/.test(linha) && /req\.|params\.|query\.|body\./.test(linha)) {
            problemas.push({
                tipo: 'unsafe-regex',
                descricao: 'RegExp com input não validado pode causar ReDoS',
                severidade: 'media',
                linha: numeroLinha,
                sugestao: 'Valide e escape o input antes de usar em RegExp'
            });
        }
        const regexLiteralMatch = linha.match(/\/([^\/\\]|\\.)+\/[gimuy]*/);
        if (regexLiteralMatch) {
            const pattern = regexLiteralMatch[0];
            const isParserLoop = /[\s\S]*?\//.test(pattern) || /[\s\S]*?/.test(pattern);
            if (!isParserLoop && (/(\([^)]+\+?\)\+|\([^)]+\+?\)\*|(\.\*){2,}|(\.\+){2,}|([a-zA-Z0-9])\+\+)/.test(pattern) || /\[[^\]]+\]\+\+/.test(pattern))) {
                problemas.push({
                    tipo: 'unsafe-regex-literal',
                    descricao: 'Regex literal detectada com padrão potencialmente vulnerável a ReDoS',
                    severidade: 'media',
                    linha: numeroLinha,
                    sugestao: 'Revise o padrão para evitar quantificadores aninhados ou curingas repetitivos (.*.*). Se necessário, adicione @tutanus-disable.'
                });
            }
        }
        if (/__proto__/.test(linhaSemStrings)) {
            problemas.push({
                tipo: 'prototype-pollution',
                descricao: 'Manipulação de __proto__ pode causar prototype pollution',
                severidade: 'alta',
                linha: numeroLinha,
                sugestao: 'Use Object.create(null) ou Object.setPrototypeOf() com cuidado'
            });
        }
        if (/\.\.\//g.test(linha) && /req\.|params\.|query\./.test(linha)) {
            problemas.push({
                tipo: 'path-traversal',
                descricao: 'Possível vulnerabilidade de path traversal',
                severidade: 'alta',
                linha: numeroLinha,
                sugestao: 'Sanitize caminhos de arquivo e use path.resolve() com cuidado'
            });
        }
    });
    detectarAsyncSemTryCatch(src, problemas);
}
function detectarAsyncSemTryCatch(src, problemas) {
    const lines = splitLines(src);
    const isNextJsServerComponent = /^['"](use server|use client)['"]/.test(src.trim()) || /export\s+(default\s+)?async\s+function/.test(src);
    const hasDynamicImport = /next\/dynamic|import\s*\(/.test(src);
    for (let indice = 0; indice < lines.length; indice++) {
        const line = lines[indice];
        const trimmedLine = line.trim();
        if (/\bawait\s+/.test(line) && !trimmedLine.startsWith('//')) {
            const contextLines = lines.slice(Math.max(0, indice - 10), indice);
            const context = contextLines.join(' ');
            const isEventHandler = /\.on\s*\(/.test(context) || /\.once\s*\(/.test(context) || /addEventListener\s*\(/.test(context) || /collector\.on\s*\(/.test(context) || /emitter\.on\s*\(/.test(context) || /process\.on\s*\(/.test(context);
            const isDynamicImport = hasDynamicImport && (/import\s*\(/.test(line) || /dynamic\s*\(/.test(context));
            if (isNextJsServerComponent || isDynamicImport) {
                continue;
            }
            const extendedContext = lines.slice(Math.max(0, indice - 100), Math.min(lines.length, indice + 100));
            const fullContext = extendedContext.join('\n');
            const hasErroHandling = /try\s*\{[\s\S]*?\}\s*catch/.test(fullContext) || /\.catch\s*\(/.test(line) || /\.catch\s*\(/.test(lines[indice + 1] || '') ||
                /\.then\s*\([^)]*\)\s*\.catch/.test(fullContext);
            if (!hasErroHandling) {
                problemas.push({
                    tipo: isEventHandler ? 'unhandled-async-event' : 'unhandled-async',
                    descricao: isEventHandler ? 'await em event handler sem tratamento de erro (considere adicionar .catch se necessário)' : 'await sem tratamento de erro pode causar crashes não tratados',
                    severidade: isEventHandler ? 'baixa' : 'media',
                    linha: indice + 1,
                    sugestao: isEventHandler ? 'Event handlers são fire-and-forget. Adicione .catch() apenas se precisar tratar erros específicos' : 'Envolva em try-catch ou use .catch() na Promise'
                });
            }
        }
    }
}
function detectarProblemasAST(ast, problemas) {
    try {
        traverse(ast.node, {
            NewExpression(path) {
                if (path.node.callee.type === 'Identifier' && path.node.callee.name === 'Function') {
                    problemas.push({
                        tipo: 'eval-usage',
                        descricao: 'Function constructor pode executar código dinâmico',
                        severidade: 'alta',
                        linha: path.node.loc?.start.line || 0,
                        sugestao: 'Evite Function constructor, use funções declaradas'
                    });
                }
            },
            CallExpression(path) {
                const callee = path.node.callee;
                const args = path.node.arguments;
                if (callee.type === 'Identifier' && ['setTimeout', 'setInterval'].includes(callee.name) && args[0]?.type === 'StringLiteral') {
                    problemas.push({
                        tipo: 'eval-usage',
                        descricao: 'setTimeout/setInterval com string executa código dinâmico',
                        severidade: 'media',
                        linha: path.node.loc?.start.line || 0,
                        sugestao: 'Use função ao invés de string'
                    });
                }
                if (callee.type === 'MemberExpression' && callee.property.type === 'Identifier' && callee.property.name === 'cookie') {
                    const object = callee.object;
                    const isRequest = object.type === 'Identifier' && (object.name === 'req' || object.name === 'request');
                    if (!isRequest) {
                        const options = args[2];
                        let hasHttpOnly = false;
                        if (options && options.type === 'ObjectExpression') {
                            hasHttpOnly = options.properties.some(prop => prop.type === 'ObjectProperty' && prop.key.type === 'Identifier' && prop.key.name === 'httpOnly' && (prop.value.type === 'BooleanLiteral' ? prop.value.value === true : false));
                        }
                        if (!hasHttpOnly) {
                            problemas.push({
                                tipo: 'insecure-cookie',
                                descricao: 'Cookie configurado sem a flag httpOnly',
                                severidade: 'media',
                                linha: path.node.loc?.start.line || 0,
                                sugestao: 'Sempre use httpOnly: true para evitar acesso via JavaScript (XSS)'
                            });
                        }
                    }
                }
                if (callee.type === 'MemberExpression' && callee.property.type === 'Identifier' && ['query', 'execute', 'raw', 'queryRaw', 'executeRaw'].includes(callee.property.name)) {
                    const firstArg = args[0];
                    const isUnsafe = firstArg && (firstArg.type === 'TemplateLiteral' && firstArg.expressions.length > 0 || firstArg.type === 'BinaryExpression' && firstArg.operator === '+');
                    if (isUnsafe) {
                        const code = firstArg.type === 'TemplateLiteral' ? firstArg.quasis.map(delimitadorString => delimitadorString.value.raw).join('') : '';
                        const temParametros = /['"`].*(\?|\$\d|:\w+).*['"`]/.test(code);
                        if (!temParametros) {
                            problemas.push({
                                tipo: 'sql-injection',
                                descricao: 'Possível SQL Injection detectada via AST (concatenação no primeiro argumento)',
                                severidade: 'critica',
                                linha: path.node.loc?.start.line || 0,
                                sugestao: 'Use queries parametrizadas ao invés de concatenar strings ou template literals com variáveis'
                            });
                        }
                    }
                }
            }
        });
    }
    catch {
    }
}
function mapearSeveridadeParaNivel(severidade) {
    switch (severidade) {
        case 'critica':
        case 'alta':
            return 'erro';
        case 'media':
            return 'aviso';
        case 'baixa':
        default:
            return 'info';
    }
}
//# sourceMappingURL=detector-security.js.map