
Hook de segurança de pré-escrita independente de hospedeiro para agente de codificação: detecta padrões de entrada do usuário via Semgrep e emite orientações de segurança determinísticas, sem LLM.
Um ponto de verificação de segurança para ferramentas de codificação com IA. Ele examina cada arquivo que um assistente de IA escreve e interrompe os perigosos antes que cheguem ao disco.
Assistentes de codificação com IA (Claude Code, Codex, …) escrevem código rápido — incluindo código que lida com coisas como senhas, e-mails, chaves de API ou entrada bruta do usuário. É fácil para um assistente enviar esses dados diretamente para uma consulta de banco de dados, um comando shell ou uma resposta HTTP sem pensar em segurança.
O VibeGate fica entre o assistente e seu sistema de arquivos. Toda vez que o assistente tenta escrever ou editar um arquivo, o VibeGate examina o novo código primeiro:
Nenhum LLM está envolvido na análise em si — é uma análise estática rápida e determinística, então nunca inventa nada e nunca custa tokens.
Aqui está tudo o que o VibeGate verifica atualmente:
| Verificação | O que detecta | Resultado |
|---|---|---|
| Injeção de comando | Entrada não sanitizada atinge um comando shell | Bloqueia |
| Injeção SQL | Entrada não sanitizada atinge uma consulta de banco de dados | Bloqueia |
| Injeção NoSQL | O corpo da requisição é usado diretamente como um filtro de banco de dados | Bloqueia |
| Injeção de template (SSTI) | A própria fonte do template, não apenas seus dados, vem da entrada do usuário | Bloqueia |
| Desserialização insegura | Dados não confiáveis atingem um desserializador inseguro (pickle, YAML inseguro, ...) | Bloqueia |
| Travessia de diretório (Path traversal) | Entrada não sanitizada atinge uma leitura, gravação ou exclusão de arquivo | Bloqueia |
| XXE | XML não confiável é analisado com entidades externas habilitadas | Bloqueia |
| XSS | Entrada não sanitizada é renderizada como HTML bruto | Bloqueia |
| Upload de arquivo irrestrito | O próprio nome do arquivo enviado é usado para construir o caminho de salvamento | Bloqueia |
| SSRF | O servidor busca uma URL que não está codificada | Avisa |
| Redirecionamento aberto | Um destino de redirecionamento que não está codificado | Avisa |
| Atribuição em massa (Mass assignment) | Todo o corpo da requisição é passado para um construtor ou atualizador de modelo | Avisa |
| Dados sensíveis em um corpo de requisição | E-mails, senhas, tokens, etc. lidos do corpo da requisição | Avisa |
| Dados sensíveis em uma URL/query | E-mails, senhas, tokens, etc. lidos da string de consulta | Avisa |
| Dados sensíveis em cabeçalhos | E-mails, senhas, tokens, etc. lidos dos cabeçalhos da requisição | Avisa |
| Caminho de arquivo vindo da entrada do usuário | Uma variável, não uma string codificada, é usada como caminho de arquivo | Avisa |
| Argumentos de CLI | Dados vêm de argumentos de linha de comando | Avisa |
| Entrada padrão (stdin) | Dados vêm da entrada padrão | Avisa |
| Variáveis de ambiente | Dados vêm de uma variável de ambiente | Avisa |
A lista completa e atual reside em guidance.TECHNICAL_RISKS e
formatter.BLOCKING_CATEGORIES, caso esta tabela algum dia se desatualize.
┌───────────────────────────────┐
│ Você pede ao Claude Code │
│ para escrever ou editar │
│ um arquivo │
└───────────────┬───────────────┘
│
▼
┌───────────────────────────────┐
│ Claude Code tenta salvar │
│ o arquivo (ferramenta │
│ Write/Edit) │
└───────────────┬───────────────┘
│
▼
┌───────────────────────────────┐
│ Hook do VibeGate │
│ (executa automaticamente, │
│ antes do arquivo ser salvo)│
└───────────────┬───────────────┘
│
escaneia o novo código com Semgrep
│
┌─────────────────────┼─────────────────────┐
│ │ │
▼ ▼ ▼
┌────────────────────┐ ┌────────────────────┐ ┌──────────────────────┐
│ Nenhuma entrada │ │ Entrada arriscada, │ │ Entrada arriscada │
│ arriscada │ │ mas risco menor │ │ atinge um sink │
│ encontrada │ │ (ex.: exibida em │ │ crítico │
│ │ │ uma resposta HTTP)│ │ (SQL/command/RCE, │
│ │ │ │ │ injeção de template)│
└─────────┬──────────┘ └─────────┬──────────┘ └───────────┬──────────┘
│ │ │
▼ ▼ ▼
Arquivo é salvo, Arquivo é salvo, Arquivo NÃO é salvo.
nada é exibido. mais um aviso no Claude Code vê
terminal com o o motivo do bloqueio
risco e como e é informado do que
corrigi-lo. corrigir.
Em resumo: código seguro passa intocado, código arriscado mas suportável é salvo com um aviso anexado, e código que está a um passo de coisas como injeção SQL, injeção de comando ou execução remota de código é interrompido antes de chegar ao disco.
Se o próprio VibeGate encontrar um erro inesperado, ele sempre permite a gravação — um bug no hook nunca deve ser o motivo pelo qual seu trabalho é bloqueado.
Cada aviso e bloqueio também carrega uma instrução explícita dizendo ao Claude Code para mencionar a descoberta a você em sua resposta, e não corrigi-la silenciosamente. É isso que torna a atividade do VibeGate visível na conversa, não apenas em um log de terminal que você teria que procurar.
| O que o VibeGate vê | O que acontece |
|---|---|
| Nenhuma entrada do usuário, ou um idioma que ainda não suporta | Arquivo salva normalmente, nada é exibido |
| Entrada do usuário encontrada, mas o risco é moderado (ex.: redirecionamento aberto, atribuição em massa) | Arquivo é salvo, terminal mostra um aviso + orientação |
| A entrada do usuário flui não sanitizada para um sink crítico (consulta SQL/NoSQL, comando shell, engine de template, desserializador, parser XML, caminho de arquivo, nome de arquivo enviado ou saída HTML bruta) | Arquivo não é salvo — Claude Code é informado do motivo |
Veja a tabela em "Qual problema isso resolve?" acima para a discriminação completa, por verificação, do que bloqueia vs. o que apenas avisa.
Hoje o VibeGate entende Python, JavaScript/TypeScript, Go, Java, PHP e Ruby, e se conecta ao Claude Code e ao Codex. Mais linguagens e ferramentas podem ser adicionadas sem tocar na lógica central.
Ele também verifica arquivos de workflow do GitHub Actions em busca de dois erros
comuns de supply-chain em CI/CD: actions fixadas em uma tag mutável (@v4) em vez de
um commit SHA, e o gatilho inseguro pull_request_target. Ambos avisam em vez de
bloquear, já que são verificações de hardening em vez de prova de uma exploração ativa.
Aqui está uma gravação real do Claude Code construindo um aplicativo leitor de feeds RSS do zero, com o VibeGate rodando o tempo todo. Observe os momentos em que o Claude Code para e diz explicitamente o que o VibeGate sinalizou e por que, antes de continuar — incluindo um risco real de SSRF no código de busca de feeds que ele corrige na hora.
Aqui está um segundo exemplo, como imagem estática: Claude Code está construindo um aplicativo que permite às pessoas enviar uma foto e ver seus detalhes. O VibeGate percebe que o nome do arquivo e outros detalhes do arquivo serão posteriormente exibidos na tela e avisa que isso poderia ser usado para injetar código prejudicial na página (isso é chamado de XSS). O Claude Code ajusta o código para que a informação seja exibida com segurança.
Em ambos os casos, nada foi bloqueado sem motivo, e ninguém precisou ler o código linha por linha para detectar o problema. O VibeGate o detectou no momento em que o arquivo foi escrito, e a IA corrigiu na hora.
Existem duas maneiras de fazer um assistente de IA escrever código mais seguro. Uma maneira é carregar um grande conjunto de instruções sobre codificação segura na conversa antes de começar, por exemplo, uma lista de verificação cobrindo injeção SQL, XSS, manipulação de senhas, uploads de arquivos e muito mais. A outra maneira é o que o VibeGate faz: verificar o código automaticamente, no momento em que um arquivo é escrito, e só falar quando algo está realmente errado.
A primeira abordagem custa tokens em cada mensagem, sejam eles necessários ou não. Uma lista de verificação de codificação segura típica cobrindo várias categorias de risco pode facilmente adicionar alguns milhares de tokens. Se um assistente de IA escreve 50 arquivos em uma sessão, e essa lista de verificação é recarregada ou mantida no contexto a cada vez, você pode estar pagando por bem mais de cem mil tokens de conselhos que, na maioria das vezes, não se aplicam ao arquivo que está sendo escrito agora. Uma página de login e um arquivo de constante de cor simples não precisam dos mesmos avisos, mas uma lista de verificação carregada não consegue distingui-los com antecedência.
O VibeGate inverte isso. Ele permanece em silêncio e não custa nada extra para cada arquivo que não possui padrão arriscado. Apenas quando encontra algo, como entrada do usuário fluindo para uma consulta de banco de dados, ele adiciona uma nota curta e específica sobre esse único problema, geralmente uma pequena fração do tamanho de uma lista de verificação completa. Então, em vez de pagar um custo fixo de tokens em cada arquivo, não importa o que, você paga um custo pequeno apenas nos arquivos que realmente precisam de atenção, e esse custo é direcionado exatamente ao problema encontrado, não a uma palestra geral sobre segurança.
Isso também torna a orientação mais confiável. Um assistente de IA instruído a "manter a segurança em mente" ao escrever cem linhas de código pode simplesmente perder uma linha arriscada entre muitas. Um gate não se cansa nem se distrai: ele verifica cada escrita, todas as vezes, usando as mesmas regras fixas.
Instale uma vez — isso também instala o Semgrep, do qual o VibeGate depende:
pipx install git+https://github.com/theMiddleBlue/vibegate
Em seguida, ative-o dentro do projeto que você deseja proteger:
cd seu-projeto
vibegate on # ativar aqui (recarregue o Claude Code depois)
vibegate status # verificar se está ativo para este projeto
vibegate off # desativar aqui
vibegate on adiciona um hook PreToolUse para Write|Edit|MultiEdit no
arquivo .claude/settings.local.json desse projeto. É limitado por projeto, então
ativá-lo em um repositório não afeta nenhum outro.
O Claude Code executa o hook como vibegate run --host claude_code — sem caminhos
absolutos envolvidos, então continua funcionando mesmo se você reinstalar ou mover as coisas.
vibegate status também mostra um log em execução do que o VibeGate realmente
capturou neste projeto — cada aviso e bloqueio, com o arquivo, linha e
categoria — para que você possa ver sua atividade ao longo do tempo em vez de apenas
saber se está ativado:
$ vibegate status
█ █ █████ ████ █████ ████ ███ █████ █████
...
● VibeGate está ATIVADO em .claude/settings.local.json
Atividade recente (últimos 2 de 2 registrados, mais recente primeiro):
2026-07-02T17:35:48+00:00 ⛔ BLOQUEADO server.py:3 EXEC_INPUT (FREE_TEXT)
2026-07-02T17:35:46+00:00 ⚠ AVISADO app.py:2 HTTP_BODY (EMAIL)
Este log reside em .vibegate/activity.jsonl na raiz do projeto — adicione-o ao
seu .gitignore, é estado local do desenvolvedor, não algo para commitar.
O VibeGate descobre com qual host está falando nesta ordem: uma flag explícita
--host <nome>, depois a variável de ambiente VIBEGATE_HOST, depois
detecção automática a partir do payload recebido, com fallback para claude_code.
Se o VibeGate sinalizar algo que você decidiu deliberadamente que é seguro, adicione um
comentário vibegate-ignore na mesma linha — funciona com qualquer sintaxe de comentário
(#, //, …), já que o VibeGate apenas procura pelo texto:
query = f"SELECT * FROM users WHERE id = {user_id}" # vibegate-ignore
Para suprimir apenas categorias específicas em vez de tudo nessa linha, liste-as após dois pontos (corresponde à categoria técnica ou ao tipo semântico, separados por vírgula, sem distinção entre maiúsculas/minúsculas):
query = f"SELECT * FROM users WHERE id = {user_id}" # vibegate-ignore: DB_QUERY
src/vibegate/
├── hook.py # ponto de entrada
├── cli.py # comandos on/off/status + o banner ASCII
├── activity_log.py # persiste avisos/bloqueios em .vibegate/activity.jsonl
├── colors.py # códigos de cor ANSI compartilhados (relatório + banner CLI)
├── core.py # pipeline independente de host
├── models.py # InputEvent / ClassifiedFinding / AnalysisResult
├── semgrep_runner.py # executa Semgrep como subprocesso (à prova de falhas)
├── classifier.py # mapeia regra Semgrep → categoria, nome de variável → tipo de dado
├── guidance.py # descrições estáticas de risco/remediação
├── formatter.py # transforma resultados em relatório de terminal + contexto do host
├── adapters/ # base, claude_code, codex + um pequeno registro
└── rules/ # regras Semgrep — um arquivo por linguagem (Python, JS/TS,
# Go, Java, PHP, Ruby) mais um placeholder genérico
O pipeline em si (core.py) nunca fala diretamente com um host específico — toda
entrada/saída específica do host reside em adapters/, então adicionar um novo host não
requer tocar na lógica de análise.
semgrep --validate --config src/vibegate/rules/ # verificar se as regras são válidas
pytest tests/ # testes unitários + de integração
Para vê-lo funcionar de ponta a ponta sem o Claude Code:
python3 -c 'import json; print(json.dumps({"tool_name":"Write","tool_input":{"file_path":"/tmp/t.py","new_content":"email = request.json.get(\"email\")"}}))' \
| python3 src/vibegate/hook.py --host claude_code
rules/<lang>-user-input.yaml, registre os novos
IDs de regra em classifier.RULE_TO_TECHNICAL, e mapeie a extensão do arquivo em
core.EXT_TO_LANGUAGE.classifier.VARNAME_TO_SEMANTIC e uma descrição em guidance.SEMANTIC_GUIDANCE.RULE_TO_TECHNICAL e um cartão em
guidance.TECHNICAL_RISKS.adapters/ e registre-o
em adapters/__init__.py.codex é um mapeamento inicial e de melhor esforço. Verifique novamente seu
contrato de evento com sua versão do Codex antes de confiar nele para bloquear
qualquer coisa."requires login" em vez da
linha real correspondida, então o classificador reconstrói o snippet por conta própria a partir do
conteúdo do arquivo usando números de linha.Edit/MultiEdit, o adaptador claude_code reconstrói o arquivo
completo pós-edição a partir do disco para que uma fonte contaminada e um sink introduzidos por
edições separadas ainda estejam conectados — mas apenas descobertas nas linhas que a edição
realmente tocou são relatadas. Se um sink já existe e uma edição posterior
adiciona apenas a fonte contaminada que o alcança, isso não será detectado
(a linha do sink não fez parte da nova edição). Esta reconstrução é
específica do Claude-Code; o adaptador codex ainda não a faz.| Ação do GitHub não fixada (unpinned) |
Um workflow usa uma tag mutável (@v4) em vez de um commit SHA |
| Avisa |
pull_request_target inseguro | Um workflow usa o gatilho pull_request_target | Avisa |
| Registro de credenciais (Credential logging) | Uma senha, chave de API ou token é passado para print/console.log/um logger | Avisa |
| Segredo codificado | Uma variável nomeada como um segredo recebe um valor literal com aparência real | Avisa |