APIトークンや秘密鍵を検出するツールであるSecretlint 5をリリースしました。

Secretlint 5は、機能追加的な変更はありません。 主に、.secretlintrc.jsonのバリデーションが強化が目的です。 また、Node.js 12 LTSが2022-04でメンテナンスが終了するので一足早くサポートを切っています。

バリデーションを改善したことで、今ままで許容されていたけど意味がない設定になっていたケースなども実行前に検出できるようになっています。 その結果、いままで動いていた(素通りしていた)設定がエラーとなるケースもあるためメジャーアップデートとしています。

具体的には、今までは次のようにプリセットの中に無効なidを指定しても単に無視されるだけでした。 5.0.0からは、これは設定エラーとして検出されるので、間違った設定がしにくくなっています。

{
  "rules": [
    {
      "id": "@secretlint/secretlint-rule-preset-recommend",
      "rules": [
        {
          "id": "invalid-id"
        }
      ]
    }
  ]
}

これは設計的な話ですが、内部的には@secretlint/config-loader@secretlint/config-validatorという二つのモジュールで設定ファイルを読んでいました。 今回の変更でバリデーションが@secretlint/config-loaderに統合されたので、@secretlint/config-validatorはなくなりました。 気持ち的にはバリデーションを別モジュールに分けるのは正しいのですが、分ける意味があまりないという問題がありました。 .secretlintrcは設定ファイルではありますが、設定ファイルに書かれたルールモジュールを読み込まないと、設定が構築できません。

これは、設定ファイルをJSON Schemaでバリデーションしてるだけだと検出できないという意味でもあります。 そのため、正しくバリデーションするには、Configをロードする必要があるため、loaderとvalidatorを分ける必要性がなかったということに気づいてこの変更をしています。

そのため、5.xでは次のような2段階のバリデーションになりました。

  • Configの設定ファイルの文字列そのもの(内部的にはConfigDescritor)をJSON Schemaでバリデーション
  • 設定ファイルを元にルールを読み込むConfigオブジェクトを構築
  • Configオブジェクトと設定ファイルの記述を元にもう一段バリデーション(preset内のidチェックなどルール側のデータを使う)

このワークフローは結構悩んだので、他のツールはどうやってるかとか、どうやると綺麗に書けるかが気になります。

SARIF形式の対応

5.0.0の変更ではないですが、SecretlintでSARIF形式の出力に対応しました。

SARIF形式は静的解析ツール向けのフォーマットで、GitHubのCode Scanningなどが対応しています。今後、Lintやスキャナーなどで対応が増えてくるフォーマットだと思います。

secretlintではformatterをプラグインとして書けるので、次のように@secretlint/secretlint-formatter-sarifをインストールとして使うだけで、SARIF形式の結果を出力できます。

npm install @secretlint/secretlint-formatter-sarif --dev
secretlint --format @secretlint/secretlint-formatter-sarif "**/*"

SARIFの対応にはnvuillam/node-sarif-builderを使いました。

Slackのxapp-形式のトークンに対応

Secretlint 5.1.0でSlackのxapp-から始まるトークンの検出に対応しました。 Socket Modeなどを扱うときはこのトークンが必要になるそうです。

Secretlintをリリースしてから2年たった

Secretlint 1.0をリリースしてから大体2年経ちました。

Secretlintを作った理由はgit-secretsとかがチーム開発するプロジェクトとかでは使いにくすぎて、他にいいものがなかったので作りました。

また、Gitのグローバルhookを使って常にコミット時にsecretlintを通すようにしています。

年に数回はうっかりコミットしそうになって、それが防げいているので作っておいてよかったなーという感じがします。

現在は、GitHub Code Scanningなどpush後に検知できるものはだいぶ充実してきた気はします(Privateだと大体有料になりますが)。

それでもpush前に検知できるツールや外部サービスに依存しないツールはやっぱり欲しいので、そういう立ち位置なのかもしれないです。

Secretlintをなんで作ったかの詳細は1.0リリース時のWhy Secretlint?に書いていました。

  • Reduce false-positive of linting
  • Integration to developing workflow
  • Empower Users to Contribute

最初に作るときにRustで書くかNodeで書くかを迷っていた気がします。 当時はEmpower Users to Contributeで書いてるように、Nodeの方が圧倒的にコミットしやすいと思ったのでNodeで書きました。 また、Rustでプラグインを書ける仕組みが当時はあんまり想像できなかったからですね。

現在もRustでプラグインをちゃんとやろうとしているのはSWCとかぐらいしか知らない(他にもあったら教えてください)ですが、2年前に比べるとだいぶRustが使われるようになったと思います。 Secretlintは、色んなファイルをスキャンしますが、現実的にはPRの差分単位で見るとかなので、めちゃくちゃ大量のファイルを見る使い方はあんまりしてません。

大量のファイルを見るにはやっぱりRustとかで書いた方が早くなるので、この辺に興味がある人がいたらRustでやってみるとかもありなのかもしれないなーと思いました。 (単純にRustでのプラグインシステムに興味がある)

Secretlintはブラウザでも動くように設計しているので、この辺も考慮したRustでプラガブルな仕組みとかどうやるんだろと考えています。