Enhance Code Security with GitHub Actions: Automatically Commenting PRs with Docker Scans
Tags: docker, webdev, devops, security
Published: 2024-07-14 03:08:16

To raise the security awareness of our development team, we've integrated a mechanism into our CI using GitHub Actions to conduct security scans on Docker images and automatically add those results as comments to pull requests (PRs).

![Screenshot 2024-07-14 9.23.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/0c31a11b-9f11-634b-6fdb-1a6b41f98aba.png)

## Overview of the `scan-and-comment` Job
This job automates a series of steps: checking out the code, building the Docker image, performing the security scan, formatting the results, and finally, posting comments to the PR. We use Trivy for scanning vulnerabilities in Docker images. More details about Trivy can be found in a separate article.

[Read about Trivy's vulnerability scanning](https://dev.to/suzuki0430/building-a-secure-cicd-workflow-for-ecs-with-github-actions-gde)

## Details of the `scan-and-comment` Job
Below is the complete CI workflow after adding the `scan-and-comment` job:

```yaml
name: backend-ci
on:
  push:
    branches:
      - main
      - staging
      - develop
  pull_request:
    types:
      - opened
    paths:
      - "backend/**"
  workflow_dispatch: # For manual execution

jobs:
  setup:
    # Details omitted for brevity

  test:
    # Details omitted for brevity

  lint:
    # Details omitted for brevity

  tsc:
    # Details omitted for brevity

  scan-and-comment:
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request' && github.event.action == 'opened'
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Build Docker Image
        run: docker build --build-arg ENV=prod -t local-image:latest -f ./backend/Dockerfile.ecs ./backend

      - name: Scan image with Trivy
        uses: aquasecurity/trivy-action@0.24.0
        with:
          image-ref: local-image:latest
          format: "table"
          severity: "CRITICAL,HIGH"
          output: trivy-result.txt

      - name: Check Trivy result file
        run: cat trivy-result.txt

      - name: Format Trivy Scan Result
        run: |
          if [ -s trivy-result.txt ]; then
            echo -e "## Vulnerability Scan Results\n<details><summary>Details</summary>\n\n\`\`\`\n$(cat trivy-result.txt)\n\`\`\`\n</details>" > formatted-trivy-result.md
          else
            echo -e "## Vulnerability Scan Results\nNo vulnerabilities were detected." > formatted-trivy-result.md
          fi

      - name: Comment PR with Trivy scan results
        uses: marocchino/sticky-pull-request-comment@v2
        with:
          path: formatted-trivy-result.md

      - name: Clean up Trivy result file
        run: rm -f trivy-result.txt formatted-trivy-result.md
```

### 1. Code Checkout
This job is executed only when a PR is newly created (`if: github.event_name == 'pull_request' && github.event.action == 'opened'`), ensuring that it does not trigger on syncs or reopens.

```yaml
scan-and-comment:
  runs-on: ubuntu-latest
  if: github.event_name == 'pull_request' && github.event.action == 'opened'
  steps:
    - name: Checkout code
      uses: actions/checkout@v4
```

### 2. Docker Image Build
The Docker image is built using the same settings as the production environment, enhancing the precision of the security scans.

```yaml
- name: Build Docker Image
  run: docker build --build-arg ENV=prod -t local-image:latest -f ./backend/Dockerfile.ecs ./backend
```
Below is the `Dockerfile` used for the build. It's configured for production builds, specifying the `ENV=prod` parameter to improve security scan accuracy.

```Dockerfile
FROM node:20.15.0 AS builder

WORKDIR /app

COPY package*.json ./
COPY yarn.lock ./

# Install dependencies ignoring Husky
# Do not use production mode here for yarn build
RUN yarn install --frozen-lockfile --ignore-scripts

COPY . .

RUN yarn build

ARG ENV

# If staging or production environment, remove node_modules and reinstall in production mode
RUN if [ "$ENV" = "prod" ] || [ "$ENV" = "stg" ]; then \
        rm -rf node_modules && yarn install --frozen-lockfile --ignore-scripts --production; \
    fi

FROM node:20.15.0-slim

WORKDIR /app

COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json

EXPOSE 8080

CMD ["node", "dist/main"]
```

### 3. Image Scan with Trivy
Trivy is used to perform the security scan, and the results are saved in a text file.

```yaml
- name: Scan image with Trivy
  uses: aquasecurity/trivy-action@0.24.0
  with:
    image-ref: local-image:latest
    format: "table"
    severity: "CRITICAL,HIGH"
    output: trivy-result.txt

- name: Check Trivy result file
  run: cat trivy-result.txt
```

https://github.com/aquasecurity/trivy-action/tree/master/

### 4. Format Trivy Scan Results
Based on the scan results, a detailed comment format is created.

```yaml
- name: Format Trivy Scan Result
  run: |
    if [ -s trivy-result.txt ]; then
      echo -e "## Vulnerability Scan Results\n<details><summary>Details</summary>\n\n\`\`\`\n$(cat trivy-result.txt)\n\`\`\`\n</details>" > formatted-trivy-result.md
    else
      echo -e "## Vulnerability Scan Results\nNo vulnerabilities were detected." > formatted-trivy-result.md
    fi
```

### 5. Comment on PR with Trivy Scan Results
The formatted scan results are posted as a comment on the PR.

```yaml
- name: Comment PR with Trivy scan results
  uses: marocchino/sticky-pull-request-comment@v2
  with:
    path: formatted-trivy-result.md
```

### 6. Cleanup
Finally, the used files are deleted to maintain a clean environment.

```yaml
- name: Clean up Trivy result file
  run: rm -f trivy-result.txt formatted-trivy-result.md
```

## Conclusion
In conclusion, integrating the `scan-and-comment` job within our CI pipeline using GitHub Actions enhances our security posture by ensuring Docker images are scanned for vulnerabilities at every critical PR juncture. This automation not only streamlines our security checks but also fosters an environment where security consciousness is an integral part of the development process, maintaining high standards across all deployments.