# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

timeout: "3600s" # 1 hour
tags:
  - "secure-cicd-ci"
substitutions:
  _ATTESTOR_FULL_NAME: "projects/${PROJECT_ID}/attestors/${_ATTESTOR_NAME}"
  _GAR_REPO_URI: "${_DEFAULT_REGION}-docker.pkg.dev/${PROJECT_ID}/${_GAR_REPOSITORY}"
options:
  substitution_option: 'ALLOW_LOOSE'
  pool:
    name: $_CLOUDBUILD_PRIVATE_POOL
artifacts:
  objects:
    location: gs://$_CACHE_BUCKET_NAME/artifacts
    paths:
      - 'build-artifacts.json'
      - 'build-artifacts-notag.json'
logsBucket: gs://$_CACHE_BUCKET_NAME/build_logs
steps:

############################### Build Containers ###########################

# Create build-installation-image
- name: $_DEFAULT_REGION-docker.pkg.dev/$PROJECT_ID/$_GAR_REPOSITORY/skaffold-builder
  id: "build-images"
  entrypoint: "/bin/bash"
  args:
    - '-xe'
    - -c
    - |
      ./mvnw clean install

      skaffold config set --global local-cluster false
      skaffold build --default-repo=${_GAR_REPO_URI} --tag=$SHORT_SHA --cache-file='/.skaffold/cache' --file-output=/artifacts/build-artifacts.json
      sed -i "s|:latest||g" /artifacts/build-artifacts.json # remove the "latest" tag (complicates things later)
      cp /artifacts/build-artifacts.json ./build-artifacts.json # allow artifact copy mechanism to capture file (see 'artifacts' above)
      while read p; do
        echo "$p"
      done < /artifacts/build-artifacts.json
  volumes:
  - path: '/artifacts'
    name: 'artifacts'

################ Securing Artifacts Before Deployment ####################

### Container Struture Test
- name: 'gcr.io/cloud-builders/docker'
  id: "container-structure"
  entrypoint: "/bin/bash"
  args:
    - '-xe'
    - '-c'
    - |
      # install jq and container-structure-test
      apt-get -y install jq
      curl -LO https://storage.googleapis.com/container-structure-test/latest/container-structure-test-linux-amd64 \
      && chmod +x container-structure-test-linux-amd64 \
      && mv container-structure-test-linux-amd64 /usr/bin/container-structure-test

      # remove commit hash from string (need either tag or digest, not both)
      sed "s|:$SHORT_SHA||g" /artifacts/build-artifacts.json > /artifacts/build-artifacts-notag.json
      cp /artifacts/build-artifacts-notag.json ./build-artifacts-notag.json # allow artifact copy mechanism to capture file (see 'artifacts' above)
      IMAGES=( $$(jq -r '.builds[].tag' /artifacts/build-artifacts-notag.json))
      for IMAGE in "$${IMAGES[@]}"; do
        docker pull "$${IMAGE}" # pull the remote image from GAR w/ digest SHA
        container-structure-test test --image "$${IMAGE}" --config policies/container-structure-policy.yaml
      done
  volumes:
  - path: '/artifacts'
    name: 'artifacts'
  waitFor: ['build-images']

### Container image analysis scanning (CVE Check)
- name: $_DEFAULT_REGION-docker.pkg.dev/$PROJECT_ID/$_GAR_REPOSITORY/skaffold-builder
  id: "container-scanner"
  entrypoint: "/bin/bash"
  args:
    - '-xe'
    - -c
    - |
      gcloud config set project ${PROJECT_ID}

      # remove commit hash from string (need either tag or digest, not both)
      sed "s|:$SHORT_SHA||g" /artifacts/build-artifacts.json > /artifacts/build-artifacts-notag.json
      IMAGES=( $$(jq -r '.builds[].tag' /artifacts/build-artifacts-notag.json))
      for IMAGE in "$${IMAGES[@]}"; do

        # Check CVEs against policy
        /signer \
        -v=10 \
        -alsologtostderr \
        -image="$${IMAGE}" \
        -policy=policies/container-analysis-policy.yaml \
        -vulnz_timeout=15m \
        -mode=check-only || error=true
        if [[ $error == true ]]; then echo "Container Analysis failed due to CVE thresholds being triggered"; exit 1; fi
      done
  volumes:
    - path: '/artifacts'
      name: 'artifacts'
  waitFor:
    - 'build-images'

### Binary Authorization
- name: $_DEFAULT_REGION-docker.pkg.dev/$PROJECT_ID/$_GAR_REPOSITORY/skaffold-builder
  id: "binary-authorization-checkpoint"
  entrypoint: "/bin/bash"
  args:
    - '-xe'
    - -c
    - |
      gcloud config set project ${PROJECT_ID}
      sed "s|:$SHORT_SHA||g" /artifacts/build-artifacts.json > /artifacts/build-artifacts-notag.json
      IMAGES=( $$(jq -r '.builds[].tag' /artifacts/build-artifacts-notag.json))
      # Public Key for attestor
      PUBLIC_KEY_ID=$(gcloud container binauthz attestors describe ${_ATTESTOR_FULL_NAME} \
        --format='value(userOwnedGrafeasNote.publicKeys[0].id)')

      for IMAGE in "$${IMAGES[@]}"; do
        HAS_ATTESTATION=$(gcloud container binauthz attestations list \
            --project="${PROJECT_ID}" \
            --attestor="${_ATTESTOR_FULL_NAME}" \
            --artifact-url="$${IMAGE}" \
            --format="value(name)")

        if [ -z $${HAS_ATTESTATION} ]; then
          gcloud beta container binauthz attestations sign-and-create \
            --artifact-url="$${IMAGE}" \
            --attestor="${_ATTESTOR_FULL_NAME}" \
            --keyversion=$( echo "$${PUBLIC_KEY_ID}" | sed "s|//cloudkms.googleapis.com/v1/||" )
        fi
      done
  volumes:
    - path: '/artifacts'
      name: 'artifacts'
  waitFor:
    - 'container-structure'
    - 'container-scanner'

## Create Cloud Deploy Release to trigger render and deploy to first env
- name: $_DEFAULT_REGION-docker.pkg.dev/$PROJECT_ID/$_GAR_REPOSITORY/skaffold-builder
  id: "cloud-deploy-release"
  entrypoint: "/bin/bash"
  args:
    - '-xe'
    - -c
    - |
      RELEASE_NAME='release-$SHORT_SHA'
      gcloud deploy releases create $${RELEASE_NAME} --delivery-pipeline=${_CLOUDDEPLOY_PIPELINE_NAME} --build-artifacts=/artifacts/build-artifacts.json --region=${_DEFAULT_REGION}
  volumes:
    - path: '/artifacts'
      name: 'artifacts'
  waitFor:
    - 'binary-authorization-checkpoint'
