
Агностичный к хосту предварительный хук безопасности для агента кодирования: обнаруживает паттерны пользовательского ввода с помощью Semgrep и выдает детерминированные рекомендации по безопасности без использования LLM.
Контрольно-пропускной пункт безопасности для ИИ-инструментов для написания кода. Он проверяет каждый файл, который пишет ИИ-ассистент, и останавливает опасные до того, как они попадут на диск.
ИИ-ассистенты для написания кода (Claude Code, Codex, …) пишут код быстро — в том числе код, который обрабатывает такие вещи, как пароли, email, ключи API или необработанный пользовательский ввод. Ассистент может легко передать эти данные напрямую в запрос базы данных, команду оболочки или HTTP-ответ, не задумываясь о безопасности.
VibeGate располагается между ассистентом и вашей файловой системой. Каждый раз, когда ассистент пытается записать или отредактировать файл, VibeGate сначала сканирует новый код:
В самом анализе не участвует LLM — это быстрый, детерминированный статический анализ, так что он никогда ничего не выдумывает и не тратит ваши токены.
Вот полный список того, что в настоящее время проверяет VibeGate:
| Проверка | Что выявляет | Результат |
|---|---|---|
| Внедрение команд (Command injection) | Несанированный ввод достигает команды оболочки | Блокирует |
| SQL-инъекция (SQL injection) | Несанированный ввод достигает запроса базы данных | Блокирует |
| NoSQL-инъекция (NoSQL injection) | Тело запроса напрямую используется как фильтр базы данных | Блокирует |
| Инъекция шаблонов (SSTI) | Сам исходный код шаблона, а не только его данные, поступает из пользовательского ввода | Блокирует |
| Небезопасная десериализация (Insecure deserialization) | Недоверенные данные попадают в небезопасный десериализатор (pickle, небезопасный YAML, ...) | Блокирует |
| Path traversal | Несанированный ввод достигает операций чтения, записи или удаления файла | Блокирует |
| XXE | Недоверенный XML обрабатывается с включёнными внешними сущностями | Блокирует |
| XSS | Несанированный ввод отображается как необработанный HTML | Блокирует |
| Неограниченная загрузка файлов | Имя самого загруженного файла используется для построения пути сохранения | Блокирует |
| SSRF | Сервер загружает URL, который не является жёстко заданным | Предупреждает |
| Открытое перенаправление (Open redirect) | Цель перенаправления не является жёстко заданной | Предупреждает |
| Массовое присваивание (Mass assignment) | Всё тело запроса передаётся в конструктор или метод обновления модели | Предупреждает |
| Конфиденциальные данные в теле запроса | Email, пароли, токены и т.д., прочитанные из тела запроса | Предупреждает |
| Конфиденциальные данные в URL/в строке запроса | Email, пароли, токены и т.д., прочитанные из строки запроса | Предупреждает |
| Конфиденциальные данные в заголовках | Email, пароли, токены и т.д., прочитанные из заголовков запроса | Предупреждает |
| Путь к файлу из пользовательского ввода | В качестве пути к файлу используется переменная, а не жёстко заданная строка | Предупреждает |
| Аргументы командной строки | Данные поступают из аргументов командной строки | Предупреждает |
| Стандартный ввод (stdin) | Данные поступают из stdin | Предупреждает |
Полный актуальный список находится в guidance.TECHNICAL_RISKS и
formatter.BLOCKING_CATEGORIES, на случай, если эта таблица устареет.
┌───────────────────────────────┐
│ Вы просите Claude Code │
│ написать или изменить файл │
└───────────────┬───────────────┘
│
▼
┌───────────────────────────────┐
│ Claude Code пытается │
│ сохранить файл │
│ (инструмент Write/Edit) │
└───────────────┬───────────────┘
│
▼
┌───────────────────────────────┐
│ Hook VibeGate │
│ (запускается автоматически, │
│ до сохранения файла) │
└───────────────┬───────────────┘
│
сканирует новый код с помощью Semgrep
│
┌─────────────────────┼─────────────────────┐
│ │ │
▼ ▼ ▼
┌────────────────────┐ ┌────────────────────┐ ┌──────────────────────┐
│ Рискованный ввод │ │ Рискованный ввод, │ │ Рискованный ввод │
│ не найден │ │ но низкий риск │ │ достигает │
│ │ │ (например, │ │ критического │
│ │ │ отображается │ │ приёмника │
│ │ │ в HTTP-ответе) │ │ (SQL/команда/RCE, │
│ │ │ │ │ инъекция шаблонов) │
└─────────┬──────────┘ └─────────┬──────────┘ └───────────┬──────────┘
│ │ │
▼ ▼ ▼
Файл сохранен, Файл сохранен, Файл НЕ сохранен.
ничего не показано. плюс предупреждение в Claude Code видит
терминале с указанием причину блокировки
риска и способа и получает указание
исправления. что исправить.
Короче: безопасный код проходит без изменений, рискованный, но выживаемый код сохраняется с предупреждением, а код, находящийся в шаге от таких вещей, как SQL-инъекция, внедрение команд или удалённое выполнение кода, останавливается до того, как попадёт на диск.
Если в самом VibeGate возникает неожиданная ошибка, он всегда разрешает запись — ошибка в хуке никогда не должна быть причиной блокировки вашей работы.
Каждое предупреждение и блокировка также содержат явную инструкцию, которая говорит Claude Code упомянуть находку в своём ответе вам, а не просто исправить её молча. Именно это делает активность VibeGate видимой в диалоге, а не только в терминальном логе, куда вам пришлось бы специально заглядывать.
| Что видит VibeGate | Что происходит |
|---|---|
| Нет пользовательского ввода, или язык ещё не поддерживается | Файл сохраняется нормально, ничего не показано |
| Пользовательский ввод найден, но риск умеренный (например, открытое перенаправление, массовое присваивание) | Файл сохраняется, в терминале появляется предупреждение + руководство |
| Пользовательский ввод без санирования попадает в критический приёмник (SQL/NoSQL-запрос, команда оболочки, движок шаблонов, десериализатор, XML-парсер, путь к файлу, имя загружаемого файла или необработанный HTML-вывод) | Файл не сохраняется — Claude Code сообщается причина |
Смотрите таблицу в разделе "Какую проблему это решает?" выше для полной разбивки по каждой проверке: что блокируется, а что только предупреждается.
На сегодня VibeGate понимает Python, JavaScript/TypeScript, Go, Java, PHP и Ruby, и подключается к Claude Code и Codex. Добавление новых языков и инструментов не требует изменения основной логики.
Он также проверяет файлы рабочих процессов GitHub Actions на две распространённые
ошибки цепочки поставок CI/CD: действия, привязанные к изменяемому тегу (@v4)
вместо SHA коммита, и небезопасный триггер pull_request_target. Обе проверки
выдают предупреждение, а не блокируют, поскольку это проверки усиления защиты,
а не доказательство активной эксплуатации.
Вот реальная запись того, как Claude Code с нуля создаёт приложение для чтения RSS-лент, при этом VibeGate работает всё время. Обратите внимание на моменты, когда Claude Code останавливается и явно говорит, что именно отметил VibeGate и почему, прежде чем продолжить — включая реальный риск SSRF в коде получения лент, который он исправляет на месте.
Вот второй пример в виде статического изображения: Claude Code создаёт приложение, которое позволяет загружать фото и просматривать его детали. VibeGate замечает, что имя файла и другие детали файла впоследствии будут отображаться на экране, и предупреждает, что это может быть использовано для внедрения вредоносного кода на страницу (это называется XSS). Claude Code корректирует код, чтобы информация отображалась безопасно.
В обоих случаях ничего не было заблокировано без причины, и никому не пришлось читать код строка за строкой, чтобы найти проблему. VibeGate поймал её в тот момент, когда файл был записан, а ИИ исправил её на месте.
Есть два способа заставить ИИ-ассистента писать более безопасный код. Один способ — загрузить большой набор инструкций по безопасному кодированию в диалог до его начала, например, чек-лист, охватывающий SQL-инъекции, XSS, обработку паролей, загрузку файлов и т.д. Другой способ — то, что делает VibeGate: проверять код автоматически, прямо при записи файла, и сообщать только когда что-то действительно не так.
Первый подход тратит токены на каждое сообщение, независимо от того, нужны они или нет. Типовой чек-лист безопасного кодирования, охватывающий несколько категорий рисков, может легко добавить несколько тысяч токенов. Если ИИ-ассистент пишет 50 файлов за одну сессию, и этот чек-лист каждый раз перезагружается или хранится в контексте, вы можете платить за более чем сто тысяч токенов советов, которые в большинстве случаев не относятся к текущему записываемому файлу. Страница входа и простой файл с константой цвета не требуют одних и тех же предупреждений, но загруженный чек-лист не может заранее их различить.
VibeGate переворачивает эту логику. Он молчит и не требует дополнительных затрат для каждого файла, в котором нет опасных шаблонов. Только когда он что-то находит, например, пользовательский ввод, поступающий в запрос базы данных, он добавляет краткое, конкретное замечание об этой одной проблеме, обычно составляющее малую долю от размера полного чек-листа. Таким образом, вместо того чтобы платить фиксированную стоимость токенов за каждый файл независимо от обстоятельств, вы платите небольшую стоимость только за те файлы, которые действительно требуют внимания, и эта стоимость точно направлена на найденную проблему, а не на общую лекцию по безопасности.
Это также делает рекомендации более надёжными. ИИ-ассистент, которому сказано "помнить о безопасности" при написании сотни строк кода, может просто пропустить одну рискованную строку среди многих. Шлюз не устаёт и не отвлекается: он проверяет каждую запись, каждый раз, используя одни и те же фиксированные правила.
Установите один раз — это также установит Semgrep, от которого зависит VibeGate:
pipx install git+https://github.com/theMiddleBlue/vibegate
Затем включите его внутри проекта, который хотите защитить:
cd your-project
vibegate on # включить здесь (после этого перезагрузите Claude Code)
vibegate status # проверить, включён ли он для этого проекта
vibegate off # выключить здесь
vibegate on добавляет хук PreToolUse для Write|Edit|MultiEdit в файл
.claude/settings.local.json этого проекта. Он действует в рамках одного проекта,
так что включение в одном репозитории не влияет на другие.
Claude Code запускает хук как vibegate run --host claude_code — без абсолютных
путей, так что он продолжает работать, даже если вы переустановите или переместите
что-то.
vibegate status также показывает текущий журнал того, что VibeGate действительно
обнаружил в этом проекте — каждое предупреждение и блокировку с файлом, строкой
и категорией — чтобы вы могли видеть его активность за период времени, а не только
то, включён ли он:
$ vibegate status
█ █ █████ ████ █████ ████ ███ █████ █████
...
● VibeGate ВКЛЮЧЁН в .claude/settings.local.json
Недавняя активность (последние 2 из 2 записанных, самые новые первыми):
2026-07-02T17:35:48+00:00 ⛔ БЛОКИРОВКА server.py:3 EXEC_INPUT (FREE_TEXT)
2026-07-02T17:35:46+00:00 ⚠ ПРЕДУПР. app.py:2 HTTP_BODY (EMAIL)
Этот журнал находится в .vibegate/activity.jsonl в корне проекта — добавьте его
в .gitignore, это локальное состояние разработчика, а не то, что нужно коммитить.
VibeGate определяет, с каким хостом он общается, в следующем порядке: явный флаг
--host <name>, затем переменная окружения VIBEGATE_HOST, затем автоматическое
определение из входящего payload, с запасным вариантом claude_code.
Если VibeGate отмечает что-то, что вы намеренно считаете безопасным, добавьте
комментарий vibegate-ignore в той же строке — он работает с любым синтаксисом
комментариев (#, //, …), так как VibeGate просто ищет текст:
query = f"SELECT * FROM users WHERE id = {user_id}" # vibegate-ignore
Чтобы игнорировать только определённые категории, а не всё в строке, перечислите их после двоеточия (соответствие либо технической категории, либо семантическому типу, через запятую, без учёта регистра):
query = f"SELECT * FROM users WHERE id = {user_id}" # vibegate-ignore: DB_QUERY
src/vibegate/
├── hook.py # точка входа
├── cli.py # команды on/off/status + ASCII-баннер
├── activity_log.py # сохраняет предупреждения/блокировки в .vibegate/activity.jsonl
├── colors.py # общие ANSI-коды цветов (отчёт + баннер CLI)
├── core.py # независимый от хоста конвейер
├── models.py # InputEvent / ClassifiedFinding / AnalysisResult
├── semgrep_runner.py # запускает Semgrep как подпроцесс (отказоустойчиво)
├── classifier.py # сопоставляет правило Semgrep → категория, имя переменной → тип данных
├── guidance.py # статические описания рисков/исправлений
├── formatter.py # преобразует результаты в отчёт терминала + контекст хоста
├── adapters/ # base, claude_code, codex + небольшой реестр
└── rules/ # правила Semgrep — по одному файлу на язык (Python, JS/TS,
# Go, Java, PHP, Ruby) плюс универсальный заполнитель
Сам конвейер (core.py) никогда не общается напрямую с конкретным хостом — весь
ввод/вывод, специфичный для хоста, находится в adapters/, поэтому добавление
нового хоста не требует изменения логики анализа.
semgrep --validate --config src/vibegate/rules/ # проверить, что правила валидны
pytest tests/ # модульные + интеграционные тесты
Чтобы увидеть его работу от начала до конца без 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, зарегистрируйте
новые ID правил в classifier.RULE_TO_TECHNICAL и сопоставьте расширение файла
в core.EXT_TO_LANGUAGE.classifier.VARNAME_TO_SEMANTIC и описание в guidance.SEMANTIC_GUIDANCE.RULE_TO_TECHNICAL
и карточку в guidance.TECHNICAL_RISKS.adapters/ и
зарегистрируйте его в adapters/__init__.py.codex является ранним, best-effort отображением. Перепроверьте его
контракт событий на соответствие вашей версии Codex, прежде чем полагаться на
него в блокировке."requires login" вместо фактической
совпавшей строки, поэтому классификатор восстанавливает фрагмент сам из содержимого
файла, используя номера строк.Edit/MultiEdit адаптер claude_code восстанавливает полный
пост-редакционный файл с диска, чтобы заражённый источник и приёмник,
добавленные разными правками, всё ещё были связаны — но сообщаются только
находки на строках, которые действительно были затронуты правкой. Если приёмник
уже существует, а более поздняя правка добавляет только заражённый источник,
который до него доходит, это не будет обнаружено (строка приёмника не была частью
новой правки). Такое восстановление специфично для Claude Code; адаптер codex
этого пока не делает.| Переменные окружения | Данные поступают из переменной окружения | Предупреждает |
| Непривязанное действие GitHub (Unpinned GitHub Action) | В рабочем процессе используется изменяемый тег (@v4) вместо SHA коммита | Предупреждает |
Небезопасный pull_request_target | В рабочем процессе используется триггер pull_request_target | Предупреждает |
| Логирование учётных данных | Пароль, ключ API или токен передаётся в print/console.log/логгер | Предупреждает |
| Жёстко заданный секрет | Переменная, названная как секрет, получает значение, похожее на реальное литеральное | Предупреждает |