CodeGuruによるレビューをCI/CDパイプラインに組み込む CodePipeline＋CodeBuild編


2024/05/21 追記：デフォルトで対応したかも
https://docs.aws.amazon.com/ja_jp/codeguru/latest/security-ug/get-started-codepipeline.html
![スクリーンショット 2024-05-21 19.04.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43631/0e60e394-f9d8-dd37-1c56-9a49f2b0a990.png)


# モチベーション

DevOpsサイクルを素早く回し、柔軟で高速なフィードバック&リリースを進めるなかで、ときにセキュリティの観点が後回しになってしまうかもしれません。
そうなるとリリースまでにリスクを除去しきれない、リスクの発見が遅れるといったことが発生し、結果的に新機能のリリースが遅れてしまいます。


### DevSecOps
DevOpsサイクルにセキュリティの自動化を組み込んだDevSecOpsという考え方があります。
サイクルの中にセキュリティを組み込み、できるだけ早い段階で問題・リスクを発見し対応することで安定したリリースにつなげます。（セキュリティのシフトレフト）

セキュリティについては専門知識も必要な部分がありますが、AWSでは様々なセキュリティ関連サービスが存在します。
参考：[AWSクラウドセキュリティ](https://aws.amazon.com/jp/security/)、[AWSアカウントのセキュリティ対策(iret.media)](https://cloudpack.media/60161)
セキュリティも専門チームだけが責任を負うのではなく、DevOpsで開発と運用が同じ目的と責任を共有するのと同じように責任を共有し、サイクルの中に組み込んでいくなかでこういったサービスやツールは活用していきたいところです。

一例として今回は[AWS CodeGuru Reviewer](https://aws.amazon.com/jp/codeguru/)を使ってコードの脆弱性の検知をDevOpsサイクルに組み込んでみたいと思います。

![devopscycle1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43631/cbe1fa77-3c64-fbd4-f4ca-4ec50803a25c.png)


### AWS CodeGuru Reviewer

[AWS CodeGuru Reviewer](https://aws.amazon.com/jp/codeguru/)はMLを活用したコードレビューサービスです。
コード品質やコストのかかるコードに関する推奨事項だけでなく[ハードコーディングされたシークレット](https://aws.amazon.com/jp/blogs/news/codeguru-reviewer-secrets-detector-identify-hardcoded-secrets/)や[ログインジェクション脆弱性](https://aws.amazon.com/jp/blogs/news/new-for-amazon-codeguru-reviewer-detector-library-and-security-detectors-for-log-injection-flaws/)などの脆弱性についてもチェックしてくれます。

リポジトリに紐づけるだけで[プルリクエスト作成時にレビューが走って推奨事項がある場合はコメントをつけてくれます](https://docs.aws.amazon.com/ja_jp/codeguru/latest/reviewer-ug/repository-analysis-vs-pull-request.html)。


![スクリーンショット 2022-06-09 15.15.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43631/b4e9dc26-09ba-6260-c341-bc824d34e312.png)

私のチームでは推奨事項コメントに対して修正をするかは人間の判断に委ねる対応をしていました。
さらに今回はセキュリティ関連の推奨事項に関しては原則必須で対応する形でやってみることにしました。
CodeGuru Reviewerをパイプラインに組み込み、コード脆弱を検知した場合は実行を中断し、リリースを失敗させる仕組みを作成します。

セキュリティを担保しつつサイクルを回すスピードを落とさないためプロセスをできるだけ自動化していきます。

![スクリーンショット 2022-06-09 15.18.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43631/776719db-4a05-815f-e2b2-dc1297b76890.png)

## 対象システム

今回は[実際のシステム](https://cloudpack.media/60032)に組み込んで運用しています。
担当は２人いて、コードは主担当が書いて、副担当(わたし)がレビューしてマージすることが多いです。
その背景を含めて記載すると以下のようになります。

![Untitled(2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43631/94ceac16-fad6-ece7-5a66-77c91f5f13fb.png)



# 構成
今回はCodeGuru ReviewerによるレビューをCodePipeline + CodeBuildで構築しているパイプラインに組み込んでみました。
CodeGuru Reviewerは[「Github Actions」との統合はサポートしています](https://aws.amazon.com/jp/about-aws/whats-new/2021/06/amazon-codeguru-reviewer-announces-ci-cd-integration-github-actions-new-security-detectors-for-java/)が、今回対象としたプロジェクトでは「CodePipeline」を利用しているので自前スクリプトを噛ませています。

[CodeGuru Reviewerのaws-cliコマンド](https://docs.aws.amazon.com/cli/latest/reference/codeguru-reviewer/index.html)を呼び出すスクリプトをCodeBuildの[buildspecファイル](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-spec-ref.html)から呼び出すことで実現しています。シェルスクリプトからaws-cliを叩いているだけなので、条件を揃えれば他のパイプラインサービスからも使えると思います。

既存のパイプラインにCodeGuru Reviewerによるレビューを追加しました。<font color="Red">赤色が今回追加したところ。</font>
![パイプライン](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43631/8b49b2de-8379-ebfc-767c-fefe62f270b2.png)


今回はフルスキャンをかけて推奨事項が１つでも残っていたらパイプラインを失敗させています。CodeGuru Reviewerでのフルスキャンは少し時間かかるので(試しに組み込んだプロジェクトで5分くらい)、場合によってはステージ別にON/OFFしやすい作りにしておくといいかもしれません。ちなみにもともとユニットテストのタイミングで「PEP8」のチェックはかけています。

# CodeGuru Reviewer 呼び出しスクリプト


## やっていること
1. レビューの生成([aws codeguru-reviewer create-code-review](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/codeguru-reviewer/create-code-review.html)) # create review
1. レビュー完了待ち([aws codeguru-reviewer wait code-review-completed](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/codeguru-reviewer/wait/code-review-completed.html) ) # wait
1. レビュー結果取得([aws codeguru-reviewer describe-code-review](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/codeguru-reviewer/describe-code-review.html)) # count recommendations
1. 推奨事項が0なら成功(exit 0)、そうでなければ失敗(exit 1)

レビュー対象のブランチ名は、パイプラインから渡しています。

## 準備

### 環境変数

以下の環境変数を設定します。
(引数にして呼び出し側で環境変数指定するようにしてもいいかも)

- **REPOSITORY_BRANCH** = 対象ブランチ名
- **CODEBUILD_BUILD_ID** = ビルドID。今回は[CodeBuildが持っている環境変数](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-env-ref-env-vars.html)から良さげなのをそのまま使いました。CodeBuild以外から使う場合はユニークなIDを設定してください。レビューにアカウント内で一意の名前をつけるのに使ってます。
- **CODEGURU_REVIEWER_ASSOCIATION_ARN** = CodeGuruReviewerの関連付けARN(※1)

**(※1) CodeGuru Reviewerの関連付けARN**
CodeGuruのリポジトリ設定は済んでいる前提でマネコンには「関連付けID」だけ表示されるので[ARNフォーマット](https://docs.aws.amazon.com/ja_jp/codeguru/latest/reviewer-api/API_RepositoryAssociation.html)では表示されないので、自分でARNフォーマットにする必要あり
arn:aws:codeguru-reviewer:{リージョン}:{AWSアカウントID}:association:{関連付けID}

### IAM権限
- codeguru-reviewer:CreateCodeReview(※２)
- codeguru-reviewer:DescribeCodeReview(※２)

**(※2) 対象resource**
resourceを絞ると正常に動かないようなのでresourceにはワイルドカード(*)を指定しています。
resourceを絞るとポリシー画面で警告が出て、実際に使ってみるとその旨のエラー(not authorized to perform: codeguru-reviewer:CreateCodeReview on resource: *)が出力されました。

![IAM_Management_Console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43631/1ec2cdc0-392a-0dd8-b8eb-3c80d6529889.png)


```
An error occurred (AccessDeniedException) when calling the CreateCodeReview operation: User: arn:aws:sts::000000000000:assumed-role/xxxx/yyyy is not authorized to perform: codeguru-reviewer:CreateCodeReview on resource: *
```

## 実際のスクリプト

```shell:codeguru_review.sh
echo "Start CodeGuru review"

# create review
type_param=$(cat << EOS
{
  "RepositoryAnalysis": {
    "RepositoryHead": {"BranchName": "${REPOSITORY_BRANCH}"}
  },
  "AnalysisTypes": ["Security", "CodeQuality"]
}
EOS
)
review_arn=`aws codeguru-reviewer create-code-review \
  --name ${CODEBUILD_BUILD_ID//:/_} \
  --repository-association-arn $CODEGURU_REVIEWER_ASSOCIATION_ARN \
  --type "${type_param}" \
  --query "CodeReview.CodeReviewArn" --output text`
echo "Create review_arn=" $review_arn

# wait
echo "Wait..."
aws codeguru-reviewer wait code-review-completed --code-review-arn $review_arn

# count recommendations
recommendations_count=$(aws codeguru-reviewer describe-code-review --code-review-arn $review_arn \
	--query "CodeReview.Metrics.FindingsCount" --output text)
echo "recommendations count=" $recommendations_count
echo "Show Details https://${AWS_REGION}.console.aws.amazon.com/codeguru/reviewer/codereviews/details/${review_arn}" 

# assert
if [[ "$recommendations_count" = "0" ]]; then
	echo "OK: Has no recommendations"
    exit 0
else
	echo "NG: Has ${recommendations_count} recommendations"
    exit 1
fi
```
## 補足
### 解析対象
今回は「セキュリティ」と「コード品質」の両方の解析を有効にしています。
```json
"AnalysisTypes": ["Security", "CodeQuality"]
```

[CodeGuru Reviewerの除外設定](https://docs.aws.amazon.com/ja_jp/codeguru/latest/reviewer-ug/recommendation-suppression.html)は今のところファイル単位のみで、細かいチューニングがはできなさそうです。そのため場合によっては「セキュリティ」だけ有効にしてもいいかもしれません。

### レビュー名
レビュー名はAWSアカウント内で一意の名前をつける必要があります。
今回はCodeBuildのビルドIDを使いました。コロン(:)が使えないのでアンダーバー(_)に変換しています。
```shell
--name ${CODEBUILD_BUILD_ID//:/_}
```

### CodeGuruの詳細ページURL

結果取得時に、ついでにレビューの詳細ページURLを出力するようにしています。

```shell
echo "Show Details https://${AWS_REGION}.console.aws.amazon.com/codeguru/reviewer/codereviews/details/${review_arn}" 
```

# パイプラインに組み込む

私が今回対象としたプロジェクトでの例です。
CodePipelineやCodeBuildの設定はCloudFormationで書いているので、そのままCloudFormationの対応差分で記載します。mm

## 必要な情報を環境変数として渡す

CodeDeployの環境変数に、CloudFormationパラメータでとっている「ブランチ名」「CodeGuruReviewerの関連付けARN」を渡す

```yml
EnvironmentVariables:
  - Name: CODEGURU_REVIEWER_ASSOCIATION_ARN
    Value: !Sub '${CodeGuruReviewerAssociationArn}'
  - Name: REPOSITORY_BRANCH
    Value: !Sub '${RepositoryBranch}'
```


## IAM権限

CodeBuildにAssumeさせているRoleに以下のポリシーを追加
```yml
- PolicyName: CodeGuruReviewerPolicy
  PolicyDocument:
    Version: "2012-10-17"
    Statement:
      - Action:
        - codeguru-reviewer:CreateCodeReview
        - codeguru-reviewer:DescribeCodeReview
        Effect: Allow
        Resource: '*'

```

## [buildspecファイル](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-spec-ref.html)

CodeBuildのbuildspecファイルのコマンドに今回用意したシェルスクリプトの呼び出しを追加します。

```yml
  build:
    commands:
      - sh codeguru_review.sh
```

# 実行

## 推奨事項あり
推奨事項が出る変更をリポジトリにpush。パイプラインが失敗します。

### パイプラインから実行されたCodeGuruのレビュー結果画面

![Amazon_CodeGuru_Reviewer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43631/07042a3d-bf80-dd34-92a0-d3fa85bbcf05.png)


### パイプライン結果
![CodePipeline_-_AWS_Developer_Tools.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43631/226c8fe6-6306-5c8c-606c-4607f4d9dd17.png)


### CodeBuildの該当箇所ログ
```
[Container] 2022/05/05 11:38:37 Running command sh codeguru_review.sh
Start CodeGuru review
Create review_arn= arn:aws:codeguru-reviewer:us-west-2:000000000000:association:ff000000-000000000-0000-000000000000:code-review:RepositoryAnalysis-xxxxxxx
Wait...
recommendations count= 1
Show Details https://us-west-2.console.aws.amazon.com/codeguru/reviewer/codereviews/details/arn:aws:codeguru-reviewer:us-west-2:000000000000:association:ff000000-000000000-0000-000000000000:code-review:RepositoryAnalysis-xxxxxxx
NG: Has 1 recommendations

[Container] 2022/05/05 11:43:15 Command did not exit successfully sh codeguru_review.sh exit status 1
[Container] 2022/05/05 11:43:15 Phase complete: BUILD State: FAILED
[Container] 2022/05/05 11:43:15 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: sh codeguru_review.sh. Reason: exit status 1
```



## 推奨事項なし
推奨事項に従ってコードを修正しpush。推奨事項が0になりパイプラインが進み、最後まで成功しました。



### パイプラインから実行されたCodeGuruのレビュー結果画面

![Amazon_CodeGuru_Reviewer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43631/2f7ba343-bf36-eb41-c248-fcbeaf66f104.png)


### パイプライン結果

![CodePipeline_-_AWS_Developer_Tools.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43631/f2f69e0f-2415-d284-090b-05eefddf2d8e.png)

### CodeBuildの該当箇所ログ
```
[Container] 2022/05/05 12:11:14 Running command sh core/codeguru_review.sh
Start CodeGuru review
Create review_arn= arn:aws:codeguru-reviewer:us-west-2:000000000000:association:ff000000-000000000-0000-000000000000:code-review:RepositoryAnalysis-xxxxxxx
Wait...
recommendations count= 0
Show Details https://us-west-2.console.aws.amazon.com/codeguru/reviewer/codereviews/details/arn:aws:codeguru-reviewer:us-west-2:000000000000:association:ff000000-000000000-0000-000000000000:code-review:RepositoryAnalysis-xxxxxxx
OK: Has no recommendations
```

# 終わりに

今回のCodeGuruによるコードレビューは[実際のシステム](https://cloudpack.media/60032)のパイプラインに組み込み１ヶ月ほど運用しています。
しばらくパイプラインに組み込んで運用していますが、パイプラインの実行時間についてはそれほど伸びた感覚はありません。(また計測してみようと思います)

CodeGuru Reviwerはリポジトリと紐づけるだけで利用でき、課金も従量制であるため手軽です。
AWSのサービスはほとんどの操作をAPIやCLIからも行うことができ、今回のような組み込みもしやすいです。これらは自前で用意するのに比べてクラウドサービスの強みと言えます。
効率的にアプリケーションのライフサイクルを循環させて価値を届けるなかで、サービスやツールも活用し自動化できるものはそうしていきたいところです。

セキュリティは大事だと認識していますが、どうもスピードは落ちるようなイメージをもってしまうところもあります。
ただ本当に安定して素早く機能を届けるためにも重要であることを認識し、早い段階で検知できるよう費用対応固化を考えながらサイクルに取り込んでいこうと思います。

![devopscycle2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43631/90061c58-6a01-4d9a-d18f-6aa38b781ba3.png)


以上です、ではまた！