手動コードレビュー:努力だけではスケールできない壁
リードエンジニアになりたての頃、私はマージ前にメンバーのコードを一行ずつチェックするのに毎日3〜4時間を費やしていました。最大の懸念はロジックの誤りではなく、SQLインジェクションやシークレットの露出、安全でない関数の使用といった初歩的なセキュリティ脆弱性でした。これまで10以上のプロジェクトを監査してきましたが、導き出された結論は残酷なものでした。深刻な脆弱性のほとんどは、締め切りに追われているときに見落としがちな、単純なコードの隙から生まれるのです。
手動レビューがスケールしないのは明らかです。そこで私はSAST(静的アプリケーションセキュリティテスト)ツールを探し始めました。しかし、初期の体験は散々なものでした。初期のツールは動作が非常に重く、大量の「誤検知(false positives)」を返してきたからです。状況が一変したのは、ここ半年間、本番環境でSemgrepを使い始めてからでした。
なぜSemgrepは他と違うのか?
Semgrepがなぜ使う価値があるのかを理解するために、私が試してきた3つの一般的なアプローチを見てみましょう。
1. 従来のGrep
passwordやeval()を探すためにgrepコマンドを使うのは最も手軽な方法です。しかし、文脈を理解できないため、柔軟性に欠けます。例えば、変数名がmy_password_labelであるだけでエラーとして報告され、膨大なノイズが発生してしまいます。
2. SonarQubeやSnykなどの「大手ツール」
これらは非常に強力なソリューションです。しかし、SonarQubeはバックグラウンドで実行するだけで最低2GBのRAMを必要とすることが多く、小規模なプロジェクトには重すぎます。また、プロジェクト独自のカスタムルールを作成するのも、複雑な構文が必要で一苦労です。
3. Semgrep – 完璧なバランス
Semgrep(Semantic Grep)は、構文構造を理解しながら、grepのような高速性を兼ね備えています。例えば、$X = 1; $X + 2と$VAR = 1; $VAR + 2が同じパターンであることを認識できます。一番の魅力は、ルールの書き方が実際のコードに酷似している点です。AST(抽象構文木)の専門家でなくても、十分に使いこなせます。
本番プロジェクトでSemgrepを採用した3つの理由
半年間の運用を経て、納得感のある具体的な数字が見えてきました:
- 圧倒的なスピード: 古いツールで15分かかっていたところ、Semgrepは1,000ファイルを30秒足らずでスキャンします。
- 5分でカスタマイズ: 古いライブラリの使用禁止や、関数呼び出し前の権限チェックの強制といったルールを即座に作成できます。
- 充実したエコシステム: Semgrep Registryには、コミュニティによる2,000以上のルール(OWASP Top 10やフレームワーク固有のもの)が用意されており、すぐに利用可能です。
数行のコマンドでSemgrepを導入する
複雑なサーバー構成は不要です。SemgrepはDocker経由で直接実行するか、Pythonのpipで素早くインストールできます。
1. インストール
ローカルマシンまたはCIサーバーで、以下のコマンドを実行します:
python3 -m pip install semgrep
または、クリーンな環境を好む場合はDockerを使用します:
docker run --rm -v $(pwd):/src returntocorp/semgrep semgrep --config=auto
2. 初めてのスキャン
コードのディレクトリに移動し、自動スキャンを実行します。Semgrepが言語を自動判別し、最適な標準ルールを適用します。
semgrep scan --config auto
結果は非常に直感的です。どのファイルのどの行が違反しているかを、具体的な修正ガイドと共に示してくれます。
カスタムルールの作成:Semgrepを専用の「ガードマン」にする
ここが最も価値のある部分です。例えば、コマンドインジェクションの脆弱性を防ぐために、Pythonでos.system()の使用を禁止し、より安全なsubprocessへの移行を促したいとします。
my-rules.yamlファイルを作成しましょう:
rules:
- id: avoid-os-system
patterns:
- pattern: os.system(...)
message: "警告: os.system()を使用しないでください。代わりにsubprocess.run(shell=False)を使用してください。"
languages: [python]
severity: ERROR
...(省略記号)は秘密兵器です。これは「任意の引数」を意味します。これは従来の正規表現(Regex)を完全に凌駕するポイントです。
CI/CDへの統合:入り口でエラーを阻止する
デプロイ直前までスキャンを待つ必要はありません。私は常に GitHub Actions に Semgrep を組み込み、プルリクエストごとにチェックしています。ERRORレベルの脆弱性が検出された場合、システムがマージをブロックするように設定しています。
参考用の.github/workflows/semgrep.yml設定:
name: Semgrep SAST
on:
pull_request: {}
jobs:
semgrep:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run Scan
run: |
python3 -m pip install semgrep
semgrep scan --config auto --error
実践的なアドバイス:誤検知に悩まされないために
Semgrepは賢いですが、時には誤検知もあります。例えば、テストコードに含まれるダミーのパスワードがフラグを立てられることがあります。ルール全体を無効にするのではなく、インラインの無視機能(inline ignore)を使いましょう。
エラーが報告された行の直前にコメントを追加するだけです:
# nosemgrep
password = "test_password_123"
この方法により、全体のセキュリティレベルを下げずに、例外を適切に管理できます。
最後に
Semgrepを導入してから、ステージング環境に流出するセキュリティバグの数が明らかに減少しました。さらに重要なのは、メンバーが日々より安全なコードを書く習慣を身につけられたことです。軽量で高速、かつ学習コストの低いSASTツールを探しているなら、Semgrepは間違いなく第一候補です。
データ漏洩が起きてからセキュリティを心配するのでは遅すぎます。今すぐSemgrepをインストールして、プロジェクトをスキャンしてみてください。その結果に、きっと驚くはずですよ!

