mirror of
https://github.com/usestrix/strix.git
synced 2026-08-16 09:26:39 +02:00
* fix(proxy,tooling): serialize+reconnect Caido client, actionable HTTPQL errors, sandbox tool guidance
Addresses the top recurring agent tool-call failures observed in telemetry:
- proxy: the shared Caido client had no locking or reconnect, so concurrent
agent calls raced ("Transport is already connected") and a dead transport
poisoned the rest of the run ("Connector is closed"/"Server disconnected").
Add an asyncio lock + bounded reconnect in caido_api.call_with_client (sandbox
path) and a scan-wide caido_lock in the run context that host-side proxy tools
hold around every call. Deterministic errors are not retried.
- proxy: list_requests now returns Caido's exact parser message, echoes the
offending query, and includes a corrected-syntax hint so agents self-correct
instead of retrying a broken HTTPQL filter.
- shell/prompt: document that write_stdin requires a process started with
tty=true; nudge toward writing Python to a file over deeply-nested one-liners;
note the venv pre-installs common libs.
- agent-browser: distinguish daemon/connection failures (run doctor, don't loop)
from malformed commands; invoke directly (no sh -c wrapper).
- containers: use POSIX '.' instead of the bashism 'source' in generated rc
files (fixes 'sh: source: not found'); add file + xxd and pre-install
requests/httpx/beautifulsoup4/lxml/pyjwt/cryptography in the sandbox venv.
- tests: cover proxy serialization/reconnect/no-retry and HTTPQL errors.
* fix(proxy): host-side reconnect, close stale clients, don't retry mutations
Addresses Greptile review on the reconnect logic:
- Host path had no reconnect: a dead shared context client (Caido restart /
network blip) previously disabled proxy tools for the rest of the scan. Add
SharedCaidoClient, a serialized reconnect-safe holder stored once per scan in
the run context and shared across agents. On a dead transport it rebuilds via
reconnect_caido, which re-selects the SAME Caido project (preserving captured
traffic) instead of creating a new empty one.
- Don't repeat completed mutations: call_with_client / SharedCaidoClient.call
take idempotent=. Reads retry once on reconnect; replay + scope
create/update/delete heal the client but re-raise instead of risking a
double-apply.
- Don't leak replaced clients: the stale client is aclose()d (best-effort) on
every reconnect.
- Extend tests to cover close-on-reconnect, non-idempotent re-raise, and the
SharedCaidoClient holder.
* fix(proxy): close replacement Caido client when project.select fails
Addresses Greptile P1: in reconnect_caido (and bootstrap_caido) a successful
connect() followed by a failing project.select()/create() discarded the
connected client without closing it, so a missing/unavailable project could
leak a transport on every retry. Close the client before re-raising.
---------
Co-authored-by: Alex Schapiro <bearsyankees@gmail.com>
116 lines
3.7 KiB
Bash
116 lines
3.7 KiB
Bash
#!/bin/bash
|
|
set -e
|
|
|
|
CAIDO_PORT=48080
|
|
CAIDO_LOG="/tmp/caido_startup.log"
|
|
|
|
if [ ! -f /app/certs/ca.p12 ]; then
|
|
echo "ERROR: CA certificate file /app/certs/ca.p12 not found."
|
|
exit 1
|
|
fi
|
|
|
|
# Caido enforces a Host allowlist (DNS-rebinding protection) and rejects requests
|
|
# whose Host header is a hostname it doesn't recognize. To reach Caido over a
|
|
# hostname (rather than an IP literal), set STRIX_CAIDO_ALLOWED_DOMAINS to a
|
|
# comma-separated list of hostnames to allow. Unset by default.
|
|
# See https://docs.caido.io/app/guides/domain_allowlist
|
|
CAIDO_UI_DOMAIN_ARGS=()
|
|
if [ -n "${STRIX_CAIDO_ALLOWED_DOMAINS:-}" ]; then
|
|
IFS=',' read -ra _caido_domains <<< "${STRIX_CAIDO_ALLOWED_DOMAINS}"
|
|
for _d in "${_caido_domains[@]}"; do
|
|
[ -n "$_d" ] && CAIDO_UI_DOMAIN_ARGS+=(--ui-domain "$_d")
|
|
done
|
|
fi
|
|
|
|
caido-cli --listen 0.0.0.0:${CAIDO_PORT} \
|
|
--allow-guests \
|
|
--no-logging \
|
|
--no-open \
|
|
"${CAIDO_UI_DOMAIN_ARGS[@]}" \
|
|
--import-ca-cert /app/certs/ca.p12 \
|
|
--import-ca-cert-pass "" > "$CAIDO_LOG" 2>&1 &
|
|
|
|
CAIDO_PID=$!
|
|
echo "Started Caido with PID $CAIDO_PID on port $CAIDO_PORT"
|
|
|
|
echo "Waiting for Caido API to be ready..."
|
|
CAIDO_READY=false
|
|
for i in {1..30}; do
|
|
if ! kill -0 $CAIDO_PID 2>/dev/null; then
|
|
echo "ERROR: Caido process died while waiting for API (iteration $i)."
|
|
echo "=== Caido log ==="
|
|
cat "$CAIDO_LOG" 2>/dev/null || echo "(no log available)"
|
|
exit 1
|
|
fi
|
|
|
|
if curl -s -o /dev/null -w "%{http_code}" http://localhost:${CAIDO_PORT}/graphql/ | grep -qE "^(200|400)$"; then
|
|
echo "Caido API is ready (attempt $i)."
|
|
CAIDO_READY=true
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
if [ "$CAIDO_READY" = false ]; then
|
|
echo "ERROR: Caido API did not become ready within 30 seconds."
|
|
echo "Caido process status: $(kill -0 $CAIDO_PID 2>&1 && echo 'running' || echo 'dead')"
|
|
echo "=== Caido log ==="
|
|
cat "$CAIDO_LOG" 2>/dev/null || echo "(no log available)"
|
|
exit 1
|
|
fi
|
|
|
|
sleep 2
|
|
|
|
echo "Caido is up — host bootstraps the guest token + project via the Python SDK."
|
|
|
|
echo "Configuring system-wide proxy settings..."
|
|
|
|
cat << EOF | sudo tee /etc/profile.d/proxy.sh
|
|
export http_proxy=http://127.0.0.1:${CAIDO_PORT}
|
|
export https_proxy=http://127.0.0.1:${CAIDO_PORT}
|
|
export HTTP_PROXY=http://127.0.0.1:${CAIDO_PORT}
|
|
export HTTPS_PROXY=http://127.0.0.1:${CAIDO_PORT}
|
|
export ALL_PROXY=http://127.0.0.1:${CAIDO_PORT}
|
|
export NO_PROXY=localhost,127.0.0.1
|
|
export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
|
|
export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
|
|
EOF
|
|
|
|
cat << EOF | sudo tee /etc/environment
|
|
http_proxy=http://127.0.0.1:${CAIDO_PORT}
|
|
https_proxy=http://127.0.0.1:${CAIDO_PORT}
|
|
HTTP_PROXY=http://127.0.0.1:${CAIDO_PORT}
|
|
HTTPS_PROXY=http://127.0.0.1:${CAIDO_PORT}
|
|
ALL_PROXY=http://127.0.0.1:${CAIDO_PORT}
|
|
NO_PROXY=localhost,127.0.0.1
|
|
EOF
|
|
|
|
cat << EOF | sudo tee /etc/wgetrc
|
|
use_proxy=yes
|
|
http_proxy=http://127.0.0.1:${CAIDO_PORT}
|
|
https_proxy=http://127.0.0.1:${CAIDO_PORT}
|
|
EOF
|
|
|
|
# Use POSIX `.` (not the bashism `source`) so these lines are safe when the rc
|
|
# files are read by a POSIX shell (e.g. `sh -lc`), which otherwise fails with
|
|
# "source: not found". `.` is understood by bash, zsh, and dash alike.
|
|
echo ". /etc/profile.d/proxy.sh" >> ~/.bashrc
|
|
echo ". /etc/profile.d/proxy.sh" >> ~/.zshrc
|
|
|
|
. /etc/profile.d/proxy.sh
|
|
|
|
echo "✅ System-wide proxy configuration complete"
|
|
|
|
echo "Adding CA to browser trust store..."
|
|
sudo -u pentester mkdir -p /home/pentester/.pki/nssdb
|
|
sudo -u pentester certutil -N -d sql:/home/pentester/.pki/nssdb --empty-password
|
|
sudo -u pentester certutil -A -n "Testing Root CA" -t "C,," -i /app/certs/ca.crt -d sql:/home/pentester/.pki/nssdb
|
|
echo "✅ CA added to browser trust store"
|
|
|
|
mkdir -p /workspace/.agent-browser-screenshots
|
|
|
|
echo "✅ Container ready"
|
|
|
|
cd /workspace
|
|
exec "$@"
|