diff --git a/containers/Dockerfile b/containers/Dockerfile index 37291c69..dc6bd8a6 100644 --- a/containers/Dockerfile +++ b/containers/Dockerfile @@ -116,28 +116,17 @@ RUN npm install -g retire@latest && \ ln -sf ast-grep /home/pentester/.npm-global/lib/node_modules/@ast-grep/cli/sg ENV AGENT_BROWSER_EXECUTABLE_PATH=/usr/bin/chromium -# The User-Agent is derived from the installed Chromium at container start -# (containers/docker-entrypoint.sh): a hardcoded version silently drifts from the -# real browser as the image is rebuilt, and a UA that disagrees with the client -# hints and JS fingerprint is exactly what bot protection looks for. -# NOTE: AGENT_BROWSER_ARGS is comma-separated, so no flag value may contain a -# comma — `--window-size=1280,800` would split into a bogus `800` argument that -# Chrome treats as a second URL ("Multiple targets are not supported"). The window -# geometry comes from the virtual display instead. +# The User-Agent is derived from the installed Chromium at container start; see +# containers/docker-entrypoint.sh. AGENT_BROWSER_ARGS is comma-separated, so no +# flag value may contain a comma (window geometry comes from the display instead). ENV AGENT_BROWSER_ARGS="--disable-blink-features=AutomationControlled,--no-first-run,--no-default-browser-check,--lang=en-US,--password-store=basic,--use-mock-keychain,--disable-dev-shm-usage" ENV AGENT_BROWSER_SCREENSHOT_DIR=/workspace/.agent-browser-screenshots ENV AGENT_BROWSER_IDLE_TIMEOUT_MS=180000 RUN /home/pentester/.npm-global/bin/agent-browser doctor --offline --quick -# `agent-browser --headed` exits 0 even when Chrome fails to launch, so a broken -# headed mode is invisible at runtime — assert it here instead. Targets are -# increasingly gated behind bot protection that rejects headless browsers, which -# makes headed mode part of the login path, not a debugging nicety. -# -# `--no-sandbox` is for this build step only: buildkit runs without unprivileged -# user namespaces, so Chromium's zygote sandbox can't initialize here. It is not -# part of the runtime browser args. The X lock and socket are removed afterwards; -# a leftover makes the runtime Xvfb refuse to start ("Server is already active"). +# `agent-browser --headed` exits 0 even when Chrome fails to launch, so assert +# headed mode here. `--no-sandbox` is build-only: buildkit has no unprivileged +# user namespaces. The stale X lock/socket must go or the runtime Xvfb refuses. RUN set -eu; \ Xvfb :99 -screen 0 1280x800x24 -nolisten tcp >/tmp/xvfb-build.log 2>&1 & \ xvfb_pid=$!; \ diff --git a/containers/docker-entrypoint.sh b/containers/docker-entrypoint.sh index b151c44e..19458530 100644 --- a/containers/docker-entrypoint.sh +++ b/containers/docker-entrypoint.sh @@ -117,17 +117,14 @@ echo ". /etc/profile.d/proxy.sh" >> ~/.zshrc echo "✅ System-wide proxy configuration complete" -# Bot protection routinely rejects headless browsers, so the agent needs to be -# able to fall back to a real headed Chrome. There is no physical display here, -# so provide a virtual one; without it headed mode dies with "Missing X server or -# $DISPLAY" while still exiting 0, which is silent from the agent's point of view. +# A virtual display so the agent can fall back to headed Chrome when a target +# rejects headless; without it headed mode dies but still exits 0. DISPLAY_NUM="${STRIX_DISPLAY_NUM:-99}" DISPLAY_GEOMETRY="${STRIX_DISPLAY_GEOMETRY:-1280x800x24}" if ! xdpyinfo -display ":${DISPLAY_NUM}" >/dev/null 2>&1; then - # No server is answering, so any lock/socket left over from an earlier boot (or - # from image build) is stale — Xvfb otherwise refuses with "Server is already - # active for display N". + # Nothing is answering, so a leftover lock/socket is stale; Xvfb refuses to + # start with one present. rm -f "/tmp/.X${DISPLAY_NUM}-lock" "/tmp/.X11-unix/X${DISPLAY_NUM}" 2>/dev/null || true Xvfb ":${DISPLAY_NUM}" -screen 0 "${DISPLAY_GEOMETRY}" -nolisten tcp \ > /tmp/xvfb.log 2>&1 & @@ -144,16 +141,15 @@ else cat /tmp/xvfb.log 2>/dev/null || echo "(no log available)" fi -# A session bus keeps headed Chrome from spewing dbus connection errors that look -# like fatal failures in tool output. Best-effort: Chrome runs fine without it. +# Best-effort session bus: without it headed Chrome spews dbus errors that read +# like fatal failures in tool output. if [ ! -S /run/dbus/system_bus_socket ]; then sudo mkdir -p /run/dbus sudo dbus-daemon --system --fork > /tmp/dbus.log 2>&1 || true fi -# Keep the advertised Chrome version in step with the browser actually installed: -# a User-Agent that disagrees with the client hints and JS fingerprint is a -# trivially detectable automation signal. +# A UA that disagrees with the installed browser's client hints is a trivially +# detectable automation signal, so derive it instead of hardcoding a version. CHROME_MAJOR="$(chromium --version 2>/dev/null | grep -oE '[0-9]+' | head -1)" BROWSER_UA="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${CHROME_MAJOR:-131}.0.0.0 Safari/537.36"