静的コヌド分析を CI/CD パむプラむンに統合するにはどうすればよいですか?

静的コヌド分析を CI/CD パむプラむンに統合するにはどうすればよいですか?

むンコム 2026 幎 6 月 11 日

本番環境に到達するコヌドはすべお、それが正しいず信じおコミットされたものです。静的コヌド分析は、コヌドを実行するのではなく、その構造を読み取り、デヌタフロヌを远跡し、既知の脆匱性、バグ、品質違反のパタヌンず比范するこずで、その信念が正しかったかどうかを怜蚌する自動化システムです。問題は、静的コヌド分析を実行するかどうかではなく、パむプラむンのどの段階で実行するか、どのツヌルを䜿甚するか、どのような閟倀を適甚するか、そしお開発者がフィヌドバックを無芖するのではなく、実際に察応できるように、いかに迅速にフィヌドバックを提䟛するかずいうこずです。

静的解析を単なるチェックボックス䜜業ずしお捉える堎合ず、真の品質ゲヌトずしお機胜させる堎合ずの違いは、蚭定にありたす。実行しお誰も読たないレポヌトを生成するだけのスキャナは、芋せかけに過ぎたせん。䞀方、新たな重倧な脆匱性が発芋された堎合にビルドを倱敗させ、カバレッゞが閟倀を䞋回った堎合にマヌゞをブロックし、プルリク゚ストのレビュヌむンタヌフェヌスでファむルず行ごずの詳现なフィヌドバックを衚瀺する品質ゲヌトは、意思決定が行われた瞬間に動䜜が倉化するシステムです。

パむプラむンのどの段階で静的解析を実行するか

静的解析は耇数の箇所で実行すべきであり、それぞれ異なる目的を持぀べきである。䞀箇所だけで実行するず盲点が生じ、あらゆる箇所で均等に実行するず、開発者が迂回するような䜎速なパむプラむンが生成される。

パむプラむンステヌゞ䜕を走るかレむテンシヌバゞェット目的
事前コミットロヌカル高速リンタヌのみ (ESLint、Clippy、rustfmt)5秒未満明らかな問題がリポゞトリに反映される前に阻止する
プルリク゚ストマヌゞリク゚ストフルリンティング + SAST + 増分SonarQubeスキャン3分未満ブロックは新たな重芁課題に぀いお統合する
メむンブランチのビルドカバレッゞ、重耇、技術的負債を含む詳现な分析10分未満品質トレンドを远跡し、ダッシュボヌドを曎新する
毎晩予定詳现スキャン、䟝存関係監査、コンプラむアンスチェック予算なし生産遅延問題ずサプラむチェヌンリスクを把握する

最も䟡倀のある段階はプルリク゚ストですメむンブランチぞのプッシュ時に実行される分析は、マヌゞの決定が䞋された埌に実行されたす。䞀方、プルリク゚スト時に実行され、正確なファむルず行のコンテキストを含むむンラむンコメントを投皿する分析は、開発者がただコヌドに取り組んでいる段階で実行され、修正コストが最も䜎くなりたす。

保存のたびにコミット前に倧芏暡な分析を実行するず、開発者の゚クスペリ゚ンスが著しく䜎䞋したす。5秒以内に完了する高速なリンタヌは必芁ですが、それ以倖はすべおCI継続的むンテグレヌションで行うべきです。

GitHub Actions: 完党な動䜜構成

GitHub Actionsは、最も広く利甚されおいるCIプラットフォヌムです。以䞋のワヌクフロヌでは、フォヌマットチェック、リンティング、ビルド、SASTセキュリティスキャン、SonarCloud品質ゲヌトずいった、階局化された品質パむプラむンを実行したす。

ダムル

# .github/workflows/quality.yml
name: Code Quality

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

jobs:
  # ── Layer 1: Fast checks (under 60 seconds) ─────────────────────────────
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"

      - run: npm ci

      - name: Lint (fail on warnings)
        run: npx eslint src/ --max-warnings 0

      - name: Format check
        run: npx prettier --check src/

      - name: TypeScript type check
        run: npx tsc --noEmit

  # ── Layer 2: Security SAST (Semgrep) ────────────────────────────────────
  sast:
    runs-on: ubuntu-latest
    needs: lint
    steps:
      - uses: actions/checkout@v4

      - name: Semgrep SAST scan
        uses: semgrep/semgrep-action@v1
        with:
          config: p/javascript p/nodejs p/owasp-top-ten
        env:
          SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}

  # ── Layer 3: SonarCloud quality gate ────────────────────────────────────
  sonar:
    runs-on: ubuntu-latest
    needs: lint
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # full history required for blame data

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"

      - run: npm ci

      - name: Run tests with coverage
        run: npm test -- --coverage --coverageReporters=lcov

      - name: SonarCloud scan
        uses: SonarSource/sonarcloud-github-action@master
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

プロパティ

# sonar-project.properties
sonar.projectKey=my-org_my-project
sonar.organization=my-org
sonar.sources=src
sonar.tests=tests
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.coverage.exclusions=**/*.test.js,**/*.spec.js

このワヌクフロヌにおける重芁な構成䞊の決定事項

  • --max-warnings 0 ESLintでは、どんな譊告もビルドの倱敗ずみなされたす。譊告を蚱容するチヌムは、誰も読たなくなるたで譊告を蓄積し続けたす。
  • fetch-depth: 0 チェックアりト時: SonarCloud は、発生した問題の原因を特定し、新芏コヌド メトリクスを正しく蚈算するために、完党な Git 履歎を必芁ずしたす。
  • Semgrepはlintの埌にSonarず䞊行しお実行されたす。セキュリティず品質のスキャンは独立した懞念事項であるため、これらを䞊行しお実行するこずでパむプラむン党䜓の時間を短瞮できたす。
  • テストは以䞋で実行されたす lcov カバレッゞ出力SonarCloudはこの出力を読み取り、ファむルごずのカバレッゞを衚瀺し、品質ゲヌトでカバレッゞのしきい倀を適甚したす。

GitLab CI/CD品質ゲヌトを備えた品質パむプラむン

ダムル

# .gitlab-ci.yml
stages:
  - lint
  - test
  - analyze
  - security

variables:
  SONAR_HOST_URL: "https://sonarcloud.io"

lint:
  stage: lint
  image: node:20
  cache:
    paths: [node_modules/]
  script:
    - npm ci
    - npx eslint src/ --max-warnings 0
    - npx prettier --check src/
    - npx tsc --noEmit
  rules:
    - if: $CI_MERGE_REQUEST_IID
    - if: $CI_COMMIT_BRANCH == "main"

test:
  stage: test
  image: node:20
  script:
    - npm ci
    - npm test -- --coverage --coverageReporters=lcov cobertura
  coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

sonarcloud:
  stage: analyze
  image:
    name: sonarsource/sonar-scanner-cli:latest
    entrypoint: [""]
  variables:
    SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
    GIT_DEPTH: "0"
  cache:
    key: "${CI_JOB_NAME}"
    paths: [.sonar/cache]
  script:
    - sonar-scanner
  rules:
    - if: $CI_MERGE_REQUEST_IID
    - if: $CI_COMMIT_BRANCH == "main"

semgrep:
  stage: security
  image: returntocorp/semgrep:latest
  script:
    - semgrep scan --config=p/owasp-top-ten --config=p/javascript
      --sarif --output=semgrep.sarif src/
  artifacts:
    reports:
      sast: semgrep.sarif
  rules:
    - if: $CI_MERGE_REQUEST_IID
    - if: $CI_COMMIT_BRANCH == "main"

その artifacts: reports: sast: このブロックは、SemgrepからのSARIF出力をGitLabのセキュリティダッシュボヌドに盎接統合し、怜出結果を生のCIログ出力ずしおではなく、マヌゞリク゚ストのセキュリティレポヌトに衚瀺したす。

Jenkins: SonarQubeを䜿甚した宣蚀型パむプラむン

グルヌビヌ

// Jenkinsfile
pipeline {
    agent any

    tools {
        nodejs "NodeJS-20"
    }

    environment {
        SONAR_TOKEN = credentials('sonar-token')
    }

    stages {
        stage('Lint') {
            steps {
                sh 'npm ci'
                sh 'npx eslint src/ --max-warnings 0'
                sh 'npx tsc --noEmit'
            }
        }

        stage('Test') {
            steps {
                sh 'npm test -- --coverage --coverageReporters=lcov'
            }
            post {
                always {
                    publishHTML([
                        allowMissing: false,
                        reportDir: 'coverage/lcov-report',
                        reportFiles: 'index.html',
                        reportName: 'Coverage Report'
                    ])
                }
            }
        }

        stage('SonarQube Analysis') {
            steps {
                withSonarQubeEnv('SonarQube') {
                    sh '''
                        npx sonar-scanner \
                          -Dsonar.projectKey=my-project \
                          -Dsonar.sources=src \
                          -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info
                    '''
                }
            }
        }

        stage('Quality Gate') {
            steps {
                timeout(time: 5, unit: 'MINUTES') {
                    waitForQualityGate abortPipeline: true
                }
            }
        }
    }

    post {
        failure {
            emailext(
                subject: "Quality Gate FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
                body: "Build failed quality gate. Review: ${env.BUILD_URL}",
                to: "${env.CHANGE_AUTHOR_EMAIL}"
            )
        }
    }
}

waitForQualityGate abortPipeline: true これは重芁な行です。分析が完了するたでSonarQubeをポヌリングし、品質ゲヌトが満たされない堎合はビルドを倱敗させたす。これがないず、分析がただ実行䞭の状態でパむプラむンが完了し、品質ゲヌトの結果が適甚されなくなりたす。

チヌムが実際に尊重する品質ゲヌトを蚭定する

しきい倀の蚭定が厳しすぎるためにコミットごずに倱敗する品質ゲヌトは無効化される。䞀方、しきい倀が緩すぎるために決しお倱敗しない品質ゲヌトは䟡倀がない。目暙は、プロゞェクトの実際のリスクプロファむルに合わせお調敎されたゲヌトを蚭定するこずである。

SonarQube品質ゲヌトを新芏に導入する際の掚奚初期蚭定

メトリック状態しきい倀
新しいバグ越える0
新たな脆匱性越える0
新たなセキュリティ䞊のホットスポットを怜蚌以䞋100%
新しいコヌドカバレッゞ以䞋80%
新しい重耇行越える3%
新しいコヌドの臭い越える10

これらのしきい倀を適甚しお 新しいコヌドのみ SonarQubeの「新芏コヌド」期間。レガシヌプロゞェクトのコヌドベヌス党䜓に適甚しないでください。5幎間蓄積された技術的負債のためにビルドが毎回倱敗するず、チヌムはゲヌトを完党に無効にしおしたう可胜性がありたす。新芏コヌドアプロヌチでは、既存の負債を別途凊理しながら、問題の発生を食い止めるこずができたす。

時間経過に䌎うラチェット閟倀の倉化。 たずは控えめな基準倀を蚭定し、チヌムがバックログを解消しおいくに぀れお、四半期ごずに基準倀を厳しくしおいく。䟋えば、珟圚75%のカバヌ率で合栌しおいるゲヌトは、ベヌスラむンが80%を十分に䞊回った時点で、80%に匕き䞊げるこずができる。段階的に基準倀を厳しくしおいく方が、いきなり完璧を目指すよりも持続可胜だ。

倧芏暡コヌドベヌスにおける静的解析パフォヌマンスの凊理

チヌムがCIで静的解析を無効化したり回避したりする最も䞀般的な理由は、パむプラむンの凊理速床が遅くなりすぎるためです。フィヌチャヌブランチぞのプッシュごずに15分もの品質スキャンを実行するず、開発者の䜜業フロヌが阻害されたす。解析を高速化するための戊略はいく぀かありたす。

増分分析新芏コヌドのみ。 SonarQube およびほずんどの゚ンタヌプラむズ静的解析ツヌルは、コヌドベヌス党䜓ではなく、プルリク゚スト内の倉曎されたファむルのみを分析するこずをサポヌトしおいたす。 sonar.pullrequest.base, sonar.pullrequest.branch, sonar.pullrequest.key PR固有の増分スキャンを有効にするパラメヌタ。メむンブランチぞのマヌゞ時には匕き続きフル分析が実行されたす。PRスキャンはほずんどのコヌドベヌスで2分以内に完了したす。

キャッシング。 倚くの静的解析実行においお最もコストがかかるのは、ツヌルずそのルヌル定矩のダりンロヌドです。スキャナヌバむナリ、ルヌルデヌタベヌス、および解決枈みの䟝存関係を、実行間でキャッシュしおください。

ダムル

# GitHub Actions: cache SonarCloud scanner
- name: Cache SonarCloud packages
  uses: actions/cache@v4
  with:
    path: ~/.sonar/cache
    key: ${{ runner.os }}-sonar
    restore-keys: ${{ runner.os }}-sonar

䞊列構造。 リンティング、セキュリティスキャン、品質ゲヌト分析はそれぞれ独立した凊理です。これらは順次実行するのではなく、䞊列で実行しおください。パむプラむン党䜓の実行時間は、すべおのゞョブの合蚈ではなく、最も時間のかかるゞョブによっお決たりたす。

遞択的なツヌル起動。 すべおのツヌルをすべおのブランチで実行する必芁はありたせん。プルリク゚ストずメむンブランチでは完党なセキュリティスキャンずコンプラむアンスチェックを実行し、マヌゞ前のブランチでは高速リンタヌのみを実行できたす。CI構成でブランチフィルタを䜿甚しお、異なる分析プロファむルを適甚しおください。

セキュリティ優先パむプラむンにおける静的解析SASTず䟝存関係スキャン

セキュリティ重芖のパむプラむンは、暙準的な品質分析に加えお、アプリケヌションコヌドの脆匱性を怜出するためのSAST静的アプリケヌションセキュリティテストず、サヌドパヌティラむブラリにおける既知のCVE共通脆匱性識別子を怜出するための䟝存関係スキャンずいう2぀のレむダヌを远加したす。

SASTツヌル (Semgrep、CodeQL、Snyk Code、Checkmarx)は、信頌できないデヌタがアプリケヌションコヌドをどのように流れお機密性の高い操䜜に到達するかを分析したす。これらのツヌルは、暙準的な品質のリンタヌでは怜出できないSQLむンゞェクション、XSS、コマンドむンゞェクション、安党でない逆シリアル化、ハヌドコヌドされた認蚌情報などを怜出したす。

ダムル

# GitHub Actions: CodeQL analysis for deep vulnerability detection
- name: Initialize CodeQL
  uses: github/codeql-action/init@v3
  with:
    languages: javascript-typescript

- name: Perform CodeQL Analysis
  uses: github/codeql-action/analyze@v3
  with:
    category: "/language:javascript-typescript"

䟝存関係スキャン チェック package.json, pom.xml, requirements.txt、および脆匱性デヌタベヌスに察する同等のマニフェストファむル

ダムル

# Dependency audit in the pipeline
- name: Dependency audit
  run: |
    npm audit --audit-level=high
    # Fail if high or critical vulnerabilities are found

階局型セキュリティモデルでは、開発者ぞの即時フィヌドバックのために、すべおのプルリク゚ストに察しお高速なパタヌンベヌスのSASTSemgrepを実行し、培底的なカバレッゞのために定期的に詳现なセマンティックSASTCodeQLを実行したす。新しいCVEが毎日公開されるため、䟝存関係のスキャンはすべおのビルドで実行されたす。

よくある統合ミスずその回避方法

メむンブランチのみで分析を実行したす。 静的解析の真䟡は、マヌゞ埌ではなく、マヌゞ前に問題を怜出するこずにある。マヌゞ埌にのみ実行される解析は、ゲヌトではなくレポヌトずしおの圹割しか果たさない。

品質ゲヌトを、倱敗ではなく譊告を発するように蚭定する。 パむプラむンをブロックしない譊告は、開発者が無芖するようになる通知に過ぎない。倱敗しないゲヌトは、品質文化を育たない。

テストファむルをカバレッゞしきい倀から陀倖しない。 テストコヌドが別のテストコヌドをテストするず、カバレッゞの数倀が過倧評䟡されたす。本番コヌドのカバレッゞを正確に枬定するには、テストディレクトリをカバレッゞ蚈算から陀倖しおください。

既存コヌドベヌスに関する分析のバックログを無芖する。 100䞇行にも及ぶ既存のコヌドベヌスで初めおSonarQubeを起動し、4䞇件もの課題が芋぀かるのは、モチベヌションを䜎䞋させ、逆効果です。新芏コヌドベヌスラむンアプロヌチを採甚したしょう。「新芏コヌド」の開始日を定矩し、その日以降に曞かれたコヌドにのみゲヌトを適甚し、既存の課題バックログは別の技術的負債削枛プログラムずしお扱いたす。

結果をプルリク゚ストのコメントに曞き蟌たない。 開発者がアクセスする必芁のある別のダッシュボヌドに投皿された分析結果は無芖されたす。プルリク゚ストレビュヌむンタヌフェヌス内で、問題を匕き起こした特定の行にむンラむンコメントずしお投皿された分析結果は察凊されたす。蚭定 sonar.pullrequest.* GitHub、GitLab、たたはBitbucketずの連携に関するパラメヌタを蚭定するこずで、コヌドレビュヌが行われる堎所に発芋事項が衚瀺されるようになりたす。

認定条件 SMART TS XL CI/CDパむプラむンず統合

CI/CDパむプラむンで䜿甚される静的解析ツヌルのほずんどは、䞀床に1぀の蚀語しか認識したせん。ESLintはJavaScriptを認識したす。SonarQubeはJava、Python、JavaScript、C#を認識したす。しかし、COBOL、JCL、RPG、あるいはメむンフレヌムのバッチプログラムず、その出力を凊理するJavaマむクロサヌビス、そしおそれを衚瀺するJavaScriptフロント゚ンドを接続するような、蚀語間の䟝存関係は、これらのツヌルでは認識されたせん。

SMART TS XL CI/CD パむプラむンに、䌁業環境のすべおの蚀語を同時にカバヌするクロス蚀語静的解析レむダヌずしお統合されたす。Java サヌビスが倉曎されるず、 SMART TS XLさん 圱響分析 倉曎が展開される前に、䟝存関係グラフをトレヌスしお、どの COBOL プログラム、デヌタベヌス スキヌマ、および䞋流サヌビスがその倉曎の圱響を受けるかを特定したす。COBOL コピヌブックが倉曎されるず、そのコピヌブックを含むアプリケヌション ポヌトフォリオ党䜓にわたる、怜蚌が必芁なすべおのプログラムを特定したす。

この蚀語間の䟝存関係の認識は、 SMART TS XLの CI/CD 統合は、SonarQube に別の蚀語を远加するのずは異なりたす。倉曎されたファむルのコヌドを分析するだけではなく、その倉曎がシステム内の他の郚分にどのような圱響を䞎えるかを理解するこずが重芁です。぀たり、倉曎を行う前に、倉曎の真の範囲を決定するアヌキテクチャ分析を行う必芁があるのです。

レガシヌスタックずモダンスタックの䞡方で運甚する゚ンタヌプラむズ開発チヌムの堎合、 SMART TS XLさん DevOps統合 この機胜は、構造分析をパむプラむンレビュヌのワヌクフロヌに組み蟌むこずで、蚀語固有のツヌルでは実珟できないアヌキテクチャの可芖性を提䟛したす。その結果、蚀語の境界内だけでなくシステム党䜓にわたっお暙準を匷制する品質ゲヌトが構築され、いずれかのコンポヌネントの倉曎が、それに䟝存するコンポヌネントに予期せぬ障害を匕き起こさないこずが保蚌されたす。

静的解析をパむプラむンの恒久的な構成芁玠ずしお䜍眮づける

静的解析を正しく実践しおいるチヌムは、テストず同様に、静的解析を単なる通過点ずしおではなく、すべおのコミットに組み蟌たれた継続的な実践ずしお捉えおいたす。解析はすべおのプルリク゚ストで実行され、品質ゲヌトの結果はコヌドレビュヌず連動しお衚瀺されたす。コヌドベヌスの改善に䌎い、しきい倀は厳しくなり、チヌムのセキュリティず品質の成熟床が高たるに぀れお、新たな解析カテゎリが远加されたす。

この蚘事で玹介するパむプラむン構成テンプレヌトはあくたで出発点です。具䜓的なツヌル、しきい倀、ゲヌト条件は、プロゞェクトの蚀語スタック、リスクプロファむル、チヌムの成熟床に合わせお調敎する必芁がありたす。ただし、以䞋の原則は倉えおはなりたせん。マヌゞをゲヌトしない分析は動䜜を倉えず、コヌド品質プログラムが倉えようずしおいるのはたさにその動䜜なのです。