#!/usr/bin/env bash

# The one download that gets to run as root, and how it earns that.
#
# OpenAI publishes no signing key and no fingerprint that a first install could
# fetch and compare against: the documented instructions are to download an RPM
# and install it, and that RPM's own root scriptlet is what decides afterwards
# which repository and which key the machine will trust. Panama pins the key
# instead -- setup/keys/ carries it, setup/lib/chatgpt-package verifies the copy
# and writes the repository -- so dnf checks a signature before root sees a byte
# of it.
#
# What must hold:
#
#   1. The pinned key is the key the library says it is. Everything else here
#      is worthless if this drifts, and a changed key must be a failing test
#      somebody reads rather than a quiet change of publisher.
#   2. A pinned key that is missing, unreadable, or simply not that key stops
#      the install and leaves the machine untouched. Failing closed is the
#      whole point; falling back to installing anyway would be worse than
#      never having checked.
#   3. What it writes actually enforces the check: gpgcheck and repo_gpgcheck
#      on, and the gpgkey pointing at the key it just installed.
#   4. Both callers go through it, and neither hands root a downloaded RPM.
#      The installer and the codex-desktop migration install `chatgpt` by name
#      from that repository, which is what makes the signature mandatory.
#
# Hermetic: the key file is read locally, root is a stub that records what it
# was asked to do, and the destinations are redirected into a temporary
# directory. Nothing here contacts OpenAI or touches /etc.

set -uo pipefail

repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
library="$repo_dir/setup/lib/chatgpt-package"
installer="$repo_dir/setup/scripts/install-packages"
migration="$repo_dir/migrations/1787804505.sh"
pinned_key="$repo_dir/setup/keys/RPM-GPG-KEY-chatgpt"

findings=()
note() { findings+=("$1"); }

[[ -r "$library" ]] || {
    printf 'chatgpt package contract: %s is missing\n' "$library" >&2
    exit 1
}

command -v gpg >/dev/null 2>&1 || {
    printf 'chatgpt package contract: gpg is required to read the pinned key\n' >&2
    exit 1
}

work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT

# Root, as a recording stub. It logs the command and then runs it for real,
# which is safe because every destination below is redirected into $work.
stub="$work/bin"
mkdir -p "$stub"
cat >"$stub/sudo" <<'STUB'
#!/usr/bin/env bash
printf '%s\n' "$*" >>"$SUDO_RECORD"
exec "$@"
STUB
cat >"$stub/rpmkeys" <<'STUB'
#!/usr/bin/env bash
printf '%s\n' "$*" >>"$RPMKEYS_RECORD"
STUB
chmod +x "$stub/sudo" "$stub/rpmkeys"
export PATH="$stub:$PATH"

# ── 1. The pinned key is the pinned key ─────────────────────────────────────

if [[ ! -r "$pinned_key" ]]; then
    note 'setup/keys/RPM-GPG-KEY-chatgpt is missing, so nothing can be verified'
else
    declared="$(grep -oP '(?<=^CHATGPT_KEY_FINGERPRINT=")[0-9A-F]+' "$library" | head -1)"
    actual="$(gpg --show-keys --with-colons "$pinned_key" 2>/dev/null \
        | awk -F: '$1 == "fpr" { print $10; exit }')"
    [[ -n "$declared" ]] \
        || note 'the library pins no fingerprint, so any key file would be accepted'
    [[ -n "$actual" ]] \
        || note 'the pinned key file does not parse as a public key'
    [[ "$declared" == "$actual" ]] \
        || note "the pinned key is $actual but the library expects $declared"
    grep -q 'RPM-GPG-KEY-chatgpt' "$repo_dir/setup/keys/README.md" 2>/dev/null \
        || note 'setup/keys/README.md does not record where the pinned key came from'
    grep -q "$actual" "$repo_dir/setup/keys/README.md" 2>/dev/null \
        || note 'setup/keys/README.md records a fingerprint other than the key it ships'
fi

# One attempt, against redirected destinations and a recording root. Every
# variable the library exposes is set here rather than in the caller's shell,
# so a case cannot leak into the next one.
attempt() {
    local dir="$1" panama_path="$2" fingerprint="${3:-}"
    mkdir -p "$dir"
    (
        export SUDO_RECORD="$dir/sudo.log" RPMKEYS_RECORD="$dir/rpmkeys.log"
        : >"$SUDO_RECORD"
        : >"$RPMKEYS_RECORD"
        PANAMA_PATH="$panama_path"
        # shellcheck source=/dev/null
        source "$library"
        CHATGPT_KEY_FILE="$dir/pki/RPM-GPG-KEY-chatgpt"
        CHATGPT_REPO_FILE="$dir/repos/chatgpt.repo"
        mkdir -p "$dir/repos"
        [[ -z "$fingerprint" ]] || CHATGPT_KEY_FINGERPRINT="$fingerprint"
        chatgpt_install_repository sudo
    ) >"$dir/out" 2>&1
}

# ── 2. It fails closed ──────────────────────────────────────────────────────

# A checkout with no pinned key at all.
empty="$work/no-key"
mkdir -p "$empty/checkout/setup/keys"
attempt "$empty" "$empty/checkout" \
    && note 'a missing pinned key still established the repository'
[[ ! -e "$empty/repos/chatgpt.repo" ]] \
    || note 'a missing pinned key still wrote a repository file'
grep -qi 'missing' "$empty/out" \
    || note 'a missing pinned key does not say so'

# A key file that is not a key.
garbage="$work/garbage-key"
mkdir -p "$garbage/checkout/setup/keys"
printf 'not a key\n' >"$garbage/checkout/setup/keys/RPM-GPG-KEY-chatgpt"
attempt "$garbage" "$garbage/checkout" \
    && note 'an unreadable pinned key still established the repository'
[[ ! -e "$garbage/repos/chatgpt.repo" ]] \
    || note 'an unreadable pinned key still wrote a repository file'

# The real key, against a fingerprint that is not its own -- the shape a
# substituted publisher would take.
wrong="$work/wrong-fingerprint"
attempt "$wrong" "$repo_dir" '0000000000000000000000000000000000000000' \
    && note 'a key that does not match the pinned fingerprint was accepted'
[[ ! -e "$wrong/repos/chatgpt.repo" ]] \
    || note 'a fingerprint mismatch still wrote a repository file'
[[ ! -s "$wrong/rpmkeys.log" ]] \
    || note 'a fingerprint mismatch still imported the key into the rpm keyring'

# ── 3. What it writes enforces the check ────────────────────────────────────

good="$work/verified"
if ! attempt "$good" "$repo_dir"; then
    note "the pinned key was rejected: $(cat "$good/out")"
else
    repo_file="$good/repos/chatgpt.repo"
    key_file="$good/pki/RPM-GPG-KEY-chatgpt"

    cmp -s "$key_file" "$pinned_key" \
        || note 'the installed key is not the pinned key'
    grep -q 'import' "$good/rpmkeys.log" \
        || note 'the verified key was never imported, so dnf has nothing to check against'

    grep -qx 'gpgcheck=1' "$repo_file" \
        || note 'the repository does not set gpgcheck=1, so package signatures go unchecked'
    grep -qx 'repo_gpgcheck=1' "$repo_file" \
        || note 'the repository does not set repo_gpgcheck=1, so the metadata goes unchecked'
    grep -qx "gpgkey=file://$key_file" "$repo_file" \
        || note 'the repository does not point gpgkey at the key that was just installed'
    grep -q 'baseurl=https://' "$repo_file" \
        || note 'the repository has no https base URL'
fi

# ── 4. Both callers go through it ───────────────────────────────────────────

for caller in "$installer" "$migration"; do
    name="${caller#"$repo_dir"/}"
    [[ -r "$caller" ]] || { note "$name is missing"; continue; }

    grep -q 'setup/lib/chatgpt-package' "$caller" \
        || note "$name does not source the verified install library"
    grep -q 'chatgpt_install_repository' "$caller" \
        || note "$name does not establish the verified repository before installing"
    grep -qE 'dnf install -y chatgpt\b' "$caller" \
        || note "$name does not install chatgpt by name from that repository"

    # The shape this contract exists to keep out: fetch an RPM, hand it to
    # root, and let its scriptlet decide what the machine trusts afterwards.
    grep -qE 'curl.*chatgpt.*\.rpm' "$caller" \
        && note "$name downloads a ChatGPT RPM instead of installing it from the verified repository"
    grep -qE 'dnf install[^|]*\$\{?chatgpt_rpm' "$caller" \
        && note "$name installs a downloaded ChatGPT RPM as root"
done

if (( ${#findings[@]} > 0 )); then
    printf 'chatgpt package contract: %d finding(s)\n' "${#findings[@]}" >&2
    printf '  - %s\n' "${findings[@]}" >&2
    exit 1
fi

printf 'chatgpt package contract: PASS\n'
