Перейти к основному содержимому

Процедуры контроля и автоматизация

Чек-лист Code Review

Ревьюер MUST убедиться:

XSS и рендеринг

  • Нет вставки HTML без санитизации (dangerouslySetInnerHTML, innerHTML через ref)
  • Используется проверенная библиотека (dompurify / isomorphic-dompurify)
  • Применён whitelist подход для разрешённых тегов
  • URL из внешних источников валидируются по whitelist протоколов (нет javascript:, data:)

Client-side Storage

  • В client-side storage отсутствуют: access/refresh токены, PII, секретные ключи
  • Данные из storage валидируются через zod / yup
  • Storage Events валидируются перед использованием
  • Cookies для токенов имеют флаги: HttpOnly, Secure, SameSite

Environment Variables

  • Клиентские env-переменные (NEXT_PUBLIC_*) не содержат секретов
  • Нет hard-coded секретов в коде
  • Серверные переменные не передаются в клиентские компоненты
  • Env-переменные валидируются при старте приложения

CSP и Security-заголовки

  • CSP настроен (нет unsafe-inline, unsafe-eval)
  • Inline-скрипты используют nonce
  • frame-ancestors настроен корректно
  • Security-заголовки присутствуют (X-Content-Type-Options, Referrer-Policy, X-Frame-Options, Permissions-Policy)
  • SRI (integrity) для скриптов/стилей с внешних CDN

Open Redirect

  • URL редиректов из внешних источников валидируются по whitelist
  • Разрешены только относительные пути (без // в начале)
  • Нет window.location = userInput без проверки

CSRF

  • CSRF-токен передаётся в заголовках мутирующих запросов (POST, PUT, DELETE, PATCH)
  • CSRF-токены не хранятся в Web Storage
  • Мутирующие запросы не выполняются через GET

Browser APIs

  • postMessage проверяет origin отправителя (точное сравнение)
  • Нет wildcard (*) в targetOrigin
  • Данные от postMessage валидируются через схему
  • Clipboard API вызывается только по явному действию пользователя

Third-party Scripts

  • Скрипты загружаются через CSP с явно разрешённых доменов
  • Новые third-party зависимости прошли аудит

Логирование

  • Debug-логи отключены в production
  • Токены и PII не логируются
  • Source maps не публикуются (productionBrowserSourceMaps: false)
  • beforeSend / beforeBreadcrumb в Sentry фильтруют PII

Зависимости

  • Lock-файлы актуальны и в репозитории
  • Изменения в lock-файле проверены
  • npm audit не показывает критических уязвимостей
  • Нет подозрительных postinstall / preinstall скриптов

При добавлении новой зависимости проверить:

  • Активность — есть активная поддержка, последний релиз не старше 1-2 лет
  • Популярность — несколько тысяч еженедельных загрузок
  • Lifecycle scripts — нет подозрительных postinstall / preinstall
  • Размер — влияние на bundle (npx bundlephobia package-name)
  • Лицензия — совместима с проектом (npx license-checker --summary)
  • Альтернативы — нет более безопасного / лёгкого варианта
# Инструменты для проверки пакета перед добавлением
npx bundlephobia package-name # Размер в bundle
npx depcheck # Неиспользуемые зависимости
npx license-checker --summary # Лицензии
npx socket security analyze package-name # Анализ на вредоносный код

Автоматизация

Pre-commit hook

# lefthook.yml
pre-commit:
commands:
# ✅ MUST: Запрет коммита .env файлов
no-env-files:
run: |
if git diff --cached --name-only | grep -E '\.env($|\.)'; then
echo "❌ Попытка закоммитить .env файл!"
exit 1
fi
lint:
run: npx lint-staged

Пример конфигурации: lefthook.yml

ESLint

// .eslintrc.js
module.exports = {
rules: {
// ❌ FORBIDDEN: dangerouslySetInnerHTML
'react/no-danger': 'error',

// ❌ FORBIDDEN: eval и new Function
'no-eval': 'error',
'no-implied-eval': 'error',
'no-new-func': 'error',

// ❌ FORBIDDEN: javascript: в URL
'no-script-url': 'error',

// ⚠️ console в production
'no-console': ['warn', { allow: ['warn', 'error'] }],
},
overrides: [
{
files: ['src/**/*.ts', 'src/**/*.tsx'],
excludedFiles: ['**/*.test.*', '**/*.stories.*'],
rules: {
'no-console': 'error',
},
},
],
};

TypeScript

// tsconfig.json
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true
}
}

SAST (OpenGrep)

# .github/workflows/sast.yml
name: SAST

on: [pull_request]

jobs:
opengrep:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run OpenGrep
run: |
curl -fsSL https://raw.githubusercontent.com/opengrep/opengrep/main/install.sh | bash
opengrep scan \
--config p/javascript \
--config p/typescript \
--config p/react \
--config p/security-audit \
src/

Минимальный ruleset:

  • p/javascript
  • p/typescript
  • p/react
  • p/security-audit

Запускать в CI и локально как self-review перед PR.

CI/CD

# .github/workflows/security-checks.yml
name: Security Checks

on:
pull_request:
push:
branches: [main, develop]

jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci --ignore-scripts # MUST: --ignore-scripts

- name: Lint
run: npm run lint

- name: Type check
run: npm run type-check

- name: Security audit
run: npm audit --audit-level=moderate

- name: Secrets scan
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: ${{ github.event.repository.default_branch }}
head: HEAD

- name: Tests
run: npm test
# .github/workflows/dependency-review.yml
name: Dependency Review

on: [pull_request]

permissions:
contents: read

jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/dependency-review-action@v3
with:
fail-on-severity: moderate

Контрольный список перед деплоем

  • Все токены и секреты только на сервере
  • CSP настроен и активен
  • Security-заголовки присутствуют
  • Source maps отключены (productionBrowserSourceMaps: false)
  • Debug-логи отключены
  • npm audit — нет критических уязвимостей
  • HTTPS включён (Secure cookies)
  • beforeSend / beforeBreadcrumb в Sentry настроены
  • Error boundaries настроены
  • .env файлы не в репозитории