Ревьюер кода, в том числе написанного ИИ
Независимое ревью pull request только на чтение: 42 опубликованных правил по безопасности, миграциям, тестам, архитектуре и надёжности. Каждое замечание — с файлом и строкой, фрагментом кода, объяснением и способом проверки. Код не изменяется и не выполняется.
Бесплатно, без регистрации
git diff, git format-patch или .diff/.patch из PR на GitHub/GitLab. Код не выполняется, не изменяется и никуда не отправляется.
Контекст проекта
Пакеты правил включаются автоматически по признакам в diff. Можно включить или выключить их явно.
Признаков не найдено — пакет не применяется. Признаки: .sql, миграции, pg/psycopg/prisma
Признаков не найдено — пакет не применяется. Признаки: каталог supabase/, @supabase/*, SUPABASE_*
Признаков не найдено — пакет не применяется. Признаки: *.rules, firebase.json, firebase
Признаков не найдено — пакет не применяется. Признаки: app/**/page|route, next.config, "use client", next/*
Как работает ревьюер
Diff
Вставьте unified diff (git diff, git format-patch, .diff/.patch из PR на GitHub или GitLab) или загрузите файл.
Контекст проекта
Пакеты правил для PostgreSQL, Supabase, Firebase и Next.js App Router включаются автоматически по признакам в diff (PostgreSQL — .sql, миграции, pg/psycopg/prisma; Supabase — каталог supabase/, @supabase/*, SUPABASE_*; Firebase — *.rules, firebase.json, firebase; Next.js App Router — app/**/page|route, next.config, "use client", next/*); их можно включить или выключить вручную. Можно ограничить проверку языками.
Правила
Каждое правило смотрит на добавленные строки (исключения указаны) и сообщает файл, строку новой версии, фрагмент с контекстом, почему это важно и как проверить. Автоисправлений нет — решение за человеком.
Решение ревьюера
У замечания — статус «исправить», «принятый риск» или «ложное срабатывание», заметка и имя. Решения сохраняются при повторной проверке: ключ замечания строится из правила, файла и текста строки, а не номера строки.
Экспорт
Markdown для комментария в PR, PDF через печать, JSON для возврата к ревью и SARIF 2.1.0 для GitHub code scanning.
Правила
Таблицы ниже строятся из того же кода, что и проверка, — правила v1.0 (13.10.2026). Важность: критично, высокая, средняя, низкая, информация.
CR-SEC-001
- Что ищем
- Похоже на секрет в добавленной строке
- Как обнаруживается
- Шаблоны ключей (AWS, GitHub, GitLab, Slack, Stripe, Google, Anthropic, OpenAI, JWT, Telegram, приватные ключи, строки подключения с паролем) и присваивания вида password=… (плейсхолдеры вроде process.env, ${VAR}, changeme не считаются).
- Важность
- критично
- Как проверить
- Проверьте, настоящее ли это значение. Если да — отзовите и перевыпустите ключ, перенесите его в переменные окружения или хранилище секретов, очистите историю.
CR-SEC-002
- Что ищем
- Файл .env добавлен или изменён в репозитории
- Как обнаруживается
- Файл .env, .env.local, .env.production и т. п. (кроме .env.example/.sample/.template) добавлен или изменён.
- Важность
- высокая
- Как проверить
- Убедитесь, что файл есть в .gitignore, а в репозитории лежит только .env.example без значений. Если значения настоящие — смените их.
CR-SEC-003
- Что ищем
- Выполнение кода из строки (eval и аналоги)
- Как обнаруживается
- JS/TS: eval(), new Function(), setTimeout/setInterval со строкой; Python: eval()/exec(); PHP, Ruby: eval().
- Важность
- высокая
- Как проверить
- Найдите, откуда берётся строка. Обычно eval заменяется разбором JSON, таблицей функций или безопасным парсером выражений.
CR-SEC-004
- Что ищем
- Команда оболочки собирается из переменных
- Как обнаруживается
- child_process exec/execSync с шаблонной строкой или конкатенацией, spawn/execFile с shell: true; subprocess с shell=True, os.system/os.popen со вставкой; exec.Command("sh", "-c", …); shell_exec/system с переменной PHP; обратные кавычки с #{} в Ruby.
- Важность
- высокая
- Как проверить
- Передавайте аргументы массивом (execFile/spawn без shell, subprocess.run([...])) и проверьте, может ли значение прийти от пользователя.
CR-SEC-005
- Что ищем
- SQL-запрос собирается вставкой значений в строку
- Как обнаруживается
- Строка с SELECT … FROM / INSERT INTO / UPDATE … SET / DELETE FROM и в той же строке — вставка: ${} в шаблонной строке, конкатенация +, f-строка, %, .format(), fmt.Sprintf, $переменная в PHP, #{} в Ruby. Безопасные тегированные шаблоны (sql
…, Prisma.sql…, $queryRaw…) не считаются. - Важность
- высокая
- Как проверить
- Используйте параметры ($1, ?, :name) или построитель запросов. Если значение — имя столбца из белого списка, это допустимо: проверьте список.
CR-SEC-006
- Что ищем
- HTML вставляется без экранирования
- Как обнаруживается
- dangerouslySetInnerHTML, innerHTML/outerHTML =, insertAdjacentHTML, document.write, v-html, фильтр |safe, autoescape off, mark_safe, Markup(), {!! !!} в Blade, .html_safe, <%==, {{{ }}}. Очистка innerHTML = "" не считается.
- Важность
- средняя
- Как проверить
- Проверьте источник HTML. Данные пользователя — только через санитайзер (например, DOMPurify) или как текст.
CR-SEC-007
- Что ищем
- Отключена проверка TLS-сертификата
- Как обнаруживается
- rejectUnauthorized: false, NODE_TLS_REJECT_UNAUTHORIZED=0, verify=False, InsecureSkipVerify: true, CURLOPT_SSL_VERIFYPEER false, ssl._create_unverified_context, strictSSL: false, check_hostname=False, VERIFY_NONE, ServerCertificateValidationCallback => true.
- Важность
- высокая
- Как проверить
- Если это локальная отладка — ограничьте настройку окружением разработки. Для своего CA укажите сертификат явно (ca, verify=путь).
CR-SEC-008
- Что ищем
- CORS: любой источник вместе с учётными данными
- Как обнаруживается
- В добавленных строках одного файла есть и разрешение любого источника (Access-Control-Allow-Origin: *, origin: '*' или true, allow_origins=["*"], AllowAllOrigins, AllowAnyOrigin), и разрешение cookies (credentials: true, Allow-Credentials: true, allow_credentials=True).
- Важность
- высокая
- Как проверить
- Задайте явный список доверенных источников. Убедитесь, что origin: true не отражает любой входящий Origin.
CR-SEC-009
- Что ищем
- JWT читается без проверки подписи
- Как обнаруживается
- jwt.decode() без jwt.verify() в добавленных строках того же файла (jsonwebtoken), algorithm(s) "none", ignoreExpiration: true, PyJWT decode с verify_signature=False / verify=False, ParseUnverified в Go.
- Важность
- высокая
- Как проверить
- Используйте verify с явным списком алгоритмов и секретом/ключом; decode — только для чтения уже проверенного токена.
CR-SEC-010
- Что ищем
- MD5 или SHA-1 для паролей
- Как обнаруживается
- Вызов md5/sha1 (createHash, hashlib, MessageDigest, Digest::MD5, md5()) в строке, где рядом (±3 добавленные строки) упоминается пароль или секрет.
- Важность
- высокая
- Как проверить
- Для паролей используйте bcrypt, scrypt или Argon2 с солью. MD5 для контрольной суммы файла допустим — тогда отметьте как ложное срабатывание.
CR-SEC-011
- Что ищем
- Небезопасный генератор случайных чисел для токена или кода
- Как обнаруживается
- Math.random(), random.* в Python, rand()/mt_rand()/uniqid() в PHP, new Random() в Java, math/rand в Go — в строке со словами token, secret, password, otp, nonce, salt, session, key, code, invite, reset, uuid.
- Важность
- средняя
- Как проверить
- Используйте crypto.randomUUID()/crypto.getRandomValues(), secrets в Python, random_bytes() в PHP, SecureRandom в Java, crypto/rand в Go.
CR-SEC-012
- Что ищем
- Перенаправление на адрес из запроса
- Как обнаруживается
- redirect(...) с req.query/params/body, request.args/GET, searchParams.get(), $_GET; location = params.get(...); redirect_to params[...].
- Важность
- средняя
- Как проверить
- Разрешайте только относительные пути или адреса из белого списка; проверяйте, что адрес начинается с одного «/», а не «//».
CR-SEC-013
- Что ищем
- Ключ service_role Supabase в клиентском коде (пакет: Supabase)
- Как обнаруживается
- service_role / SERVICE_ROLE в файле с "use client" (или *.client.tsx), либо переменная NEXT_PUBLIC_/VITE_/REACT_APP_/EXPO_PUBLIC_…SERVICE_ROLE в любом файле.
- Важность
- критично
- Как проверить
- Ключ service_role — только на сервере (route handler, server action, edge function) без префикса NEXT_PUBLIC_. В клиенте — anon-ключ и политики RLS. Если ключ уже попал в сборку — перевыпустите его.
CR-SEC-014
- Что ищем
- RLS отключён или политика пропускает всех (пакет: Supabase / PostgreSQL)
- Как обнаруживается
- ALTER TABLE … DISABLE ROW LEVEL SECURITY, USING (true), WITH CHECK (true), GRANT ALL … TO anon/public в .sql и миграциях.
- Важность
- высокая
- Как проверить
- Проверьте, что таблица действительно публичная (например, справочник). Иначе — политика с условием по auth.uid() и роли.
CR-SEC-015
- Что ищем
- Правила Firebase открывают данные всем (пакет: Firebase)
- Как обнаруживается
- allow … : if true; allow read, write; без условия; тестовый режим if request.time < timestamp.date(…); ".read"/".write": true в Realtime Database.
- Важность
- критично
- Как проверить
- Разрешайте доступ по request.auth и владельцу документа; проверьте правила в эмуляторе Firebase.
CR-SEC-016
- Что ищем
- Режим отладки в боевой конфигурации
- Как обнаруживается
- DEBUG = True в settings/config, app.run(debug=True), а также FLASK_DEBUG/APP_DEBUG/DEBUG=true или debug: true в файлах с prod/production в пути.
- Важность
- средняя
- Как проверить
- Включайте отладку только переменной окружения разработки и проверьте значение на боевом сервере.
CR-SEC-017
- Что ищем
- В лог пишется пароль, токен или ключ
- Как обнаруживается
- Вызов логирования (console.*, logger.*, logging.*, print, fmt.Print*, System.out, puts, error_log) с переменной, в имени которой password, secret, token, api_key, authorization, cookie, card, cvv, ssn. Слова внутри строковых литералов не считаются.
- Важность
- средняя
- Как проверить
- Уберите значение из лога или маскируйте его; проверьте, не осталось ли уже записанных значений.
CR-SEC-018
- Что ищем
- Небезопасная десериализация
- Как обнаруживается
- pickle.load(s), yaml.load без SafeLoader, yaml.unsafe_load, Marshal.load, unserialize($_GET/$_POST/…), new ObjectInputStream.
- Важность
- высокая
- Как проверить
- Убедитесь, что данные приходят только из доверенного источника; для внешних данных — JSON или yaml.safe_load.
CR-SEC-019
- Что ищем
- Права 777 на файлы или каталоги
- Как обнаруживается
- chmod 777, chmod(…, 0o777), os.chmod(…, 0o777), os.Chmod/MkdirAll/WriteFile с 0777.
- Важность
- средняя
- Как проверить
- Выдайте минимально нужные права (обычно 644 для файлов, 755 для каталогов) и владельца.
CR-MIG-001
- Что ищем
- Удаление таблицы или столбца
- Как обнаруживается
- DROP TABLE, DROP COLUMN, TRUNCATE TABLE, dropTable/dropColumn, op.drop_table/drop_column, remove_column, drop_table, dropIfExists.
- Важность
- высокая
- Как проверить
- Проверьте бэкап и план отката; удаляйте в два релиза: сначала перестаньте использовать столбец, затем удалите. Если это down-миграция — отметьте как ложное срабатывание.
CR-MIG-002
- Что ищем
- Новый столбец NOT NULL без значения по умолчанию
- Как обнаруживается
- ADD [COLUMN] … NOT NULL без DEFAULT в той же строке; add_column … null: false без default:; op.add_column(… nullable=False) без server_default.
- Важность
- высокая
- Как проверить
- Добавьте столбец nullable или с DEFAULT, заполните данные, затем включите NOT NULL отдельным шагом.
CR-MIG-003
- Что ищем
- Переименование столбца или таблицы
- Как обнаруживается
- RENAME COLUMN, ALTER TABLE … RENAME TO, RENAME TABLE, renameColumn, rename_column/rename_table, op.alter_column(new_column_name=…), sp_rename.
- Важность
- средняя
- Как проверить
- Найдите все обращения к старому имени; при выкате без простоя — новый столбец, двойная запись, переключение, удаление старого.
CR-MIG-004
- Что ищем
- Изменение типа столбца
- Как обнаруживается
- ALTER COLUMN … [SET DATA] TYPE, MODIFY COLUMN, change_column, op.alter_column(type_=…).
- Важность
- средняя
- Как проверить
- Оцените размер таблицы и время блокировки на копии данных; проверьте, что все значения помещаются в новый тип.
CR-MIG-005
- Что ищем
- CREATE INDEX без CONCURRENTLY (PostgreSQL) (пакет: PostgreSQL)
- Как обнаруживается
- CREATE [UNIQUE] INDEX без CONCURRENTLY на таблице, которая не создаётся в этом же файле.
- Важность
- средняя
- Как проверить
- Для больших таблиц используйте CREATE INDEX CONCURRENTLY (вне транзакции миграции). Для маленьких или новых таблиц — отметьте как принятый риск.
CR-MIG-006
- Что ищем
- Изменена уже существующая миграция
- Как обнаруживается
- Файл в каталоге миграций (migrations/, db/migrate/, alembic/versions/, prisma/migrations/, supabase/migrations/) изменён, а не добавлен.
- Важность
- высокая
- Как проверить
- Если миграция уже применялась где-либо, верните её и оформите изменение новой миграцией.
CR-MIG-007
- Что ищем
- DELETE или UPDATE без WHERE
- Как обнаруживается
- В .sql — оператор DELETE FROM / UPDATE … SET без WHERE (по добавленным строкам до «;»); в коде — строка с таким запросом целиком в одной строке без WHERE.
- Важность
- высокая
- Как проверить
- Убедитесь, что изменение всех строк задумано (например, заполнение нового столбца), и проверьте его на копии данных.
CR-TST-001
- Что ищем
- Код изменён, тесты — нет
- Как обнаруживается
- В diff есть изменённые файлы кода (не миграции и не сгенерированные), но ни одного тестового файла (по соглашениям путей).
- Важность
- средняя
- Как проверить
- Проверьте, покрыто ли поведение существующими тестами; если нет — добавьте тест на изменённую ветку логики.
CR-TST-002
- Что ищем
- Фокус на одном тесте (.only)
- Как обнаруживается
- describe/it/test/context.only(…), fit(, fdescribe( в тестовых файлах.
- Важность
- высокая
- Как проверить
- Уберите .only; включите в CI запрет (например, --forbid-only в Mocha/Playwright, правило no-focused-tests).
CR-TST-003
- Что ищем
- Тест отключён (skip)
- Как обнаруживается
- .skip/.todo, xit/xdescribe/xtest, @pytest.mark.skip/skipif/xfail, @unittest.skip, t.Skip, @Disabled, @Ignore, skip/pending в RSpec.
- Важность
- средняя
- Как проверить
- Должна быть причина и задача на возврат теста. Если тест мешал зелёному CI — разберитесь с причиной падения.
CR-TST-004
- Что ищем
- Из тестов удалены проверки
- Как обнаруживается
- В тестовом файле удалено больше строк с проверками (expect, assert*, should, t.Error/Fatal, require.*, assertThat), чем добавлено. Проверяются удалённые строки.
- Важность
- средняя
- Как проверить
- Сравните удалённые и новые проверки: покрывают ли новые то же поведение?
CR-TST-005
- Что ищем
- Крупное обновление снимков (snapshots)
- Как обнаруживается
- Файл снимков изменён на 200+ строк.
- Важность
- низкая
- Как проверить
- Просмотрите отличия снимков выборочно; убедитесь, что обновление ожидаемо из-за изменений в этом PR.
CR-ARC-001
- Что ищем
- Новые зависимости
- Как обнаруживается
- Новые пакеты в package.json, requirements*.txt, pyproject.toml, Pipfile, go.mod, Cargo.toml, Gemfile, composer.json (пакет есть в добавленных строках и отсутствует в удалённых).
- Важность
- информация
- Как проверить
- Проверьте, что пакет существует, поддерживается, с понятной лицензией и точным именем (без опечаток); нельзя ли обойтись уже подключёнными.
CR-ARC-002
- Что ищем
- Зависимости изменены, lock-файл — нет
- Как обнаруживается
- В манифесте добавлены или изменены зависимости, а соответствующего lock-файла (package-lock.json, yarn.lock, pnpm-lock.yaml, poetry.lock, go.sum, Cargo.lock, Gemfile.lock, composer.lock…) в diff нет.
- Важность
- средняя
- Как проверить
- Обновите lock-файл тем же менеджером пакетов и закоммитьте его вместе с манифестом.
CR-ARC-003
- Что ищем
- Серверный модуль импортируется в клиентский компонент (пакет: Next.js App Router)
- Как обнаруживается
- В файле с "use client" добавлен импорт server-only, next/headers, fs, child_process, node:*, @prisma/client, pg, mysql2, mongoose, bcrypt, nodemailer, firebase-admin, stripe.
- Важность
- высокая
- Как проверить
- Перенесите работу с модулем в серверный компонент, route handler или server action и передайте в клиент только данные.
CR-ARC-004
- Что ищем
- Непубличная переменная окружения в клиентском компоненте (пакет: Next.js App Router)
- Как обнаруживается
- process.env.X без префикса NEXT_PUBLIC_ (кроме NODE_ENV) в файле с "use client".
- Важность
- средняя
- Как проверить
- Если значение секретное — читайте его на сервере. Если публичное — переименуйте с префиксом NEXT_PUBLIC_.
CR-REL-001
- Что ищем
- Ошибка перехватывается и молча игнорируется
- Как обнаруживается
- Пустой catch {} (в одной строке или catch (…) { и следующая строка «}»), .catch(() => {}), except: pass / except Exception: pass (в одной или двух строках), пустой if err != nil {} в Go.
- Важность
- средняя
- Как проверить
- Обработайте ошибку явно: лог с контекстом, повтор, сообщение пользователю или проброс дальше. Если игнорирование задумано — оставьте комментарий.
CR-REL-002
- Что ищем
- Адрес localhost или внутренний IP в коде
- Как обнаруживается
- http(s)://localhost, 127.0.0.1, 0.0.0.0, [::1], 10.x, 192.168.x, 172.16–31.x в добавленных строках кода (кроме тестов). Если рядом process.env/os.environ — уверенность низкая.
- Важность
- низкая
- Как проверить
- Вынесите адрес в переменную окружения или конфигурацию и проверьте значение для каждого окружения.
CR-MNT-001
- Что ищем
- Очень большое изменение в одном файле
- Как обнаруживается
- Больше 400 добавленных строк в одном файле кода (кроме тестов и сгенерированных файлов).
- Важность
- низкая
- Как проверить
- Разбейте изменение на логические части или проведите ревью по разделам; проверьте на дубли с существующим кодом.
CR-MNT-002
- Что ищем
- Добавлен TODO / FIXME / HACK
- Как обнаруживается
- Слова TODO, FIXME, HACK, XXX (заглавными) в добавленных строках кода.
- Важность
- информация
- Как проверить
- Проверьте, что пометка не прячет недоделанную логику; заведите задачу или доделайте до слияния.
CR-MNT-003
- Что ищем
- Отладочный вывод или точка останова
- Как обнаруживается
- console.log/debug/trace/dir, debugger, print( в Python, breakpoint(), pdb.set_trace(), var_dump/print_r/dd, binding.pry, puts, System.out.print, printStackTrace, Console.Write — кроме тестов и каталогов scripts/, bin/, cli/, tools/.
- Важность
- низкая
- Как проверить
- Удалите или замените структурированным логированием с уровнем.
CR-MNT-004
- Что ищем
- Отключение проверки типов или линтера
- Как обнаруживается
- any / as any, @ts-ignore, @ts-expect-error, @ts-nocheck, eslint-disable, # type: ignore, # noqa, pylint: disable, //nolint, @SuppressWarnings, rubocop:disable, #pragma warning disable. @ts-nocheck и eslint-disable на весь файл — средняя важность.
- Важность
- низкая
- Как проверить
- Проверьте, можно ли описать тип точно или исправить причину предупреждения; оставьте комментарий, почему подавление нужно.
CR-MNT-005
- Что ищем
- Добавлен большой закомментированный блок кода
- Как обнаруживается
- 5+ подряд добавленных строк-комментариев, похожих на код (; { } ( ) = или return/if/for/const/def/import в начале).
- Важность
- низкая
- Как проверить
- Удалите блок или объясните, зачем он оставлен.
Очень большое изменение файла (CR-MNT-001)
- Значение
- больше 400 добавленных строк
Крупное обновление снимков (CR-TST-005)
- Значение
- 200+ изменённых строк
Закомментированный код (CR-MNT-005)
- Значение
- 5+ строк подряд
Замечаний одного правила в одном файле
- Значение
- до 20, остальные — счётчиком
Тестовые файлы определяются по путям: tests/…, __tests__/…, e2e/…; login.test.ts, cart.spec.js; auth_test.go; test_auth.py, auth_test.py; AuthServiceTest.java; AuthTests.cs; user_spec.rb; UserTest.php; LoginTests.swift; __snapshots__/x.snap.
Ограничения
- Код не выполняется; строки diff сопоставляются с шаблонами. Шаблоны могут пропустить проблему (например, SQL, собранный в нескольких строках) или сработать ложно.
- Связи между файлами, типы и поток данных не анализируются: правило видит строку, а не программу. Файлы вне diff не учитываются.
- Это не замена SAST, тестам и инженерному ревью — это быстрый первый проход с доказательствами, который помогает не пропустить типичные ошибки.
Приватность
- Diff разбирается в вашем браузере: код не отправляется на сервер и не попадает в адрес страницы.
- Черновик, решения и версии хранятся только в этом браузере (localStorage); «Очистить всё» удаляет их.
- В доказательствах и экспорте значения, похожие на секреты, заменяются на [REDACTED]; перед экспортом показывается предупреждение.
- В аналитику уходит только факт ревью или экспорта и категория (наибольшая важность, формат файла) — без кода и путей.
- Результат уходит к нам, только если вы сами оставите заявку и не снимете галочку «Приложить результат» — что именно уйдёт, видно в форме. В сводке — счётчики и правило · файл:строка · заголовок, без кода.
Похожие инструменты
Все инструменты · 40
Самопроверка зрелости MVP16 вопросов о коде, доступах, данных, тестах и релизах. Покажет, что проверить в первую очередь, — без оценок «из 100» и без регистрации.
Context Vault: контекст проектаДокументы, решения и ограничения проекта в одном месте: поиск по источникам, контроль устаревания и контекстные пакеты под задачу для AI-агента или нового разработчика.
Spec Drift Detector: код против ТЗТребования ↔ изменения Git/PR: карта трассируемости, вероятные расхождения, изменения без требований и требования без тестовых доказательств — с разбором ревьюером.
Project Handoff: передача проектаСнимок репозитория, архитектура, прогресс, блокеры, риски, тесты и следующие задачи → проверяемый пакет передачи для разработчика или AI-агента.
Готовность к релизуМатрица проверок с доказательствами: CI, миграции, резервные копии, план отката, мониторинг, исключения из рисков и согласование → решение GO / NO-GO.Нужно ревью кода, написанного с AI-агентами?
Аудит кода: доступы и роли, секреты, база данных и RLS, API, тесты и деплой — с картой рисков и планом исправлений. Первый короткий разговор бесплатный; объём и стоимость — после брифа.
- Первый короткий разговор бесплатный — выбираем формат работы
- NDA — по вашему или нашему шаблону
- Стоимость — после брифа и согласования объёма