TriageAI
Você recebe achado de scanner, pentest e bug bounty de todo lado e não sabe qual é real, qual é repetido e qual pode ir para o dev.
O TriageAI é um funil único para achados de vulnerabilidade que chegam de fontes diferentes. Você cadastra a fonte (bugbounty, pentest, dast, asm ou api), recebe um segredo de webhook uma única vez, e a ferramenta passa a postar achados nesse endpoint. A ingestão não usa login de usuário: ela é autenticada pelo segredo da fonte, e o tenant do dono da fonte é resolvido no servidor. O corpo do achado nunca decide de quem é o dado.
Antes de gravar, o texto do achado passa por um sanitizador determinístico: segredo por nome de chave e por padrão de valor (JWT, sk-, AKIA, PEM, PAT, token de Slack) vira [redigido], CPF/e-mail/telefone viram marcador, o raw é truncado num teto de 64 KB e as strings são neutralizadas para não forjar a cerca <untrusted_context>. Só então o achado entra no banco e um evento transacional dispara a triagem em segundo plano.
A triagem combina duas camadas. A determinística sempre roda: similaridade de Jaccard contra até 200 achados abertos da mesma fonte (limiar 0,6) para apontar candidato a duplicata, mais um score anti-slop de 0 a 1 calculado por sinais do relatório. A camada de IA só roda se a fonte estiver marcada com ai_poc_egress='approved' — a task é classificada como SENSITIVE e o gateway bloqueia o envio por padrão. Sem essa aprovação explícita, a PoC não sai da sua infraestrutura e o achado é triado só pela heurística. A IA sugere veredito, severidade, CVSS com vetor e uma frase de justificativa; ela move o achado de 'novo' para 'em_revisao_ia' e para por aí. Quem decide é o analista, pela máquina de estados.
Capacidades em operação
Cada item corresponde a funcionalidade que existe no código e é alcançável por um usuário com o escopo correto.
-
Webhook de ingestão por fonte
POST /api/v1/triageai/ingest aceita achado de qualquer ferramenta, autenticado pelo segredo da fonte (comparação em tempo constante), sem JWT. Dedup natural por (fonte, referência externa): reentrega HTTP não cria duplicata.
-
Sanitização antes de persistir
Título, descrição, PoC, ativo e raw passam por redação de segredo, mascaramento de PII e teto de bytes antes do INSERT. O detalhe do achado devolve a PoC já redigida — nunca o payload cru.
-
Deduplicação determinística
Jaccard sobre tokens de título, tipo e ativo contra os achados abertos da mesma fonte. Acima de 0,6 o achado recebe candidato a duplicata e o veredito vira 'duplicado_suspeito'.
-
Score anti-slop
Score 0..1 calculado sem IA a partir de sinais objetivos: PoC curta ou sem comando/URL sobe o score; marcadores de impacto e PoC executável descem; frases típicas de texto gerado e placeholders sobem.
-
Veredito, CVSS e justificativa por IA
Veredito (valido/invalido/precisa_info/duplicado_suspeito), severidade, CVSS 0.0–10.0 com vetor CVSS:3.x e justificativa em português. Se o modelo cair ou o egress for negado, a triagem grava a heurística e segue — o achado nunca fica preso.
-
Egress fail-closed por fonte
A task de triagem é SENSITIVE. O envio da PoC ao provedor de IA é bloqueado por padrão e só passa se aquela fonte específica estiver aprovada. É uma decisão por fonte, não uma chave global.
-
Máquina de estados com auditoria
Transições validadas (novo, em_revisao_ia, triado, roteado, retest, duplicado, descartado), com duplicado e descartado terminais. Cada mudança grava linha em audit_log com estado de origem e destino.
-
Promoção ao Vuln Tracker
Um achado triado vira registro em portal_vulnerabilities com source='import', severidade e CVSS herdados e link de volta ao achado. É idempotente: promover duas vezes devolve o mesmo registro.
-
Métricas calculadas na leitura
Volume total, distribuição por status/severidade/veredito, taxa de falso-positivo, taxa de duplicados, MTTR mediano da triagem, ingestão dos últimos 30 dias e série diária de 30 dias com os dias sem ingestão marcados como zero (não como buraco).
-
Acurácia da IA contra a decisão humana
Cruza o veredito da IA com o status decidido pelo analista, sobre a base restrita dos achados já decididos. A folha devolve também o tamanho da base; base zero retorna nulo, não 0%.
Entregáveis
- Fila de achados triados em /app/triageai, com veredito, severidade, CVSS + vetor e justificativa em português
- Segredo do webhook da fonte, entregue uma única vez na criação (só o hash fica no banco)
- Painel de métricas em /app/triageai/metricas: MTTR de triagem, taxa de falso-positivo, taxa de duplicados, acurácia IA x humano e série diária de 30 dias
- Achado promovido dentro do Vuln Tracker (portal_vulnerabilities, source='import')
- Trilha de auditoria em audit_log para ingestão, transição, roteamento e promoção
O que este módulo não faz
O roteamento para Jira, Slack e GitHub NÃO cria ticket, issue nem mensagem. O módulo routing.py é puro, sem rede: gera um recibo determinístico e grava routed_to com "stub": true e URL no formato stub://. A interface do produto marca isso explicitamente (isStubRoute). Não existe CRUD de destinos de roteamento — o escopo triageai:route:manage está no catálogo RBAC mas nenhuma rota o exige. A verificação do webhook não é HMAC do corpo: o cliente reenvia o segredo recebido e o servidor compara o hash dele em tempo constante; o corpo cru é lido mas não assinado (o próprio código documenta a evolução como pendente). A deduplicação é textual (Jaccard), não vetorial — não há embeddings nem banco vetorial. A triagem por IA roda uma vez, apenas quando o achado está em 'novo'; POST /findings/{id}/retest muda o estado mas não redispara o modelo. Não há rota para editar ou desativar uma fonte já criada (só criar e listar). A flag JC_TRIAGEAI_ENABLED (default false) desliga apenas a task do worker: com ela desligada a ingestão continua aceitando achados, que ficam parados em 'novo' sem triagem.
Declaramos o limite porque software de segurança que promete tudo não é auditável. Se o que falta aqui é o que você precisa, o serviço consultivo cobre — ou dizemos que não cobre.
Conecta com
- Gateway de IA da plataforma (task triageai_triage, classe SENSITIVE, trilha em ai_usage_event/ai_egress_log)
- Outbox transacional + worker Celery (triageai.finding.ingested → triageai.triage_finding, fila default)
- Vuln Tracker do portal (portal_vulnerabilities) pela promoção
- RBAC e RLS FORCE da plataforma (isolamento por tenant)
- audit_log e Idempotency-Key da fundação
Controle de acesso
O acesso é concedido por escopo, não por perfil genérico. Quem só precisa ler não recebe permissão de escrita, e a barreira é o banco de dados, não a interface.
- triageai:source:read
- triageai:source:write
- triageai:finding:read
- triageai:finding:write
- triageai:route:manage (no catálogo, sem rota que o exija)
Quer ver funcionando?
Demonstração com dado do seu ambiente, não com base de exemplo. É a única forma de saber se serve.