name: PR Code Security Scan

on:
  pull_request_review:
    types:
      - submitted
      - edited
    paths:
      - 'package.json'
      - 'api/go.mod'
      - 'gruntfile.js'
      - 'build/linux/Dockerfile'
      - 'build/linux/alpine.Dockerfile'
      - 'build/windows/Dockerfile'
      - '.github/workflows/pr-security.yml'

jobs:
  client-dependencies:
    name: Client Dependency Check
    runs-on: ubuntu-latest
    if: >-
      github.event.pull_request &&
      github.event.review.body == '/scan'
    outputs:
      jsdiff: ${{ steps.set-diff-matrix.outputs.js_diff_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 pull-request artifact
        uses: actions/upload-artifact@v3
        with:
          name: js-security-scan-feat-result
          path: snyk.json

      - name: download artifacts from develop branch built by nightly scan
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          mv ./snyk.json ./js-snyk-feature.json
          (gh run download -n js-security-scan-develop-result -R ${{ github.repository }} 2>&1 >/dev/null) || :
          if [[ -e ./snyk.json ]]; then
            mv ./snyk.json ./js-snyk-develop.json
          else
            echo "null" > ./js-snyk-develop.json
          fi

      - name: pr vs develop scan report comparison export to html
        run: |
          $(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest diff --report-type=snyk --path="/data/js-snyk-feature.json" --compare-to="/data/js-snyk-develop.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-compare-to-develop-${{github.run_id}}
          path: js-result.html

      - name: analyse different vulnerabilities against develop branch
        id: set-diff-matrix
        run: |
          result=$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest diff --report-type=snyk --path="/data/js-snyk-feature.json" --compare-to="/data/js-snyk-develop.json" --output-type=matrix)
          echo "js_diff_result=${result}" >> $GITHUB_OUTPUT

  server-dependencies:
    name: Server Dependency Check
    runs-on: ubuntu-latest
    if: >-
      github.event.pull_request &&
      github.event.review.body == '/scan'
    outputs:
      godiff: ${{ steps.set-diff-matrix.outputs.go_diff_result }}
    steps:
      - name: checkout repository
        uses: actions/checkout@master

      - name: install Go
        uses: actions/setup-go@v3
        with:
          go-version: '1.19.5'

      - 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=./api/go.mod --json-file-output=snyk.json 2>/dev/null || :

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

      - name: download artifacts from develop branch built by nightly scan
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          mv ./snyk.json ./go-snyk-feature.json
          (gh run download -n go-security-scan-develop-result -R ${{ github.repository }} 2>&1 >/dev/null) || :
          if [[ -e ./snyk.json ]]; then
            mv ./snyk.json ./go-snyk-develop.json
          else
            echo "null" > ./go-snyk-develop.json
          fi

      - name: pr vs develop scan report comparison export to html
        run: |
          $(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest diff --report-type=snyk --path="/data/go-snyk-feature.json" --compare-to="/data/go-snyk-develop.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-compare-to-develop-${{github.run_id}}
          path: go-result.html

      - name: analyse different vulnerabilities against develop branch
        id: set-diff-matrix
        run: |
          result=$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest diff --report-type=snyk --path="/data/go-snyk-feature.json" --compare-to="/data/go-snyk-develop.json" --output-type=matrix)
          echo "go_diff_result=${result}" >> $GITHUB_OUTPUT

  image-vulnerability:
    name: Image Vulnerability Check
    runs-on: ubuntu-latest
    if: >-
      github.event.pull_request &&
      github.event.review.body == '/scan'
    outputs:
      imagediff: ${{ steps.set-diff-matrix.outputs.image_diff_result }}
    steps:
      - name: checkout code
        uses: actions/checkout@master

      - name: install Go 1.19.5
        uses: actions/setup-go@v3
        with:
          go-version: '1.19.5'

      - name: install Node.js 18.x
        uses: actions/setup-node@v3
        with:
          node-version: 18.x

      - name: Install packages
        run: yarn --frozen-lockfile

      - name: build
        run: make build-all

      - name: set up docker buildx
        uses: docker/setup-buildx-action@v2

      - name: build and compress image
        uses: docker/build-push-action@v4
        with:
          context: .
          file: build/linux/Dockerfile
          tags: trivy-portainer:${{ github.sha }}
          outputs: type=docker,dest=/tmp/trivy-portainer-image.tar

      - name: load docker image
        run: |
          docker load --input /tmp/trivy-portainer-image.tar

      - 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 trivy-portainer:${{ github.sha }}

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

      - name: download artifacts from develop branch built by nightly scan
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          mv ./image-trivy.json ./image-trivy-feature.json
          (gh run download -n image-security-scan-develop-result -R ${{ github.repository }} 2>&1 >/dev/null) || :
          if [[ -e ./image-trivy.json ]]; then
            mv ./image-trivy.json ./image-trivy-develop.json
          else
            echo "null" > ./image-trivy-develop.json
          fi

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

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

      - name: analyse different vulnerabilities against develop branch
        id: set-diff-matrix
        run: |
          result=$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest diff --report-type=trivy --path="/data/image-trivy-feature.json" --compare-to="/data/image-trivy-develop.json" --output-type=matrix)
          echo "image_diff_result=${result}" >> $GITHUB_OUTPUT

  result-analysis:
    name: Analyse Scan Result Against develop Branch
    needs: [client-dependencies, server-dependencies, image-vulnerability]
    runs-on: ubuntu-latest
    if: >-
      github.event.pull_request &&
      github.event.review.body == '/scan'
    strategy:
      matrix:
        jsdiff: ${{fromJson(needs.client-dependencies.outputs.jsdiff)}}
        godiff: ${{fromJson(needs.server-dependencies.outputs.godiff)}}
        imagediff: ${{fromJson(needs.image-vulnerability.outputs.imagediff)}}
    steps:
      - name: check job status of diff result
        if: >-
          matrix.jsdiff.status == 'failure' ||
          matrix.godiff.status == 'failure' ||
          matrix.imagediff.status == 'failure'
        run: |
          echo "${{ matrix.jsdiff.status }}"
          echo "${{ matrix.godiff.status }}"
          echo "${{ matrix.imagediff.status }}"
          echo "${{ matrix.jsdiff.summary }}"
          echo "${{ matrix.godiff.summary }}"
          echo "${{ matrix.imagediff.summary }}"
          exit 1
