JustCyber

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.

O que faz hoje

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%.

O que fica com você

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)
TriageAI

Quer ver funcionando?

Demonstração com dado do seu ambiente, não com base de exemplo. É a única forma de saber se serve.