Files
A-Team-Security-Infra-Agent…/LESSONS_LEARNED.md

19 KiB

OCI CIS Agent — Lições Aprendidas (Erros & Acertos)

Documento de referência para uso como prompt em projetos futuros. Projeto: FastAPI + React 19 SPA + OCI GenAI + MCP + Terraform Agent Evolução: v1.0 → v2.8 (~4 meses de desenvolvimento iterativo)


1. Arquitetura & Decisões Fundamentais

ACERTOS

1.1 Chat Assíncrono desde o início (v2.0)

  • Padrão: POST /api/chat retorna imediatamente com message_id; frontend faz polling em /api/chat/{mid}/status
  • Resultado: Eliminou 504 timeouts em respostas lentas do GenAI
  • Regra: Para qualquer endpoint que dependa de LLM, adote async-first desde o dia 1. Polling a cada 2s é aceitável

1.2 SQLite como banco principal para monolito

  • Decisão: SQLite com WAL mode + busy_timeout=30s para estado compartilhado
  • Resultado: Rápido (<5ms leitura), ACID, zero containers extras, funciona até ~12 chats simultâneos
  • Regra: Use SQLite para monolitos. Migre para banco dedicado quando atingir ~50 requests concorrentes

1.3 Multi-worker com estado no banco (não em memória)

  • Problema: 8 workers uvicorn = 8 processos separados. Dicts em memória NÃO são compartilhados
  • Solução: Todo estado compartilhado vai para SQLite (sessões, cache MCP, workspaces Terraform)
  • Regra: Ao escalar workers, assuma que memória é local. Banco de dados é a fonte de verdade

1.4 Cache agressivo para APIs externas

  • Padrão: MCP sessions cacheadas por 2h; Terraform resource docs cacheados no SQLite
  • Resultado: Scans OCI caíram de ~3min para <10s no cache hit
  • Regra: Cache tudo que vem de API externa. TTL de 2h é prático para OCI; ajuste por serviço

1.5 System prompts compactos para geração de código

  • Evolução: Prompt do Terraform foi de ~200 linhas → ~30 linhas (~2.3K chars)
  • Descoberta: GPT-5.2/o3 via OCI funcionam melhor com regras concisas que com exemplos verbosos
  • Regra: Para agentes de código, priorize regras > exemplos. Alvo: <3K chars no system prompt. Use exemplos apenas para casos ambíguos

1.6 Validação de tipos com fuzzy matching

  • Implementação: terraform providers schema -json → SQLite (~937 tipos OCI) + difflib.get_close_matches
  • Resultado: Modelo gera oci_core_vcn_x → sistema sugere oci_core_vcn
  • Regra: Para geração de código, valide contra catálogo estático. Gere na startup do container, cache no banco

1.7 Geração de referência no startup (não no build)

  • Problema: Arquivo gerado no build → perdido quando volume Docker sobrescreve o diretório
  • Solução: Gerar no entrypoint do container, salvar em /data/
  • Regra: Docker volume mounts sobrescrevem artefatos de build. Dados dinâmicos devem ser gerados em runtime

1.8 RAG separado por contexto de agente

  • Problema: Terraform Agent tentava buscar RAG no ADB (banco vetorial externo) que ficava parado, travando o chat
  • Solução: Terraform usa apenas SQLite local (resource reference + docs oficiais). ADB só para Chat Agent
  • Regra: Cada agente deve ter suas fontes de dados bem definidas. Não compartilhe pipelines de RAG entre agentes com necessidades diferentes

ERROS

1.9 Provider region hardcoded no Terraform

  • Bug: provider.tf era gerado com region = "us-ashburn-1" (string fixa da OCI config) em vez de var.region
  • Impacto: Recursos provisionados em Ashburn ao invés de Madrid → erro de quota → recursos órfãos para limpar manualmente
  • Correção: Provider sempre usa var.region. Se modelo não declara variable "region", plan é bloqueado com erro explicativo
  • Regra: NUNCA hardcode valores de infraestrutura. Sempre use variáveis. Implemente guardrails que bloqueiem antes de provisionar

1.10 Estado travado após restart do container

  • Bug: Workspace com status planning/applying fica preso se container reinicia — nenhum botão aparece no frontend
  • Correção: Frontend mostra botão Plan para todos os estados (incluindo os "travados")
  • Regra: Sempre considere o cenário de restart. Status intermediários devem ter recovery path

1.11 HCL single-line blocks inválidos

  • Bug: Modelos geram variable "x" { type = string, default = "y" } (inválido em HCL)
  • Correção: Regex auto-fix em frontend E backend para expandir para multi-line
  • Regra: Para geração de código, implemente auto-correção de sintaxe em múltiplas camadas

1.12 Modelo descarta arquivos na correção

  • Bug: Ao corrigir erros, modelo retorna APENAS arquivos corrigidos, descartando os não alterados
  • Correção: Merge-based updates: arquivos existentes com mesmo nome → substituídos; sem match → preservados
  • Regra: Workflows de correção AI precisam de semântica de merge, não de substituição

1.13 sqlite3.Row não suporta .get()

  • Bug: row.get("column") crasha porque sqlite3.Row não tem método .get()
  • Correção: Usar try/except com acesso direto row["column"] ou converter para dict(row)
  • Regra: Conheça as limitações do seu ORM/driver. sqlite3.Row é dict-like mas não é dict

1.14 reasoning_effort case-sensitive no OCI SDK

  • Bug: OCI SDK requer "HIGH" (uppercase), não "high"
  • Regra: Sempre consulte a documentação do SDK para formatos exatos. Normalize inputs com .upper() antes de enviar

1.15 Index.html monolítico

  • Status: ~2820 linhas de vanilla JS — manutenção cada vez mais difícil
  • Limite prático: ~2000 linhas para SPA vanilla. Acima disso, framework de componentes se paga
  • Plano: Migração para React (Vite + TypeScript), mantendo FastAPI backend

2. Pipeline de Geração de Código (Terraform)

ACERTO: Pipeline 3 camadas

  1. Frontend (_splitTfMonolith): Detecta arquivos monolíticos (≤2 blocos + >2000 chars) e divide
  2. Backend (_split_tf_monolith): Validação Python, split por categoria (networking.tf, compute.tf)
  3. Deduplicação (_write_tf_files): Remove declarações duplicadas de recursos

Regra: Para geração de código, implemente validação + auto-correção em camadas (client + server). Belt-and-suspenders funciona.

ACERTO: Checklist de validação no prompt

14 pontos obrigatórios no system prompt:

  • Cross-references entre recursos
  • Validação de CIDRs
  • Security lists + route tables
  • DRG attachments + RPC peering
  • Sintaxe HCL (blocos multi-line)
  • Tipos de recursos válidos
  • Declaração de variáveis

Regra: Checklists no prompt funcionam melhor que exemplos para evitar erros comuns

ACERTO: Auto-inject de terraform.tfvars

  • Scan de variáveis declaradas nos .tf files
  • Map automático: tenancy_ocid, user_ocid, fingerprint, compartment_id, ssh_public_key
  • Valores vêm da OCI config ativa (criptografada no banco)

Regra: Automatize tudo que pode ser derivado de configuração existente. Menos inputs manuais = menos erros

ERRO: Timeout de polling insuficiente

  • Bug: Frontend tinha 1h de timeout para polling, mas operações grandes (multi-region) podiam levar mais
  • Regra: Timeout de polling deve ser generoso. 1h para plan, 2h para apply. Melhor sobrar que faltar

3. Integração com OCI

ACERTOS

  • OCID-based model resolution: Catálogo mapeia model IDs para OCIDs por região
  • 16 regiões OCI: Endpoints auto-gerados (https://inference.generativeai.{region}.oci.oraclecloud.com)
  • Wallet auto-parse: Upload de .zip → extrai password + DSN automaticamente
  • GenAI config auto-resolve: Detecta config a partir de credenciais OCI (menos setup manual)

ERROS

  • VCN deletion em cascata: VCNs não podem ser deletadas com dependências (subnets, route tables, security lists, DRG attachments, RPCs). Ordem: RPCs → DRG Attachments → Subnets → SGWs → non-default RTs/SLs → VCNs → DRGs
  • Default resources não deletáveis: Route tables e security lists default são tied ao lifecycle da VCN — não tente deletar independentemente
  • SSH key ≠ API key: SSH public key (para compute) é diferente da private key OCI API. Armazene separadamente na config

4. Frontend / UX

ACERTOS

  • SVG icons (Lucide): Consistência cross-OS, themeable via CSS, zero CDN
  • CSS variables para tema: Dark/light mode com toggle simples (:root { --bg: ... })
  • Oracle Dark Premium: Palette documentada (#0D0F12 background, #C74634 accent)
  • Chart.js para dashboards: Leve, sem dependência de React, gauge + donut + bar
  • Confirmation modals: Digitar "DESTROY"/"ROLLBACK" para ações destrutivas

ERROS

  • Emojis inconsistentes: ⚠️ renderizam diferente em cada OS → SVG resolveu
  • Layout complexo sem framework: Resize handles, split panels, tabs — tudo manual em vanilla JS. Funciona mas é frágil
  • Scroll references quebradas: Renomear CSS classes quebra scroll automático silenciosamente

5. Operacional & Deploy

Padrão de Deploy

docker compose up -d --build backend && docker compose restart frontend
  • Backend: rebuild necessário (código Python)
  • Frontend: restart necessário (nginx serve index.html via bind mount, mas precisa reload)

RCP (Shortcut do usuário)

  • README: atualizar changelog
  • Commit: mensagem descritiva
  • Push: enviar para remote

Versionamento

  • Bump de versão no badge do README + entrada no changelog
  • Versão semântica simplificada (v2.1, v2.2, etc.)

6. Regras para AI Coding Assistant

Fazer

  • Ler arquivo antes de editar (entender contexto)
  • Preferir editar existente vs criar novo (evitar file bloat)
  • Deploy combo sempre (backend build + frontend restart)
  • Testar cenários de restart/recovery
  • Validar inputs em múltiplas camadas

Não fazer

  • Incluir atribuição AI em commits/código
  • Hardcode valores de infraestrutura
  • Compartilhar pipeline de RAG entre agentes diferentes
  • Injetar variáveis automaticamente quando o modelo esquece (forçar correção)
  • Usar .get() em sqlite3.Row
  • Confiar que status intermediários vão se resolver sozinhos

7. Stack & Trade-offs

Componente Escolha Por quê Limite
Backend FastAPI Python-first (OCI SDK), async nativo Mais lento que Go/Rust
Frontend Vanilla JS → React Zero build step inicialmente ~2000 linhas é o limite prático
Banco SQLite → Oracle ATP Simples, ACID, sem container extra Single-machine, ~50 req/s
Cache In-memory → Redis Implícito; migrar quando distribuir Não compartilhado entre workers
IaC Terraform CLI (subprocess) Testado, familiar Overhead de processo; file locking
Charts Chart.js Leve, funciona sem React Customização limitada vs D3
Icons SVG inline (Lucide) Themeable, consistente Cherry-picking manual

8. Métricas de Referência

Arquivo Linhas Propósito
backend/app.py ~7200 FastAPI server, todos endpoints, state
frontend-react/src/ ~15500 React 19 SPA (42 source files)
backend/mcp_cis_server.py ~700 MCP server, 12 tools CIS
backend/gen_tf_reference.py ~70 Gerador de catálogo Terraform

9. ADB Oracle — Embedding & Vector Search (v2.8+)

ACERTOS

9.1 Auto-mapeamento CSV → tabela ADB

  • Cada CSV do CIS report mapeia automaticamente para sua tabela: cis_Identity_*.csvidentityandaccess, cis_Networking_*.csvnetworking, etc.
  • Regra: Quando possível, derive o destino do nome do arquivo. Menos configuração manual = menos erro

9.2 Purge antes de re-embed

  • Deletar dados antigos do mesmo tenancy + extract_date antes de inserir novos
  • Usa JSON_VALUE(METADATA, '$.tenancy') para filtrar
  • Regra: Sempre limpe antes de re-inserir. Duplicatas em bases vetoriais poluem resultados

9.3 Auto-detect dimensão de embedding por tabela

  • _get_table_embedding_dim()_DIM_TO_MODEL = {1536: "text-embedding-3-small", 3072: "text-embedding-3-large"}
  • Cache de embeddings por modelo: se 5 tabelas usam o mesmo modelo, gera 1 embedding só
  • Regra: Nunca assuma que todas as tabelas têm a mesma dimensão. Detecte dinamicamente

9.4 Chunking com header de contexto

  • Textos >8000 chars divididos em partes, cada parte repete header (Tenancy, Resource, ID)
  • Cada chunk é auto-suficiente para busca vetorial
  • Regra: Chunks sem contexto são inúteis. Sempre repita metadados essenciais

9.5 Tabelas globais vs tenant-specific

  • cisrecom e engineerknowledgebase: busca SEM filtro de tenancy (dados genéricos)
  • Tabelas de findings: busca COM filtro de tenancy (dados específicos)
  • Regra: Separe dados genéricos de dados específicos. Filtrar dados genéricos por tenancy retorna zero resultados

9.6 Hierarquia de fontes no RAG

  • Findings (dados reais) > cisrecom (remediação oficial) > engineerknowledgebase (complementar)
  • RAG vs MCP Tools: system prompt diferencia dados armazenados vs scan em tempo real
  • Regra: O modelo precisa saber a confiabilidade relativa de cada fonte

ERROS

9.7 ID RAW(16) no ADB

  • Tabelas usam ID RAW(16), não VARCHAR. Inserts devem usar HEXTORAW(:1) com uuid.uuid4().hex.upper()
  • Erro: ORA-01465: invalid hex number
  • Regra: Verifique o schema da tabela ANTES de implementar inserts

9.8 METADATA tipo JSON, não CLOB

  • LIKE '%..%' não funciona com colunas JSON. Usar JSON_VALUE(METADATA, '$.field') = :1
  • Erro: ORA-01722: unable to convert string containing '%' to a number
  • Regra: Cada tipo Oracle tem suas funções. JSON → JSON_VALUE/JSON_EXISTS. CLOB → LIKE

9.9 FETCH FIRST bind variable

  • Oracle não aceita bind variable no FETCH FIRST :1 ROWS ONLY
  • Usar f-string: FETCH FIRST {int(n)} ROWS ONLY (safe pois n é int do Python)
  • Regra: Nem toda cláusula SQL aceita bind variables. Teste cada uso

9.10 Token limit do modelo de embedding

  • text-embedding-3-large aceita max 8192 tokens. truncate="END" do OCI não funciona com modelos OpenAI
  • Solução: truncar no código Python (8000 chars max) + chunking (8000 chars por parte)
  • Regra: Não confie em flags do SDK para limites. Implemente truncation no seu código

9.11 Dados legados sem metadata estruturado

  • Embeddings antigos tinham metadata em formato diferente (source: blob, blobType, doc_id), sem campo tenancy
  • Filtro JSON_VALUE(METADATA, '$.tenancy') IS NULL incluía esses dados indevidamente
  • Solução: remover fallback IS NULL, aceitar que dados legados precisam ser re-embedados
  • Regra: Ao mudar schema de metadata, considere migração dos dados existentes

10. React Migration & State Management (v2.7→v2.8)

ACERTOS

10.1 Zustand para persistência entre abas

  • Todas as configurações (model, params, tools, sessions, messages) no Zustand store
  • Sobrevive navegação entre páginas sem re-fetch
  • Regra: Estado que o usuário espera manter ao voltar → Zustand. Estado transiente (loading, dropdown open) → useState local

10.2 Code splitting com React.lazy

  • Bundle de 1.3MB → ~550KB initial load (15 chunks por página)
  • Suspense fallback com spinner
  • Regra: Implementar lazy loading desde o início. Custo zero de implementação, ganho significativo

10.3 Background tasks com polling persistente

  • task_id salvo no Zustand store → polling retoma ao voltar para a página
  • Status em arquivo no disco (não dict em memória) → visível entre workers
  • Regra: Todo task longo precisa: 1) backend salvar status em disco/DB, 2) frontend salvar task_id no store, 3) retomar polling ao montar

ERROS

10.4 useCallback com closure stale

  • sessionId capturado na closure do useCallback ficava desatualizado
  • Solução: usar useAppStore.getState() para ler valor atual no momento da execução
  • Regra: Em callbacks assíncronos, sempre leia state do store diretamente, nunca da closure

10.5 setInterval inline no onClick

  • Não limpa no unmount, pode duplicar. Race condition com estado
  • Solução: useEffect com cleanup, ou usePolling hook customizado
  • Regra: Nunca crie timers/intervals dentro de event handlers. Sempre use useEffect com cleanup

10.6 X-Frame-Options DENY bloqueando iframes internos

  • Security header bloqueava compliance report e HTML report renderizados em iframes
  • Solução: SAMEORIGIN no bloco /api/ do nginx
  • Regra: Ao adicionar security headers, teste TODOS os fluxos que usam iframes

11. Security & Docker Hardening (v2.8)

ACERTOS

  • Non-root container: gosu entrypoint para chown do volume + exec como user agent
  • Security headers nginx: X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, Referrer-Policy, Permissions-Policy
  • Rate limiting: 10 tentativas/5min por IP com threading.Lock
  • CORS restritivo: métodos/headers explícitos, origins configurável via env
  • .gitignore + .dockerignore: prevenir leak de secrets e reduzir build context
  • Graceful shutdown: terminar subprocesses + executor no shutdown event
  • File upload validation: 50MB max, extensão whitelist por endpoint
  • 16 SQLite indexes: em foreign keys e colunas frequentemente consultadas

ERROS

  • CORS allow_origins=["*"]: acidentalmente permitia CSRF. Sempre restringir
  • Container rodando como root: padrão do Docker se não especificar USER
  • Bare except clauses: 13 instâncias engolindo erros silenciosamente. Sempre logar
  • Sem timeout em chamadas externas: GenAI e ADB podiam travar indefinidamente. Sempre definir timeout

12. Gaps Conhecidos (para endereçar em projetos futuros)

  1. Testes automatizados: Sem cobertura. Pré-requisito para migração PostgreSQL
  2. PostgreSQL + Redis: Planejado para escalar (multi-container, concorrência real)
  3. Dashboard real: Painel com compliance score, atividade recente, status ADB
  4. Notificações: Toast/badge quando tasks longos completam
  5. Busca cross-report: Comparar compliance entre datas
  6. Drift detection: Terraform não detecta mudanças manuais

13. Recomendações para Próximo Projeto

  1. Async-first para qualquer coisa que dependa de LLM
  2. SQLite para começar, migrar quando necessário
  3. System prompts compactos (<3K chars, regras > exemplos)
  4. Validação em 3 camadas para geração de código
  5. Cache agressivo para APIs externas (TTL 2h)
  6. CSS variables para tema desde o dia 1
  7. SVG icons em vez de emojis
  8. Framework de componentes quando ultrapassar ~2K linhas de frontend
  9. Auto-update de ferramentas terceiras no startup do container
  10. Guardrails de segurança (bloquear antes de provisionar, não corrigir silenciosamente)
  11. Recovery paths para todos os estados intermediários
  12. Separar fontes de RAG por agente/contexto
  13. Nunca hardcode valores de infraestrutura — sempre variáveis
  14. Documentar deploy patterns desde o início