#!/bin/bash
#
# Perform vulnerability scan of a container image built by OBS using ClamAV.

# Strict mode
set -euo pipefail

TOPDIR="/usr/src/packages"
OUTPUTDIR="$TOPDIR/DOCKER"

# Helper functions
log() {
    echo "$@" >&2
}
check_required_environment_variables() {
    local required_environment_variable
    for required_environment_variable in "$@"; do
        if [[ -z ${!required_environment_variable:-} ]]; then
            log "Missing environment variable '$required_environment_variable'"
            return 1
        fi
    done
}
containerinfos() {
    find "$OUTPUTDIR" -type f -name '*.containerinfo'
}
clamscan_exec() {
    local jobs=""
    if clamscan -j2 --help >/dev/null 2>/dev/null; then
        jobs="$(getconf _NPROCESSORS_ONLN)"
    fi
    if [[ -n "$jobs" ]]; then
        clamscan "-j$jobs" "$@"
    else
        clamscan "$@"
    fi
}

check_required_environment_variables BUILD_DIR BUILD_ROOT

log "Retrieving container image metadata"
CONTAINERINFO="$(find "$OUTPUTDIR" -name '*.containerinfo' -print -quit)"
CONTAINER_NAME="$(basename "$CONTAINERINFO" | sed 's/\.containerinfo$//')"
CONTAINER_TAG="$(python3 -c \
    'import json, sys; print(json.load(sys.stdin)["tags"][0])' \
    <"$CONTAINERINFO")"
log "Container image tag to check: $CONTAINER_TAG"

log "Starting Docker"
"$BUILD_DIR/startdockerd" \
    --root "$BUILD_ROOT" \
    --webserver "$TOPDIR/SOURCES/repos" \
    --webserver-upload "$TOPDIR/SOURCES/repos/UPLOAD"

log "Exporting container image filesystem"
CONTAINER_EXPORT_FILE="$(mktemp)"
if ! CONTAINER_IMAGE_ID="$(docker run -d --entrypoint=sh "$CONTAINER_TAG" \
    -c 'exit 1')"; then
    log "Could not start the container image"
    exit 1
fi
docker export "$CONTAINER_IMAGE_ID" -o "$CONTAINER_EXPORT_FILE"

log "Stopping Docker"
"$BUILD_DIR/startdockerd" \
    --root "$BUILD_ROOT" \
    --kill

log "Decompressing container image"
CONTAINER_EXPORT_PATH="$(mktemp -d)"
tar xf "$CONTAINER_EXPORT_FILE" -C "$CONTAINER_EXPORT_PATH"

log "Scanning image with ClamAV"
CLAMAV_RESULT="$(mktemp)"
clamscan_exec --infected --recursive "$CONTAINER_EXPORT_PATH" \
    >"$CLAMAV_RESULT"

# Convert file to the Cosign Generic Predicate Spec format
# https://github.com/sigstore/cosign/blob/main/specs/COSIGN_VULN_ATTESTATION_SPEC.md
# NOTE: The subject will be filled by OBS during publishing
CLAMAV_VERSION="$(
    clamscan --version | awk '{print $2}' | awk -F/ '{print $1}'
)"
SCAN_TIMESTAMP="$(date '+%Y-%m-%dT%H:%M:%S.%N%:z')"
jq --rawfile result "$CLAMAV_RESULT" '.predicate.data = $result' \
    >"$OUTPUTDIR/$CONTAINER_NAME.clamav.intoto.json" <<EOF
{
  "_type": "https://in-toto.io/Statement/v0.1",
  "predicateType": "https://cosign.sigstore.dev/attestation/v1",
  "subject": [
    {
      "name": "",
      "digest": {
        "sha256": ""
      }
    }
  ],
  "predicate": {
    "data": "",
    "timestamp": "$SCAN_TIMESTAMP"
  }
}
EOF

log "Cleaning up"
rm -rf "$CONTAINER_EXPORT_FILE" "$CONTAINER_EXPORT_PATH" "$CLAMAV_RESULT"
