perf: bootstrap Caido concurrently with the scan start (#1143)

Co-authored-by: Ahmed Allam <ahmed39652003@gmail.com>
This commit is contained in:
devin-ai-integration[bot]
2026-08-21 12:09:59 -07:00
committed by GitHub
co-authored by Ahmed Allam
parent 1ce43d1b94
commit 1c499c5b2d
7 changed files with 306 additions and 21 deletions
+5 -3
View File
@@ -96,15 +96,17 @@ async def bootstrap_caido(
access_token = await _login_as_guest(session, container_url=container_url)
client = Client(host_url, auth=TokenAuthOptions(token=access_token))
await client.connect()
try:
# connect() is inside the guard as well: a cancellation there (scan
# teardown while the bootstrap is still in flight) would otherwise
# leave the half-connected transport behind.
await client.connect()
project = await client.project.create(
CreateProjectOptions(name="sandbox", temporary=True),
)
await client.project.select(project.id)
except BaseException:
# The connected client never reaches the session bundle if project
# The client never reaches the session bundle if connect or project
# setup fails, so close it here to avoid leaking the transport.
with contextlib.suppress(Exception):
await client.aclose()
+60
View File
@@ -0,0 +1,60 @@
"""Handle for a Caido bootstrap running concurrently with the scan start.
The Caido sidecar login + project setup costs a couple of seconds of
guest-side polling, and nothing needs the client until the first proxy
tool call (or the first traffic poll). :class:`CaidoBootstrapHandle`
wraps the in-flight bootstrap task so session bring-up can return as
soon as the container is up; consumers resolve the client at first use.
"""
from __future__ import annotations
import asyncio
import contextlib
import logging
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from caido_sdk_client import Client
logger = logging.getLogger(__name__)
class CaidoBootstrapHandle:
"""Resolves to the connected Caido client once the bootstrap finishes.
A failed bootstrap is surfaced (once) to every ``get()`` caller as the
original exception; proxy tools degrade to their "client unavailable"
result instead of the failure killing the scan at bring-up.
"""
def __init__(self, task: asyncio.Task[Client]) -> None:
self._task = task
async def get(self) -> Client:
"""Wait for the bootstrap and return the client.
Shielded so one caller's cancellation (e.g. a tool timeout) does not
cancel the shared bootstrap for everyone else.
"""
return await asyncio.shield(self._task)
def peek(self) -> Client | None:
"""Return the client if the bootstrap already finished cleanly."""
if self._task.done() and not self._task.cancelled() and self._task.exception() is None:
return self._task.result()
return None
async def aclose(self) -> None:
"""Cancel an in-flight bootstrap or close the finished client."""
if not self._task.done():
self._task.cancel()
with contextlib.suppress(asyncio.CancelledError, Exception):
await self._task
return
client = self.peek()
if client is not None:
with contextlib.suppress(Exception):
await client.aclose()
+15 -4
View File
@@ -2,6 +2,7 @@
from __future__ import annotations
import asyncio
import logging
import os
import sys
@@ -15,6 +16,7 @@ from strix.config import load_settings
from strix.core.paths import run_dir_for, runtime_state_dir
from strix.runtime.backends import backend_supports_bind_mounts, get_backend
from strix.runtime.caido_bootstrap import bootstrap_caido
from strix.runtime.caido_handle import CaidoBootstrapHandle
if TYPE_CHECKING:
@@ -333,10 +335,19 @@ async def create_or_reuse(
host_caido_url = f"{scheme}://{caido_endpoint.host}:{caido_endpoint.port}"
logger.debug("Caido host endpoint resolved: %s", host_caido_url)
caido_client = await bootstrap_caido(
session,
host_url=host_caido_url,
container_url=container_caido_url,
# The Caido login + project setup polls the guest for a couple of seconds
# and nothing needs the client before the first proxy tool call, so it
# runs concurrently with the rest of scan start; consumers resolve the
# handle at first use (see CaidoBootstrapHandle).
caido_client = CaidoBootstrapHandle(
asyncio.create_task(
bootstrap_caido(
session,
host_url=host_caido_url,
container_url=container_caido_url,
),
name=f"caido-bootstrap-{scan_id}",
)
)
bundle = {