静的解析

コードを実行せずに構文・型・データの流れから性質を推論し、バグ・脆弱性・規約違反の候補を検出する手法

開発ツール品質

静的解析とは

静的解析 (Static Analysis) は、コードを実行せずに、構文木・型注釈・データの流れからプログラムの性質を推論する検査手法である。単一のツールではなく、型チェッカからセキュリティスキャナまでを含む層の総称として使われる。

実行しないため、入力が届かない経路や再現条件が揃わない不具合も机上で拾える。一方でプログラムの意味に関する非自明な性質は一般に判定できないので、解析は必ず近似になる。近似の倒し方は 2 通りしかない。見逃しを減らす代わりに実際には起きない指摘まで出すか、指摘の確度を上げる代わりに一部を取りこぼすかである。どちらへ倒しているかがツールごとに違うため、静的解析が通ったことは「実行時に落ちない」ことの証明にはならない。

静的解析の種類

同じ静的解析でも、層ごとに見ているものが違う。型チェッカは注釈と代入の整合、リンターは構文木上のパターン、セキュリティスキャンは入力元から危険な出力先までのデータの流れ、IaC スキャンはテンプレートの属性値を見る。層が違えば互いを代替しないので、リンターの規則をいくら厳しくしても SQL 注入は見つからない。

種類ツール検出対象
型チェックTypeScript型エラー
リンターESLint, Biomeコードスタイル、バグパターン
セキュリティスキャンSemgrep, CodeQL脆弱性 (SQLi, XSS)
IaC スキャンcfn-lint, Checkovインフラの設定ミス
依存脆弱性npm audit, Snykパッケージの脆弱性

TypeScript の型チェック

型チェックは、値がどこから来てどこへ渡るかを注釈から追い、代入できない組み合わせを実行前に落とす。

// TypeScript が実行前にエラーを検出
function add(a: number, b: number): number {
  return a + b;
}
add(1, '2'); // ❌ TS2345: string 型の引数を number 型の引数に渡せない

ESLint

リンターは型ではなく、構文木上の危険なパターンと規約違反を規則単位で拾う。設定形式は ESLint 9.0.0 (2024 年 4 月 5 日) で eslint.config.js が既定になり、10.0.0 (2026 年 2 月 6 日) で旧 .eslintrc 形式は読み込まれなくなった (2026 年 8 月時点の最新は 10.8.1)。

// eslint.config.js
export default [
  {
    rules: {
      'no-unused-vars': 'error',
      'no-console': 'warn',
      eqeqeq: 'error',
    },
  },
];
// ESLint が検出
if (x == null) { }  // ❌ eqeqeq: === を使うべき
const unused = 42;   // ❌ no-unused-vars: 未使用の変数

CI/CD への組み込み

CI に載せるときは、落とす層と警告に留める層を分けておく。型チェックと高危険度の脆弱性は失敗させ、スタイル寄りの規則は警告に置くと、無関係な指摘で緑が壊れて誰も見なくなる状態を避けられる。

# GitHub Actions
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npx tsc --noEmit        # 型チェック
      - run: npx eslint .             # リンター
      - run: npm audit --audit-level=high  # 脆弱性
      - run: cfn-lint template.yaml   # IaC スキャン

cfn-lint (CloudFormation)

IaC のスキャンは、スタックを作らずにテンプレートの属性値と参照関係を規則番号付きで報告する。規則番号の頭文字が重大度で、E (error) は仕様違反、W (warning) は動くが疑わしい書き方を指す。

cfn-lint template.yaml
# E3030 列挙値の違反 (Runtime に定義済みランタイム名以外を指定)
# E3012 プロパティの型が仕様と不一致
# W2001 定義したのに参照されていないパラメータ

重大度の切り分けは CI の設計に直結する。E だけを失敗条件にし、W は集計して定期的に棚卸しする形が扱いやすい。

静的解析 vs 動的解析

静的解析と動的解析は優劣ではなく、見える範囲が相補的である。

観点静的解析動的解析
実行コードを実行しないコードを実行する
速度高速低速
走査範囲実行されない経路も含めてコード全体実行されたパスのみ
取りこぼす型実行時の入力・環境に依存する不具合実行されなかった経路の欠陥
誤検知実際には到達しない経路も報告し得る少ない
TypeScript, ESLintテスト, DAST

Shift Left

静的解析は「Shift Left」(問題を開発の早い段階で発見) の代表的な手法。コミット前 (pre-commit hook) で実行すると、CI の待ち時間を削減できる。

# pre-commit hook
npx tsc --noEmit && npx eslint --cache .

導入で効くのは規則の足し算ではない

既存コードに厳しい規則を一括で当てると、初回の指摘が数千件になって全部無視される。実務では現状を基準線として凍結し、新しく触った範囲だけ厳しい規則を適用する形から始める。

指摘の抑制コメントも同じ問題を抱える。抑制が広い範囲に付いたり理由が書かれていないと、検査は形だけ残って実質は無効になる。抑制には対象の規則名と理由の記述を求め、抑制の総数を推移として見ておく。

静的解析の価値は検出件数ではなく、指摘が信頼されて直されるかで決まる。誤検知の多い規則を切る判断も、規則を追加する判断と同じだけ重要である。

この記事は役に立ちましたか?

関連用語

関連する記事