AI-ревьюер безопасности кода для GitHub Pull Request'ов. Слушает webhook
pull_request / pull_request_review_comment / issue_comment, выкачивает
diff, отдаёт фильтрованный код в LLM (polza.ai / OpenRouter — DeepSeek V4
Flash / Qwen3 Coder / MiMo V2 Pro), пост-фильтрует ложные срабатывания и
публикует inline-комментарии на конкретные строки + summary в PR
conversation. Поддерживает диалог — отвечает на reply'и и @mention.
Полное ТЗ — requirements/prd.md. Архитектура (ADR-1..ADR-7) —
agents/artifacts/architect/system_design.md. Маршрут сдачи и
демо-сценарии — demo/README.md.
- Поиск трёх классов уязвимостей в PR: SQL-инъекции (f-string /
.format()/%), hardcoded secrets (AWS / Stripe / GitHub PAT / OpenAI / JWT / DB connection strings), XSS (Jinja2|safe/autoescape false- DOM-XSS в JS).
- Inline-комментарии с указанием строки, severity, CWE и предложением фикса; summary-комментарий на русском для PR-автора.
- Reply-режим (T-019): бот ведёт диалог — отвечает на reply на свой
inline-комментарий и на
@<BOT_USERNAME>в PR conversation. - Multi-provider LLM:
polza.ai(gpt-4o-mini, baseline) иOpenRouter(DeepSeek V4 Flash primary, Qwen3 Coder free, MiMo V2 Pro fallback) — переключается одной env-переменнойLLM_PROVIDER. У каждого провайдера независимый бюджет-kill-switch. - FalsePositiveFilter (T-013): pre-scan регексами (entropy + known prefixes) + post-filter LLM-находок по confidence-threshold.
- Console UI (M-9): локальная панель управления — регистрация репо
(
/api/console/repos), история чек-ов, бюджет, manual analyze. Под флагомENABLE_CONSOLE_UI=true. - Sandbox Test UI (M-6): одностраничник для проверки LLM-пайплайна
без GitHub-webhook. Под флагом
ENABLE_TEST_UI=true. - SQLite-персистенс (M-9, ADR-6): история PR'ов, findings,
опубликованных комментариев и реестр репо — durable между
рестартами контейнера. Файл
./data/sunsec.db. - Безопасность: все секреты только в env, JSON-логгер с маской
token/secret/api_key/authorization/password, HMAC-валидация webhook'а,Settings.__repr__маскирует sensitive-поля.
- Python 3.11+ (CI прогоняется на 3.11–3.14)
- FastAPI + Uvicorn — HTTP webhook receiver, REST API, статический UI
- Pydantic v2 — контракты компонентов (см.
system_design.md §4) openaiSDK с подменённымbase_url— единый клиент для polza.ai и OpenRouteraiosqlite+ raw SQL — persistence (без ORM/Alembic, ADR-6)cachetools.TTLCache— in-memory idempotency / dedup- structlog-style JSON-логи на stdlib + redaction-фильтр
- vanilla HTML + Tailwind CDN + Iconify — Console UI (без сборки)
src/sunsec/
├── app.py # FastAPI application factory
├── __main__.py # python -m sunsec → uvicorn
├── config/ # Settings (pydantic-settings), безопасный repr
├── contracts/ # Pydantic-модели всех компонентов
├── logging_ext/ # JSON-логгер + маскирование секретов
├── webhook/ # /webhook/github + /health + HMAC + reply-режим
├── vcs/ # VCSAdapter Protocol + GitHubAdapter (diff/comments)
├── filter/ # DiffFilter (PRDiff → FilteredDiff, по ext/name/glob)
├── llm/ # LLMProvider + PolzaProvider + OpenRouterProvider + Budget
├── ml/ # FalsePositiveFilter (pre-scan + confidence post-filter)
├── comments/ # CommentPublisher (inline + summary + empty + budget)
├── storage/ # SQLite store (M-9): checks/findings/comments/repos
├── ui/ # Test UI (M-6) + Console UI (M-9) routers + static
└── pipeline/ # PipelineOrchestrator — связующий слой webhook → publish
demo/ # 5 сценариев для live-демо (см. demo/README.md)
presa/ # Презентация
tests/unit # 19 файлов (≈216 тестов)
tests/integration # pipeline M-2 + M-9 e2e smoke
tests/e2e # full pipeline + polza smoke (живой LLM)
tests/goldens # фикстуры для регресса промпта
agents/ # артефакты проектирования (Architect/Researcher/QA)
data/ # SQLite + repos_secrets.env (gitignored)
# 1) Конфиг
cp .env.example .env
# Минимально заполнить:
# VCS_TOKEN=ghp_... (GitHub PAT с repo-scope)
# WEBHOOK_SECRET=<long-random> (HMAC для X-Hub-Signature-256)
# LLM_PROVIDER=openrouter (или polza)
# OPENROUTER_API_KEY=sk-or-v1-... (для openrouter)
# POLZA_API_KEY=... (для polza)
# ENABLE_CONSOLE_UI=true (открыть Console UI)
# SUNSEC_DB_PATH=./data/sunsec.db (durable persistence)
# 2) Сборка и запуск
docker compose up -d --build
# 3) Проверка
curl http://localhost:8000/health
# → {"status":"ok","service":"sunsec"}
# 4) Открыть Console UI
open http://localhost:8000/ui # выбор репо / история / бюджет
docker compose logs -f sunsec # live-логи pipeline'а
docker compose down # остановитьВажно: single-process, single-replica (ADR-3).
--scale sunsec=N>1запрещён — состояние (бюджет, idempotency, dedup) хранится в памяти процесса + SQLite на volume. Healthcheck встроен вDockerfile, multi-stage build, runtime-юзер не-root. Подробности —agents/artifacts/devops/deployment.md.
cp .env.example .env # см. выше
make dev # установит runtime + pytest/ruff/mypy
make run # uvicorn на HOST:PORT из .env
curl http://localhost:8000/healthПять готовых сценариев в demo/, каждый — отдельный PR в remote_test_rep:
| Сценарий | Что покажет бот | Ожидаемое |
|---|---|---|
01_sqli_login |
3 SQL-инъекции (f-string / .format() / %), auth-bypass |
3× critical SQLi |
02_hardcoded_keys |
AWS / Stripe / GitHub / OpenAI / JWT / DB connection string | 6× hardcoded_secret |
03_xss_jinja2 |
Jinja2 |safe и autoescape false на user-input |
2× high XSS |
04_clean_baseline |
Параметризованные SQL, os.environ, html.escape |
0 findings, рус. summary |
05_mixed_pr |
Stripe live key + 2 SQLi + Sentry DSN + GitHub PAT | ~5 findings, разные классы |
Запуск (требует поднятый бот + туннель до GitHub):
demo/scripts/new_pr.sh 01_sqli_login # создаст бранч, PR, дождётся webhookПолный сценарий показа — demo/README.md. Презентация — presa/presentation.zip.
make test # pytest: 19 файлов unit + integration + e2e (~216 тестов)
make lint # ruff check
make format # ruff format
make typecheck # mypy (strict mode на src/)Где смотреть:
tests/unit/— компонентные тесты всех модулей (config, redaction, webhook, vcs, filter, llm/polza, llm/openrouter, fp-filter, comments, sqlite_store, ui-router, console-router, reply-mode, ...).tests/integration/test_m9_e2e_smoke.py— Console UI + persistence end-to-end (без сети).tests/e2e/run_polza_smoke.py— живой smoke против polza.ai (требуетPOLZA_API_KEY).tests/goldens/— фикстуры регресса промпта (SQLi / secrets / XSS / clean / mixed).
Под флагом ENABLE_CONSOLE_UI=true поднимается админ-панель — порт
tmp/front.html в src/sunsec/ui/static/, без сборки.
Views:
- Dashboard — последние чек-и, статусы, totals.
- Checks History — список PR'ов с findings + переход в детали.
- Check Details — все findings одного PR + опубликованные комментарии.
- Settings — runtime-конфиг (бюджеты, провайдер).
- Budget — текущий расход polza.ai + OpenRouter.
- Repos — онбординг репо:
full_name+vcs_token+webhook_secret→ бот начинает обрабатывать webhook'и без перезапуска. Токены пишутся в gitignoreddata/repos_secrets.env(durable, RT-012 fix) + вos.environпроцесса.
REST API под Mock-контракт фронта:
GET /api/console/dashboard # totals + last checks
GET /api/console/checks # history (filter/paginate)
GET /api/console/checks/{id} # findings + comments
GET /api/console/budget # polza + openrouter
GET /api/console/settings # runtime config
POST /api/console/repos # register repo
GET /api/console/repos # list
DELETE /api/console/repos/{id} # unregister
POST /api/console/manual/analyze # ad-hoc анализ кода
Безопасность (R-13 HIGH, system_design §11.7): MVP не имеет authn.
/api/console/*НЕ выставлять в публичную сеть без middleware (basic-auth / JWT / VPN). Single-user dev-режим.
Под флагом ENABLE_TEST_UI=true поднимается изолированная страница
для ручной проверки бота без поднятия webhook + ngrok + реального
PR. Закорачивает пайплайн: сырой код → DiffFilter (synthetic-helper)
→ LLMClient → FalsePositiveFilter → рендер findings.
ENABLE_TEST_UI=true
HOST=127.0.0.1 # ВАЖНО: bind на localhost
make run
open http://localhost:8000/uiEndpoints: GET /ui, POST /api/ui/analyze, GET /api/ui/budget,
GET /api/ui/examples (4 встроенных примера: SQLi / secret / XSS / clean).
- Секреты только в env (
.envв.gitignore, шаблон в.env.example). - JSON-логгер маскирует
token/secret/api_key/authorization/passwordи вextra, и в exception tracebacks (src/sunsec/logging_ext/redaction.py). - HMAC-валидация webhook'а —
X-Hub-Signature-256, constant-time compare, отбой при ошибке. - Бюджет-kill-switch:
POLZA_BUDGET_LIMIT_RUB(default 80 ₽) иOPENROUTER_BUDGET_LIMIT_RUB(default 50 ₽) — независимые корзины. При превышенииBudgetExceeded→ бот не зовёт LLM и публикует «бюджет исчерпан» в PR. - Anti-loop reply-режима: self-filter по
BOT_USERNAME— бот не отвечает на свои же комментарии. Settings.__repr__маскирует sensitive-поля — безопасно логировать конфиг целиком.- Docker: runtime-юзер не-root, healthcheck, multi-stage build.
Проект разработан для хакатона в составе Черемша.