Skip to content

feat: agente de triagem de incidentes de seguranca com Spring AI e padroes de projeto - #1

Merged
DanielDPereira merged 13 commits into
mainfrom
feat/security-agent
Aug 10, 2026
Merged

feat: agente de triagem de incidentes de seguranca com Spring AI e padroes de projeto#1
DanielDPereira merged 13 commits into
mainfrom
feat/security-agent

Conversation

@Garakis

@Garakis Garakis commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Agente de triagem de incidentes de seguranca construido com Spring AI e modelos de linguagem abertos executados localmente via Ollama. Nenhuma chamada a servico externo, nenhuma chave de API.

Categoria de caso de uso: Security Agents — investigar alertas, correlacionar eventos e acionar contencao, com governanca estrita e aprovacao humana obrigatoria para acoes criticas.

Padroes de projeto

Padrao Pacote Papel no dominio
State state/ Ciclo de vida do incidente; conter um host antes da aprovacao e impossivel por construcao
Command command/ Acoes de resposta enfileiraveis, auditaveis e reversiveis
Strategy strategy/ Tres politicas de planejamento trocaveis em tempo de execucao
Observer observer/ Trilha de auditoria, console e painel alimentados pelos mesmos eventos
Composite gui/tree/ Arvore de evidencias na interface grafica

Documentacao completa com 8 diagramas UML em docs/arquitetura.md, validados com o parser do mermaid 11.

Decisoes de projeto

O modelo nao executa nada. Ele classifica, justifica e sugere nomes de ferramenta. A CommandFactory e o unico ponto onde uma sugestao vira acao e opera por lista de permissao — ferramenta alucinada e descartada com registro em log, nunca interpretada. Ha teste cobrindo esse caso.

A barreira de aprovacao e redundante de proposito. O State so libera a transicao para contencao apos decisao humana, e o CommandInvoker recusa comandos aprovaveis fora dessa fase. Se um planejador futuro esquecer de marcar uma acao como destrutiva, a outra camada ainda barra.

Falha degrada para revisao humana. Modelo indisponivel, resposta fora do formato ou classificacao desconhecida resultam em NEEDS_HUMAN_REVIEW.

Licenciamento

Toda a pilha e software livre. O modelo padrao e o Qwen 2.5 7B, sob Apache 2.0 — licenca aprovada pela OSI. O Llama 3.1 funciona igualmente bem, mas sua licenca e source-available, com restricao de uso acima de 700 milhoes de usuarios mensais.

Verificacao

  • 24 testes automatizados, todos offline, sem Ollama e sem rede
  • Fluxo completo executado contra o Qwen 2.5 7B: trilha de auditoria de 18 eventos, da abertura ao encerramento
  • Barreira de governanca confirmada nas duas camadas independentes

Limitacao conhecida e documentada

O cenario "Backup noturno" e uma transferencia legitima em janela de manutencao. O Qwen 2.5 7B classifica como verdadeiro positivo com confianca 0,80 e propoe isolar o servidor de backup.

O comportamento esta documentado no README como resultado medido, nao corrigido artificialmente. Com autonomia total o agente derrubaria a infraestrutura de backup durante a manutencao; a barreira de aprovacao impede o estrago. A causa raiz e ausencia de enriquecimento — o agente nao sabe que BKP-SRV-01 e servidor de backup nem que existe janela aprovada. Inventario de ativos e calendario de mudancas no prompt resolvem, e ficam registrados como evolucao.

Como testar

ollama serve
ollama pull qwen2.5:7b
./mvnw test
./mvnw spring-boot:run

Garakis added 13 commits August 10, 2026 10:29
Estrutura inicial do agente de triagem de seguranca:
- Spring AI com starter Ollama para inferencia local
- Java 21, Maven wrapper (somente script)
- Modelo padrao qwen2.5:7b, configuravel por variavel de ambiente
- gitignore ampliado para target/, IDEs e macOS
Tipos imutaveis que servem de base para todos os patterns:
- Severity com peso, usado na agregacao recursiva do Composite
- Alert e Ioc representando a deteccao bruta, nunca alterados pelo agente
- MitreTechnique para classificacao ATT&CK
- TriageVerdict separando o que o modelo conclui do que o agente executa
AgentEventBus e o Subject; AgentEventListener e o Observer.

- Publicacao isolada por observador: falha de um nao interrompe os demais,
  garantindo que a trilha de auditoria sobreviva a erros de renderizacao
- CopyOnWriteArrayList porque a GUI registra observadores pela EDT do Swing
  enquanto o agente publica de outra thread
- AuditTrailListener materializa a trilha exigida por conformidade
- ConsoleListener espelha o progresso sem depender da GUI
Incident e o Context; IncidentState e a abstracao de estado.

- Operacoes ilegais sao impossiveis por construcao: a interface declara
  todas as operacoes com implementacao padrao que lanca excecao, e cada
  estado concreto sobrescreve apenas o que permite
- Nenhum switch sobre fase; nova fase = nova classe, sem alterar o Context
- AwaitingApprovalState e a barreira de governanca do dominio: contencao
  so e liberada apos decisao humana explicita
- Cada transicao publica evento no barramento, ligando State e Observer
- 4 testes cobrindo fluxo completo, barreira de aprovacao, negacao e
  estado terminal
AgentCommand e a abstracao; CommandInvoker e o Invoker; ContainmentGateway
isola a fronteira com EDR, firewall e NAC.

- Comandos sao codigo Java, nunca texto gerado pelo modelo: o modelo escolhe
  qual invocar e com quais parametros, o efeito e fixado em compilacao
- A barreira de aprovacao vive no invoker, nao em cada comando, entao um
  comando novo ja nasce protegido
- Acao compensatoria via undo: reverter contencao equivocada importa tanto
  quanto aplica-la
- Falha de um comando nao interrompe a fila
- 6 testes cobrindo recusa sem aprovacao, execucao, undo, ordem e falha
TriagePlanner e a abstracao; ReAct, PlanThenExecute e HumanInTheLoop sao as
estrategias concretas. ThreatAnalyst isola o modelo atras de uma porta.

- CommandFactory e o unico ponto onde saida de modelo vira acao, e opera por
  lista de permissao: ferramenta desconhecida e descartada, nunca interpretada
- ReAct executa apenas leitura durante o ciclo e acumula contencao para depois
  da aprovacao, senao o ciclo adaptativo contornaria a governanca
- HumanInTheLoop e Strategy que tambem decora: forca aprovacao sem duplicar
  o planejamento
- MitreRepository mantem ATT&CK offline, sem dependencia de rede
- 5 testes com analista falso, incluindo rejeicao de ferramenta alucinada
LlmThreatAnalyst implementa a porta usando ChatClient com saida estruturada.

- Papel do modelo restrito a classificar, justificar e sugerir nomes de
  ferramenta; nao executa nada e nao decide se a acao acontece
- Prompt de sistema declara a lista de permissao e proibe inventar host, IP
  ou identificador de tecnica ausente do alerta
- DTOs planos em vez de mapas aninhados: modelos de 7B erram menos formato
- Toda falha degrada para NEEDS_HUMAN_REVIEW; agente que nao conclui escala,
  nunca adivinha
- Classificacao nao reconhecida tambem vira revisao humana
IncidentTriageService coordena os quatro padroes sem conter regra propria:
State define o que pode acontecer, Strategy decide o que fazer, Command
executa e Observer difunde.

- PlannerRegistry permite trocar a estrategia em tempo de execucao, o que
  torna o Strategy observavel na GUI
- Nenhuma acao destrutiva ocorre na triagem: o State barra e o Invoker recusa
- Veredito abaixo da confianca minima vai para aprovacao mesmo se o plano
  nao tiver acao destrutiva
- Configuracao exposta via soc-agent.* no application.yml
EvidenceNode e o Component, EvidenceLeaf a folha, EvidenceGroup o composto.

- Operacoes recursivas como metodos padrao da interface: folha e composto
  respondem identicamente a leafCount, highestSeverity, depth e render
- Severidade agrega de baixo para cima, entao um grupo LOW com IOC CRITICAL
  aparece como CRITICAL sem recalculo manual
- Folha recusa filhos, preservando a transparencia sem permitir composicao
  invalida
- EvidenceTreeBuilder monta incidente, alertas, IOCs, tecnicas e decisao
- 5 testes cobrindo recursao, agregacao e tratamento uniforme
Cada regiao da tela expoe um padrao: arvore a esquerda e o Composite, log a
direita e o Observer, seletor no topo troca o Strategy em execucao e os
botoes inferiores sao Commands habilitados conforme o State.

- A janela e ela propria um AgentEventListener; como o agente publica de
  outra thread, toda atualizacao visual volta para a EDT
- Triagem roda em SwingWorker: a chamada ao modelo levaria segundos e
  congelaria a interface na EDT
- Botao de aprovar so habilita na fase AWAITING_APPROVAL, tornando a
  barreira de governanca visivel na interface
- Quatro cenarios cobrindo exfiltracao, forca bruta, ransomware e um falso
  positivo, com hosts e enderecos ficticios
- Ambiente headless nao impede a aplicacao de subir
A trilha de auditoria subia vazia: AuditTrailListener e ConsoleListener
existiam no contexto mas ninguem os registrava, entao nenhum evento chegava
neles. So a janela do painel se inscrevia, por conta propria.

Em um agente cujo requisito central e auditabilidade, perder a trilha
inteira e a falha mais grave possivel, e ela passava despercebida porque
nada quebrava.

- EventListenerRegistrar inscreve todo AgentEventListener do contexto
- Acrescentar observador novo passa a exigir apenas declara-lo como bean
- 4 testes cobrindo difusao, isolamento de falha, registro e filtro por tipo

Tambem adiciona HeadlessDemoRunner para exercitar o fluxo completo em
terminal, sem interface grafica.
Oito diagramas em Mermaid, renderizados nativamente pelo GitHub e validados
com o parser do mermaid 11:

- Fluxograma de camadas mostrando a regra de dependencia
- Diagrama de classes para cada um dos cinco padroes
- Maquina de estados do ciclo de vida do incidente
- Diagrama de sequencia do fluxo completo, destacando a pausa para
  aprovacao humana

Inclui a tabela de verificacao da barreira de governanca, que mapeia a
invariante 'nenhuma acao destrutiva sem decisao humana' aos dois mecanismos
independentes que a protegem e aos testes que a cobrem.
O runner aprovava sozinho e executava a contencao, o que anulava justamente
a barreira que o projeto existe para demonstrar. No cenario de backup
noturno isso significava isolar o servidor de backup sem decisao humana
alguma.

Agora o padrao e parar em AWAITING_APPROVAL, como pararia em producao;
a aprovacao simulada exige --aprovar explicito.

- Busca de cenario por trecho do nome, ja que a linha de comando quebra o
  argumento nos espacos
- README documenta o resultado real medido: o Qwen 2.5 7B classifica o
  backup legitimo como verdadeiro positivo com confianca 0,80 e propoe
  isolar o servidor
- A causa raiz (ausencia de inventario de ativos e calendario de mudancas)
  fica registrada como evolucao do projeto

@DanielDPereira DanielDPereira left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Muito bom!

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Este PR introduz um agente de triagem de incidentes de segurança (offline) em Java/Spring Boot + Spring AI (Ollama), estruturado para demonstrar e aplicar padrões GoF (State/Command/Strategy/Observer/Composite) com barreira de governança e aprovação humana antes de contenção.

Changes:

  • Implementa o núcleo de domínio (State/Command/Strategy/Observer) com trilha de auditoria e fila de comandos reversíveis.
  • Adiciona integração com LLM local via Spring AI (porta ThreatAnalyst) e lista de permissão de ferramentas via CommandFactory.
  • Cria GUI Swing + runner headless, documentação (README + arquitetura) e suíte de testes offline cobrindo os fluxos principais.

Reviewed changes

Copilot reviewed 59 out of 60 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/test/java/br/fatec/esiii/socagent/strategy/TriagePlannerTest.java Testes das estratégias (plan-then-execute, react, human-in-the-loop) e whitelist de ferramentas
src/test/java/br/fatec/esiii/socagent/state/IncidentStateTest.java Testes do fluxo de fases do incidente e barreira de aprovação no State
src/test/java/br/fatec/esiii/socagent/observer/AgentEventBusTest.java Testes do barramento de eventos e isolamento de falhas de observadores
src/test/java/br/fatec/esiii/socagent/gui/tree/EvidenceNodeTest.java Testes do Composite (contagem, severidade agregada, profundidade, render)
src/test/java/br/fatec/esiii/socagent/command/CommandInvokerTest.java Testes do invoker (aprovação, execução, fila, undo e falhas)
src/main/resources/application.yml Configuração do Spring AI/Ollama e parâmetros do agente (planner, confiança mínima, aprovador padrão)
src/main/java/br/fatec/esiii/socagent/strategy/TriagePlanner.java Interface Strategy e record Plan para resultado de planejamento
src/main/java/br/fatec/esiii/socagent/strategy/ThreatAnalyst.java Porta de saída para o modelo (classificar e propor ações)
src/main/java/br/fatec/esiii/socagent/strategy/ReActPlanner.java Planner ReAct com ciclo iterativo e separação leitura vs contenção
src/main/java/br/fatec/esiii/socagent/strategy/ProposedAction.java DTO inerte de ação sugerida pelo modelo (tool + args + rationale)
src/main/java/br/fatec/esiii/socagent/strategy/PlanThenExecutePlanner.java Planner “plano completo” e avaliação de aprovação necessária
src/main/java/br/fatec/esiii/socagent/strategy/HumanInTheLoopPlanner.java Decorator que força aprovação humana para planos não vazios
src/main/java/br/fatec/esiii/socagent/strategy/CommandFactory.java Tradução por lista de permissão de sugestões do modelo em AgentCommand
src/main/java/br/fatec/esiii/socagent/state/IncidentStates.java Implementações concretas do State e fluxo permitido de fases
src/main/java/br/fatec/esiii/socagent/state/IncidentState.java Interface State com operações permitidas e exceção padrão em transições ilegais
src/main/java/br/fatec/esiii/socagent/state/IncidentPhase.java Enum de fases para GUI/relatórios
src/main/java/br/fatec/esiii/socagent/state/Incident.java Context do State + publicação de eventos no barramento
src/main/java/br/fatec/esiii/socagent/state/IllegalTransitionException.java Exceção para transição/ação não permitida pela fase atual
src/main/java/br/fatec/esiii/socagent/SocAgentApplication.java Entry point Spring Boot + scan de properties
src/main/java/br/fatec/esiii/socagent/service/SampleAlertCatalog.java Catálogo de cenários de alertas para demonstração
src/main/java/br/fatec/esiii/socagent/service/PlannerRegistry.java Registro e ativação runtime de estratégias (Strategy)
src/main/java/br/fatec/esiii/socagent/service/IncidentTriageService.java Orquestrador (integra State/Strategy/Command/Observer) e caminhos approve/deny/undo
src/main/java/br/fatec/esiii/socagent/service/HeadlessDemoRunner.java Runner headless para demonstrar fluxo completo no terminal
src/main/java/br/fatec/esiii/socagent/service/AgentProperties.java Properties do agente (planner/confiança/aprovador)
src/main/java/br/fatec/esiii/socagent/observer/EventListenerRegistrar.java Registro automático de listeners declarados como bean
src/main/java/br/fatec/esiii/socagent/observer/ConsoleListener.java Listener para log no console (Observer)
src/main/java/br/fatec/esiii/socagent/observer/AuditTrailListener.java Listener que materializa trilha de auditoria em memória
src/main/java/br/fatec/esiii/socagent/observer/AgentEventListener.java Interface de listener com supports e listenerName
src/main/java/br/fatec/esiii/socagent/observer/AgentEventBus.java Barramento com isolamento de falhas e CopyOnWriteArrayList
src/main/java/br/fatec/esiii/socagent/observer/AgentEvent.java Record de evento e enum de tipos publicados pelo agente
src/main/java/br/fatec/esiii/socagent/mitre/MitreRepository.java Repositório offline de técnicas MITRE ATT&CK
src/main/java/br/fatec/esiii/socagent/gui/tree/EvidenceTreeBuilder.java Builder da árvore de evidências (Composite) a partir do incidente
src/main/java/br/fatec/esiii/socagent/gui/tree/EvidenceNode.java Interface do Composite com operações recursivas (render, severidade, contagem)
src/main/java/br/fatec/esiii/socagent/gui/tree/EvidenceLeaf.java Folha do Composite (IOC/técnica/nota)
src/main/java/br/fatec/esiii/socagent/gui/tree/EvidenceGroup.java Composto do Composite (agrupamento de nós)
src/main/java/br/fatec/esiii/socagent/gui/SocDashboardFrame.java GUI Swing (painel), seleção de cenário/estratégia, botões e renderização da árvore
src/main/java/br/fatec/esiii/socagent/gui/GuiLauncher.java Launcher condicionado a profile/headless e setup de Look&Feel
src/main/java/br/fatec/esiii/socagent/domain/TriageVerdict.java Modelo de veredito (classificação/confiança/justificativa/técnicas) e regra de acionabilidade
src/main/java/br/fatec/esiii/socagent/domain/Severity.java Enum de severidade com agregação e regra de contenção por nível
src/main/java/br/fatec/esiii/socagent/domain/MitreTechnique.java Record de técnica ATT&CK (inclui unknown)
src/main/java/br/fatec/esiii/socagent/domain/Ioc.java Record de IOC com validação e helpers de criação
src/main/java/br/fatec/esiii/socagent/domain/Alert.java Record de alerta e formatação compacta para prompt
src/main/java/br/fatec/esiii/socagent/command/SimulatedContainmentGateway.java Gateway simulado com estado em memória (isolamento/bloqueio)
src/main/java/br/fatec/esiii/socagent/command/LookupMitreCommand.java Comando de consulta MITRE (somente leitura)
src/main/java/br/fatec/esiii/socagent/command/IsolateHostCommand.java Comando destrutivo e reversível (isolar/restaurar host)
src/main/java/br/fatec/esiii/socagent/command/ContainmentGateway.java Porta para execução de contenção (EDR/firewall/NAC)
src/main/java/br/fatec/esiii/socagent/command/CommandResult.java Record de resultado (ok/failure/refused) com duração
src/main/java/br/fatec/esiii/socagent/command/CommandInvoker.java Invoker: fila, autorização, execução, undo e publicação de eventos
src/main/java/br/fatec/esiii/socagent/command/CollectForensicsCommand.java Comando de coleta forense (leitura)
src/main/java/br/fatec/esiii/socagent/command/BlockIpCommand.java Comando destrutivo e reversível (bloquear/desbloquear IP)
src/main/java/br/fatec/esiii/socagent/command/AgentCommand.java Interface base de Command (requiresApproval/undoable/undo)
src/main/java/br/fatec/esiii/socagent/ai/LlmThreatAnalyst.java Implementação ThreatAnalyst com Spring AI + prompts estruturados e degradação para revisão humana
src/main/java/br/fatec/esiii/socagent/ai/AnalystResponses.java Records de saída estruturada (veredito/plano/próximo passo)
README.md Documentação de uso, stack/licenças, cenários, configuração e decisões de projeto
pom.xml Build Maven (Spring Boot 3.5.3, Spring AI BOM 1.0.0, Java 21, dependências)
mvnw.cmd Maven Wrapper (Windows)
mvnw Maven Wrapper (Unix)
docs/arquitetura.md Documento de arquitetura com diagramas Mermaid (padrões e fluxos)
.mvn/wrapper/maven-wrapper.properties Propriedades do Maven Wrapper (URL da distribuição)
.gitignore Ignora target/IDE/OS artifacts

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +74 to +79
public List<AgentCommand> createAll(List<ProposedAction> actions, String defaultHostname) {
return actions.stream()
.map(action -> create(action, defaultHostname))
.flatMap(Optional::stream)
.toList();
}
Comment on lines +70 to +74
if (candidate.requiresApproval()) {
pendingContainment.add(candidate);
observations.add("Acao '%s' acumulada para apos aprovacao humana".formatted(candidate.name()));
} else {
observations.add(candidate.execute().output());
List<String> techniqueIds) {

public TriageVerdict {
confidence = Math.clamp(confidence, 0.0, 1.0);
Comment on lines +251 to +258
private void setBusy(boolean busy) {
triageButton.setEnabled(!busy);
scenarioSelector.setEnabled(!busy);
plannerSelector.setEnabled(!busy);
if (busy) {
statusLabel.setText("Consultando o modelo...");
}
}
Comment on lines +42 to +45
public Incident open(List<Alert> alerts) {
String id = "INC-%04d".formatted(sequence.getAndIncrement());
return new Incident(id, alerts, eventBus);
}
Comment on lines +12 to +15
* <p>O runtime do agente conhece apenas esta interface. Trocar de
* {@link ReActPlanner} para {@link PlanThenExecutePlanner} nao altera uma linha
* do orquestrador, e envolver qualquer um deles em
* {@link HumanInTheLoopPlanner} acrescenta governanca sem modifica-los.
@DanielDPereira
DanielDPereira merged commit 66e47fc into main Aug 10, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants