portainer/portainer

View on GitHub
.github/workflows/nightly-security-scan.yml

Summary

Maintainability
Test Coverage
name: Nightly Code Security Scan

on:
  schedule:
    - cron: '0 20 * * *'
  workflow_dispatch:

env:
  GO_VERSION: 1.21.9

jobs:
  client-dependencies:
    name: Client Dependency Check
    runs-on: ubuntu-latest
    if: >- # only run for develop branch
      github.ref == 'refs/heads/develop'
    outputs:
      js: ${{ steps.set-matrix.outputs.js_result }}
    steps:
      - name: checkout repository
        uses: actions/checkout@master

      - name: scan vulnerabilities by Snyk
        uses: snyk/actions/node@master
        continue-on-error: true # To make sure that artifact upload gets called
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          json: true

      - name: upload scan result as develop artifact
        uses: actions/upload-artifact@v3
        with:
          name: js-security-scan-develop-result
          path: snyk.json

      - name: develop scan report export to html
        run: |
          $(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest summary --report-type=snyk --path="/data/snyk.json" --output-type=table --export --export-filename="/data/js-result")

      - name: upload html file as artifact
        uses: actions/upload-artifact@v3
        with:
          name: html-js-result-${{github.run_id}}
          path: js-result.html

      - name: analyse vulnerabilities
        id: set-matrix
        run: |
          result=$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest summary --report-type=snyk --path="/data/snyk.json" --output-type=matrix)
          echo "js_result=${result}" >> $GITHUB_OUTPUT

  server-dependencies:
    name: Server Dependency Check
    runs-on: ubuntu-latest
    if: >- # only run for develop branch
      github.ref == 'refs/heads/develop'
    outputs:
      go: ${{ steps.set-matrix.outputs.go_result }}
    steps:
      - name: checkout repository
        uses: actions/checkout@master

      - name: install Go
        uses: actions/setup-go@v3
        with:
          go-version: ${{ env.GO_VERSION }}

      - name: download Go modules
        run: cd ./api && go get -t -v -d ./...

      - name: scan vulnerabilities by Snyk
        continue-on-error: true # To make sure that artifact upload gets called
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        run: |
          yarn global add snyk
          snyk test --file=./go.mod --json-file-output=snyk.json 2>/dev/null || :

      - name: upload scan result as develop artifact
        uses: actions/upload-artifact@v3
        with:
          name: go-security-scan-develop-result
          path: snyk.json

      - name: develop scan report export to html
        run: |
          $(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest summary --report-type=snyk --path="/data/snyk.json" --output-type=table --export --export-filename="/data/go-result")

      - name: upload html file as artifact
        uses: actions/upload-artifact@v3
        with:
          name: html-go-result-${{github.run_id}}
          path: go-result.html

      - name: analyse vulnerabilities
        id: set-matrix
        run: |
          result=$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest summary --report-type=snyk --path="/data/snyk.json" --output-type=matrix)
          echo "go_result=${result}" >> $GITHUB_OUTPUT

  image-vulnerability:
    name: Image Vulnerability Check
    runs-on: ubuntu-latest
    if: >-
      github.ref == 'refs/heads/develop'
    outputs:
      image-trivy: ${{ steps.set-trivy-matrix.outputs.image_trivy_result }}
      image-docker-scout: ${{ steps.set-docker-scout-matrix.outputs.image_docker_scout_result }}
    steps:
      - name: scan vulnerabilities by Trivy
        uses: docker://docker.io/aquasec/trivy:latest
        continue-on-error: true
        with:
          args: image --ignore-unfixed=true --vuln-type="os,library" --exit-code=1 --format="json" --output="image-trivy.json" --no-progress portainerci/portainer:develop

      - name: upload Trivy image security scan result as artifact
        uses: actions/upload-artifact@v3
        with:
          name: image-security-scan-develop-result
          path: image-trivy.json

      - name: develop Trivy scan report export to html
        run: |
          $(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest summary --report-type=trivy --path="/data/image-trivy.json" --output-type=table --export --export-filename="/data/image-trivy-result")

      - name: upload html file as Trivy artifact
        uses: actions/upload-artifact@v3
        with:
          name: html-image-result-${{github.run_id}}
          path: image-trivy-result.html

      - name: analyse vulnerabilities from Trivy
        id: set-trivy-matrix
        run: |
          result=$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest summary --report-type=trivy --path="/data/image-trivy.json" --output-type=matrix)
          echo "image_trivy_result=${result}" >> $GITHUB_OUTPUT

      - name: scan vulnerabilities by Docker Scout
        uses: docker/scout-action@v1
        continue-on-error: true
        with:
          command: cves
          image: portainerci/portainer:develop
          sarif-file: image-docker-scout.json
          dockerhub-user: ${{ secrets.DOCKER_HUB_USERNAME }}
          dockerhub-password: ${{ secrets.DOCKER_HUB_PASSWORD }}

      - name: upload Docker Scout image security scan result as artifact
        uses: actions/upload-artifact@v3
        with:
          name: image-security-scan-develop-result
          path: image-docker-scout.json

      - name: develop Docker Scout scan report export to html
        run: |
          $(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest summary --report-type=docker-scout --path="/data/image-docker-scout.json" --output-type=table --export --export-filename="/data/image-docker-scout-result")

      - name: upload html file as Docker Scout artifact
        uses: actions/upload-artifact@v3
        with:
          name: html-image-result-${{github.run_id}}
          path: image-docker-scout-result.html

      - name: analyse vulnerabilities from Docker Scout
        id: set-docker-scout-matrix
        run: |
          result=$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest summary --report-type=docker-scout --path="/data/image-docker-scout.json" --output-type=matrix)
          echo "image_docker_scout_result=${result}" >> $GITHUB_OUTPUT

  result-analysis:
    name: Analyse Scan Results
    needs: [client-dependencies, server-dependencies, image-vulnerability]
    runs-on: ubuntu-latest
    if: >-
      github.ref == 'refs/heads/develop'
    strategy:
      matrix:
        js: ${{fromJson(needs.client-dependencies.outputs.js)}}
        go: ${{fromJson(needs.server-dependencies.outputs.go)}}
        image-trivy: ${{fromJson(needs.image-vulnerability.outputs.image-trivy)}}
        image-docker-scout: ${{fromJson(needs.image-vulnerability.outputs.image-docker-scout)}}
    steps:
      - name: display the results of js, Go, and image scan
        run: |
          echo "${{ matrix.js.status }}"
          echo "${{ matrix.go.status }}"
          echo "${{ matrix.image-trivy.status }}"
          echo "${{ matrix.image-docker-scout.status }}"
          echo "${{ matrix.js.summary }}"
          echo "${{ matrix.go.summary }}"
          echo "${{ matrix.image-trivy.summary }}"
          echo "${{ matrix.image-docker-scout.summary }}"

      - name: send message to Slack
        if: >-
          matrix.js.status == 'failure' ||
          matrix.go.status == 'failure' || 
          matrix.image-trivy.status == 'failure' || 
          matrix.image-docker-scout.status == 'failure'
        uses: slackapi/slack-github-action@v1.23.0
        with:
          payload: |
            {
              "blocks": [
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": "Code Scanning Result (*${{ github.repository }}*)\n*<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|GitHub Actions Workflow URL>*"
                  }
                }
              ],
              "attachments": [
                {
                  "color": "#FF0000",
                  "blocks": [
                    {
                      "type": "section",
                      "text": {
                        "type": "mrkdwn",
                        "text": "*JS dependency check*: *${{ matrix.js.status }}*\n${{ matrix.js.summary }}"
                      }
                    },
                    {
                      "type": "section",
                      "text": {
                        "type": "mrkdwn",
                        "text": "*Go dependency check*: *${{ matrix.go.status }}*\n${{ matrix.go.summary }}"
                      }
                    },
                    {
                      "type": "section",
                      "text": {
                        "type": "mrkdwn",
                        "text": "*Image Trivy vulnerability check*: *${{ matrix.image-trivy.status }}*\n${{ matrix.image-trivy.summary }}\n"
                      }
                    },
                    {
                      "type": "section",
                      "text": {
                        "type": "mrkdwn",
                        "text": "*Image Docker Scout vulnerability check*: *${{ matrix.image-docker-scout.status }}*\n${{ matrix.image-docker-scout.summary }}\n"
                      }
                    }
                  ]
                }
              ]
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SECURITY_SLACK_WEBHOOK_URL }}
          SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK