#!/usr/bin/env bash
# Real demos for P014 "Why Phishing Beats Good Security (and How to Spot It)". Runs on render01 (Docker).
# Defensive only: no fake login page, nothing captured, nothing sent anywhere. The viewer's machine is a
# lab "laptop" (python:3.13-slim, hostname laptop, user ana, no network needed after the build) with four
# small scripts from lab/. Every address is a reserved test name (RFC 2606/6761: bank.example, *.test).
# Every command is typed into one real interactive bash (a pty) as ana, in the folder the prompt shows.
# Output goes to demo_output.json; build_script.py reads every number and line from it.
set -euo pipefail
cd "$(dirname "$0")"
LAPTOP=ce-p014-laptop
W=/tmp/ce-p014
cleanup() { docker rm -f $LAPTOP >/dev/null 2>&1 || true; }
cleanup
trap cleanup EXIT
rm -rf $W && mkdir -p $W/img

cat > $W/img/Dockerfile <<'EOF'
FROM python:3.13-slim
RUN pip install -q --no-cache-dir --root-user-action=ignore webauthn==3.0.1 pyotp==2.10.0 \
 && useradd -m -s /bin/bash ana
EOF
docker build -q -t ce-p014-laptop $W/img >/dev/null
# --network none: the lab can't reach anything, which is the point
docker run -d --name $LAPTOP --hostname laptop --network none ce-p014-laptop sleep infinity >/dev/null
docker exec $LAPTOP mkdir -p /home/ana/lab
docker cp lab/. $LAPTOP:/home/ana/lab/
docker exec $LAPTOP chown -R ana /home/ana

python3 - "$LAPTOP" <<'PY'
import fcntl, json, os, pty, re, select, struct, subprocess, sys, termios, time
LAPTOP = sys.argv[1]
READY = re.compile(rb"__READY__:(.*?):__ ")
steps = {}

class Shell:
    def __init__(self):
        pid, fd = pty.fork()
        if pid == 0:
            fcntl.ioctl(0, termios.TIOCSWINSZ, struct.pack("HHHH", 50, 200, 0, 0))
            os.execvp("docker", ["docker", "exec", "-it", "-u", "ana", "-w", "/home/ana/lab", "-e", "HOME=/home/ana",
                                 "-e", "TERM=dumb", LAPTOP, "bash", "-i"])
        self.fd, self.where = fd, "~/lab"
        os.write(fd, b"stty -echo; stty cols 200 rows 50; PS1='__REA''DY__:\\w:__ '\n")
        self.read()

    def read(self, timeout=120):
        buf, end = b"", time.time() + timeout
        while time.time() < end:
            if select.select([self.fd], [], [], 0.3)[0]:
                buf += os.read(self.fd, 65536)
                m = None
                for m in READY.finditer(buf):
                    pass
                if m:
                    self.where = m.group(1).decode()
                    return buf[:m.start()]
        raise SystemExit(f"timed out; last output: {buf[-600:]!r}")

    def run(self, key, cmd):
        shown = f"ana@laptop:{self.where}$ {cmd}"
        os.write(self.fd, cmd.encode() + b"\n")
        raw = self.read()
        out = raw.replace(b"\r\r\n", b"\n").replace(b"\r\n", b"\n").decode().strip("\n")
        steps[key] = {"cmd": shown, "out": out}
        print(f"--- {key}\n{shown}\n{out}\n", flush=True)
        return out

s = Shell()
# Spot it: read the real address (the host, read right to left)
s.run("url_real", "python3 url_check.py https://bank.example/login")
s.run("url_sub", "python3 url_check.py https://bank.example.account-check.test/login")
s.run("url_at", "python3 url_check.py https://bank.example@account-check.test/login")
s.run("url_dash", "python3 url_check.py https://bank-example.test/login")
s.run("url_path", "python3 url_check.py https://account-check.test/bank.example/login")
# Spot it: the sender's real address
s.run("sender_fake", "python3 sender_check.py 'Bank Security <alerts@bank-example.test>'")
s.run("sender_real", "python3 sender_check.py 'Bank <alerts@bank.example>'")
# Why codes don't help: what the server receives
s.run("code", "python3 code_check.py")
# Stop it without noticing: a passkey, checked by py_webauthn
s.run("pk_register", "python3 passkey_demo.py register")
s.run("pk_real", "python3 passkey_demo.py https://bank.example/login")
s.run("pk_fake", "python3 passkey_demo.py https://bank-example.test/login")
s.run("pk_anyway", "python3 passkey_demo.py https://bank-example.test/login --sign-anyway")

ver = subprocess.run(["docker", "exec", LAPTOP, "sh", "-c",
                      "python3 --version; pip list 2>/dev/null | grep -iE '^(webauthn|pyotp|cryptography) '"],
                     capture_output=True, text=True).stdout.strip()
steps["_meta"] = {"cmd": "", "out": json.dumps({"versions": ver.splitlines(), "run_at": time.strftime("%Y-%m-%d %H:%M %Z")})}
json.dump(steps, open("demo_output.json", "w"), indent=1)
print("saved demo_output.json")
PY
