#!/usr/bin/env bash
# E2E real SSH for ssh-cli — anti-leak by design (G-AUD-10 / G-E2E-05 / v0.5.2).
#
# Preferred credential sources (XDG / CLI — not product env store):
#   1) --config-dir DIR with hosts already registered via `ssh-cli vps add`
#   2) Lab file: $XDG_CONFIG_HOME/ssh-cli-e2e/lab.toml (host/user/key paths)
#   3) Flags: --host --user --key [--port]
#   4) Maintainer-only: --from-grok-config ($HOME/.grok/config.toml outside repo)
#
# Legacy SSH_CLI_E2E_* env vars are accepted ONLY by this harness (not product runtime).
# If no lab host is configured, exit 0 with SKIP (do not fail offline runs).
#
# Default binary: target/release/ssh-cli.
#
# Hygiene (GAP-SSH-SEC-002):
#   - Grok/MCP config MUST live under $HOME (default ~/.grok/config.toml), NEVER in this repo.
#   - Never prints host/user/password. Prints only PASS/FAIL/SKIP E0n.
#   - Uses /tmp (outside workspace). Temp dir destroyed on exit.
#   - Refuses grok config paths that resolve inside the repository root.
#
# GAP-SSH-ENV-001 (fail2ban / sshd ban policy):
#   - PROIBIDO loops de autenticação falha em host de produção.
#   - Matrix oficial E01–E16 NÃO inclui mass wrong-password.
set -euo pipefail
set +x

ROOT="$(cd "$(dirname "$0")/.." && pwd)"
BIN="${SSH_CLI_E2E_BIN:-$ROOT/target/release/ssh-cli}"
# Did the caller NAME a binary, or are we on the default? An explicitly named binary
# that cannot be executed is a hard failure: silently building the default and testing
# that instead would report PASS for a binary the operator never asked about.
BIN_EXPLICIT=0
[[ -n "${SSH_CLI_E2E_BIN:-}" ]] && BIN_EXPLICIT=1
FROM_GROK=0
SOFT_SUDO=0
FAILS=0
CONFIG_DIR=""
LOCAL_SSHD=0
SSHD_PID=""
LOCAL_KEY=""
LOCAL_KEY_PASS=""

pass() { echo "PASS $1"; }
fail() { echo "FAIL $1"; FAILS=$((FAILS + 1)); }
soft() { echo "SOFT $1 (skipped: $2)"; }
skip_all() { echo "SKIP E2E real SSH: $1"; exit 0; }

usage() {
  cat <<'EOF'
Usage: e2e_real_ssh.sh [options]
  --bin PATH              binary (default: target/release/ssh-cli)
  --config-dir DIR        isolated XDG config dir with pre-registered hosts
  --local-sshd            spawn an unprivileged sshd on loopback and run the full matrix
  --from-grok-config      read $HOME/.grok/config.toml only (never inside this repository)
  Env (harness-only, not product store): SSH_CLI_E2E_HOST PORT USER PASSWORD [SUDO_PASSWORD]
  Without a lab host and without --local-sshd, exits 0 with SKIP. Never prints secrets.
  With --local-sshd, an unusable sshd is a FAILURE — never a silent skip.
EOF
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --from-grok-config) FROM_GROK=1; shift ;;
    --local-sshd) LOCAL_SSHD=1; shift ;;
    --bin) BIN="$2"; BIN_EXPLICIT=1; shift 2 ;;
    --config-dir) CONFIG_DIR="$2"; shift 2 ;;
    -h|--help) usage; exit 0 ;;
    *) echo "unknown arg: $1" >&2; exit 2 ;;
  esac
done

if [[ ! -x "$BIN" ]]; then
  if [[ "$BIN_EXPLICIT" -eq 1 ]]; then
    # The operator named this binary. Substituting the default here would run the
    # whole E01-E16 matrix against a different artifact and print PASS for every
    # case, which is worse than useless: it is a green report about untested code.
    echo "FAIL E00: named binary is not executable: $BIN" >&2
    exit 2
  fi
  echo "building ssh-cli release..." >&2
  (cd "$ROOT" && cargo build --release -q)
  BIN="$ROOT/target/release/ssh-cli"
fi

if [[ "$FROM_GROK" -eq 1 ]]; then
  GROK_CFG="${SSH_CLI_E2E_GROK_CONFIG:-$HOME/.grok/config.toml}"
  if [[ ! -f "$GROK_CFG" ]]; then
    echo "FAIL E00: grok config missing" >&2
    exit 1
  fi
  # Refuse configs that live inside the git workspace (must stay outside the repo).
  GROK_CFG_ABS="$(cd "$(dirname "$GROK_CFG")" && pwd)/$(basename "$GROK_CFG")"
  ROOT_ABS="$(cd "$ROOT" && pwd)"
  case "$GROK_CFG_ABS" in
    "$ROOT_ABS"|"$ROOT_ABS"/*)
      echo "FAIL E00: grok config must not live inside the repository ($GROK_CFG_ABS)" >&2
      exit 1
      ;;
  esac
  # Parse via helper file to avoid shell quoting hell; values only in env exports.
  HELPER="$(mktemp /tmp/ssh-cli-e2e-parse.XXXXXX.py)"
  cat >"$HELPER" <<'PY'
import sys, shlex
try:
    import tomllib
except ImportError:
    import tomli as tomllib  # type: ignore

path = sys.argv[1]
with open(path, "rb") as f:
    data = tomllib.load(f)
servers = data.get("mcp_servers") or {}
srv = servers.get("ssh-flowaiper") or {}
args = srv.get("args") or []
flag_map = {
    "--host": "SSH_CLI_E2E_HOST",
    "--port": "SSH_CLI_E2E_PORT",
    "--user": "SSH_CLI_E2E_USER",
    "--password": "SSH_CLI_E2E_PASSWORD",
    "--sudoPassword": "SSH_CLI_E2E_SUDO_PASSWORD",
}
vals = {}
i = 0
while i < len(args):
    a = args[i]
    if not isinstance(a, str):
        i += 1
        continue
    for flag, envk in flag_map.items():
        if a.startswith(flag + "="):
            vals[envk] = a[len(flag) + 1 :]
            break
        if a == flag and i + 1 < len(args) and isinstance(args[i + 1], str):
            vals[envk] = args[i + 1]
            i += 1
            break
    i += 1
for k, v in vals.items():
    print(f"export {k}={shlex.quote(v)}")
need = ("SSH_CLI_E2E_HOST", "SSH_CLI_E2E_USER", "SSH_CLI_E2E_PASSWORD")
if any(k not in vals or not vals[k] for k in need):
    print("echo 'FAIL E00: incomplete daemon args' >&2; exit 1")
PY
  eval "$(python3 "$HELPER" "$GROK_CFG")"
  rm -f "$HELPER"
fi

TMP="$(mktemp -d /tmp/ssh-cli-e2e.XXXXXX)"
cleanup() {
  if [[ -n "$SSHD_PID" ]]; then
    kill "$SSHD_PID" 2>/dev/null || true
    wait "$SSHD_PID" 2>/dev/null || true
  fi
  rm -rf "$TMP"
  unset SSH_CLI_E2E_PASSWORD SSH_CLI_E2E_SUDO_PASSWORD SSH_CLI_E2E_HOST SSH_CLI_E2E_USER 2>/dev/null || true
}
trap cleanup EXIT

# --- D1: unprivileged local sshd ---------------------------------------------
# The official E01–E18 matrix had never executed once: without a lab host the
# script called skip_all and exited 0, so every "all gates green" scoreboard
# silently counted eighteen no-ops. Six documents said "prefer local sshd" and
# the harness had no code to start one. This mode closes that gap: the whole
# matrix runs against a real OpenSSH server on loopback, with no lab, no
# network and no secrets on disk outside $TMP.
#
# Auth is a passphrase-protected ed25519 key rather than a password: a system
# account password cannot be driven without PAM/root, and the passphrase keeps
# E02's real assertion (secrets are encrypted at rest) while additionally
# exercising --key-passphrase-stdin, which the password path never touched.
sshd_binary() {
  command -v sshd 2>/dev/null || { [[ -x /usr/sbin/sshd ]] && echo /usr/sbin/sshd; } \
    || { [[ -x /usr/bin/sshd ]] && echo /usr/bin/sshd; } || true
}

sftp_server_binary() {
  local c
  for c in /usr/libexec/openssh/sftp-server /usr/lib/openssh/sftp-server \
           /usr/libexec/sftp-server /usr/lib/ssh/sftp-server; do
    [[ -x "$c" ]] && { echo "$c"; return 0; }
  done
  return 1
}

port_is_free() {
  # A refused connect means nothing is listening there.
  ! (exec 3<>/dev/tcp/127.0.0.1/"$1") 2>/dev/null
}

free_loopback_port() {
  # sshd rejects Port 0, so probe upward from a randomized offset instead of
  # hardcoding a literal that could collide with a developer's own service.
  local base p i
  base=$(( 20000 + (RANDOM % 20000) ))
  for i in $(seq 0 199); do
    p=$(( base + i ))
    if port_is_free "$p"; then
      echo "$p"
      return 0
    fi
  done
  return 1
}

start_local_sshd() {
  local sshd sftp port cfg
  sshd="$(sshd_binary)"
  if [[ -z "$sshd" ]]; then
    echo "FAIL E00: --local-sshd requested but no sshd binary found" >&2
    return 1
  fi
  if ! sftp="$(sftp_server_binary)"; then
    echo "FAIL E00: --local-sshd requested but no sftp-server binary found" >&2
    return 1
  fi

  LOCAL_KEY="$TMP/e2e_id_ed25519"
  LOCAL_KEY_PASS="$(od -An -tx1 -N16 /dev/urandom | tr -d ' \n')"
  ssh-keygen -q -t ed25519 -f "$TMP/e2e_hostkey" -N '' -C e2e-host </dev/null
  ssh-keygen -q -t ed25519 -f "$LOCAL_KEY" -N "$LOCAL_KEY_PASS" -C e2e-user </dev/null
  cp "$LOCAL_KEY.pub" "$TMP/authorized_keys"
  chmod 600 "$TMP/e2e_hostkey" "$LOCAL_KEY" "$TMP/authorized_keys"

  if ! port="$(free_loopback_port)"; then
    echo "FAIL E00: no free loopback port for local sshd" >&2
    return 1
  fi

  cfg="$TMP/sshd_config"
  {
    printf 'Port %s\n' "$port"
    printf 'ListenAddress 127.0.0.1\n'
    printf 'HostKey %s/e2e_hostkey\n' "$TMP"
    printf 'PidFile %s/sshd.pid\n' "$TMP"
    printf 'AuthorizedKeysFile %s/authorized_keys\n' "$TMP"
    printf 'PubkeyAuthentication yes\n'
    printf 'PasswordAuthentication no\n'
    printf 'KbdInteractiveAuthentication no\n'
    printf 'UsePAM no\n'
    printf 'StrictModes no\n'
    printf 'AllowTcpForwarding yes\n'
    printf 'PermitTTY yes\n'
    printf 'LogLevel ERROR\n'
    printf 'Subsystem sftp %s\n' "$sftp"
  } >"$cfg"

  if ! "$sshd" -t -f "$cfg" 2>"$TMP/sshd-validate.err"; then
    echo "FAIL E00: local sshd config rejected" >&2
    return 1
  fi

  "$sshd" -D -f "$cfg" -E "$TMP/sshd.log" &
  SSHD_PID=$!

  # Wait for the listener instead of sleeping a magic number.
  local i ready=0
  for i in $(seq 1 60); do
    if ! kill -0 "$SSHD_PID" 2>/dev/null; then
      echo "FAIL E00: local sshd exited during startup" >&2
      return 1
    fi
    if ! port_is_free "$port"; then
      ready=1
      break
    fi
    sleep 0.1
  done
  if [[ "$ready" -ne 1 ]]; then
    echo "FAIL E00: local sshd never listened on 127.0.0.1:$port" >&2
    return 1
  fi

  SSH_CLI_E2E_HOST=127.0.0.1
  SSH_CLI_E2E_PORT="$port"
  SSH_CLI_E2E_USER="$(id -un)"
  export SSH_CLI_E2E_HOST SSH_CLI_E2E_PORT SSH_CLI_E2E_USER
  return 0
}

if [[ "$LOCAL_SSHD" -eq 1 ]]; then
  # Explicit mode never degrades to SKIP: an unusable sshd is a hard failure.
  if ! start_local_sshd; then
    echo "---"
    echo "fails=1 local_sshd=requested_but_unusable"
    exit 1
  fi
fi

# G-E2E-05: offline / no lab → SKIP exit 0 (not FAIL).
# D1: the skip is no longer silent. When a usable sshd exists, say so, so that a
# reader can tell "nothing to run" apart from "a runnable matrix was not run".
if [[ "$LOCAL_SSHD" -eq 0 ]] \
  && [[ -z "${SSH_CLI_E2E_HOST:-}" || -z "${SSH_CLI_E2E_USER:-}" || -z "${SSH_CLI_E2E_PASSWORD:-}" ]]; then
  if [[ -n "$(sshd_binary)" ]]; then
    skip_all "no lab host, but sshd IS available here — re-run with --local-sshd to execute E01-E18"
  fi
  skip_all "no lab host and no sshd (set harness SSH_CLI_E2E_* or --from-grok-config / --config-dir / --local-sshd)"
fi
SSH_CLI_E2E_PORT="${SSH_CLI_E2E_PORT:-22}"

export SSH_CLI_HOME="$TMP"
export HOME="$TMP"
export XDG_CONFIG_HOME="$TMP"
unset SSH_CLI_ALLOW_PLAINTEXT_SECRETS || true

cli() {
  "$BIN" --config-dir "$TMP" --output-format json "$@"
}

if cli secrets status >/dev/null 2>&1; then
  pass E01
else
  fail E01
fi

# E02: register the host and prove the secret is encrypted at rest.
# Local mode authenticates with a passphrase-protected key, so the secret under
# test is the passphrase; the at-rest assertion is identical either way.
E02_SECRET=""
if [[ "$LOCAL_SSHD" -eq 1 ]]; then
  E02_SECRET="$LOCAL_KEY_PASS"
  E02_ADDED=0
  printf '%s' "$LOCAL_KEY_PASS" | cli vps add \
    --name e2e \
    --host "$SSH_CLI_E2E_HOST" \
    --port "$SSH_CLI_E2E_PORT" \
    --user "$SSH_CLI_E2E_USER" \
    --key "$LOCAL_KEY" \
    --key-passphrase-stdin \
    --timeout 60000 \
    --check >/dev/null 2>&1 && E02_ADDED=1
else
  E02_SECRET="$SSH_CLI_E2E_PASSWORD"
  E02_ADDED=0
  printf '%s' "$SSH_CLI_E2E_PASSWORD" | cli vps add \
    --name e2e \
    --host "$SSH_CLI_E2E_HOST" \
    --port "$SSH_CLI_E2E_PORT" \
    --user "$SSH_CLI_E2E_USER" \
    --password-stdin \
    --timeout 60000 \
    --check >/dev/null 2>&1 && E02_ADDED=1
fi
# The prefix is matched version-agnostically. This assertion was frozen at
# `sshcli-enc:v1:` while the product had already moved to v2 — a stale literal
# nobody could notice, because the harness had never executed (D1).
if [[ "$E02_ADDED" -eq 1 ]] \
  && rg -q 'sshcli-enc:v[0-9]+:' "$TMP/config.toml" 2>/dev/null \
  && ! rg -qF "$E02_SECRET" "$TMP/config.toml" 2>/dev/null; then
  pass E02
else
  fail E02
fi

OUT="$(cli exec e2e 'echo e2e-ok' 2>/dev/null || true)"
if printf '%s' "$OUT" | rg -q 'e2e-ok'; then
  pass E03
else
  fail E03
fi

if [[ -n "${SSH_CLI_E2E_SUDO_PASSWORD:-}" ]]; then
  printf '%s' "$SSH_CLI_E2E_SUDO_PASSWORD" | cli vps edit e2e --sudo-password-stdin >/dev/null 2>&1 || true
fi
if cli sudo-exec e2e 'true' >/dev/null 2>&1; then
  pass E04
else
  soft E04 "sudo unavailable or auth failed"
  SOFT_SUDO=1
fi

if cli health-check e2e >/dev/null 2>&1; then
  pass E05
else
  fail E05
fi

DOUT="$(cli vps doctor 2>/dev/null || true)"
if printf '%s' "$DOUT" | rg -q 'encrypted'; then
  pass E06
else
  fail E06
fi

# E07: the registered secret must never surface in list/show output.
# Uses E02_SECRET, not SSH_CLI_E2E_PASSWORD: under --local-sshd the password is
# empty, and `rg -F ""` matches every line, which would fail E07 spuriously.
LOUT="$(cli vps list 2>/dev/null || true)$(cli vps show e2e 2>/dev/null || true)"
if [[ -n "$E02_SECRET" ]] && printf '%s' "$LOUT" | rg -qF "$E02_SECRET"; then
  fail E07
else
  pass E07
fi

if ! cli exec --timeout 2000 e2e 'sleep 30' >/dev/null 2>&1; then
  pass E08
else
  soft E08 "sleep completed within timeout"
fi

OUT2="$(cli exec e2e 'echo e2e-ok' 2>/dev/null || true)"
if printf '%s' "$OUT2" | rg -q 'e2e-ok'; then
  pass E09
else
  fail E09
fi

# --- SCP (GAP-SSH-SCP-016): E10 upload, E11 download, E12 integrity, E13 missing remote ---
REMOTE_SCP="/tmp/ssh-cli-e2e-scp-$$.bin"
REMOTE_SPACE="/tmp/ssh-cli e2e space $$ .bin"
UP_PLAIN="$TMP/up-plain.txt"
UP_SPACE="$TMP/up space file.txt"
UP_1M="$TMP/up-1m.bin"
DOWN_PLAIN="$TMP/down-plain.txt"
DOWN_SPACE="$TMP/down space file.txt"
DOWN_1M="$TMP/down-1m.bin"

printf 'e2e-scp-payload\n' >"$UP_PLAIN"
printf 'space-payload\n' >"$UP_SPACE"
# ≥1 MiB payload for streaming/wire stress
dd if=/dev/urandom of="$UP_1M" bs=1024 count=1024 status=none 2>/dev/null || \
  head -c 1048576 /dev/urandom >"$UP_1M"

if cli scp upload --timeout 120000 e2e "$UP_PLAIN" "$REMOTE_SCP" >/dev/null 2>&1 \
  && cli scp upload --timeout 120000 e2e "$UP_SPACE" "$REMOTE_SPACE" >/dev/null 2>&1 \
  && cli scp upload --timeout 180000 e2e "$UP_1M" "${REMOTE_SCP}.1m" >/dev/null 2>&1; then
  pass E10
else
  fail E10
fi

if cli scp download --timeout 120000 e2e "$REMOTE_SCP" "$DOWN_PLAIN" >/dev/null 2>&1 \
  && cli scp download --timeout 120000 e2e "$REMOTE_SPACE" "$DOWN_SPACE" >/dev/null 2>&1 \
  && cli scp download --timeout 180000 e2e "${REMOTE_SCP}.1m" "$DOWN_1M" >/dev/null 2>&1; then
  pass E11
else
  fail E11
fi

if cmp -s "$UP_PLAIN" "$DOWN_PLAIN" \
  && cmp -s "$UP_SPACE" "$DOWN_SPACE" \
  && cmp -s "$UP_1M" "$DOWN_1M"; then
  pass E12
else
  fail E12
fi

# E13: remote missing → exit 66 (ArquivoNaoEncontrado / GAP-SSH-IO-010); no residual local file
set +e
cli scp download --timeout 30000 e2e "/tmp/ssh-cli-e2e-missing-$$-no-such" "$TMP/should-not-exist" >/dev/null 2>&1
E13_EC=$?
set -e
if [[ "$E13_EC" -eq 66 && ! -f "$TMP/should-not-exist" ]]; then
  pass E13
else
  fail E13
fi

# E14: SCP-023 preserve mode+mtime on upload AND download (OpenSSH -p / linha T)
PRESERVE_LOCAL="$TMP/preserve-src.bin"
PRESERVE_REMOTE="/tmp/ssh-cli-e2e-preserve-$$.bin"
PRESERVE_DOWN="$TMP/preserve-down.bin"
printf 'preserve-payload\n' >"$PRESERVE_LOCAL"
chmod 600 "$PRESERVE_LOCAL"
# Epoch seconds (portable): @epoch avoids the TZ ambiguity of touch -d strings.
touch -d "@1579089600" "$PRESERVE_LOCAL" || true  # 2020-01-15 12:00:00 UTC
LOCAL_MODE="$(stat -c '%a' "$PRESERVE_LOCAL" 2>/dev/null || stat -f '%OLp' "$PRESERVE_LOCAL")"
LOCAL_MTIME="$(stat -c '%Y' "$PRESERVE_LOCAL" 2>/dev/null || stat -f '%m' "$PRESERVE_LOCAL")"
if cli scp upload --timeout 60000 e2e "$PRESERVE_LOCAL" "$PRESERVE_REMOTE" >/dev/null 2>&1 \
  && cli scp download --timeout 60000 e2e "$PRESERVE_REMOTE" "$PRESERVE_DOWN" >/dev/null 2>&1; then
  # Non-TTY default is JSON envelope — extract stdout field (text format has banners).
  REMOTE_JSON="$(cli exec --json --timeout 30000 e2e "stat -c '%a %Y' $(printf '%q' "$PRESERVE_REMOTE")" 2>/dev/null || true)"
  REMOTE_LINE="$(printf '%s' "$REMOTE_JSON" | jaq -r '(.stdout // "") | split("\n") | .[0] // ""' 2>/dev/null || true)"
  REMOTE_MODE="$(printf '%s\n' "$REMOTE_LINE" | choose 0)"
  REMOTE_MTIME="$(printf '%s\n' "$REMOTE_LINE" | choose 1)"
  DOWN_MODE="$(stat -c '%a' "$PRESERVE_DOWN" 2>/dev/null || stat -f '%OLp' "$PRESERVE_DOWN")"
  DOWN_MTIME="$(stat -c '%Y' "$PRESERVE_DOWN" 2>/dev/null || stat -f '%m' "$PRESERVE_DOWN")"
  if [[ "$REMOTE_MODE" == "$LOCAL_MODE" && "$REMOTE_MTIME" == "$LOCAL_MTIME" \
    && "$DOWN_MODE" == "$LOCAL_MODE" && "$DOWN_MTIME" == "$LOCAL_MTIME" ]]; then
    pass E14
  else
    fail E14
  fi
else
  fail E14
fi
cli exec --json e2e "rm -f $(printf '%q' "$PRESERVE_REMOTE")" >/dev/null 2>&1 || true

# E15: GAP-SSH-TUN-003 — local port 0 binds ephemeral; JSON local_port must be >= 1
TUN_JSON_OUT="$TMP/tunnel-e15.json"
set +e
# wall timeout; tunnel --timeout-ms 2000 one-shot post-bind exit 0
timeout 8 "$BIN" --config-dir "$TMP" --output-format json tunnel e2e 0 127.0.0.1 "$SSH_CLI_E2E_PORT" --timeout-ms 2000 --json >"$TUN_JSON_OUT" 2>/dev/null
set -e
# `tunnel` legitimately emits NDJSON: `tunnel_listening` then `tunnel_closed`.
# A bare `jaq -e` reports the status of the LAST document, so it would judge the
# run by the close event and always fail. Slurp and look for the bind event.
if jaq -s -e 'any(.[]; .ok == true and .event == "tunnel_listening" and ((.local_port // 0) >= 1))' \
    "$TUN_JSON_OUT" >/dev/null 2>&1; then
  pass E15
else
  fail E15
fi

# E16: GAP-SSH-SCP-024 — symlink: follow target content if regular file; else contract fail
SYM_TARGET="$TMP/symlink-target.txt"
SYM_LINK="$TMP/symlink-link.txt"
SYM_REMOTE="/tmp/ssh-cli-e2e-symlink-$$.txt"
SYM_DOWN="$TMP/symlink-down.txt"
printf 'symlink-payload-v1\n' >"$SYM_TARGET"
ln -sfn "$SYM_TARGET" "$SYM_LINK"
if cli scp upload --timeout 60000 e2e "$SYM_LINK" "$SYM_REMOTE" >/dev/null 2>&1 \
  && cli scp download --timeout 60000 e2e "$SYM_REMOTE" "$SYM_DOWN" >/dev/null 2>&1 \
  && cmp -s "$SYM_TARGET" "$SYM_DOWN"; then
  pass E16
else
  # OpenSSH scp may reject non-regular; still no residual wrong content
  fail E16
fi
cli exec e2e "rm -f $(printf '%q' "$SYM_REMOTE")" >/dev/null 2>&1 || true

# E17: G7 — SFTP upload/download round-trip with destination checksum (never trust client bytes)
# Matrix: empty, 1B, 760B, 32KiB, 32KiB+1, 64KiB (covers chunk boundary and G1 sizes).
SFTP_OK=1
for SFTP_SIZE in 0 1 760 32768 32769 65536; do
  SFTP_LOCAL="$TMP/sftp-up-${SFTP_SIZE}.bin"
  SFTP_REMOTE="/tmp/ssh-cli-e2e-sftp-${SFTP_SIZE}-$$.bin"
  SFTP_DOWN="$TMP/sftp-down-${SFTP_SIZE}.bin"
  if [[ "$SFTP_SIZE" -eq 0 ]]; then
    : >"$SFTP_LOCAL"
  else
    dd if=/dev/urandom of="$SFTP_LOCAL" bs=1 count="$SFTP_SIZE" status=none 2>/dev/null \
      || head -c "$SFTP_SIZE" /dev/urandom >"$SFTP_LOCAL"
  fi
  LOCAL_HASH="$(sha256sum "$SFTP_LOCAL" | choose 0)"
  if ! cli sftp upload --timeout 60000 e2e "$SFTP_LOCAL" "$SFTP_REMOTE" >/dev/null 2>&1; then
    SFTP_OK=0
    break
  fi
  # Measure EFFECT on destination via remote sha256 — not the JSON bytes field.
  REMOTE_HASH="$(cli exec --json e2e "sha256sum $(printf '%q' "$SFTP_REMOTE")" 2>/dev/null \
    | jaq -r '(.stdout // "") | split(" ") | .[0] // ""' 2>/dev/null || true)"
  if [[ -z "$REMOTE_HASH" || "$REMOTE_HASH" != "$LOCAL_HASH" ]]; then
    SFTP_OK=0
    cli exec e2e "rm -f $(printf '%q' "$SFTP_REMOTE")" >/dev/null 2>&1 || true
    break
  fi
  if ! cli sftp download --timeout 60000 e2e "$SFTP_REMOTE" "$SFTP_DOWN" >/dev/null 2>&1 \
    || ! cmp -s "$SFTP_LOCAL" "$SFTP_DOWN"; then
    SFTP_OK=0
    cli exec e2e "rm -f $(printf '%q' "$SFTP_REMOTE")" >/dev/null 2>&1 || true
    break
  fi
  cli exec e2e "rm -f $(printf '%q' "$SFTP_REMOTE")" >/dev/null 2>&1 || true
done
if [[ "$SFTP_OK" -eq 1 ]]; then
  pass E17
else
  fail E17
fi

# E18: G7 — SFTP recursive tree upload + remote checksum of one leaf
SFTP_TREE="$TMP/sftp-tree-$$"
mkdir -p "$SFTP_TREE/sub"
printf 'leaf-a\n' >"$SFTP_TREE/a.txt"
printf 'leaf-b\n' >"$SFTP_TREE/sub/b.txt"
SFTP_TREE_REMOTE="/tmp/ssh-cli-e2e-sftp-tree-$$"
LEAF_HASH="$(sha256sum "$SFTP_TREE/sub/b.txt" | choose 0)"
if cli sftp upload --recursive --timeout 120000 e2e "$SFTP_TREE" "$SFTP_TREE_REMOTE" >/dev/null 2>&1; then
  REMOTE_LEAF_HASH="$(cli exec --json e2e "sha256sum $(printf '%q' "$SFTP_TREE_REMOTE/sub/b.txt")" 2>/dev/null \
    | jaq -r '(.stdout // "") | split(" ") | .[0] // ""' 2>/dev/null || true)"
  if [[ -n "$REMOTE_LEAF_HASH" && "$REMOTE_LEAF_HASH" == "$LEAF_HASH" ]]; then
    pass E18
  else
    fail E18
  fi
else
  fail E18
fi
cli exec e2e "rm -rf $(printf '%q' "$SFTP_TREE_REMOTE")" >/dev/null 2>&1 || true

# Best-effort remote cleanup (never print paths with secrets)
cli exec e2e "rm -f $(printf '%q' "$REMOTE_SCP") $(printf '%q' "$REMOTE_SPACE") $(printf '%q' "${REMOTE_SCP}.1m")" >/dev/null 2>&1 || true

echo "---"
echo "fails=$FAILS soft_sudo=$SOFT_SUDO tmp_destroyed=yes"
if [[ "$FAILS" -gt 0 ]]; then
  exit 1
fi
exit 0
