Плагин eslint-plugin-security

ESLint представляет собой расширяемую систему статического анализа кода, ориентированную на выявление ошибок, несоответствий стилю и потенциальных проблем качества. В экосистеме линтинга особое место занимают плагины, ориентированные на безопасность. Одним из наиболее известных решений этого класса является eslint-plugin-security, предназначенный для обнаружения распространённых уязвимостей и небезопасных паттернов в JavaScript-приложениях.

Архитектура и назначение плагина безопасности

eslint-plugin-security реализует набор правил, направленных на выявление типичных ошибок, приводящих к уязвимостям уровня OWASP Top 10. Анализ осуществляется на уровне AST (Abstract Syntax Tree), что позволяет инспектировать структуру кода без его выполнения.

Основная цель плагина заключается не в полной замене инструментов динамического анализа, а в раннем выявлении опасных конструкций на этапе разработки:

  • небезопасные вызовы функций;
  • потенциальные инъекции;
  • уязвимые способы работы с путями и файловой системой;
  • использование опасных API;
  • подозрительные регулярные выражения;
  • небезопасная десериализация данных.

Плагин работает совместно с ESLint, расширяя его систему правил и подключаясь как стандартное расширение через конфигурацию.


Установка и подключение

Плагин распространяется через npm и интегрируется в конфигурацию линтера стандартным образом.

npm install eslint-plugin-security --save-dev

После установки подключение осуществляется через конфигурационный файл ESLint:

module.exports = {
  plugins: ["security"],
  extends: ["plugin:security/recommended"]
};

Режим recommended включает набор правил, считающихся базовыми для большинства проектов, ориентированных на безопасность.


Основные категории правил

Плагин группирует проверки по смысловым зонам риска. Каждая категория ориентирована на определённый класс уязвимостей.

1. Проверки исполнения кода

Критически важная группа правил, направленная на предотвращение выполнения динамического кода.

Типовые правила:

  • запрет использования eval
  • анализ Function constructor
  • обнаружение динамической интерпретации строк как кода

Пример проблемного кода:

const result = eval(userInput);

Подобная конструкция рассматривается как прямой риск инъекции.


2. Проверки путей и файловой системы

Группа правил ориентирована на предотвращение path traversal атак.

Типичные случаи:

  • неконтролируемая конкатенация путей;
  • использование пользовательского ввода в файловых операциях;
  • отсутствие нормализации путей.

Пример:

const fs = require("fs");

fs.readFile("/app/data/" + userInput, "utf8", callback);

Риск заключается в возможности выхода за пределы разрешённой директории через последовательности ../.


3. Регулярные выражения

Некорректные или сложные регулярные выражения могут приводить к ReDoS (Regular Expression Denial of Service).

Проверки включают:

  • вложенные квантификаторы;
  • чрезмерную сложность;
  • потенциальные катастрофические бэктрекинги.

Пример:

const regex = /(a+)+$/;
regex.test(userInput);

Такие конструкции могут приводить к экспоненциальному времени выполнения.


4. Работа с внешними данными

Особое внимание уделяется данным, поступающим из ненадёжных источников:

  • req.body
  • req.query
  • process.env
  • внешние API

Плагин отслеживает случаи, когда такие данные напрямую передаются в чувствительные функции.


5. Потенциально опасные API

Некоторые встроенные API JavaScript и Node.js считаются рискованными при неправильном использовании.

Примеры:

  • child_process.exec
  • setTimeout с строкой
  • динамическая загрузка модулей
const { exec } = require("child_process");
exec(userCommand);

Конфигурация правил

Плагин позволяет гибко управлять поведением через конфигурационный файл ESLint.

module.exports = {
  plugins: ["security"],
  rules: {
    "security/detect-eval-with-expression": "error",
    "security/detect-non-literal-fs-filename": "warn",
    "security/detect-child-process": "error"
  }
};

Уровни строгости:

  • error — нарушение блокирует сборку или commit-процесс;
  • warn — предупреждение без остановки процесса;
  • off — отключение проверки.

Интеграция в процесс разработки

eslint-plugin-security часто используется на нескольких этапах жизненного цикла приложения.

Локальная разработка

Линтер запускается через npm-скрипты:

{
  "scripts": {
    "lint": "eslint ."
  }
}

CI/CD пайплайны

В системах непрерывной интеграции анализ выполняется автоматически, предотвращая попадание уязвимого кода в основную ветку.

Pre-commit hooks

Интеграция через husky или аналогичные инструменты позволяет блокировать коммиты при обнаружении проблем.


Типовые сценарии обнаружения уязвимостей

Инъекции через строки

const query = "SEL ECT * FR OM users WHERE id = " + userId;

Хотя SQL-инъекции формально выходят за рамки ESLint, plugin может сигнализировать о небезопасной конкатенации данных.


Уязвимости десериализации

const obj = JSON.parse(userInput);

Сам по себе вызов безопасен, но плагин фиксирует случаи, когда результат напрямую используется в критических операциях.


Динамическое создание функций

const fn = new Function("a", "b", userCode);

Подобный подход считается эквивалентом выполнения произвольного кода.


Ограничения статического анализа

Несмотря на широкое покрытие, плагин имеет принципиальные ограничения:

  • невозможность анализа реального поведения приложения;
  • отсутствие контекста runtime-данных;
  • ограниченная способность выявления сложных цепочек уязвимостей;
  • зависимость от качества конфигурации ESLint.

Статический анализ выявляет только шаблонные и структурные риски.


Совместимость с другими инструментами безопасности

eslint-plugin-security часто используется в связке с дополнительными инструментами:

  • SAST-сканеры;
  • анализаторы зависимостей;
  • инструменты проверки лицензий;
  • runtime WAF-решения.

В рамках комплексного подхода он занимает роль раннего фильтра, предотвращающего попадание очевидно опасных конструкций.


Поведение правил на уровне AST

Работа плагина основана на обходе AST-дерева. Каждый узел анализируется на соответствие набору паттернов:

  • CallExpression
  • MemberExpression
  • NewExpression
  • Identifier
  • Literal

На основе этих узлов формируются сигналы о потенциальной угрозе. Такой подход обеспечивает предсказуемость и высокую производительность анализа.


Производительность анализа

Плагин оптимизирован для работы в больших кодовых базах:

  • линейная сложность обхода AST;
  • отсутствие выполнения кода;
  • минимальные накладные расходы на память;
  • возможность параллельного анализа файлов.

В большинстве случаев влияние на время сборки минимально, что делает его пригодным для CI-процессов крупных проектов.


Расширение набора правил

Архитектура ESLint позволяет добавлять собственные правила поверх существующих проверок security-плагина.

Пользовательские правила могут:

  • усиливать существующие проверки;
  • добавлять контекстные ограничения;
  • учитывать особенности доменной логики;
  • интегрироваться с внутренними политиками безопасности.

Пример структуры кастомного правила:

module.exports = {
  create(context) {
    return {
      CallEx * pression(node) {
        // анализ вызовов
      }
    };
  }
};

Практики применения в корпоративной среде

В больших проектах eslint-plugin-security применяется как часть многоуровневой системы контроля:

  • обязательный линтинг перед merge request;
  • единые security-правила для всех репозиториев;
  • централизованные конфигурации ESLint;
  • автоматическое обновление правил безопасности.

Такая модель снижает вероятность появления уязвимостей, связанных с человеческим фактором.


Типовые причины ложных срабатываний

Несмотря на полезность, возможны ситуации, когда правила срабатывают избыточно:

  • использование динамических, но контролируемых шаблонов;
  • легитимные административные функции;
  • специфические архитектурные паттерны;
  • обёртки над безопасными API.

В таких случаях применяется точечное отключение правил через комментарии:

// eslint-disable-next-line security/detect-eval-with-expression

Роль в современной экосистеме JavaScript-безопасности

eslint-plugin-security занимает промежуточный уровень между базовым линтингом и специализированными системами анализа уязвимостей. Его ценность заключается в интеграции в ежедневный процесс разработки и способности выявлять очевидные риски на раннем этапе, до этапа тестирования или деплоя.