Files
strix/strix/skills/tooling/python.md
T
e4548cb28c fix(proxy,tooling): serialize+reconnect Caido client, actionable HTTPQL errors, sandbox tool guidance (#794)
* 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>
2026-07-17 13:31:57 -04:00

4.0 KiB

name, description
name description
python Run Python through exec_command in the SDK sandbox. Use the image-baked caido_api module for Caido proxy automation from Python scripts.

Python In The Sandbox

Use exec_command for Python. There is no separate Strix Python executor.

Prefer writing reusable scripts to a .py file and running them with python3 <name>.py. For short one-off transformations, python3 -c or a small here-document is fine.

The shell parameter on exec_command is for swapping POSIX shells (bash/zsh/sh), not for picking interpreters. Put the interpreter invocation in cmd instead: cmd="python3 -c '...'", not shell=python3, cmd="...". The shell=<interpreter> shortcut breaks in subtle ways — python3 works only with login=False (because the SDK adds -l/-i), and other interpreters (node, ruby, perl) take -e not -c so they fail even with login=False.

Proxy Automation From Python

The sandbox image includes an installed caido_api module. Import it explicitly when Python code needs Caido traffic or replay access:

from caido_api import (
    list_requests,
    list_sitemap,
    repeat_request,
    scope_rules,
    view_request,
    view_sitemap_entry,
)

All helpers are async. Use them inside asyncio.run(...) or an async function:

import asyncio

from caido_api import list_requests, view_request


async def main():
    posts = await list_requests(
        httpql_filter='req.method.eq:"POST" AND req.path.cont:"/api/"',
        first=50,
    )
    candidates = []
    for edge in posts.edges:
        request_id = edge.node.request.id
        body = await view_request(request_id, part="request")
        raw = body.request.raw.decode("utf-8", errors="replace")
        if "id=" in raw or "user=" in raw:
            candidates.append(request_id)

    print(f"{len(candidates)} candidates")
    print(candidates[:10])


asyncio.run(main())

Available helpers:

  • list_requests(httpql_filter=, first=50, after=, sort_by=, sort_order=, scope_id=) returns a cursor-paginated Caido SDK Connection.
  • view_request(request_id, part="request") returns a Caido SDK request object with raw request/response bytes.
  • repeat_request(request_id, modifications={...}) replays a captured request after modifying url, params, headers, body, or cookies.
  • list_sitemap(scope_id=, parent_id=, depth="DIRECT", page=1) walks Caido's request-tree view of the discovered surface. Omit parent_id for root domains; pass an entry id with depth="DIRECT" or "ALL" to drill in.
  • view_sitemap_entry(entry_id) returns one entry plus its 30 most recent related requests.
  • scope_rules(action, allowlist=, denylist=, scope_id=, scope_name=) manages Caido scopes.

For one-off arbitrary requests (e.g. probing a fresh endpoint, hitting an external API), use exec_command with curl / httpx / requests. The sandbox's HTTP_PROXY env routes all such traffic through Caido automatically, so it shows up in list_requests and you can use repeat_request to replay-and-modify any of it.

Workflow

For iterative exploit work, put code in a file:

1. Create or edit a task-unique script (e.g. `poc_<task-id>.py`, so it can't
   clobber a project file or another agent's script) with `apply_patch`.
2. Run it with `exec_command`: `python3 poc_<task-id>.py`.
3. Edit and rerun until the proof-of-concept is reliable.

Installing extra packages

The sandbox's Python lives in /app/.venv, and it is the active virtualenv (python3 / pip already resolve to it). The following common libraries are pre-installed — import them directly, no install step needed: requests, httpx, beautifulsoup4 (bs4), lxml, pyjwt (jwt), cryptography.

To add a one-off dependency for an exploit script, use uv (already in the image and much faster than pip):

uv pip install --python /app/.venv/bin/python <package>

Plain pip install <package> also works because the venv is active. Install before you import, so scripts don't fail with ModuleNotFoundError.