Static Code Analysis (SAST) のセットアップ

Code Security は サイトでは利用できません。

概要

Datadog SAST をアプリ内でセットアップするには、Security > Code Security に移動してください。

静的コード解析スキャンを実行する場所を選択する

Datadogホスティングによるスキャン

GitHub リポジトリの場合、Datadog のインフラ上で直接 Datadog Static Code Analysis スキャンを実行できます。開始するには、Code Security ページに移動してください。

CI パイプラインでスキャンする

Datadog Static Code Analysis は、datadog-ci CLI を使用して CI パイプライン上で実行されます。

まず、Datadog API とアプリケーションキーを設定します。DD_APP_KEYDD_API_KEY をシークレットとして追加し、Datadog アプリケーションキーに code_analysis_read スコープがあることを確認してください。

次に、下記の使用する CI プロバイダごとの手順に従って、Static Code Analysis を実行してください。

使用している CI プロバイダに応じた手順を参照:


ソースコード管理プロバイダを選択する

Datadog Static Code Analysis はすべてのソースコード管理プロバイダをサポートしており、GitHub をネイティブにサポートしています。

GitHub インテグレーションのセットアップ

ソースコード管理プロバイダが GitHub の場合、GitHub インテグレーションタイルを使用して GitHub App を設定し、ソースコードインテグレーションを構成してインラインのコードスニペットを表示し、プルリクエストコメントを有効にする必要があります。

GitHub App をインストールする際、以下のパーミッションが特定の機能を有効にするために必要です:

  • Content: Read: Datadog でコードスニペットを表示するために必要
  • Pull Request: Read & Write: Datadog が プルリクエストコメントを使用してプルリクエスト内で直接違反に対するフィードバックを追加し、脆弱性の修正用のプルリクエストを作成できるようにするために必要

その他のソースコード管理プロバイダ

他のソースコード管理プロバイダを使用している場合は、datadog-ci CLI ツールを使用して CI パイプライン内で Static Code Analysis を実行し、Datadog にその結果をアップロードしてください。 Code Security ページに結果が表示されるようになる前に、デフォルトブランチ上で一度リポジトリの解析を実行する必要があります

設定のカスタマイズ

デフォルトでは、Datadog Static Code Analysis はプログラミング言語ごとの Datadog のデフォルトルールセットを使用してリポジトリをスキャンします。実行するルールセットやルールの選択・無視、その他のパラメータを自由にカスタマイズできます。これらの設定はリポジトリ内または Datadog アプリ上で行えます。

設定の場所

Datadog Static Code Analysis は、Datadog 内、またはリポジトリのルートディレクトリに配置されたファイルのいずれかで設定できます。

設定には以下の3つのレベルがあります:

  • 組織 (Org) レベルの設定 (Datadog)
  • リポジトリ レベルの設定 (Datadog)
  • リポジトリ レベルの設定 (リポジトリファイル)

すべて同じ YAML 形式を使用し、順番に基づいてオーバーレイ/パッチマージが行われます。たとえば、以下の2つのサンプル YAML ファイルを見てみましょう:

rulesets:
 - A
   rules:
      foo:
        ignore: ["**"]
        args: ["my_arg1", "my_arg2"]
rulesets:
 - A
    rules:
        foo:
            ignore: ["my_ignored_file.file"]
        bar:
            only: ["the_only_file.file"]
 - B

最初のファイルに対して2番目のファイルを順にマージ (オーバーレイ/パッチ方式) すると、最終的に以下のようになります:

rulesets:
 - A
    rules:
        foo:
            ignore: ["my_ignored_file.file"]
            args: ["my_arg1", "my_arg2"]
        bar:
            only: ["the_only_file.file"]
 - B

ご覧のとおり、最初のファイルの ignore: ["**"] は競合が発生したため、後に読み込まれたファイルの ignore: ["my_ignored_file.file"] に上書きされています。一方で、最初のファイルにある args フィールドは競合する値がないためそのまま保持されています。

組織 (Org) レベルの設定

作成されたルール

組織レベルでの設定は、解析対象となるすべてのリポジトリに適用されます。必ず実行したいルールや、グローバルで除外すべきパス/ファイルを定義するのに適した場所です。

リポジトリ レベルの設定

作成されたルール

リポジトリ レベルの設定は、選択した特定のリポジトリのみに適用されます。これらの設定は組織レベルの設定とマージされ、リポジトリ レベルの設定が優先されます。リポジトリ特有の詳細を上書きしたり、そのリポジトリだけに固有のルールを追加したい場合に有用です。

リポジトリ レベルの設定 (ファイル)

組織とリポジトリ レベルで提供される設定に加え、リポジトリのルートにある static-analysis.datadog.yml ファイルで設定を定義することもできます。このファイルは Datadog 上で定義したリポジトリ レベルの設定より優先されます。リポジトリのファイルベース設定は、ルールの設定を変更しながらセットアップやテストを繰り返す場合に便利です。

設定フォーマット

以下の設定フォーマットは、組織レベル、リポジトリ レベル、リポジトリ レベル(ファイル) のすべてに共通して適用されます。

設定ファイルの全体的な構造は以下のとおりです:

rulesets:
  - ruleset-name # デフォルト設定で実行したいルールセット
  - ruleset-name:
    # このルールセットを特定のパス/ファイルのみに適用する
    only:
      - "path/example"
      - "**/*.file"
    # このルールセットを特定のパス/ファイルでは適用しない
    ignore:
      - "path/example"
      - "**/*.file"
  - ruleset-name:
    rules:
      rule-name:
        # このルールを特定のパス/ファイルのみに適用する
        only:
          - "path/example"
          - "**/*.file"
        # このルールを特定のパス/ファイルでは適用しない
        ignore:
          - "path/example"
          - "**/*.file"
        arguments:
          # ルールの引数を値に設定
          argument-name: value
      rule-name:
        arguments:
          # 異なるサブツリーに対して異なる引数値を設定する
          argument-name:
            # デフォルト(リポジトリのルート)は value_1
            /: value_1
            # 特定のパスには value_2 を設定
            path/example: value_2
# すべてのルールセットを以下のパス/ファイルのみに対して解析する
only:
  - "path/example"
  - "**/*.file"
# すべてのルールセットで以下のパス/ファイルを解析対象外にする
ignore:
  - "path/example"
  - "**/*.file"

この YAML 設定ファイルでは、以下のトップレベルキーがサポートされます:

プロパティタイプ説明デフォルト
rulesetsArray解析対象のルールセット一覧。文字列 (ルールセット名) または詳細設定を含むオブジェクトのいずれかを要素として指定できます。必須
onlyArrayファイルパスまたはグロブパターンのリスト。指定した場合、すべてのルールセットにおいてこれらにマッチするファイルだけが解析されます。なし
ignoreArrayファイルパスまたはグロブパターンのリスト。すべてのルールセットにおいて、これらにマッチするファイルは解析対象から除外されます。なし

注: ここで示した onlyignore は、設定ファイル全体に適用されるファイルフィルタとして機能します。


ルールセットの設定

rulesets 配列の各要素は、次のいずれかの方法で定義できます:

  1. シンプルなルールセット宣言: たとえば ruleset-name のように単なる文字列を指定すると、そのルールセットはデフォルト設定で実行されます。
  2. 詳細なルールセット オブジェクト: キーをルールセット名、値を追加の設定を含むオブジェクトとして指定します。詳細設定可能なプロパティは以下のとおりです:
プロパティタイプ説明デフォルト
onlyArrayファイルパスまたはグロブパターンのリスト。このルールセットでは、これらのパターンに一致するファイルのみ解析対象にします。なし
ignoreArrayファイルパスまたはグロブパターンのリスト。このルールセットの解析対象から除外するファイルを指定します。なし
rulesObject個々のルール名とその設定オブジェクトのマッピングです。なし

ルールの設定

ルールセットの rules プロパティの中で、それぞれのルールは名前と設定によって定義されます。各ルールで利用できるプロパティは以下のとおりです:

プロパティタイプ説明デフォルト
onlyArrayファイルパスまたはグロブパターンのリスト。このルールは、これらのパターンに一致するファイルにのみ適用されます。なし
ignoreArrayファイルパスまたはグロブパターンのリスト。このルールの適用対象から除外するファイルを指定します。なし
argumentsObjectルールのパラメータと値を指定します。スカラー値またはパス単位で指定することができます。なし

引数 (arguments) の設定

ルールの引数は次の2通りの形式で定義できます:

  1. 固定値 (Static Value): 引数に直接値を割り当てます。

    arguments:
      argument-name: value
    
  2. パスごとの値割り当て (Path-Specific Mapping): ファイルパスに応じて異なる値を設定します。特別なキー / はデフォルト値 (リポジトリルートに適用) を示します。

    arguments:
      argument-name:
        /: value_default
        path/example: value_specific
    
キータイプ説明デフォルト
/Any特定のパスに一致しない場合に使用されるデフォルトの引数値です。なし
specific pathAny指定したパスまたはグロブパターンに一致するファイルに対して設定される引数値です。なし

設定例

rulesets:
  - python-best-practices
  - python-security
  - python-code-style:
    rules:
      max-function-lines:
        # max-function-lines ルールを以下のファイルでは適用しない
        ignore:
          - "src/main/util/process.py"
          - "src/main/util/datetime.py"
        arguments:
          # max-function-lines のしきい値を 150 行に設定
          max-lines: 150
      max-class-lines:
        arguments:
          # max-class-lines ルールのしきい値をサブツリーごとに変更
          max-lines:
            # デフォルト (リポジトリのルート) は 200 行
            /: 200
            # src/main/backend 配下は 100 行
            src/main/backend: 100
  - python-inclusive
  - python-django:
    # python-django ルールセットを以下のパスのみに適用
    only:
      - "src/main/backend"
      - "src/main/django"
    # 以下のパターンにマッチするファイルには python-django ルールセットを適用しない
    ignore:
      - "src/main/backend/util/*.py"
# ソースコードのみ解析対象にする
only:
  - "src/main"
  - "src/tests"
  - "**/*.py"
# サードパーティや生成ファイルは解析対象外
ignore:
  - "lib/third_party"
  - "**/*.generated.py"
  - "**/*.pb.py"
名前説明必須デフォルト
rulesetsルールセット名および設定のリスト。利用可能なすべてのルールセットはこちらtrue
ignore無視するパスやグロブパターンのリスト。これらに一致するファイルは解析されません。false
only解析対象とするパスやグロブパターンのリスト。これらに一致するファイルのみ解析されます。false
ignore-gitignore.gitignore に記載されたパスを解析対象外として扱わないようにするかどうかを指定します。falsefalse
max-file-size-kb指定したサイズ(kB)より大きいファイルを無視します。false200

static-analysis.datadog.yml ファイル内で使用できるルールセット (ruleset) オプションは以下のとおりです:

名前説明必須
rulesルールセットに属する各ルールの設定リストです。false
ignoreこのルールセットだけで無視するパスやグロブパターンのリスト。該当するファイルは解析されません。false
onlyこのルールセットだけで解析対象にするパスやグロブパターンのリスト。該当するファイルのみ解析します。false

同様に、ルール (rule) 単位で設定できるオプションは以下のとおりです:

名前説明必須
ignoreこのルールだけで無視するパスやグロブパターンのリスト。該当するファイルは解析されません。false
onlyこのルールだけで解析対象にするパスやグロブパターンのリスト。該当するファイルのみ解析します。false
argumentsカスタマイズ可能な引数をサポートするルールに対して値を指定するマップです。false

arguments フィールドのマップは、引数の名前をキーとし、値として文字列またはマップを指定します:

  • リポジトリ全体で同じ値を設定する場合は、単純に文字列として指定します。
  • リポジトリ内の異なるサブツリーごとに値を変えたい場合は、サブツリーのプレフィックスをキーとして、そこに適用する値をマップ形式で指定します。

違反の無視

リポジトリ単位で無視する

static-analysis.datadog.yml ファイルに無視ルールを追加します。以下の例では、javascript-express/reduce-server-fingerprinting というルールをすべてのディレクトリに対して無視しています。

rulesets:
  - javascript-express:
    rules:
      reduce-server-fingerprinting:
        ignore: "**"

ファイルやディレクトリ単位で無視する

static-analysis.datadog.yml ファイルに無視ルールを追加します。下記の例では、javascript-express/reduce-server-fingerprinting というルールを特定のファイルに対して無視しています。パスによる無視の詳しい方法については、設定のカスタマイズセクションを参照してください。

rulesets:
  - javascript-express:
    rules:
      reduce-server-fingerprinting:
        ignore: "ad-server/src/app.js"

特定の違反インスタンスを無視する

特定の違反インスタンスを無視するには、対象行の上に no-dd-sa コメントを入れます。こうすると、その行からは違反が検出されなくなります。以下の Python コード例では、foo = 1 の行が Static Code Analysis スキャンで無視されます。

#no-dd-sa
foo = 1
bar = 2

また、no-dd-sa を使って特定のルールだけを無視することもできます。その場合は、無視したいルール名を <rule-name> の代わりに指定します。

no-dd-sa:<rule-name>

たとえば、以下の JavaScript コード例では、my_foo = 1javascript-code-style/assignment-name ルール以外のすべてのルールで解析されます。このルールは開発者に snake_case ではなく camelCase を使うよう指示するものです。

// no-dd-sa:javascript-code-style/assignment-name
my_foo = 1
myBar = 2

Datadog のサービスやチームに結果を関連付ける

サービスへの関連付け

Datadog は、以下の仕組みを用いて静的コードおよびライブラリスキャンの結果を該当するサービスに関連付けます。

  1. Software Catalog を用いたコード配置場所の特定
  2. 他の Datadog 製品内でのファイル使用パターンの検出
  3. ファイルパスやリポジトリ名からサービス名を検索

いずれか1つの方法で関連付けに成功すると、それ以降のマッピングは行われません。それぞれのマッピング方法は以下で詳しく説明します。

Software Catalog でコード配置場所を特定する

Software Catalog の スキーマバージョン v3 以降では、サービスがどのリポジトリに存在し、どのパスを含むかを codeLocations セクションで定義できます。

paths 属性には、リポジトリ内のパスとマッチするグロブパターンのリストを指定します。

entity.datadog.yaml

apiVersion: v3
kind: service
metadata:
  name: my-service
datadog:
  codeLocations:
    - repositoryURL: https://github.com/myorganization/myrepo.git
      paths:
        - path/to/service/code/**

ファイル使用パターンの検出

Datadog は Error Tracking などの追加機能でファイルの使用状況を検出し、実行時のサービスとファイルを関連付けます。たとえば、foo というサービスがあり、ログやスタックトレースの中に /modules/foo/bar.py というファイルパスが含まれている場合、Datadog は /modules/foo/bar.py をサービス foo に関連付けます。

パスやリポジトリ名からサービス名を検出する

Datadog はパスやリポジトリ名に含まれるサービス名を検出し、一致があればファイルをそのサービスに関連付けます。

たとえば、myservice というサービスがあって、リポジトリ URL が https://github.com/myorganization/myservice.git の場合は、リポジトリ内のすべてのファイルが myservice に関連付けられます。

もしリポジトリとの一致が見つからなかった場合、Datadog はファイルの path を調べて一致するサービスを検索します。たとえば、myservice というサービス名が存在し、ファイルパスが /path/to/myservice/foo.py であれば、パスの一部に myservice が含まれるためファイルは myservice に関連付けられます。パスに 2 つのサービス名が含まれている場合は、ファイル名に近いほうのサービス名が選択されます。

チームへの関連付け

Datadog は、違反や脆弱性が検出された際に、そのファイルが関連付けられているサービスにひも付いたチームを自動的に関連付けます。たとえば、domains/ecommerce/apps/myservice/foo.pymyservice に関連付けられていれば、このファイルで見つかった違反はチーム myservice に関連付けられます。

サービスやチームが見つからない場合は、リポジトリ内の CODEOWNERS ファイルが使用されます。このファイルにより、Git プロバイダ上でどのチームがどのファイルを所有するかが決定されます。

: この機能を正しく動作させるには、Git プロバイダのチームと Datadog のチームを正しくマッピングしておく必要があります。

差分(diff)-Aware スキャン

差分スキャンを使用すると、Datadog の静的アナライザは機能ブランチ内でコミットによって変更されたファイルだけをスキャンします。これにより、リポジトリ全体を毎回スキャンしなくてよいので、スキャン時間が大幅に短縮されます。CI パイプラインで差分スキャンを有効にするには、以下の手順に従ってください:

  1. DD_APP_KEYDD_SITEDD_API_KEY の各変数を CI パイプラインで設定してください。
  2. 静的アナライザを呼び出す前に datadog-ci git-metadata upload を実行します。これにより、Datadog バックエンドが必要とする Git メタデータが準備されます。ファイル数を算出するのに Git メタデータが必須です。
  3. datadog-static-analyzer を呼び出す際に --diff-aware フラグを付けて実行してください。

以下はコマンド実行例です (これらはすべて Git リポジトリ内で実行してください):

datadog-ci git-metadata upload

datadog-static-analyzer -i /path/to/directory -g -o sarif.json -f sarif –-diff-aware <...other-options...>

注: 差分スキャンが実行できない場合は、ディレクトリ全体がスキャン対象となります。

サードパーティ製静的解析の結果を Datadog にアップロードする

SARIF インポートは Snyk、CodeQL、Semgrep、Checkov、Gitleaks、Sysdig でテスト済みです。他の SARIF に準拠したツールで問題がある場合は Datadog サポートにお問い合わせください。

相互運用可能な静的分析結果交換形式 (SARIF) であることを条件に、サードパーティーの静的分析ツールから Datadog へ結果を送信することができます。Node.js バージョン 14 以降が必要です。

SARIF レポートをアップロードするには

  1. DD_API_KEY 変数と DD_APP_KEY 変数が定義されていることを確認します。

  2. 必要に応じて DD_SITE 変数を設定します (デフォルトは datadoghq.com)。

  3. datadog-ci ユーティリティをインストールします。

    npm install -g @datadog/datadog-ci
    
  4. サードパーティの静的分析ツールをコード上で実行し、結果を SARIF 形式で出力します。

  5. 結果を Datadog にアップロードします。

    datadog-ci sarif upload $OUTPUT_LOCATION
    
PREVIEWING: domalessi/docs-10186