#!/usr/bin/env bash
# Offline inventory by default. --apply upgrades installed Ubuntu kernel tracks.
# No exploit, scanner service, automatic reboot, or unsupported FIXED verdict.
set -euo pipefail
umask 077
apply=0
out="pwnescape-audit-ubuntu.json"
while (($#)); do
  case "$1" in
    --apply) apply=1; shift ;;
    --out) out="${2:?--out requires a path}"; shift 2 ;;
    --help) printf '%s\n' 'bash pwnescape-ubuntu.sh [--apply] [--out report.json]'; exit 0 ;;
    *) printf '%s\n' 'Unknown option.' >&2; exit 1 ;;
  esac
done
[[ "$(uname -s)" == Linux ]] || { printf '%s\n' 'Linux is required.' >&2; exit 1; }
ID=unknown
VERSION_ID=unknown
if [[ -r /etc/os-release ]]; then . /etc/os-release; fi
os_id="$ID"
os_version="$VERSION_ID"
running="$(uname -r)"
for value in "$os_id" "$os_version" "$running"; do
  [[ "$value" =~ ^[A-Za-z0-9._+~-]+$ ]] || { printf '%s\n' 'Unexpected metadata.' >&2; exit 1; }
done
container=unknown
if [[ -e /.dockerenv || -e /run/.containerenv ]]; then
  container=yes
elif command -v systemd-detect-virt >/dev/null 2>&1; then
  if systemd-detect-virt --quiet --container; then container=yes; else container=no; fi
fi
kernel_package=unknown
kernel_package_version=unknown
if command -v dpkg-query >/dev/null 2>&1; then
  owner="$(dpkg-query -S "/boot/vmlinuz-$running" 2>/dev/null || true)"
  if [[ "$owner" == *': /boot/'* ]]; then
    kernel_package="${owner%%: /boot/*}"
    [[ "$kernel_package" =~ ^[A-Za-z0-9.+:~-]+$ ]] || exit 1
    kernel_package_version="$(dpkg-query -W -f='${Version}' "$kernel_package")"
    [[ "$kernel_package_version" =~ ^[A-Za-z0-9.+:~_-]+$ ]] || exit 1
  fi
fi
update=not_requested
if ((apply)); then
  [[ "$os_id" == ubuntu && "$os_version" == 26.04 ]] || {
    printf '%s\n' '--apply supports Ubuntu 26.04 only.' >&2; exit 1;
  }
  [[ "$container" == no ]] || {
    printf '%s\n' 'Run updates on the owning host; container status must be known.' >&2; exit 1;
  }
  [[ "$running" != *microsoft* && "$running" != *Microsoft* ]] || {
    printf '%s\n' 'Use the Windows WSL update channel for a WSL kernel.' >&2; exit 1;
  }
  ((EUID == 0)) || { printf '%s\n' 'Use sudo for --apply.' >&2; exit 1; }
  command -v apt-get >/dev/null
  metas=()
  while IFS=$'\t' read -r state package; do
    [[ "$state" == installed ]] || continue
    package="${package%%:*}"
    case "$package" in
      linux-generic|linux-generic-hwe-*|linux-generic-64k|linux-virtual|linux-lowlatency|linux-aws|linux-azure|linux-gcp|linux-oracle|linux-kvm|linux-raspi)
        metas+=("$package") ;;
    esac
  done < <(dpkg-query -W -f='${db:Status-Status}\t${binary:Package}\n' 'linux-*' 2>/dev/null || true)
  ((${#metas[@]})) || {
    printf '%s\n' 'No supported installed kernel metapackage; follow the vendor track.' >&2; exit 1;
  }
  # APT uses the machine's existing repository/signature policy. No new sources.
  # Keep package-manager logs private; print only the resulting action status.
  update_log="$(mktemp)"
  trap 'rm -f "$update_log"' EXIT
  if ! apt-get update >"$update_log" 2>&1; then
    printf '%s\n' 'APT metadata refresh failed; no fixed verdict.' >&2; exit 1;
  fi
  if ! DEBIAN_FRONTEND=noninteractive apt-get -y --no-remove --only-upgrade install "${metas[@]}" >"$update_log" 2>&1; then
    printf '%s\n' 'Kernel package update failed; no fixed verdict.' >&2; exit 1;
  fi
  update=completed_vendor_confirmation_required
fi
reboot=not_reported
[[ ! -e /var/run/reboot-required ]] || reboot=required
if ((apply)); then reboot=activation_review_required; fi
printf '{\n  "cve": "CVE-2026-99999",\n  "os": "%s",\n  "os_version": "%s",\n  "running_kernel": "%s",\n  "running_kernel_package": "%s",\n  "package_version": "%s",\n  "container": "%s",\n  "update": "%s",\n  "reboot": "%s",\n  "verdict": "VENDOR_CONFIRMATION_REQUIRED"\n}\n' \
  "$os_id" "$os_version" "$running" "$kernel_package" "$kernel_package_version" "$container" "$update" "$reboot" >"$out"
printf '%s\n' 'Audit saved locally. VENDOR_CONFIRMATION_REQUIRED.'
# Exit 2 is an unresolved CVE assessment, not an execution error.
exit 2
