ESLintプラグイン eslint-plugin-security はReactアプリの実装にも反応してくれるか試してみた

Reactアプリを実装するとき、LinterとしてESLintを使っています。

また、Vite.jsを使ってReactアプリを実装する場合、デフォルトで導入される

eslint-plugin-react などを使ってたりします。

 
そんな中、他にもReactのセキュリティ面を見てくれるESLintプラグインがないか調べたところ、Reactのセキュリティ関係のものについては

という状況でした。

 
一方、React以外のセキュリティについては eslint-plugin-security がありました。

リポジトリのREADMEには

ESLint rules for Node Security

This project will help identify potential security hotspots, but finds a lot of false positives which need triage by a human.

と書かれていましたが、Node.js以外にも使えそうな気がしたことから、ためしてみたときのメモを残します。

 
目次

 

環境

  • React 18.2.0
  • Vite 5.2.0
  • ESLint 8.57.0
  • eslint-plugin-security 3.0.0
  • ReDosに関する他のESLintプラグイン
    • eslint-plugin-regexp 2.5.0
    • eslint-plugin-redos 4.4.5

 

eslint-plugin-securityについて

READMEに記載のある通り、セキュリティに関するルールが定義されています。
https://github.com/eslint-community/eslint-plugin-security?tab=readme-ov-file#rules

Node.jsに関するものもありますが、以下の Regular Expression Denial of Service (ReDoS) のように Node.js環境以外でも使えそうなものも定義されたりします。
https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/regular-expression-dos-and-node.md

 
そこで今回は、ReDoSが発生するコードを実装した時、ESLintプラグイン eslint-plugin-security がルール違反を検知するか確認します。

 

Reactアプリの作成

まずは、ReDoSが発生するReactアプリを作成します。

 
最初に、Vite.jsでひな形となるReactアプリを生成します。

手順としてはVite.jsのドキュメントのままなので、ここでは記述を省略します。
https://vitejs.dev/guide/#scaffolding-your-first-vite-project

 
次に、メールアドレスに対してバリデーションが行うフォームを作成します。

そのフォームでは

  • eslint-plugin-security のドキュメントにある正規表現を使って、メールアドレスに対してバリデーションする
  • バリデーションの実行時間を計測する

を実装します。

import './App.css'

function App() {
  const handleSubmit = (e: React.SyntheticEvent) => {
    // https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/forms_and_events/
    e.preventDefault()
    const startsAt = performance.now()
    const target = e.target as typeof e.target & {
      email: { value: string }
    }
    const email = target.email.value
    console.log(email)

    // ReDoS が発生するコード
    const emailExpression = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/
    if (emailExpression.test(email)) {
      console.log("OK")
    } else {
      console.log("NG")
    }
    console.log(performance.now() - startsAt)
  }

  return (
    <>
      <form onSubmit={handleSubmit}>
        <input type="text" name={"email"} />
        <button type="submit">Run</button>
      </form>
    </>
  )
}

export default App

 
Reactアプリができたので、動作を確認します。

フォームの入力値を色々試したところ、文字数が増えるごとに処理時間が増えました。そのため、ReDoSが発生していると分かりました。

 

ESLintプラグイン eslint-plugin-security の挙動を確認する

ReDoSの発生が確認できたので、次は eslint-plugin-security の挙動を確認します。

まず、READMEに従い、 eslint-plugin-security をインストールします。
https://github.com/eslint-community/eslint-plugin-security

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

 
続いて、プラグインを設定します。

なお、Vite 5.2.0 の時点では .eslintrc.cjs が生成されるので、READMEの eslintrc config (deprecated) な書き方で設定します。

module.exports = {
  // ...
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:react-hooks/recommended',
    'plugin:security/recommended-legacy',  // 追加
  ],
// ...

 
準備ができたのでWebStormでソースコードを開いたところ、該当箇所でESLintがルール違反を検知しました。

 
これより、 eslint-plugin-security はReactアプリであってもルール違反を検知すると分かりました。

 

余談:ReDoSに対する、ESLintプラグインについて

今回調べたReDoSについては、以下に詳しい記事がありました。
正規表現の脆弱性 (ReDoS) を JavaScript で学ぶ

また、他にもいくつかESLintプラグインがあったため、それらがどんな状況だったかをメモしておきます。

 

eslint-plugin-regexp

こちらは正規表現の誤りやスタイルガイドのESLintプラグインです。

こちらにもReDoSに関するルールはあります。
regexp/no-super-linear-backtracking | eslint-plugin-regexp

ただ、今回のコードの場合、ReDoSのルール以外で検知していました。

 
そこで、 eslint-plugin-regexp のドキュメントにあるコードを追加したところ、ルール違反を検知しました。

 

eslint-plugin-redos

以下の記事や資料を読んで、このプラグインを知りました。

 
インストールしてためしてみたところ、ESLintがルール違反を検知しました。

 
eslint-plugin-regexp 向けに追加したコードに対しても、ルール違反を検知しています。

 

ソースコード

Githubにあげました。
https://github.com/thinkAmi-sandbox/react_and_node_with_eslint-example

今回のプルリクはこちら。
https://github.com/thinkAmi-sandbox/react_and_node_with_eslint-example/pull/1