Santosdevbjj/geo-explorer-com-ibm-bob:基于 IBM Bob 的智能 DevSecOps 安全分析流水线
Santosdevbjj/geo-explorer-com-ibm-bob
Santosdevbjj/geo-explorer-com-ibm-bob 是一个基于 IBM Bob 开发的防御性安全分析流水线,接收 auth_attempt、http_request、dns_query 和 network_flow 事件,经 Pydantic 校验、显式规则检测与 IsolationForest 基线模型,输出带 score、优先级和触发信号的告警。
Intelligent DevSecOps Sentinel
Como transformar um volume alto de eventos de segurança em alertas priorizados, explicáveis e mensuráveis, sem tratar toda anomalia como ataque confirmado?
Visão geral
O Sentinel é um pipeline analítico de segurança defensiva. Ele recebe eventos de autenticação, HTTP e rede, valida a qualidade dos dados, aplica regras transparentes de detecção e entrega alertas com score, prioridade e os sinais exatos que os geraram. Um modelo de ML (IsolationForest) roda ao lado como baseline experimental.
Quem se beneficia: o analista que precisa decidir o que investigar primeiro, o engenheiro de dados que precisa confiar na entrada, e o revisor técnico que precisa rastrear por que um alerta existe.
Importante: o projeto roda sobre dados sintéticos. Ele demonstra engenharia, método e governança. Ele não é um SIEM de produção e não comprova detecção de ataques reais.
Desenvolvido a partir do desafio Geo-Explorer com IBM Bob, usando o IBM Bob como assistente de desenvolvimento (comandos em .bob/commands/).
Objetivo do projeto
Eu criei este projeto para praticar, ponta a ponta, o trabalho de Security Data Science: sair do dado bruto, passar por validação, regras, modelo, avaliação e monitoramento, e chegar a uma API e um dashboard que outra pessoa consegue usar e auditar.
Três decisões guiaram tudo:
- Explicar antes de impressionar. Todo alerta carrega seus sinais, limiares e limitações.
- Não afirmar o que não foi medido. Cada resultado é separado em fato medido, hipótese e limitação.
- Resolver o problema do analista, não exibir uma ferramenta. O foco é reduzir ruído na triagem; o scikit-learn é um meio.
1. Problema de negócio
Equipes de segurança recebem eventos de autenticação, HTTP e rede em volume elevado. Alertas sem contexto geram ruído, aumentam o tempo de triagem e dificultam explicar por que uma atividade foi priorizada. O risco é duplo: perder um ataque real no meio do ruído, ou tratar comportamento legítimo como ataque.
A pergunta de negócio deste projeto:
Como priorizar eventos suspeitos de forma que o analista saiba o que investigar primeiro e por quê?
| Item | Descrição |
|---|---|
| Quem decide | Analista de segurança (o que investigar), engenheiro de dados/ML (qualidade da entrada), revisor técnico (rastreabilidade) |
| Dados de entrada | Eventos auth_attempt, http_request, dns_query e network_flow (schema em docs/data-dictionary.md) |
| Ameaças cobertas | Força bruta, pico de tráfego e padrões de injeção (SQL, XSS, path traversal) |
| Fora do escopo | Bloqueio automático de IPs, resposta a incidentes, atribuição de intenção a pessoas, garantia de precisão ou de economia financeira |
Detalhes em docs/business-context.md.
3. Premissas
- Os eventos são sintéticos, gerados com seed fixo por
scripts/generate_synthetic_data.py. Nenhum dado real, pessoal ou segredo é usado. - O campo
metadata.labelexiste só para avaliação e nunca entra como feature (sem vazamento de alvo). - O score de alerta não é probabilidade calibrada.
- As análises identificam padrões, não causalidade.
- Data drift não prova perda de acurácia. Medir degradação exige rótulos posteriores.
- Um alerta é um sinal para investigação, não confirmação de ataque. Ações que afetam sistemas ou usuários exigem revisão humana.
4. Baseline
Um projeto de dados só prova valor quando supera uma referência. Aqui há duas camadas:
| Camada | O que é | Papel |
|---|---|---|
| Status quo (referência de negócio) | Triagem sem priorização: todo evento anômalo recebe o mesmo peso | O que o Sentinel precisa superar em organização e explicabilidade |
| Baseline técnica | Regras explícitas (BruteForceRule, TrafficSpikeRule, InjectionRule) |
Referência determinística contra a qual o IsolationForest é comparado |
O módulo evaluation/metrics.py calcula a comparação com a baseline e registra o resultado mesmo quando o modelo não a supera. Os números não são publicados neste README porque dependem do dataset, do hash e do split de cada execução (veja Performance).
5. Planejamento da solução
logs / eventos
│
▼
┌──────────────────────────────────────────────────────────┐
│ src/sentinel/ │
│ ├── data/validation.py ← schema, ausentes, duplicatas
│ ├── features/engineering.py ← features por IP e janela │
│ ├── detection/ │
│ │ ├── rules.py ← regras explícitas │
│ │ ├── model.py ← IsolationForest (baseline)│
│ │ └── scoring.py ← score, prioridade, sinais │
│ ├── evaluation/metrics.py ← precision/recall/F1/PR-AUC│
│ └── monitoring/drift.py ← data drift (PSI) │
└────────────────┬─────────────────────────────────────────┘
▼
api/index.py (FastAPI)
│
┌──────┴──────┐
▼ ▼
Dashboard Servidor MCP
(Next.js) (somente leitura)
| Etapa | Ferramenta |
|---|---|
| Contratos de dados e configuração | Pydantic v2, pydantic-settings |
| Processamento e features | Pandas, NumPy |
| Modelo | Scikit-learn (IsolationForest + StandardScaler), Joblib |
| API | FastAPI, Uvicorn |
| Dashboard | Next.js 14, TypeScript, Tailwind CSS |
| Integração com IA | Servidor MCP (SDK oficial, transporte stdio) |
| Qualidade e segurança | Pytest, Ruff, mypy, Bandit, pip-audit, Gitleaks |
| Deploy | GitHub Actions (CI), Vercel |
6. Limpeza e validação dos dados
A entrada passa por data/validation.py antes de qualquer análise:
- validação de schema com Pydantic (tipos, enums, faixas, ex.: porta 1–65535);
- detecção de campos ausentes e de
event_idduplicado; - registros inválidos são injetados de propósito no gerador (
--include-invalid) para provar que a validação funciona; - limites de segurança: tamanho máximo de lote (
MAX_BATCH_SIZE) e de evento (64 KiB).
Resultado observado no notebook de exploração (2.008 registros no arquivo): 8 inválidos intencionais foram separados, restando 2.000 para análise, e 1.993 passaram na validação Pydantic. Os 7 que falharam têm campos opcionais ausentes ou tipos errados, comportamento esperado nos casos "benignos parecidos" do gerador.
7. Análise exploratória (EDA)
O notebook 01_exploratory_analysis.ipynb trata cada observação como hipótese a validar, nunca como causa.
| Observação (fato medido no dataset sintético) | Interpretação cuidadosa |
|---|---|
| 2.000 eventos em ~1 hora, 1.916 IPs únicos | O gerador cria IPs aleatórios para tráfego normal. Em dados reais haveria IPs recorrentes; features baseadas em histórico de IP perdem poder aqui. |
request_path e user_agent ausentes em 52,4% |
Estrutural: só eventos HTTP têm esses campos. Não é dado corrompido. |
| Mix de eventos: 47,6% HTTP, 28,9% auth, 13,7% DNS, 9,8% fluxo de rede | Reflete os pesos configurados no gerador. |
| Status: 78,1% sucesso, 13,4% falha, 5,6% bloqueado, 3,0% timeout | Idem. Não assumimos que representa tráfego real. |
| Taxa de erro parecida entre tipos de evento (16% a 20%) | Tráfego normal usa os mesmos pesos de status; o sinal de ataque não aparece na média, aparece por IP e janela. |
| Janelas de 5 min suavizam picos que ocorrem em sub-segundos | Limitação identificada. Motivou a feature inter_event_min_ms. |
8. Preparação de dados e features
features/engineering.py calcula, por source_ip e janela deslizante (padrão 300 s), 16 colunas: duas de identificação (source_ip, window_start) e 14 features.
| Grupo | Features |
|---|---|
| Autenticação | auth_failure_rate, auth_failure_count |
| HTTP | request_count, unique_paths, req_per_second, scan_path_hits |
| Falhas | failure_ratio, error_count, timeout_ratio |
| Payload | payload_size_mean, payload_size_std |
| Temporal | inter_event_mean_ms, inter_event_min_ms |
| User-agent | scanner_agent_ratio |
Princípios aplicados: nenhuma feature usa metadata.label; todas são computáveis em inferência; a saída é determinística. O StandardScaler é ajustado somente no treino e reutilizado na inferência.
9. Treinamento e justificativa do algoritmo
| Escolha | Justificativa | Alternativa considerada |
|---|---|---|
| IsolationForest | Não supervisionado: não existe conjunto rotulado e autorizado. Rápido, sem exigir distribuição específica. | Modelo supervisionado, que exigiria rótulos reais |
Divisão temporal (test_split=0.20) |
Evita olhar o futuro durante o treino, ao contrário de um split aleatório | Split aleatório |
contamination=0.05, n_estimators=100, random_state=42 |
Parâmetros documentados e reproduzíveis | Ajuste por busca, sem dados reais que o justifiquem |
scripts/train_model.py registra seed, hash SHA-256 do dataset, features, configuração e metadados em models/metadata_<versão>.json. Binários de modelo não são versionados.
10. Performance do modelo
Métricas calculadas em numpy puro por evaluation/metrics.py: precision, recall, F1, macro-F1, PR-AUC, matriz de confusão e taxa de falsos positivos. A accuracy não é usada isoladamente, e métricas com denominador zero retornam None em vez de um número inventado.
python scripts/train_model.py
python scripts/evaluate_model.py # gera o relatório; a API o expõe em GET /metricsFato medido sobre a qualidade do código (execução de validação registrada em CHANGELOG.md e MIGRATION_NOTES.md): 357 passed, cobertura de 97% em src/sentinel/.
O que ainda não foi medido: Ruff, mypy, Bandit, pip-audit e o build do frontend não foram executados nesse ambiente de validação. Rode-os antes de qualquer release.
Por que não há números de precision/recall aqui: métricas em dados sintéticos só descrevem o comportamento desse experimento. Publicá-las sem dataset, hash, split e baseline seria uma alegação sem evidência.
11. Business performance
O passo que converte o erro do modelo em impacto para o negócio. Sem dados reais, não existe valor honesto em R$, mas o modelo de custo já está definido:
custo_mensal_residual = (FN × custo_de_um_incidente_não_detectado)
+ (FP × custo_de_triagem_de_um_falso_alerta)
| Parâmetro | Quem fornece | Status |
|---|---|---|
FN e FP por período |
Avaliação em dataset autorizado e rotulado | Pendente |
| Custo de um incidente não detectado | Gestão de risco / segurança | Pendente |
| Custo de triagem de um falso alerta | Tempo médio do analista × custo-hora | Pendente |
Critérios para afirmar valor no futuro (de docs/business-context.md): dataset autorizado, baseline comparável, custo de FP e FN, latência p95, throughput, estabilidade por segmento e protocolo de revisão humana.
12. Modelo e API em produção
A API é um adaptador HTTP fino: a lógica analítica vive em src/sentinel/ e nunca importa de api/.
| Método | Rota | Auth | Finalidade |
|---|---|---|---|
| GET | /health |
Pública | Saúde do serviço (não garante modelo carregado) |
| GET | /model-info |
Pública | Versão, disponibilidade do modelo, modo de inferência |
| POST | /ingest |
X-Api-Key |
Validação de lote |
| POST | /detect |
X-Api-Key |
Regras e geração de alertas |
| POST | /analyze |
X-Api-Key |
Análise de um evento tipado |
| POST | /analyze/batch |
X-Api-Key |
Análise de lote com limite configurável |
| GET | /alerts, /alerts/{id} |
Pública | Alertas da memória do processo |
| GET | /metrics |
Pública | Relatório de avaliação previamente gerado |
Estados que não se confundem: API saudável não implica modelo carregado, e modelo carregado não implica modelo útil. Se o artefato estiver ausente, a API continua em modo rules-only.
Segurança embutida: o segredo padrão da API é rejeitado em SENTINEL_ENV=production; schemas Pydantic validam a entrada; lotes e eventos têm limites; Gitleaks, Bandit e pip-audit rodam no CI. Veja docs/threat-model.md e docs/security-review.md.
Servidor MCP (IBM Bob): quatro ferramentas somente leitura (get_model_info, analyze_event, get_metrics_summary, get_drift_summary). Sem shell, sem escrita, sem deploy, sem caminhos escolhidos pelo cliente. Decisão em ADR-007.
Monitoramento: o PSI compara a distribuição atual das features com a referência (DRIFT_ALERT_THRESHOLD=0.1). Drift é gatilho de investigação, não de retreino automático (docs/model-monitoring.md).
13. Storytelling: do evento ao alerta
Exemplo ilustrativo, derivado das regras do código: um IP gera 18 falhas de autenticação em menos de 5 minutos.
{
"category": "brute_force",
"priority": "critical",
"score": 1.0,
"signals": [
{
"name": "repeated_auth_failures",
"source": "rule",
"value": 18.0,
"threshold": 5.0,
"contribution": 1.0
}
],
"limitations": "Score calculado a partir de regras heurísticas com limiares fixos. Sem calibração estatística: o valor numérico não representa uma probabilidade. Falsos positivos são esperados. Revise os sinais antes de tomar ação.",
"model_version": "rules-only"
}O analista lê: o que aconteceu (força bruta), quanto (18 falhas contra limiar 5), quão grave (crítico) e o que não pode concluir (o score não é probabilidade).
Sinais fracos não viram alerta por padrão: com ALERT_SCORE_THRESHOLD=0.6, uma rajada precisa de pelo menos 9 falhas para passar o corte. O dashboard Next.js (/, /alerts/[id], /metrics) apresenta esses alertas, seus sinais e as limitações.
Decisões técnicas
Cada decisão está registrada como ADR em docs/decisions/, com alternativas e trade-offs.
| ADR | Decisão | Alternativa rejeitada e por quê |
|---|---|---|
| 001 | Python modular (src/sentinel/) + FastAPI |
Monolito Flask e notebook como produto: dificultam teste e CI. Microserviços: prematuros para a v0.1. |
| 002 | Python 3.14 local, 3.12 no CI, 3.13 como smoke test | Fixar só 3.14: wheels e imagens de CI ainda podem faltar. Trade-off aceito: divergência entre local e CI. |
| 003 | Alertas em memória | SQLite em plataforma de disco efêmero seria falsa persistência. Revisar quando houver réplicas, auditoria ou SLA. |
| 004 | IsolationForest como baseline opcional | Só regras (sem ML), supervisionado (sem rótulos) ou ensemble (sem calibração). |
| 005 | API Key via X-Api-Key nos endpoints de escrita |
OAuth2/JWT e mTLS: custo alto para um portfólio sem múltiplos usuários. |
| 006 | Vercel (API serverless + Next.js) | Render, Fly.io e AWS Lambda: mais infraestrutura para um portfólio. |
| 007 | MCP local, stdio, somente leitura |
Ferramentas genéricas de shell/arquivos: superfície de risco inaceitável. |
Tecnologias utilizadas
- Python 3.12+ (alvo 3.14), FastAPI, Pydantic v2
- Pandas, NumPy, Scikit-learn (IsolationForest)
- Next.js 14, TypeScript, Tailwind CSS (dashboard)
- MCP (SDK oficial) para integração com o IBM Bob
- Pytest, Ruff, mypy, Bandit, pip-audit, Gitleaks e GitHub Actions
- Vercel para deploy
Como executar o projeto
Pré-requisitos: Python 3.12 ou superior (3.14 recomendado, veja .python-version), pip ou uv. Para o dashboard, Node.js 20.
# 1. Clonar git clone https://github.com/Santosdevbjj/geo-explorer-com-ibm-bob.git cd geo-explorer-com-ibm-bob # 2. Ambiente virtual python -m venv .venv source .venv/bin/activate # Linux/macOS .venv\Scripts\activate # Windows # 3. Dependências pip install -e ".[dev]" # Opcional, para o servidor MCP: pip install -e ".[dev,mcp]" # 4. Variáveis de ambiente cp .env.example .env # 5. Gerar dados sintéticos, validar e treinar python scripts/generate_synthetic_data.py python scripts/validate_data.py --input data/synthetic/events.jsonl python scripts/train_model.py # 6. Subir a API uvicorn api.index:app --reload # → http://localhost:8000/docs
Dashboard (opcional):
cd frontend
npm install
NEXT_PUBLIC_API_URL=http://localhost:8000 npm run devTestes e qualidade:
pytest # todos os testes com cobertura pytest tests/unit/ # unitários pytest tests/integration/ # integração (API e MCP) ruff check . # lint mypy src/sentinel/ api/ # tipagem bandit -r src/ api/ scripts/ -c pyproject.toml pip-audit # vulnerabilidades em dependências
Estrutura:
geo-explorer-com-ibm-bob/
├── .bob/ ← comandos e configuração do IBM Bob
├── .github/ ← CI (GitHub Actions)
├── api/ ← FastAPI
├── data/ ← raw, processed e synthetic
├── docs/ ← arquitetura, ADRs, model card, threat model
├── frontend/ ← dashboard Next.js
├── mcp/ ← servidor MCP
├── models/ ← artefatos treinados (não versionados)
├── notebooks/ ← análise exploratória
├── scripts/ ← geração de dados, validação, treino, avaliação
├── src/sentinel/ ← biblioteca principal
└── tests/ ← unit, integration, security
Aprendizados
- Honestidade técnica é um recurso de design. Escrever
limitationsdentro de cada alerta e um model card que proíbe "ensemble híbrido" enquanto o ML não compõe o score me forçou a separar o que o código faz do que eu gostaria que ele fizesse. - Dado sintético ensina engenharia, não eficácia. O notebook mostrou onde o gerador distorce a realidade (IPs quase todos únicos, janelas largas demais para picos de milissegundos). Esse tipo de achado só aparece quando se desconfia do próprio dado.
- Estado de saúde tem camadas. Separar
/health,/model-infoe a utilidade do modelo evitou o erro de tratar "API no ar" como "modelo funcionando". - Segurança desde o primeiro commit (Gitleaks, Bandit, pip-audit, segredo padrão rejeitado em produção) custa pouco no início e muito depois.
- O que faria diferente: definir o contrato de score por evento antes de escrever o modelo, e gerar o
package-lock.jsondo frontend desde o começo. - Ferramenta de IA como parceira, não como autora. O IBM Bob acelerou a revisão e a documentação; as decisões e a validação continuam sendo minhas.
Limitações assumidas
- Dados sintéticos não reproduzem a variedade de tráfego e ataques reais.
- O IsolationForest ainda não compõe o score final dos alertas: o score vem das regras explícitas.
- Alertas ficam em memória; reiniciar o processo os apaga.
- No deploy serverless, o modelo não é persistido e a API opera em modo
rules-only. docs/security-review.mdedocs/release-readiness.mdsão checklists a preencher com evidências, não certificações.
Próximos passos
- Obter um dataset autorizado e rotulado e repetir a avaliação com baseline comparável
- Definir o contrato de score por evento e integrar o IsolationForest ao scoring com calibração e limiares
- Preencher o modelo de custo FP/FN (seção Business performance) com dados reais
- Persistência durável de alertas e trilha de auditoria (gatilho do ADR-003)
- Rate limiting por IP e autenticação por usuário
- Gerar
package-lock.jsone executarnpm audit - Migrar o CI para Python 3.14 quando os critérios do ADR-002 forem atendidos
- Executar e registrar Ruff, mypy, Bandit, pip-audit e Gitleaks no commit candidato a release
Contribuição e segurança
Veja CONTRIBUTING.md e SECURITY.md (política de divulgação responsável).
Licença
MIT. Veja LICENSE.
Autor: Sérgio Santos, Cientista de Dados | Ambientes Críticos e Governança de Dados
来源:GitHub · github.com