
Сканирует диффы кода с контекстом для построения графа влияния и использует LLM для поиска уязвимостей, поддерживая сканирование нескольких репозиториев и CI-гейтинг с выводом в формате SARIF.
Обычные сканеры безопасности упускают последствия изменений, Zairo находит эти последствия и ищет уязвимости. Zairo сканирует, что изменилось в вашем коде, с учётом контекста, строит подграф для просмотра и находит уязвимости с помощью LLM на ваш выбор.

pipx install zairo
# Сканировать всё, что вы ещё не закоммитили
zairo .
# Сканировать diff PR/ветки
zairo . --base main --target HEAD
# Остановить сборку, если найдено что-то высокой степени серьёзности
zairo . --base main --target HEAD --fail-on high
Передайте ему более одного репозитория — как дополнительные аргументы или по одному на строку в --repos-file (или и то, и другое, объединённое в один список), — и он сам переключится в режим нескольких репозиториев: каждый репозиторий получит свой отчёт, плюс один объединённый сводный отчёт.
zairo backend frontend infra --base main --fail-on high -o zairo_multi_out
--base/--target (и все остальные опции) применяются одинаково к каждому репозиторию в списке, поэтому режим нескольких репозиториев лучше всего подходит, когда все они сравниваются с одним и тем же (например, с общим main). Репозиториям с разными соглашениями нужны отдельные запуски.
Что сканировать
--base, -b (нет): ссылка, от которой считать diff, например main или HEAD~3. Если не указано, zairo сканирует незакоммиченные изменения.--target, -t (нет): ссылка, до которой считать diff. Требует --base; если не указано (при заданном --base), сравнивает с вашим рабочим деревом.--depth, -d (1): сколько переходов по вызывающим/вызываемым функциям включить в граф влияния вокруг каждого изменения.--language, -l (авто): принудительно задать язык вместо автоматического определения Trailmark.Сканирование LLM
--graph-only (выкл.): пропустить сканирование уязвимостей и только построить граф влияния — без находок, без report.sarif.--model (gemini/gemini-2.5-pro): любая строка модели LiteLLM.--concurrency, -c (5): параллельные LLM-запросы в рамках сканирования одного репозитория.--batch-size (1): группировать это количество узлов в один LLM-запрос вместо одного вызова на узел — меньше запросов (помогает с лимитами частоты провайдера), но ценой общей изоляции сбоев: плохой/некорректный ответ приводит к сбою всех узлов в этом пакете, а не только одного. Кэширование в любом случае остаётся поузловым.--max-tokens (4096): бюджет вывода на запрос. Модели рассуждения тратят его и на внутренние размышления, поэтому увеличьте его, если видите пустые ответы.--cache / --no-cache (кэш вкл.): пропустить повторное сканирование кода, который не менялся с прошлого запуска (кэшируется по хэшу содержимого в <output>/.llm_cache.json).--tokens (выкл.): вывести, сколько токенов фактически использовало сканирование (попадания в кэш не считаются, так как они не делали вызовов).Вывод и контроль
--output, -o (zairo_out): куда сохраняются отчёты. В режиме нескольких репозиториев: каждый репозиторий получает свой <output>/<repo-slug>/, плюс объединённый rollup.* здесь же.--fail-on (нет): завершиться с ненулевым кодом, если найдена находка серьёзности не ниже указанной (low/medium/high/critical). Ошибка при сочетании с --graph-only (не на чем основывать контроль). В режиме нескольких репозиториев: проверяется по всем репозиториям вместе. См. Контроль CI / PR.--verbose, -v (выкл.): выводить, что происходит, шаг за шагом (git-команды, настройка рабочего дерева, прогресс сканирования по узлам).--debug, -vv (выкл.): всё, что выводит --verbose, плюс точный промпт, отправленный LLM, и его сырой ответ для каждого узла — записывается в <output>/debug.log (по репозиторию в режиме нескольких репозиториев), так как это слишком много для вывода в консоль.Только режим нескольких репозиториев
--repos-file (нет): по одному пути репозитория на строку (допускаются комментарии #), объединяется с репозиториями, заданными напрямую.--repo-concurrency (1): сколько репозиториев сканировать одновременно. Общее количество выполняемых LLM-запросов может достигать --concurrency × --repo-concurrency, поэтому следите за лимитами частоты вашего провайдера. При значении выше 1 прогресс выводится одной сводной строкой на репозиторий по завершении вместо пошаговых деталей в реальном времени.--continue-on-error / --stop-on-error (continue): продолжать сканирование остальной части списка или остановиться, когда один репозиторий завершится с ошибкой. В любом случае любой завершившийся с ошибкой репозиторий всё равно приводит к ненулевому общему коду выхода.В любой момент запустите zairo --help, чтобы увидеть этот же список из CLI.
report.json (всегда): сырой граф влияния (узлы, рёбра и любые прикреплённые находки) в виде данных.report.html (всегда): автономный интерактивный просмотрщик графа зависимостей (Cytoscape.js). Нажмите на узел, чтобы увидеть его находки.report.sarif (если не используется --graph-only): находки в формате SARIF 2.1.0 для GitHub code scanning или любого другого потребителя SARIF. Записывается всегда, даже при чистом сканировании (пустой, но валидный журнал), чтобы интерфейс сканирования мог пометить ранее сообщённые предупреждения как устранённые. Находки группируются в правила по CWE, если модель указала тег, поэтому повторяющиеся проблемы одного типа объединяются в одно правило вместо нового на каждый вариант формулировки.Режим нескольких репозиториев создаёт те же три файла для каждого репозитория, плюс rollup.json / rollup.html / rollup.sarif: статус и количество по серьёзности для каждого репозитория, таблица-панель со ссылками на отчёты каждого репозитория и все результаты SARIF всех репозиториев, объединённые в один журнал с несколькими запусками.
Функция/класс/модуль, удалённый полностью (а не просто отредактированный), всё равно отображается в report.html со статусом deleted: пунктирный, затемнённый узел, отмечающий, где он раньше находился. Граф Trailmark не может представить это сам по себе (он всегда отражает только дерево в его текущем состоянии), поэтому zairo обнаруживает удаления отдельно: он также разбирает изменённые файлы в том виде, в котором они существовали на --base (или HEAD, если --base не был задан), и сравнивает два набора символов. Удалённая функция никогда не отправляется в LLM-сканер (не осталось живого кода для сканирования), поэтому она несёт только своё имя, тип и прежнее местоположение, но никогда — находки.
--fail-on <low|medium|high|critical> завершается с ненулевым кодом, если найдена любая находка серьёзности не ниже указанной (по всем репозиториям вместе в режиме нескольких репозиториев), поэтому шаг CI может заблокировать слияние на этом основании. Пара вещей, о которых стоит знать:
--graph-only (не на чем было бы основывать контроль).zairo . --base "$BASE_REF" --target HEAD --fail-on high -o zairo_out
См. examples/github-actions/zairo-pr-scan.yml
для полного рабочего процесса сканирования PR: он запускает zairo на diff PR, загружает
report.sarif в code scanning GitHub и завершает задание с ошибкой, если контроль провален.