5449af2456
Drop our 797-LoC manual GraphQL ``ProxyManager`` and the in-container
sandbox dispatch. Caido goes host-side via the official async Python
SDK. The Caido CLI still runs as a sidecar in the container — only the
control-plane moves.
Bootstrap moves host-side:
- New ``strix/sandbox/caido_bootstrap.py``: ``loginAsGuest`` via
aiohttp (5 retries), then ``client.project.create(temporary=True)``
+ ``client.project.select(...)``, then return the connected
``caido_sdk_client.Client``. Drop the equivalent bash from
``docker-entrypoint.sh`` (~60 lines of curl + jq).
- ``entry.py`` calls ``bootstrap_caido_client`` after the
``wait_for_tcp_ready`` healthcheck, stashes the client in the bundle
and threads it through ``make_agent_context(caido_client=...)``.
``agents_graph.create_agent`` propagates the same client to children.
- ``session_manager.cleanup`` ``await``s ``client.aclose()`` before
tearing down the container.
- Drop ``CAIDO_PORT`` from the manifest env (only the in-container
ProxyManager read it) and ``CAIDO_API_TOKEN`` from the entrypoint's
``/etc/profile.d/proxy.sh`` + ``/etc/environment`` heredocs.
Tools (``strix/tools/proxy/tools.py``):
- ``list_requests`` → ``client.request.list().filter().first().after()``
with ascending/descending order. **Pagination changes from
start_page/end_page (1-indexed) to first/after cursors** matching the
SDK's native shape; response includes ``page_info.end_cursor`` for
the model to thread.
- ``view_request`` → ``client.request.get(id, RequestGetOptions(...))``;
decode raw bytes locally; existing regex-search and line-pagination
modes preserved.
- ``send_request`` → synthesize raw HTTP bytes, parse URL into
``ConnectionInfoInput(host, port, is_tls)``, create a replay session
via ``client.replay.sessions.create(CreateReplaySessionFromRaw(...))``,
then ``client.replay.send(session_id, ReplaySendOptions(...))``.
- ``repeat_request`` → ``client.request.get(id, request_raw=True)`` →
port the existing parse/_apply_modifications/build helpers verbatim →
send via the same replay flow as ``send_request``.
- ``scope_rules`` → direct mapping to ``client.scope.{list, get, create,
update, delete}``.
- **Drop ``list_sitemap`` + ``view_sitemap_entry``** — the official SDK
has no sitemap module. The model uses HTTPQL filters
(``req.host.eq:"X" AND req.path.cont:"/api/"``) for the same
drill-down workflow.
Deletions:
- ``strix/tools/proxy/proxy_manager.py`` (797 LoC)
- ``strix/tools/proxy/proxy_actions.py`` (113 LoC)
- The 6-line proxy_actions pre-import in ``python_instance.py``
(broken once proxy_actions is gone; that file is queued for deletion
in commit 2 anyway).
Deps:
- Add ``caido-sdk-client>=0.2.0`` and ``aiohttp>=3.10.0`` to runtime
``[project] dependencies``.
- Drop ``gql[requests]>=3.5.3`` from ``[project.optional-dependencies]
sandbox`` — only the in-container ProxyManager used the sync transport
variant; the SDK pulls in ``gql[aiohttp]`` transitively for us.
- ``[[tool.mypy.overrides]]``: add ``caido_sdk_client.*`` and
``aiohttp.*`` to the missing-imports list with
``disable_error_code=["import-untyped"]`` (neither ships ``py.typed``).
- ``[tool.ruff.lint.per-file-ignores]``: bump the proxy/tools.py
ignore to also include ``PLR0911`` (the scope_rules action dispatcher
has many short-circuit returns).
ruff drops from 21 → 12 errors; mypy moves from 82 → 84 (the +2 are in
already-flaky files unrelated to this change). All touched files mypy
clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
123 lines
3.6 KiB
Bash
123 lines
3.6 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-cli --listen 0.0.0.0:${CAIDO_PORT} \
|
|
--allow-guests \
|
|
--no-logging \
|
|
--no-open \
|
|
--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 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}
|
|
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
|
|
|
|
echo "source /etc/profile.d/proxy.sh" >> ~/.bashrc
|
|
echo "source /etc/profile.d/proxy.sh" >> ~/.zshrc
|
|
|
|
source /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"
|
|
|
|
echo "Starting tool server..."
|
|
cd /app
|
|
export PYTHONPATH=/app
|
|
export STRIX_SANDBOX_MODE=true
|
|
export TOOL_SERVER_TIMEOUT="${STRIX_SANDBOX_EXECUTION_TIMEOUT:-120}"
|
|
TOOL_SERVER_LOG="/tmp/tool_server.log"
|
|
|
|
sudo -E -u pentester \
|
|
/app/.venv/bin/python -m strix.runtime.tool_server \
|
|
--token="$TOOL_SERVER_TOKEN" \
|
|
--host=0.0.0.0 \
|
|
--port="$TOOL_SERVER_PORT" \
|
|
--timeout="$TOOL_SERVER_TIMEOUT" > "$TOOL_SERVER_LOG" 2>&1 &
|
|
|
|
for i in {1..10}; do
|
|
if curl -s "http://127.0.0.1:$TOOL_SERVER_PORT/health" | grep -q '"status":"healthy"'; then
|
|
echo "✅ Tool server healthy on port $TOOL_SERVER_PORT"
|
|
break
|
|
fi
|
|
if [ $i -eq 10 ]; then
|
|
echo "ERROR: Tool server failed to become healthy"
|
|
echo "=== Tool server log ==="
|
|
cat "$TOOL_SERVER_LOG" 2>/dev/null || echo "(no log)"
|
|
exit 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
echo "✅ Container ready"
|
|
|
|
cd /workspace
|
|
exec "$@"
|