#!/usr/bin/env bash
# Reads the live cgroup an agent launch actually lands in. Both escapes covered here
# shipped unbounded: a nested scope created outside agent.slice inherits CONFINE_ACTIVE
# and was trusted as proof of confinement, and a launch inside an active tmpjail skipped
# the scope entirely.
set -uo pipefail

REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd)"
CLAUDE="$REPO/modules/workstation/claude"
CONFINE="$CLAUDE/lib/confine.sh"

if ! systemctl --user show-environment >/dev/null 2>&1; then
  echo "SKIP confine scope escape (no user systemd manager)"
  exit 0
fi

pass=0
fail=0

check() { # $1=name $2=actual $3=expected
  if [[ "$2" == "$3" ]]; then
    pass=$((pass + 1))
    echo "PASS $1"
  else
    fail=$((fail + 1))
    echo "FAIL $1 (want=$3 got=$2)"
  fi
}

check_glob() { # $1=name $2=actual $3=pattern
  # shellcheck disable=SC2053
  if [[ "$2" == $3 ]]; then
    pass=$((pass + 1))
    echo "PASS $1"
  else
    fail=$((fail + 1))
    echo "FAIL $1 (want=$3 got=$2)"
  fi
}

leaf='cut -d: -f3 /proc/self/cgroup | tail -1'
report="printf '%s %s\\n' \"\$($leaf)\" \"\$(cat /sys/fs/cgroup\$($leaf)/memory.max)\""
last_line() { tail -1 <<<"$1"; }

app_scope() { # runs "$@" (from index 2) in a fresh scope outside agent.slice
  local unit="confine-escape-test-$$-$1"
  shift
  systemd-run --user --scope --quiet --collect --unit="$unit" -- "$@"
}

# 1. A scope created outside agent.slice carries CONFINE_ACTIVE but no ceiling.
out="$(last_line "$(app_scope nested \
  env CONFINE_ACTIVE=agent:cgroup "$CONFINE" agent bash -c "$report" 2>&1)")"
check_glob "nested-scope-lands-in-agent-slice" "${out% *}" "*/agent.slice/confine-agent-*.scope"
check "nested-scope-has-no-size-ceiling" "${out##* }" max

# 2. Inside a scope that already holds the class ceiling, the launch must reuse it
#    rather than stack one scope per nested agent.
out="$(last_line "$("$CONFINE" agent bash -c \
  "outer=\$($leaf); inner=\$($CONFINE agent bash -c '$leaf');
   [[ \"\$outer\" == \"\$inner\" ]] && echo reused || echo \"restacked \$outer -> \$inner\"" 2>&1)")"
check "confined-scope-is-not-restacked" "$out" "reused"

# 3. An agent launched with a jail already active still gets its scope.
fixture="$(mktemp -d)"
trap 'rm -rf "$fixture"' EXIT
mkdir -p "$fixture/home/.claude/bin" "$fixture/home/.claude/lib" "$fixture/real"
ln -s "$CONFINE" "$fixture/home/.claude/lib/confine.sh"
ln -s "$CLAUDE/bin/_agent-build-scope" "$fixture/home/.claude/bin/_agent-build-scope"
ln -s "$CLAUDE/bin/_tmpjail-shim.sh" "$fixture/home/.claude/bin/_tmpjail-shim.sh"
ln -s _tmpjail-shim.sh "$fixture/home/.claude/bin/probeagent"
printf '#!/usr/bin/env bash\n%s\n' "$report" > "$fixture/real/probeagent"
chmod +x "$fixture/real/probeagent"

out="$(last_line "$(HOME="$fixture/home" TMPJAIL_ACTIVE=1 \
  PATH="$fixture/home/.claude/bin:$fixture/real:$PATH" \
  app_scope jailed probeagent 2>&1)")"
check_glob "jailed-launch-lands-in-agent-slice" "${out% *}" "*/agent.slice/confine-agent-*.scope"
check "jailed-launch-has-no-size-ceiling" "${out##* }" max

printf 'confine-scope-escape: pass=%d fail=%d\n' "$pass" "$fail"
(( fail == 0 ))
