
Hook de seguridad previo a la escritura, independiente del host, para agentes de codificación: detecta patrones de entrada del usuario mediante Semgrep y emite orientación de seguridad determinista y sin LLM.
Un punto de control de seguridad para herramientas de codificación con IA. Revisa cada archivo que un asistente de IA escribe y detiene los peligrosos antes de que lleguen al disco.
Los asistentes de codificación con IA (Claude Code, Codex, …) escriben código rápido, incluido código que maneja cosas como contraseñas, correos electrónicos, claves API o entrada de usuario sin procesar. Es fácil que un asistente envíe esos datos directamente a una consulta de base de datos, un comando de shell o una respuesta HTTP sin pensar en la seguridad.
VibeGate se sitúa entre el asistente y tu sistema de archivos. Cada vez que el asistente intenta escribir o editar un archivo, VibeGate escanea el nuevo código primero:
No interviene ningún LLM en el análisis en sí mismo: es análisis estático rápido y determinista, por lo que nunca inventa cosas y nunca te cuesta tokens.
Esto es todo lo que VibeGate verifica actualmente:
La lista completa y actualizada vive en guidance.TECHNICAL_RISKS y formatter.BLOCKING_CATEGORIES, por si esta tabla alguna vez se desvía.
┌───────────────────────────────┐
│ Le pides a Claude Code que │
│ escriba o edite un archivo │
└───────────────┬───────────────┘
│
▼
┌───────────────────────────────┐
│ Claude Code intenta guardar │
│ el archivo (herramienta │
│ Write/Edit) │
└───────────────┬───────────────┘
│
▼
┌───────────────────────────────┐
│ Hook de VibeGate │
│ (se ejecuta automáticamente, │
│ antes de guardar el archivo)│
└───────────────┬───────────────┘
│
escanea el nuevo código con Semgrep
│
┌─────────────────────┼─────────────────────┐
│ │ │
▼ ▼ ▼
┌────────────────────┐ ┌────────────────────┐ ┌──────────────────────┐
│ No se encontró │ │ Entrada riesgosa,│ │ Entrada riesgosa │
│ entrada riesgosa │ │ pero riesgo bajo │ │ llega a un sumidero│
│ │ │ (p. ej. mostrada │ │ crítico (SQL/ │
│ │ │ en una respuesta │ │ comando/RCE, │
│ │ │ HTTP) │ │ inyección de │
│ │ │ │ │ plantillas) │
└─────────┬──────────┘ └─────────┬──────────┘ └───────────┬──────────┘
│ │ │
▼ ▼ ▼
El archivo se guarda, El archivo se guarda, El archivo NO se guarda.
no se muestra nada. además de una advertencia Claude Code ve el motivo
en la terminal con el del bloqueo y se le dice
riesgo y cómo solucionarlo. qué corregir.
En resumen: el código seguro pasa sin cambios, el código riesgoso pero sobrevivible se guarda con una advertencia adjunta, y el código que está a un paso de cosas como inyección SQL, inyección de comandos o ejecución remota de código se detiene antes de llegar al disco.
Si VibeGate encuentra un error inesperado, siempre deja que la escritura continúe: un error en el hook nunca debería ser la razón por la que tu trabajo se bloquea.
Cada advertencia y bloqueo también lleva una instrucción explícita que le dice a Claude Code que mencione el hallazgo en su respuesta, no solo que lo corrija en silencio. Así es como la actividad de VibeGate se vuelve visible en la conversación, no solo en un registro de terminal que tendrías que buscar.
| Lo que VibeGate ve | Lo que sucede |
|---|---|
| Sin entrada de usuario, o un lenguaje que aún no admite | El archivo se guarda normalmente, no se muestra nada |
| Se encuentra entrada de usuario, pero el riesgo es moderado (p. ej., redirección abierta, asignación masiva) | El archivo se guarda, la terminal muestra una advertencia + orientación |
Consulta la tabla en "¿Qué problema resuelve esto?" más arriba para ver el desglose completo por comprobación de lo que bloquea frente a lo que solo advierte.
Hoy VibeGate entiende Python, JavaScript/TypeScript, Go, Java, PHP y Ruby, y se conecta con Claude Code y Codex. Se pueden agregar más lenguajes y herramientas sin tocar la lógica central.
También verifica los archivos de flujo de trabajo de GitHub Actions para detectar dos errores comunes de la cadena de suministro en CI/CD: acciones fijadas a una etiqueta mutable (@v4) en lugar de un SHA de commit, y el disparador inseguro pull_request_target. Ambos advierten en lugar de bloquear, ya que son comprobaciones de endurecimiento más que pruebas de un exploit activo.
Aquí hay una grabación real de Claude Code construyendo una aplicación lectora de feeds RSS desde cero, con VibeGate ejecutándose todo el tiempo. Observa los momentos en que Claude Code se detiene y dice explícitamente qué señaló VibeGate y por qué, antes de continuar, incluido un riesgo real de SSRF en el código de obtención de feeds que corrige en el acto.
Aquí hay un segundo ejemplo, como imagen fija: Claude Code está construyendo una aplicación que permite a los usuarios subir una foto y ver sus detalles. VibeGate nota que el nombre del archivo y otros detalles del archivo se mostrarán más tarde en pantalla, y advierte que esto podría usarse para inyectar código dañino en la página (esto se llama XSS). Claude Code ajusta el código para que la información se muestre de manera segura.
En ambos casos, nada se bloqueó sin motivo, y nadie tuvo que leer el código línea por línea para detectar el problema. VibeGate lo detectó en el momento en que se escribió el archivo, y la IA lo corrigió en el acto.
Hay dos formas de lograr que un asistente de IA escriba código más seguro. Una es cargar un gran conjunto de instrucciones sobre codificación segura en la conversación antes de que comience, por ejemplo, una lista de verificación que cubra inyección SQL, XSS, manejo de contraseñas, subidas de archivos y más. La otra forma es lo que hace VibeGate: verificar el código automáticamente, justo cuando se escribe un archivo, y solo hablar cuando algo está realmente mal.
El primer enfoque cuesta tokens en cada mensaje, se necesiten o no. Una lista de verificación de codificación segura típica que cubra varias categorías de riesgo puede agregar fácilmente unos miles de tokens. Si un asistente de IA escribe 50 archivos en una sesión, y esa lista se recarga o se mantiene en contexto cada vez, podrías estar pagando por más de cien mil tokens de consejos que, la mayoría de las veces, no se aplican al archivo que se está escribiendo en ese momento. Una página de inicio de sesión y un simple archivo de constantes de color no necesitan las mismas advertencias, pero una lista de verificación cargada no puede distinguirlos de antemano.
VibeGate invierte esto. Permanece en silencio, sin costo adicional, para cada archivo que no tiene un patrón riesgoso. Solo cuando encuentra algo, como entrada de usuario que fluye hacia una consulta de base de datos, agrega una nota breve y específica sobre ese único problema, generalmente una pequeña fracción del tamaño de una lista de verificación completa. Por lo tanto, en lugar de pagar un costo fijo de tokens en cada archivo pase lo que pase, pagas un costo pequeño solo en los archivos que realmente necesitan atención, y ese costo está dirigido exactamente al problema encontrado, no a una conferencia general sobre seguridad.
Esto también hace que la orientación sea más confiable. A un asistente de IA al que se le pide que "tenga en cuenta la seguridad" mientras escribe cien líneas de código, simplemente se le puede pasar una línea riesgosa entre muchas. Una compuerta no se cansa ni se distrae: verifica cada escritura, cada vez, usando las mismas reglas fijas.
Instálalo una vez; esto también instala Semgrep, del cual depende VibeGate:
pipx install git+https://github.com/theMiddleBlue/vibegate
Luego actívalo dentro del proyecto que quieras proteger:
cd tu-proyecto
vibegate on # activar aquí (recarga Claude Code después)
vibegate status # verificar si está activo para este proyecto
vibegate off # desactivar aquí
vibegate on agrega un hook PreToolUse para Write|Edit|MultiEdit al archivo .claude/settings.local.json de ese proyecto. Está limitado por proyecto, por lo que activarlo en un repositorio no afecta a ningún otro.
Claude Code ejecuta el hook como vibegate run --host claude_code — no se usan rutas absolutas, por lo que sigue funcionando incluso si reinstalas o mueves cosas.
vibegate status también muestra un registro de lo que VibeGate ha detectado realmente en este proyecto: cada advertencia y bloqueo, con el archivo, la línea y la categoría, para que puedas ver su actividad a lo largo del tiempo en lugar de solo si está activado:
$ vibegate status
█ █ █████ ████ █████ ████ ███ █████ █████
...
● VibeGate está HABILITADO en .claude/settings.local.json
Actividad reciente (últimas 2 de 2 registradas, más reciente primero):
2026-07-02T17:35:48+00:00 ⛔ BLOQUEADO server.py:3 EXEC_INPUT (FREE_TEXT)
2026-07-02T17:35:46+00:00 ⚠ ADVERTIDO app.py:2 HTTP_BODY (EMAIL)
Este registro vive en .vibegate/activity.jsonl en la raíz del proyecto; agrégalo a tu .gitignore, es estado local del desarrollador, no algo para commitear.
VibeGate determina con qué host está hablando en este orden: una bandera explícita --host <nombre>, luego la variable de entorno VIBEGATE_HOST, luego la detección automática desde la carga útil entrante, y por último claude_code.
Si VibeGate señala algo que has decidido deliberadamente que es seguro, agrega un comentario vibegate-ignore en la misma línea; funciona con cualquier sintaxis de comentario (#, //, …), ya que VibeGate solo busca el texto:
query = f"SELECT * FROM users WHERE id = {user_id}" # vibegate-ignore
Para suprimir solo categorías específicas en lugar de todo en esa línea, enuméralas después de dos puntos (coincide con la categoría técnica o el tipo semántico, separado por comas, sin distinción entre mayúsculas y minúsculas):
query = f"SELECT * FROM users WHERE id = {user_id}" # vibegate-ignore: DB_QUERY
src/vibegate/
├── hook.py # punto de entrada
├── cli.py # comandos on/off/status + el banner ASCII
├── activity_log.py # persiste advertencias/bloqueos en .vibegate/activity.jsonl
├── colors.py # códigos de color ANSI compartidos (informe + banner CLI)
├── core.py # la canalización independiente del host
├── models.py # InputEvent / ClassifiedFinding / AnalysisResult
├── semgrep_runner.py # ejecuta Semgrep como subproceso (a prueba de fallos)
├── classifier.py # mapea regla de Semgrep → categoría, nombre de variable → tipo de dato
├── guidance.py # las descripciones estáticas de riesgo/remediación
├── formatter.py # convierte resultados en un informe de terminal + contexto del host
├── adapters/ # base, claude_code, codex + un pequeño registro
└── rules/ # reglas de Semgrep — un archivo por lenguaje (Python, JS/TS,
# Go, Java, PHP, Ruby) más un marcador de posición genérico
La canalización en sí (core.py) nunca habla directamente con un host específico; toda la entrada/salida específica del host vive en adapters/, por lo que agregar un nuevo host no requiere tocar la lógica de análisis.
semgrep --validate --config src/vibegate/rules/ # verificar que las reglas sean válidas
pytest tests/ # pruebas unitarias + de integración
Para verlo funcionar de extremo a extremo sin 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/<lenguaje>-user-input.yaml, registra los nuevos IDs de reglas en classifier.RULE_TO_TECHNICAL y mapea la extensión de archivo en core.EXT_TO_LANGUAGE.classifier.VARNAME_TO_SEMANTIC y una descripción en guidance.SEMANTIC_GUIDANCE.RULE_TO_TECHNICAL y una tarjeta en guidance.TECHNICAL_RISKS.adapters/ y regístralo en adapters/__init__.py.codex es un mapeo temprano, con el mejor esfuerzo. Verifica su contrato de eventos con tu versión de Codex antes de confiar en él para bloquear algo."requires login" en lugar de la línea coincidente real, por lo que el clasificador reconstruye el fragmento por sí mismo a partir del contenido del archivo usando números de línea.Edit/MultiEdit, el adaptador claude_code reconstruye el archivo completo post-edición desde el disco para que una fuente contaminada y un sumidero introducidos por ediciones separadas aún estén conectados, pero solo se informan los hallazgos en las líneas que la edición realmente tocó. Si un sumidero ya existe y una edición posterior solo agrega la fuente contaminada que lo alcanza, eso no se detectará (la línea del sumidero no era parte de la nueva edición). Esta reconstrucción es específica de Claude Code; el adaptador codex aún no lo hace.| Comprobación | Qué detecta | Resultado |
|---|
| Command injection | La entrada no saneada llega a un comando de shell | Bloquea |
| SQL injection | La entrada no saneada llega a una consulta de base de datos | Bloquea |
| NoSQL injection | El cuerpo de la solicitud se usa directamente como filtro de base de datos | Bloquea |
| Template injection (SSTI) | La propia fuente de la plantilla, no solo sus datos, proviene de la entrada del usuario | Bloquea |
| Insecure deserialization | Datos no confiables llegan a un deserializador inseguro (pickle, YAML inseguro, ...) | Bloquea |
| Path traversal | La entrada no saneada llega a una lectura, escritura o eliminación de archivo | Bloquea |
| XXE | XML no confiable se analiza con entidades externas habilitadas | Bloquea |
| XSS | La entrada no saneada se renderiza como HTML sin procesar | Bloquea |
| Unrestricted file upload | El nombre del propio archivo subido se usa para construir la ruta de guardado | Bloquea |
| SSRF | El servidor obtiene una URL que no está codificada | Advierte |
| Open redirect | Un destino de redirección que no está codificado | Advierte |
| Mass assignment | Todo el cuerpo de la solicitud se pasa a un constructor o actualización de modelo | Advierte |
| Sensitive data in a request body | Correos, contraseñas, tokens, etc. leídos del cuerpo de la solicitud | Advierte |
| Sensitive data in a URL/query | Correos, contraseñas, tokens, etc. leídos de la cadena de consulta | Advierte |
| Sensitive data in headers | Correos, contraseñas, tokens, etc. leídos de las cabeceras de la solicitud | Advierte |
| File path from user input | Se usa una variable, no una cadena codificada, como ruta de archivo | Advierte |
| CLI arguments | Los datos provienen de argumentos de línea de comandos | Advierte |
| Standard input | Los datos provienen de stdin | Advierte |
| Environment variables | Los datos provienen de una variable de entorno | Advierte |
| Unpinned GitHub Action | Un flujo de trabajo usa una etiqueta mutable (@v4) en lugar de un SHA de commit | Advierte |
Unsafe pull_request_target | Un flujo de trabajo usa el disparador pull_request_target | Advierte |
| Credential logging | Una contraseña, clave API o token se pasa a print/console.log/un logger | Advierte |
| Hardcoded secret | Una variable con nombre similar a un secreto se asigna a un valor literal que parece real | Advierte |
| La entrada de usuario fluye sin sanear hacia un sumidero crítico (consulta SQL/NoSQL, comando de shell, motor de plantillas, deserializador, analizador XML, ruta de archivo, nombre de archivo subido o salida HTML sin procesar) | El archivo no se guarda — se le dice a Claude Code por qué |