
Локальный статический сканер безопасности для приложений Next.js, Supabase и Stripe: 24 проверки на основе AST для утёкших ключей, отсутствующих RLS и грантов, небезопасных вебхуков. CLI и GitHub Action.
Бесплатное локальное сканирование безопасности для приложений Next.js + Supabase + Stripe: утёкшие ключи, отсутствующие RLS и гранты, небезопасные вебхуки Stripe.
Ваш ИИ-агент выпустил приложение. А вместе с ним — ваш секретный ключ Stripe, таблицу без RLS или вебхук, который может вызвать кто угодно? Одна команда выяснит это локально, менее чем за секунду.
npx github:edtrackai/nextjs-supabase-stripe-security-checker /path/to/your/app
Он ещё не опубликован в npm, поэтому npx запускает его напрямую из этого репозитория GitHub (требуются Node 18+ и git).
- uses: edtrackai/nextjs-supabase-stripe-security-checker@v0
with:
fail-on: high
Полный рабочий процесс и разрешения описаны ниже и в docs/github-action.md.
Проверка Supabase RLS · Безопасность вебхуков Stripe · Сканирование безопасности Next.js · Права Data API Supabase (42501)

git clone https://github.com/edtrackai/nextjs-supabase-stripe-security-checker.git
cd nextjs-supabase-stripe-security-checker && npm install
node bin/nss-security-check.js /path/to/your/app
Требуется Node 18 или новее. Есть одна зависимость (@babel/parser). Инструмент бесплатный, распространяется по лицензии MIT, не требует учётной записи, не выполняет сетевых вызовов и никогда не загружает ваш код.
--fail-on=high завершается с кодом 1, а --json выдаёт машиночитаемый вывод.Реклама (платная, тот же автор): Нужны исправления? Launch Audit (12 $ плюс НДС, где применимо) добавляет 28 проверок, агента-аудитора ИИ без инструментов Edit и Write и план исправлений в формате file:line.
| Флаг | Что делает |
|---|---|
--json | Машиночитаемый вывод: {tool, version, root, filesScanned, counts, findings[]} |
--fail-on=<sev> | Завершение с кодом 1, если какая-либо находка имеет уровень critical / high / medium / low или выше (none — никогда не завершается с ошибкой). Код 2 при неверных аргументах. |
--list | Вывести каталог проверок |
--quiet | Не выводить однострочный футер о платных инструментах (печатается в stderr только в терминале; также отключается при NO_PROMO=1, CI или GITHUB_ACTIONS) |
--help | Справка по использованию |
В CI: node path/to/bin/nss-security-check.js . --fail-on=high, либо используйте GitHub Action (сводка задания, аннотации, один закреплённый комментарий к PR, только GITHUB_TOKEN). Пример workflow и разрешения см. в docs/github-action.md.
# .github/workflows/security-check.yml
name: Security check
on: [pull_request]
permissions:
contents: read
pull-requests: write # only for the PR comment; drop it with comment: false
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: edtrackai/nextjs-supabase-stripe-security-checker@v0 # or pin @v0.5.0 / a commit SHA
with:
path: .
fail-on: high
Изменения перечислены в CHANGELOG.md (версия 0.5.0 добавляет Action).
| ID | Критичность | Область | Проверка |
|---|---|---|---|
| K1 | critical | Ключи | Живой секретный/ограниченный ключ Stripe (sk_live_/rk_live_) в исходном коде, включая ключи, разделённые с помощью + (доказательство скрыто) |
| K2 | critical | Ключи | Переменная NEXT_PUBLIC_, похожая на секрет (service role, секретный ключ, секрет вебхука…) |
| K3 | critical | Ключи | Файл .env со значениями, отслеживаемыми git |
| K4 | critical | Ключи | Серверный секрет или админ-клиент Supabase, используемый в компоненте "use client" |
| A1 | high | Auth | auth.getSession() используется на сервере вместо getUser()/getClaims() |
| A2 | high | Auth | Server Action записывает данные без проверки аутентификации |
| A3 | high | Auth | API-маршрут (POST/PUT/PATCH/DELETE) записывает данные без проверки аутентификации (маршруты вебхуков, cron и auth исключены) |
| A4 | high | Auth | Админ-клиент запрашивается по id из запроса без фильтра по владельцу (IDOR) |
| A5 | medium | Auth | Открытый редирект из параметра запроса |
| R1 | critical | RLS | Таблица создана без row level security |
| R2 | critical | RLS | Политика записи с using (true) / with check (true) |
| R3 | high | RLS | Политика, позволяющая роли anon записывать данные |
| R4 | high | RLS | Функция security definer без фиксированного search_path |
| R5 | medium | RLS | Таблица в public без явного GRANT. Supabase объявила, что прекратит выдачу разрешений по умолчанию для новых публичных таблиц с 30 октября 2026 г., но в конце августа мейнтейнер Supabase CLI написал (supabase/cli PR #6337), что более раннее изменение поведения по умолчанию «так и не было внедрено» и размещённые проекты по-прежнему выдавали разрешения автоматически, поэтому дата может сдвинуться: проверьте, как ведёт себя ваш собственный проект. Без разрешения Data API возвращает 42501 |
| R6 | high | RLS | Сплошной grant ... on all tables in schema public / alter default privileges ... grant для anon/authenticated (быстрое исправление ошибки 42501, которое также раскрывает таблицы без RLS) |
| S1 | critical | Stripe | Вебхук без проверки подписи (constructEvent) |
| S2 | critical | Stripe | Вебхук разбирает request.json() вместо сырого тела запроса |
| S3 | medium | Stripe | Вебхук не обеспечивает идемпотентность по event.id |
| S4 | high | Stripe | Цена или сумма checkout берётся из запроса |
| S5 | high | Stripe | Доступ предоставляется на странице успешного checkout вместо вебхука |
| S6 | medium | Stripe | Дедупликация вебхуков, которая может проглотить неудачное событие: дубликаты пропускаются без статуса pending/completed |
| S7 | low | Stripe | Период оплаты читается из items.data[0] (неверно для подписок с несколькими позициями начиная с API 2025-03-31.basil) |
| H1 | medium | Hardening | Отсутствуют заголовки безопасности в next.config |
| H2 | medium | Hardening | dangerouslySetInnerHTML с нелитеральным значением |