Security Review of Generated Code
SQL injection from concatenation, missing permission checks, hardcoded secrets, and invented packages: where generated code is insecure by default and how to raise a SAST gate in CI.
Lesson content is currently available in Russian.
What you will learn
- →Находить в AI-диффах типовые уязвимости: инъекции, пропущенную авторизацию, слабое крипто, path traversal.
- →Проверять цепочку поставок: существование, репутацию и целостность каждой новой зависимости.
- →Встраивать SAST, secret scanning и dependency review в CI как обязательный гейт для AI-PR.
Небезопасно по умолчанию
Модель оптимизирует правдоподобие, а не безопасность. В обучающих данных уязвимого кода огромное количество — Stack Overflow полон примеров «просто чтобы работало», а туториалы десятилетиями учат конкатенировать SQL-строки. Модель честно воспроизводит это распределение: в исследовании «Asleep at the Keyboard» около 40% сгенерированных фрагментов в сценариях, соотнесённых с CWE-списком опасных дефектов, оказались уязвимыми. При этом уязвимый код почти всегда функционально корректен: инъекция не мешает запросу работать на демо-данных, отсутствие проверки прав незаметно, пока все ходят под админом.
Поэтому security-ревью AI-кода строится на презумпции: сгенерированный дифф — недоверенный вклад, как PR от незнакомого внешнего контрибьютора. Никакие заслуги агента в прошлых задачах не переносятся на текущий дифф — у модели нет памяти о том, что «в этом проекте мы так не делаем», если это не написано в её контексте явно.
Вторая причина усиленного внимания — эффект масштаба. Человек пишет один уязвимый эндпоинт в неделю, агент — двадцать за вечер, и все двадцать с одинаковой дырой, потому что паттерн один. Уязвимость в AI-кодовой базе — это почти всегда класс, а не экземпляр: нашёл одну инъекцию — ищи такую же во всех местах, которые агент генерировал по тому же образцу. Отсюда практическое правило: каждая найденная уязвимость превращается в Semgrep-правило или тест, который ищет весь класс по всей базе.
Классика уязвимостей в сгенерированном коде
Пять классов, которые стоит проверять в каждом AI-диффе, трогающем ввод, данные или файлы.
Инъекции из конкатенации. Самый частый класс:
// Сгенерировано: конкатенация — SQL-инъекция
const rows = await db.query(
"SELECT * FROM orders WHERE user_id = " + req.params.userId
);
// Параметризованный запрос
const rows = await db.query(
"SELECT * FROM orders WHERE user_id = $1",
[req.params.userId]
);
Пропущенная авторизация (IDOR). Агент генерирует эндпоинт по образцу соседнего, но проверку «этот ресурс принадлежит этому пользователю» образец не содержал — и она не появилась. Любой новый маршрут в диффе проверяется на два вопроса: кто может вызвать и чьи данные он отдаёт.
Захардкоженные секреты. Модель охотно вставляет ключи-плейсхолдеры прямо в код, а агент с доступом к .env может скопировать реальный ключ в исходник «для простоты».
Слабое крипто и legacy-паттерны. MD5/SHA1 для паролей, Math.random для токенов, собственный «шифр» вместо библиотеки — наследие обучающих данных из прошлого урока.
Path traversal и SSRF. Склейка пути из пользовательского ввода без нормализации, fetch по URL из запроса без allowlist. Оба класса модель воспроизводит из бесчисленных туториалов «file server за 20 строк».
Цепочка поставок: выдуманные и подменённые пакеты
Галлюцинации API из первого урока имеют security-измерение. Модели стабильно выдумывают несуществующие имена пакетов — и злоумышленники это заметили. Атака slopsquatting: собрать частые галлюцинации моделей, зарегистрировать пакеты с этими именами и начинить их вредоносным кодом. Агент импортирует выдуманный пакет, npm install находит его в реестре — и вредоносный postinstall-скрипт уже выполняется на машине разработчика или в CI. Родственная атака typosquatting эксплуатирует пакеты с именами, похожими на популярные.
Проверка каждой новой зависимости в AI-диффе обязательна: пакет существует давно, а не зарегистрирован на прошлой неделе; у него есть репозиторий, соответствующий содержимому; количество загрузок и зависимых проектов правдоподобно; имя не является вариацией популярного пакета. Это две минуты на пакет — против компрометации CI.
Механика защиты уровнем ниже: фиксируй версии через lockfile и не позволяй агенту его редактировать руками; включи запрет install-скриптов там, где возможно (npm config set ignore-scripts true для CI-шагов, которым скрипты не нужны); используй dependency review в CI, чтобы новая зависимость была видна в PR как отдельное событие, а не строчка в шуме lockfile. Правило для команды: новая зависимость — всегда отдельное решение человека, никогда — побочный эффект генерации. Агент может предложить пакет, но добавление в package.json проходит явную проверку по чек-листу выше.
SAST-заслон: проверки, которые не устают
Глаза ревьюера — худший инструмент для поиска однотипных уязвимостей: внимание падает, паттерны сливаются. Машинные проверки не устают, поэтому security-гейт для AI-PR строится в CI.
- SAST на каждый PR. Semgrep с набором правил под ваш стек ловит инъекции, слабое крипто и опасные API за секунды; CodeQL глубже анализирует потоки данных от источника к стоку. Найденная вручную уязвимость превращается в собственное правило — так база правил накапливает память команды.
- Secret scanning. Сканирование диффа на ключи и токены до мержа. Найденный секрет означает не «удалить строку», а «ротировать ключ» — история Git помнит всё.
- Dependency review. Шаг CI, который показывает новые зависимости и известные уязвимости в них прямо в PR.
- Чек-лист security-ревью для человека — то, что автоматика не видит:
Security-чек-лист AI-диффа
- каждый новый маршрут: кто может вызвать? чьи данные отдаёт?
- каждый пользовательский ввод: валидируется до использования в запросе, пути, URL, команде?
- каждая новая зависимость: проверена по чек-листу цепочки поставок?
- секреты: только из окружения, ни одного литерала в коде?
- крипто: только стандартные библиотеки и современные алгоритмы?
Критические находки SAST блокируют мерж без исключений для AI-PR: у агента нет права на «я знаю, что делаю», которое иногда есть у senior-человека. Исключение оформляется человеком явным подавлением правила с комментарием-обоснованием — и это событие само по себе видно в ревью.
Practice
Security-гейт своими руками
Сгенерируйте агентом небольшой REST-сервис (3–4 эндпоинта с БД и загрузкой файлов) без упоминания безопасности в промпте. Прогоните по security-чек-листу урока и Semgrep с дефолтным набором правил. Классифицируйте находки по классам урока, почините критические и добавьте Semgrep-шаг в CI.
Deliverable
Отчёт: список находок «класс → файл → строка → фикс», вывод Semgrep до и после фиксов и работающий CI-шаг, который падает при повторном внесении любой из найденных уязвимостей.
Checkpoint
Объясните, почему сгенерированный код считается недоверенным по умолчанию, и опишите ваш security-гейт для AI-PR: что проверяет автоматика, что остаётся человеку и что блокирует мерж.
- ✓ Названы причины: модель воспроизводит уязвимые паттерны обучающих данных, уязвимый код функционально корректен, дефекты тиражируются классами.
- ✓ Перечислены минимум три класса уязвимостей с способом обнаружения каждого (SAST-правило, secret scanning, ревью авторизации человеком).
- ✓ Описана политика цепочки поставок: проверка новых пакетов, lockfile, запрет install-скриптов, новая зависимость как явное решение человека.
Check yourself
Sources