Amazon CodeGuru Reviewer を GitHub Actions から実行する

記事タイトルとURLをコピーする

Amazon CodeGuru Reviewer とは

aws.amazon.com

機械学習を使ってアプリケーションコードのセキュリティ脆弱性、見つけにくいバグを特定し推奨事項を提供してくれるサービスです。2022-09 現在、Java と Python に対応しています。

GitHub Actions から実行してみる

AWS が公式提供する以下のアクションがあるので、こちらの内容に沿って実行してみます。認証等、一部アレンジを加えている部分があります。

github.com

完成形はこちらのリポジトリを参照ください。

1. GitHub Actions のワークフローファイルを準備する

ワークフローの内容は以下の通りです。 詳細な構文等は公式のリファレンス を参照ください。

# .github/workflows/codeguru-reviewer.yml

name: Review

on:
  pull_request:
  workflow_dispatch:

jobs:
  review:
    runs-on: ubuntu-latest
    permissions:
      security-events: write
      id-token: write
      contents: read    # This is required for actions/checkout

    steps:

    # Step 1: Checkout the repository and provide your AWS credentials
    - name: Checkout repository
      uses: actions/checkout@v3
      with:
        fetch-depth: 0

    - name: Configure AWS Credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        role-to-assume: ${{secrets.ROLE_ARN}}
        role-session-name: codeguru-reviewr-session
        aws-region: ${{secrets.AWS_REGION}}

    # Step 2: Add CodeGuru Reviewer Action
    - name: CodeGuru Reviewer
      uses: aws-actions/codeguru-reviewer@v1.1
      with:
        s3_bucket: ${{secrets.BUCKET_NAME}}  # S3 Bucket with "codeguru-reviewer-*" prefix
    
    # Step 3: Upload results into GitHub
    - name: Upload review result
      if: ${{ github.event_name != 'push' }}
      uses: github/codeql-action/upload-sarif@v2
      with:
        sarif_file: codeguru-results.sarif.json

各ステップを解説します。

    # Step 1: Checkout the repository and provide your AWS credentials
    - name: Checkout repository
      uses: actions/checkout@v3
      with:
        fetch-depth: 0

    - name: Configure AWS Credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        role-to-assume: ${{secrets.ROLE_ARN}}
        role-session-name: codeguru-reviewr-session
        aws-region: ${{secrets.AWS_REGION}}

CodeGuru Reviewer の対象となるリポジトリのチェックアウト、及び CodeGuru へアクセスするための AWS 認証情報の設定です。 ポイントは fetch-depth: 0 の部分で、これはすべてのブランチとタグの全履歴を取得する設定です。通常の CI では fetch-depth: 1 のように直近のコミットのみ取得することが多いと思いますが、CodeGuru Reviewer を使う場合はこの設定が必要なことに注意です。
また、参考ドキュメントでは actions/checkout@v2v2 が使用されていましたが、検証時点で v3 がリリースされていましたので変更しています。(特に問題なく動作する様です)

AWS への認証についても、参考ドキュメントでは AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY を指定していますが、よりセキュアな方法として OpenID Connect での設定としています。詳細は以下の記事を参照ください。

blog.serverworks.co.jp

    # Step 2: Add CodeGuru Reviewer Action
    - name: CodeGuru Reviewer
      uses: aws-actions/codeguru-reviewer@v1.1
      with:
        s3_bucket: ${{secrets.BUCKET_NAME}}  # S3 Bucket with "codeguru-reviewer-*" prefix

CodeGuru Reviewer の公式アクションを呼び出します。 ポイントは s3_bucket です。CodeGuru Reviewer のインプットとなるソースコードがこのバケットに配置されます。命名ルールとして codeguru-reviewer- を先頭につける必要があります。 その他のオプションについてはドキュメントで網羅的に解説されていないため、以下を参照ください。

github.com

    # Step 3: Upload results into GitHub
    - name: Upload review result
      if: ${{ github.event_name != 'push' }}
      uses: github/codeql-action/upload-sarif@v2
      with:
        sarif_file: codeguru-results.sarif.json

コードの解析結果は SARIF と呼ばれるフォーマットで取得できますが、これを GitHub にアップロードします。 これにより、GitHub の Code Scanning 機能と連携して計測結果を GitHub リポジトリで表示することが出来ます。 注意点として、 Code Scanning 機能は プライベートリポジトリの場合、GitHub の Enterprise 契約が必要です。

2. AWS 側に必要なリソースを用意

AWS 側に必要なリソースを用意します。以下の CloudFormation テンプレートを用意しました。 パラメータの解説は前述のブログと同様です。

# template.yml

Parameters:
  GitHubOrg:
    Type: String
  RepositoryName:
    Type: String
  OIDCProviderArn:
    Description: Arn for the GitHub OIDC Provider.
    Default: ""
    Type: String
  GitHubOIDCThumbprint:
    Description: GitHub OIDC thumbprint. see also https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html
    Default: 6938fd4d98bab03faadb97b34396831e3780aea1
    Type: String

Conditions:
  CreateOIDCProvider: !Equals 
    - !Ref OIDCProviderArn
    - ""

Resources:
  CodeGuruReviewerBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub codeguru-reviewer-${AWS::AccountId}
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true

  CodeGuruReviewerRole:
    Type: AWS::IAM::Role
    Properties:
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonCodeGuruReviewerFullAccess
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Action: sts:AssumeRoleWithWebIdentity
            Principal:
              Federated: !If 
                - CreateOIDCProvider
                - !Ref GithubOidcProvider
                - !Ref OIDCProviderArn
            Condition:
              StringLike:
                token.actions.githubusercontent.com:sub: !Sub repo:${GitHubOrg}/${RepositoryName}:*

  CodeGuruReviewerS3Policy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - "s3:ListBucket"
            Resource:
              - !GetAtt CodeGuruReviewerBucket.Arn
          - Effect: Allow
            Action:
              - "s3:PutObject"
              - "s3:GetObject"
            Resource:
              - Fn::Join:
                - "/"
                - - !GetAtt CodeGuruReviewerBucket.Arn
                  - "*"
      PolicyName: code-guru-reviewer-s3-policy
      Roles:
        - !Ref CodeGuruReviewerRole

  GithubOidcProvider:
    Type: AWS::IAM::OIDCProvider
    Condition: CreateOIDCProvider
    Properties:
      Url: https://token.actions.githubusercontent.com
      ClientIdList: 
        - sts.amazonaws.com
      ThumbprintList:
        - !Ref GitHubOIDCThumbprint

Outputs:
  CodeGuruReviewerBucketName:
    Value: !Ref CodeGuruReviewerBucket
  CodeGuruReviewerRoleArn:
    Value: !GetAtt CodeGuruReviewerRole.Arn
  GithubOidcProviderArn:
    Condition: CreateOIDCProvider
    Value: !GetAtt GithubOidcProvider.Arn

以下、ポイント解説です。

  CodeGuruReviewerBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub codeguru-reviewer-${AWS::AccountId}

前述の通り、S3 バケットの先頭には codeguru-reviewer- をつける必要があります。

  CodeGuruReviewerRole:
    Type: AWS::IAM::Role
    Properties:
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonCodeGuruReviewerFullAccess

# 中略

  CodeGuruReviewerS3Policy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - "s3:ListBucket"
            Resource:
              - !GetAtt CodeGuruReviewerBucket.Arn
          - Effect: Allow
            Action:
              - "s3:PutObject"
              - "s3:GetObject"
            Resource:
              - Fn::Join:
                - "/"
                - - !GetAtt CodeGuruReviewerBucket.Arn
                  - "*"

GitHub Actions 側から AWS へアクセスするために AmazonCodeGuruReviewerFullAccess と 各種 S3 へのアクセスポリシーを付与しています。

3. その他準備

GitHub Actions の Secrets 設定

Action 内で、${{secrets.XXXX}} と記載している部分は Secrets の設定 が必要です。
設定内容は以下の通りです。

シークレット名 設定内容
ROLE_ARN CFn で作成した IAM Role ARN
AWS_REGION CodeGuru を実行する AWS リージョン (e.g. ap-northeast-1)
BUCKET_NAME CFn で作成した S3 バケット名

4. プルリクエストを作成し、CodeGuru Reviwer を実行する

以下のようなコードを追加するプルリクエストを作成します。 あくまで CodeGuru Reviewer に検知させるためのものなので、コード自体に意味はありません。

# src/hello.py

print("Hello CodeGuru Reviewer!")

import datetime
base_datetime = datetime.datetime.now() - datetime.timedelta(days=365) 
print(base_datetime)

プルリクエストを作成すると、作成した GitHub Actions が起動し、CodeGuru Reviewer による増分レビューが実行されます。 指摘は以下のように通常のレビューコメントの形で表示されます。

なお、リポジトリ全体に対するフルリポジトリスキャンも実行可能 (当該アクションを任意のブランチに対して手動実行) ですが、この場合 10 USD / 回 の費用がかかりますのでご注意ください。

aws.amazon.com

まとめ

Amazon CodeGuru Reviewer を GitHub Actions から実行する方法についてご紹介しました。 一度設定してしまえば、プルリクエストの度に自動でレビューを行ってくれるので、コードの品質維持のために導入してみてはいかがでしょうか。