#!/usr/bin/env bash
# 00-download.sh — run on the CONNECTED builder only.
#
# Fetches everything the air-gapped RHEL 9.6 environment needs to run
# Drone 2.24 with Syft, Grype and Trivy:
#   images/          Drone server, Docker runner, clone image, UBI 9 base
#                    (pinned by digest, docker-archive format)
#   downloads/       Syft, Grype, Trivy and Drone CLI release tarballs
#   downloads/rpms/  jq/tar/gzip RPMs for the scanner image (from UBI repos)
#   build/db/        Grype + Trivy vulnerability data (input to
#                    05-build-scanner-image.sh; not shipped on its own)
#   metadata/        provenance: digests, image IDs, DB build dates
#
# Every artefact is verified (SHA-256 or registry digest) before it is kept.
# Re-running is safe: verified artefacts are not downloaded again, the
# vulnerability data is always refreshed.
set -euo pipefail

ROOT="$(cd "$(dirname "$0")/.." && pwd)"
source "$ROOT/config/versions.env"

log() { printf '[%s] %s\n' "$(date -u +%H:%M:%SZ)" "$*"; }
die() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }

for tool in curl sha256sum jq skopeo podman tar; do
  command -v "$tool" >/dev/null || die "$tool is required on the connected builder"
done

mkdir -p "$ROOT/images" "$ROOT/downloads/rpms" "$ROOT/metadata" \
         "$ROOT/build/tools" "$ROOT/build/db"

# fetch URL DEST SHA256 — download to a temp name, verify, then move into place.
fetch() {
  local url="$1" dest="$2" sha="$3"
  if [[ -f "$dest" ]] && echo "$sha  $dest" | sha256sum --check --status; then
    log "ok (cached) $(basename "$dest")"; return 0
  fi
  log "downloading $(basename "$dest")"
  curl -fL --retry 5 --retry-delay 3 -o "$dest.part" "$url"
  echo "$sha  $dest.part" | sha256sum --check --status \
    || { rm -f "$dest.part"; die "checksum mismatch for $url"; }
  mv "$dest.part" "$dest"
}

# ---------------------------------------------------------------------------
# 1. Container images, pinned to the linux/amd64 manifest digest.
# ---------------------------------------------------------------------------
IMAGES_TSV="$ROOT/metadata/images.tsv"
printf 'name\tarchive\tlocal_tag\tamd64_manifest\timage_id\n' > "$IMAGES_TSV.part"

# pin_image NAME REPO INDEX_DIGEST AMD64_DIGEST LOCAL_TAG
pin_image() {
  local name="$1" repo="$2" index="$3" amd64="$4" tag="$5"
  local tar="images/${name}-amd64.tar"
  log "resolving $repo@$index (linux/amd64)"
  local resolved
  resolved="$(skopeo inspect --raw "docker://$repo@$index" \
    | jq -r '.manifests[] | select(.platform.os=="linux" and .platform.architecture=="amd64") | .digest')"
  [[ "$resolved" == "$amd64" ]] \
    || die "$repo index now points amd64 at $resolved (expected $amd64) — review before updating config/versions.env"

  # The image ID podman/docker report after `load` is the config digest.
  local image_id
  image_id="$(skopeo inspect --raw "docker://$repo@$amd64" | jq -r '.config.digest')"

  local sumfile="$ROOT/metadata/${name}-image.sha256"
  if [[ -s "$ROOT/$tar" && -s "$sumfile" ]] && (cd "$ROOT" && sha256sum --check --status "$sumfile"); then
    log "ok (cached) $tar"
  else
    rm -f "$ROOT/$tar"
    log "copying $repo@$amd64 -> $tar"
    # docker-archive cannot hold registry signatures (Red Hat images have
    # them); integrity comes from the pinned manifest digest instead.
    skopeo copy --quiet --remove-signatures "docker://$repo@$amd64" "docker-archive:$ROOT/$tar:$tag"
    (cd "$ROOT" && sha256sum "$tar") > "$sumfile"
  fi
  skopeo inspect --config "docker://$repo@$amd64" > "$ROOT/metadata/${name}-image-config.json"
  printf '%s\t%s\t%s\t%s\t%s\n' "$name" "$tar" "$tag" "$amd64" "$image_id" >> "$IMAGES_TSV.part"
}

pin_image "drone-server-${DRONE_VERSION}" "$DRONE_IMAGE_REPO"  "$DRONE_IMAGE_INDEX_DIGEST"  "$DRONE_IMAGE_AMD64_DIGEST"  "$DRONE_IMAGE_LOCAL"
pin_image "drone-runner-docker-${RUNNER_VERSION}" "$RUNNER_IMAGE_REPO" "$RUNNER_IMAGE_INDEX_DIGEST" "$RUNNER_IMAGE_AMD64_DIGEST" "$RUNNER_IMAGE_LOCAL"
pin_image "drone-git"                   "$GIT_IMAGE_REPO"    "$GIT_IMAGE_INDEX_DIGEST"    "$GIT_IMAGE_AMD64_DIGEST"    "$GIT_IMAGE_LOCAL"
pin_image "ubi9-minimal"                "$UBI_IMAGE_REPO"    "$UBI_IMAGE_INDEX_DIGEST"    "$UBI_IMAGE_AMD64_DIGEST"    "$UBI_IMAGE_LOCAL"
mv "$IMAGES_TSV.part" "$IMAGES_TSV"

# ---------------------------------------------------------------------------
# 2. Scanner and CLI release tarballs. The pins in versions.env are also
#    cross-checked against the checksums file upstream publishes.
# ---------------------------------------------------------------------------
# release FILE URL_BASE SHA CHECKSUMS_FILE [DEST_NAME]
release() {
  local file="$1" base="$2" sha="$3" sums="$4" dest="${5:-$1}"
  local upstream
  upstream="$(curl -fsSL --retry 5 "$base/$sums" | awk -v f="$file" '$2==f || $2=="release/"f {print $1}')"
  [[ "$upstream" == "$sha" ]] || die "$file: upstream checksum '$upstream' != pinned $sha"
  fetch "$base/$file" "$ROOT/downloads/$dest" "$sha"
}

SYFT_TGZ="syft_${SYFT_VERSION}_linux_amd64.tar.gz"
GRYPE_TGZ="grype_${GRYPE_VERSION}_linux_amd64.tar.gz"
TRIVY_TGZ="trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz"
release "$SYFT_TGZ"  "https://github.com/anchore/syft/releases/download/v${SYFT_VERSION}" \
        "$SYFT_SHA256"  "syft_${SYFT_VERSION}_checksums.txt"
release "$GRYPE_TGZ" "https://github.com/anchore/grype/releases/download/v${GRYPE_VERSION}" \
        "$GRYPE_SHA256" "grype_${GRYPE_VERSION}_checksums.txt"
release "$TRIVY_TGZ" "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}" \
        "$TRIVY_SHA256" "trivy_${TRIVY_VERSION}_checksums.txt"
# Upstream names the CLI tarball without a version; keep it versioned here.
release drone_linux_amd64.tar.gz "https://github.com/harness/drone-cli/releases/download/v${DRONE_CLI_VERSION}" \
        "$DRONE_CLI_SHA256" "drone_checksums.txt" "drone-cli_${DRONE_CLI_VERSION}_linux_amd64.tar.gz"

# Builder-side copies of the scanners, used below to fetch/import the DBs.
tar -xzf "$ROOT/downloads/$GRYPE_TGZ" -C "$ROOT/build/tools" grype
tar -xzf "$ROOT/downloads/$TRIVY_TGZ" -C "$ROOT/build/tools" trivy

# ---------------------------------------------------------------------------
# 3. RPMs for the scanner image, from the pinned UBI image's own repos.
#    Signatures are checked again (rpm -K) inside the offline image build.
# ---------------------------------------------------------------------------
log "downloading UBI RPMs: $UBI_EXTRA_RPMS"
rm -f "$ROOT/downloads/rpms/"*.rpm
podman run --rm --pull=missing \
  -v "$ROOT/downloads/rpms:/out:Z" \
  "$UBI_IMAGE_REPO@$UBI_IMAGE_AMD64_DIGEST" \
  bash -c "cd /out && microdnf -y --setopt=install_weak_deps=0 download --archlist=x86_64,noarch $UBI_EXTRA_RPMS >/dev/null && ls -1 /out"
(cd "$ROOT/downloads/rpms" && sha256sum *.rpm) > "$ROOT/metadata/rpms.sha256"

# ---------------------------------------------------------------------------
# 4. Vulnerability data (always refreshed).
# ---------------------------------------------------------------------------
DB="$ROOT/build/db"
rm -rf "$DB/grype" "$DB/trivy"
mkdir -p "$DB/grype" "$DB/trivy"

# 4a. Grype: download the archive named by the official listing, verify it,
#     then import it into a clean cache directory.
listing="$(curl -fsSL --retry 5 "https://grype.anchore.io/databases/${GRYPE_DB_SCHEMA}/latest.json")"
echo "$listing" > "$ROOT/metadata/grype-db-listing.json"
gdb_file="$(jq -r .path <<<"$listing")"
gdb_sha="$(jq -r '.checksum | sub("^sha256:";"")' <<<"$listing")"
fetch "https://grype.anchore.io/databases/${GRYPE_DB_SCHEMA}/$gdb_file" "$ROOT/build/$gdb_file" "$gdb_sha"
log "importing Grype DB $gdb_file"
GRYPE_DB_CACHE_DIR="$DB/grype" GRYPE_CHECK_FOR_APP_UPDATE=false \
  "$ROOT/build/tools/grype" db import "$ROOT/build/$gdb_file"
GRYPE_DB_CACHE_DIR="$DB/grype" GRYPE_DB_AUTO_UPDATE=false GRYPE_CHECK_FOR_APP_UPDATE=false \
  "$ROOT/build/tools/grype" db status -o json > "$ROOT/metadata/grype-db-status.json"
# Remove older archives so build/ does not grow without bound.
find "$ROOT/build" -maxdepth 1 -name 'vulnerability-db_*.tar.zst' ! -name "$gdb_file" -delete

# 4b. Trivy: let trivy pull its own OCI artefacts (digest-verified on pull).
log "downloading Trivy vulnerability DB"
"$ROOT/build/tools/trivy" image --quiet --download-db-only \
  --cache-dir "$DB/trivy" --db-repository "$TRIVY_DB_REPOSITORY"
log "downloading Trivy Java DB"
"$ROOT/build/tools/trivy" image --quiet --download-java-db-only \
  --cache-dir "$DB/trivy" --java-db-repository "$TRIVY_JAVA_DB_REPOSITORY"
log "downloading Trivy misconfiguration checks bundle"
empty="$(mktemp -d)"
"$ROOT/build/tools/trivy" config --quiet --cache-dir "$DB/trivy" \
  --checks-bundle-repository "$TRIVY_CHECKS_BUNDLE_REPOSITORY" "$empty" >/dev/null
rmdir "$empty"
rm -rf "$DB/trivy/fanal"           # scan cache, not data
for f in db/metadata.json java-db/metadata.json policy/metadata.json; do
  [[ -s "$DB/trivy/$f" ]] || die "trivy download incomplete: $f missing"
done
cp "$DB/trivy/db/metadata.json"      "$ROOT/metadata/trivy-db-metadata.json"
cp "$DB/trivy/java-db/metadata.json" "$ROOT/metadata/trivy-java-db-metadata.json"
cp "$DB/trivy/policy/metadata.json"  "$ROOT/metadata/trivy-checks-metadata.json"

# ---------------------------------------------------------------------------
# 5. Provenance record.
# ---------------------------------------------------------------------------
cat > "$ROOT/metadata/download-provenance.txt" <<EOF
downloaded_utc=$(date -u +%Y-%m-%dT%H:%M:%SZ)
builder=$(hostname)
drone=$DRONE_IMAGE_REPO@$DRONE_IMAGE_AMD64_DIGEST ($DRONE_VERSION)
runner=$RUNNER_IMAGE_REPO@$RUNNER_IMAGE_AMD64_DIGEST ($RUNNER_VERSION)
clone=$GIT_IMAGE_REPO@$GIT_IMAGE_AMD64_DIGEST
ubi=$UBI_IMAGE_REPO@$UBI_IMAGE_AMD64_DIGEST
syft=$SYFT_VERSION sha256=$SYFT_SHA256
grype=$GRYPE_VERSION sha256=$GRYPE_SHA256
trivy=$TRIVY_VERSION sha256=$TRIVY_SHA256
drone_cli=$DRONE_CLI_VERSION sha256=$DRONE_CLI_SHA256
grype_db=$gdb_file sha256=$gdb_sha built=$(jq -r .built <<<"$listing")
trivy_db_updated=$(jq -r .UpdatedAt "$DB/trivy/db/metadata.json")
trivy_java_db_updated=$(jq -r .UpdatedAt "$DB/trivy/java-db/metadata.json")
trivy_checks_digest=$(jq -r .Digest "$DB/trivy/policy/metadata.json")
EOF
log "download complete — next: scripts/05-build-scanner-image.sh"
