#!/usr/bin/env bash
# 30-verify-offline.sh — end-to-end proof that Drone 2.24 + Syft/Grype/Trivy
# work with no Internet, using only images from this bundle (+ a Gitea image).
#
# Builds a disposable stack on a podman --internal network (no gateway):
#   Gitea (test instance)  <->  Drone server 2.24.0  <->  Docker runner 1.8.5
# then, like a real user: logs in to Drone through Gitea OAuth, activates two
# repositories (Drone installs the Gitea webhooks), pushes them and waits for
# the webhook-triggered builds of drone/.drone.yml:
#   demo-vulnerable (private) -> must FAIL at security-gate, all scans succeed
#   demo-clean                -> must PASS
# Step logs, build JSON and a summary land in evidence/verify-<timestamp>/.
#
# Requirements: root, podman with podman.socket, python3, git, images loaded by
# 10-load.sh, and a Gitea image (GITEA_IMAGE, default docker.io/gitea/gitea:1.22
# as shipped in the company devsecops-gitea bundle).
#
#   KEEP=1 ./30-verify-offline.sh      leave the stack running afterwards
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
source "$ROOT/config/versions.env"
source "$ROOT/config/scanner-image.env"

GITEA_IMAGE="${GITEA_IMAGE:-docker.io/gitea/gitea:1.22}"
NET="${E2E_NETWORK:-drone-e2e}"
SUBNET="${E2E_SUBNET:-10.89.231}"
IP_GITEA="$SUBNET.10"; IP_DRONE="$SUBNET.11"
PREFIX=drone-e2e
TS="$(date -u +%Y%m%dT%H%M%SZ)"
EVID="$ROOT/evidence/verify-$TS"
WORK="$(mktemp -d)"
LOG="$ROOT/metadata/verify-offline.log"

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

[[ $EUID -eq 0 ]] || die "run as root (the runner uses the rootful podman socket)"
for t in podman python3 git curl openssl; do command -v "$t" >/dev/null || die "$t is required"; done
for img in "$DRONE_IMAGE_LOCAL" "$RUNNER_IMAGE_LOCAL" "$GIT_IMAGE_LOCAL" "$SCANNER_IMAGE_REPO:stable" "$GITEA_IMAGE"; do
  podman image exists "$img" || die "image $img is not loaded (run scripts/10-load.sh; Gitea comes from devsecops-gitea)"
done

cleanup() {
  if [[ "${KEEP:-0}" == 1 ]]; then
    log "KEEP=1: stack left running — Drone http://$IP_DRONE  Gitea http://$IP_GITEA:3000"
    log "        user drone-e2e-admin / password in $WORK/credentials"
    return
  fi
  podman rm -f -t 5 "$PREFIX-runner" "$PREFIX-drone" "$PREFIX-gitea" >/dev/null 2>&1 || true
  podman network rm -f "$NET" >/dev/null 2>&1 || true
  rm -rf "$WORK"
}
trap cleanup EXIT

exec > >(tee "$LOG") 2>&1
mkdir -p "$EVID"
log "evidence directory: ${EVID#$ROOT/}"

# ---------------------------------------------------------------------------
# 0. podman.socket (the runner's Docker API)
# ---------------------------------------------------------------------------
if ! systemctl is-active --quiet podman.socket; then
  log "starting podman.socket"
  systemctl start podman.socket
fi
curl -fsS --unix-socket /run/podman/podman.sock http://d/_ping >/dev/null || die "podman socket not answering"

# ---------------------------------------------------------------------------
# 1. Internal network: no gateway, no route to anything outside the subnet
# ---------------------------------------------------------------------------
podman rm -f -t 5 "$PREFIX-runner" "$PREFIX-drone" "$PREFIX-gitea" >/dev/null 2>&1 || true
podman network rm -f "$NET" >/dev/null 2>&1 || true
if podman network ls --format '{{range .Subnets}}{{.Subnet}} {{end}}' | tr ' ' '\n' | grep -q "^$SUBNET\."; then
  die "subnet $SUBNET.0/24 already used by another podman network; set E2E_SUBNET"
fi
podman network create --internal --subnet "$SUBNET.0/24" "$NET" >/dev/null
log "created internal network $NET ($SUBNET.0/24): $(podman network inspect "$NET" --format '{{.Internal}}' | sed 's/true/internal=true/')"

# ---------------------------------------------------------------------------
# 2. Test Gitea
# ---------------------------------------------------------------------------
ADMIN=drone-e2e-admin
PASS="$(openssl rand -hex 12)"
TOKEN="$(openssl rand -hex 16)"
printf 'user=%s\npassword=%s\ndrone_token=%s\n' "$ADMIN" "$PASS" "$TOKEN" > "$WORK/credentials"
chmod 600 "$WORK/credentials"

log "starting Gitea ($GITEA_IMAGE) at $IP_GITEA:3000"
podman run -d --name "$PREFIX-gitea" --pull=never --network "$NET" --ip "$IP_GITEA" \
  -e USER_UID=1000 -e USER_GID=1000 \
  -e GITEA__database__DB_TYPE=sqlite3 \
  -e GITEA__server__DOMAIN="$IP_GITEA" \
  -e GITEA__server__ROOT_URL="http://$IP_GITEA:3000/" \
  -e GITEA__server__DISABLE_SSH=true \
  -e GITEA__server__OFFLINE_MODE=true \
  -e GITEA__security__INSTALL_LOCK=true \
  -e GITEA__service__DISABLE_REGISTRATION=true \
  -e GITEA__webhook__ALLOWED_HOST_LIST=private \
  -e GITEA__actions__ENABLED=false \
  -e GITEA__picture__DISABLE_GRAVATAR=true \
  "$GITEA_IMAGE" >/dev/null
for _ in $(seq 90); do curl -fsS "http://$IP_GITEA:3000/api/healthz" >/dev/null 2>&1 && break; sleep 1; done
curl -fsS "http://$IP_GITEA:3000/api/healthz" >/dev/null || die "Gitea did not start"
podman exec -u git "$PREFIX-gitea" gitea admin user create --admin --username "$ADMIN" \
  --password "$PASS" --email "$ADMIN@example.invalid" --must-change-password=false >/dev/null
read -r CLIENT_ID CLIENT_SECRET < <(python3 "$ROOT/scripts/lib/e2e_driver.py" oauth-app \
  --gitea "http://$IP_GITEA:3000" --drone "http://$IP_DRONE" --user "$ADMIN" --password "$PASS")
log "Gitea ready; OAuth2 application created (client_id ${CLIENT_ID:0:8}…)"

# ---------------------------------------------------------------------------
# 3. Drone server — env file generated from config/drone-server.env.example
# ---------------------------------------------------------------------------
RPC_SECRET="$(openssl rand -hex 16)"
sed -e "s|^DRONE_SERVER_HOST=.*|DRONE_SERVER_HOST=$IP_DRONE|" \
    -e "s|^DRONE_SERVER_PROTO=.*|DRONE_SERVER_PROTO=http|" \
    -e "s|^DRONE_GITEA_SERVER=.*|DRONE_GITEA_SERVER=http://$IP_GITEA:3000|" \
    -e "s|^DRONE_GITEA_CLIENT_ID=.*|DRONE_GITEA_CLIENT_ID=$CLIENT_ID|" \
    -e "s|^DRONE_GITEA_CLIENT_SECRET=.*|DRONE_GITEA_CLIENT_SECRET=$CLIENT_SECRET|" \
    -e "s|^DRONE_RPC_SECRET=.*|DRONE_RPC_SECRET=$RPC_SECRET|" \
    -e "s|^DRONE_DATABASE_SECRET=.*|DRONE_DATABASE_SECRET=$(openssl rand -hex 16)|" \
    -e "s|^DRONE_COOKIE_SECRET=.*|DRONE_COOKIE_SECRET=$(openssl rand -hex 16)|" \
    -e "s|^DRONE_USER_CREATE=.*|DRONE_USER_CREATE=username:$ADMIN,admin:true,token:$TOKEN|" \
    "$ROOT/config/drone-server.env.example" > "$WORK/server.env"
grep -qE '^[A-Z_]+=.*__[A-Z_]+__' "$WORK/server.env" && die "server.env template placeholders left"

log "starting Drone server $DRONE_IMAGE_LOCAL at $IP_DRONE:80"
podman run -d --name "$PREFIX-drone" --pull=never --network "$NET" --ip "$IP_DRONE" \
  --env-file "$WORK/server.env" "$DRONE_IMAGE_LOCAL" >/dev/null
for _ in $(seq 60); do curl -fsS "http://$IP_DRONE/healthz" >/dev/null 2>&1 && break; sleep 1; done
curl -fsS "http://$IP_DRONE/healthz" >/dev/null || { podman logs "$PREFIX-drone" | tail; die "Drone server did not start"; }
log "Drone server healthy"

# ---------------------------------------------------------------------------
# 4. Docker runner on the podman socket — env from config/drone-runner.env.example
# ---------------------------------------------------------------------------
sed -e "s|^DRONE_RPC_PROTO=.*|DRONE_RPC_PROTO=http|" \
    -e "s|^DRONE_RPC_HOST=.*|DRONE_RPC_HOST=$IP_DRONE|" \
    -e "s|^DRONE_RPC_SECRET=.*|DRONE_RPC_SECRET=$RPC_SECRET|" \
    -e "s|^DRONE_RUNNER_NAME=.*|DRONE_RUNNER_NAME=e2e-runner|" \
    "$ROOT/config/drone-runner.env.example" > "$WORK/runner.env"
# Test-only additions: attach step containers to the internal network (so the
# clone step reaches Gitea), and give every step a black-hole proxy so any
# attempt to reach the Internet fails loudly instead of silently succeeding.
cat >> "$WORK/runner.env" <<EOF
DRONE_RUNNER_NETWORKS=$NET
DRONE_RUNNER_ENV_FILE=/etc/drone-e2e/steps.env
EOF
cat > "$WORK/steps.env" <<EOF
HTTP_PROXY=http://127.0.0.1:9
HTTPS_PROXY=http://127.0.0.1:9
http_proxy=http://127.0.0.1:9
https_proxy=http://127.0.0.1:9
NO_PROXY=$IP_GITEA,$IP_DRONE
no_proxy=$IP_GITEA,$IP_DRONE
EOF
grep -qE '^[A-Z_]+=.*__[A-Z_]+__' "$WORK/runner.env" && die "runner.env template placeholders left"

log "starting Drone runner $RUNNER_IMAGE_LOCAL (podman socket)"
podman run -d --name "$PREFIX-runner" --pull=never --network "$NET" \
  --security-opt label=disable \
  -v /run/podman/podman.sock:/var/run/docker.sock \
  -v "$WORK/steps.env:/etc/drone-e2e/steps.env:ro" \
  --env-file "$WORK/runner.env" "$RUNNER_IMAGE_LOCAL" >/dev/null
for _ in $(seq 60); do
  podman logs "$PREFIX-runner" 2>&1 | grep -q 'successfully pinged the remote server' && break; sleep 1
done
podman logs "$PREFIX-runner" 2>&1 | grep -q 'successfully pinged the remote server' \
  || { podman logs "$PREFIX-runner" | tail; die "runner could not reach the server"; }
log "runner connected: $(podman logs "$PREFIX-runner" 2>&1 | grep -m1 'polling the remote server' | sed 's/.*msg="\([^"]*\)".*/\1/')"

# Proof the stack itself is cut off from the Internet.
if podman exec "$PREFIX-drone" wget -q -T 5 -O /dev/null https://github.com 2>/dev/null; then
  die "the Drone server container can reach the Internet — network is not isolated"
fi
log "confirmed: Drone server container cannot reach https://github.com"

# ---------------------------------------------------------------------------
# 5. Drive the real user flow + assertions
# ---------------------------------------------------------------------------
python3 "$ROOT/scripts/lib/e2e_driver.py" run \
  --gitea "http://$IP_GITEA:3000" --drone "http://$IP_DRONE" \
  --user "$ADMIN" --password "$PASS" --token "$TOKEN" \
  --demo "$ROOT/demo" --pipeline "$ROOT/drone/.drone.yml" \
  --image-archive "$ROOT/images/ubi9-minimal-amd64.tar" \
  --workdir "$WORK/repos" --evidence "$EVID"

podman logs "$PREFIX-drone"  > "$EVID/drone-server.log" 2>&1
podman logs "$PREFIX-runner" > "$EVID/drone-runner.log" 2>&1
sed -i -e "s/$CLIENT_SECRET/<redacted>/g" -e "s/$RPC_SECRET/<redacted>/g" -e "s/$TOKEN/<redacted>/g" \
  "$EVID/drone-server.log" "$EVID/drone-runner.log"
cp "$LOG" "$EVID/verify-offline.log"
log "PASS: Drone $DRONE_VERSION + runner $RUNNER_VERSION + $SCANNER_IMAGE verified end-to-end on an internal network"
