😾

Brakeman(静的解析)にignoreの理由を書くことを促すActionを自作してみました

2024/11/10に公開

こんにちは。ダイの大冒険ガチ勢のbun913と申します。

みなさん静的解析を行っていますか?TypeScriptやJavaScriptなどの言語ではESLintやTSLint、RubyではRuboCopなどが有名ですね。

Ruby向けの静的解析ツールとしては脆弱性のあるコードの検出を試みるBrakemanもあります。

https://github.com/presidentbeef/brakeman

ただし、静的解析はあくまで実行時の挙動を見るものではないので、すべての警告が必ずしも問題とは限らず、偽陽性(本当は問題ないのに警告が出る)と判断することがあります。

とはいえ、無視設定が溜まっていき、その理由がわからなくなることや、後で修正するつもりだったけど忘れてしまうこともありますよね。

そこで、今回はBrakemanの警告に対して、無視する理由を記述することを促すGitHub Actionsを自作してみました。

作ってみたツール

以下のリポジトリにて公開しています。

https://github.com/bun913/please-write-ignore-reason

以下のようにGitHub Actionsで呼び出すことで、指定したBrakemanのignoreファイルに対して、無視理由の記述を促します。

jobs:
  lint-workflow:
    name: Check dist/
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        id: checkout
        uses: actions/checkout@v4

      - name: Brakeman ignore reason check
        id: valid-json
        # バージョンは最新のものを指定してください
        uses: bun913/please-write-ignore-reason@v0.9.2
        with:
          # ↓ 一応複数のファイルを指定できるようにしています
          fileListString: samples/brakeman.ignore,samples/valid2.json

以下のように、指定したファイルにignoreの理由が書かれていなかったり、存在しないファイルを指定するとエラーが出力されます。

brakeman-action-image

ざっくりとした仕組み

このツールの挙動は非常にシンプルで、以下のようなbrakeman.ignoreのファイルに対して、ignore_warningsの配下の要素のnoteプロパティが5文字未満であったり、ファイルが存在しない場合にエラーを出力します。

{
  "ignored_warnings": [
    {
      "warning_type": "Remote Code Execution",
      "warning_code": 25,
      "fingerprint": "006ac5fe3834bf2e73ee51b67eb111066f618be46e391d493c541ea2a906a82f",
      "check_name": "Deserialize",
      "message": "`Oj.load` called with parameter value",
      "file": "app/controllers/users_controller.rb",
      "line": 52,
      "link": "https://brakemanscanner.org/docs/warning_types/unsafe_deserialization",
      "code": "Oj.load(params[:json], :mode => :object)",
      "render_path": null,
      "location": {
        "type": "method",
        "class": "UsersController",
        "method": "some_api"
      },
      "user_input": "params[:json]",
      "confidence": "High",
      // ↓5文字未満なのでエラーになります
      "note": ""
    },
    {
      "warning_type": "Remote Code Execution",
      "warning_code": 25,
      "fingerprint": "3bc375c9cb79d8bcd9e7f1c09a574fa3deeab17f924cf20455cbd4c15e9c66eb",
      "check_name": "Deserialize",
      "message": "`Oj.object_load` called with parameter value",
      "file": "app/controllers/users_controller.rb",
      "line": 53,
      "link": "https://brakemanscanner.org/docs/warning_types/unsafe_deserialization",
      "code": "Oj.object_load(params[:json], :mode => :strict)",
      "render_path": null,
      "location": {
        "type": "method",
        "class": "UsersController",
        "method": "some_api"
      },
      "user_input": "params[:json]",
      "confidence": "High",
      // 5文字未満なのでエラーになります
      "note": "a"
    },
    {
      "warning_type": "Remote Code Execution",
      "warning_code": 25,
      "fingerprint": "97ecaa5677c8eadaed09217a704e59092921fab24cc751e05dfb7b167beda2cf",
      "check_name": "Deserialize",
      "message": "`Oj.load` called with parameter value",
      "file": "app/controllers/users_controller.rb",
      "line": 51,
      "link": "https://brakemanscanner.org/docs/warning_types/unsafe_deserialization",
      "code": "Oj.load(params[:json])",
      "render_path": null,
      "location": {
        "type": "method",
        "class": "UsersController",
        "method": "some_api"
      },
      "user_input": "params[:json]",
      "confidence": "High",
      // ↓OKです
      "note": "Ref: https://example.com/issue/45"
    }
  ],
  "updated": "2019-04-02 12:15:05 -0700",
  "brakeman_version": "4.5.0"
}

処理自体はTypeScriptで記述しており、バリデーションの実装はValibotというライブラリでサクッと実装しています。

https://github.com/bun913/please-write-ignore-reason/blob/383bde9d18e9ac393eb23b27112773c9b924e20a/src/modules/brakeman/type.ts

JSONファイルからパースする際に、ValibotによりJSONデータの型が自動的に検証されるため、型の安全性を確保しやすいです。(as で型アサーションするだけだと、型チェックとしては不十分ですので)

GitHub Actions の Action を TypeScript で作る際は、以下の Public Template を利用するとスムーズに作成できます。使い方は、README に書かれおり非常にわかりやすいです。

https://github.com/actions/typescript-action

今後の展望・感想

今後やりたいこと

  • GitHub Actions だけではなく CircleCI などで利用できるようにしたい
  • Brakeman だけでなく、他の静的解析ツールにも対応をしたい(かもしれません)
  • ルールに柔軟性を持たせる
    • 強制的に5文字以上の理由としてますが、緩すぎる気もしますし、微妙な気もします。
      • Ref:#45 のようなリンクを貼ることを考えて、ざっくりと5文字と設定しました

他にも生成AIを使って理由の正当性チェックが可能かもしれませんが、現在はそこまでの機能は不要と考えています。

あくまでも、うっかりミスの予防や、チームで既存の無視理由を考えるためのツールとしての利用を想定しています。

感想

何より、今回は数時間でサクサクと作って公開することが目的でしたので、かなりシンプルな実装になっています。

公開さえすれば親切な方がより良い形にしてくれるかもしれませんので、Better than Nothingの精神でやってしまいました。

かなりニッチなツールなのですが、使いやすいように改良をしていきたいと思います。

以上、最後までお読みいただきありがとうございました。

GitHubで編集を提案

Discussion