Процедуры контроля и автоматизация
Чек-лист 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/javascriptp/typescriptp/reactp/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 включён (
Securecookies) -
beforeSend/beforeBreadcrumbв Sentry настроены - Error boundaries настроены
-
.envфайлы не в репозитории