diff --git a/containers/docker-entrypoint.sh b/containers/docker-entrypoint.sh index 8d6fc58..cbef471 100644 --- a/containers/docker-entrypoint.sh +++ b/containers/docker-entrypoint.sh @@ -9,7 +9,7 @@ if [ ! -f /app/certs/ca.p12 ]; then exit 1 fi -caido-cli --listen 127.0.0.1:${CAIDO_PORT} \ +caido-cli --listen 0.0.0.0:${CAIDO_PORT} \ --allow-guests \ --no-logging \ --no-open \ diff --git a/docs/advanced/configuration.mdx b/docs/advanced/configuration.mdx index d82f822..9c94630 100644 --- a/docs/advanced/configuration.mdx +++ b/docs/advanced/configuration.mdx @@ -51,7 +51,7 @@ Configure Strix using environment variables or a config file. ## Docker Configuration - + Docker image to use for the sandbox container. diff --git a/scripts/install.sh b/scripts/install.sh index 67a0e19..868a95e 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -4,7 +4,7 @@ set -euo pipefail APP=strix REPO="usestrix/strix" -STRIX_IMAGE="ghcr.io/usestrix/strix-sandbox:0.1.11" +STRIX_IMAGE="ghcr.io/usestrix/strix-sandbox:0.1.12" MUTED='\033[0;2m' RED='\033[0;31m' diff --git a/strix/agents/base_agent.py b/strix/agents/base_agent.py index f955892..99d0332 100644 --- a/strix/agents/base_agent.py +++ b/strix/agents/base_agent.py @@ -333,6 +333,14 @@ class BaseAgent(metaclass=AgentMeta): if "agent_id" in sandbox_info: self.state.sandbox_info["agent_id"] = sandbox_info["agent_id"] + + caido_port = sandbox_info.get("caido_port") + if caido_port: + from strix.telemetry.tracer import get_global_tracer + + tracer = get_global_tracer() + if tracer: + tracer.caido_url = f"localhost:{caido_port}" except Exception as e: from strix.telemetry import posthog diff --git a/strix/config/config.py b/strix/config/config.py index f8836b2..7578b61 100644 --- a/strix/config/config.py +++ b/strix/config/config.py @@ -40,7 +40,7 @@ class Config: strix_disable_browser = "false" # Runtime Configuration - strix_image = "ghcr.io/usestrix/strix-sandbox:0.1.11" + strix_image = "ghcr.io/usestrix/strix-sandbox:0.1.12" strix_runtime_backend = "docker" strix_sandbox_execution_timeout = "120" strix_sandbox_connect_timeout = "10" diff --git a/strix/interface/assets/tui_styles.tcss b/strix/interface/assets/tui_styles.tcss index 7ebefd2..d1097de 100644 --- a/strix/interface/assets/tui_styles.tcss +++ b/strix/interface/assets/tui_styles.tcss @@ -77,12 +77,21 @@ Toast.-information .toast--title { margin-bottom: 0; } -#stats_display { +#stats_scroll { height: auto; max-height: 15; background: transparent; padding: 0; margin: 0; + border: round #333333; + scrollbar-size: 0 0; +} + +#stats_display { + height: auto; + background: transparent; + padding: 0 1; + margin: 0; } #vulnerabilities_panel { diff --git a/strix/interface/main.py b/strix/interface/main.py index 85c0427..33785e6 100644 --- a/strix/interface/main.py +++ b/strix/interface/main.py @@ -462,7 +462,7 @@ def display_completion_message(args: argparse.Namespace, results_path: Path) -> console.print("\n") console.print(panel) console.print() - console.print("[#60a5fa]models.strix.ai[/]") + console.print("[#60a5fa]models.strix.ai[/] [dim]·[/] [#60a5fa]discord.gg/strix-ai[/]") console.print() diff --git a/strix/interface/tui.py b/strix/interface/tui.py index eeaa2ea..1a62255 100644 --- a/strix/interface/tui.py +++ b/strix/interface/tui.py @@ -829,11 +829,11 @@ class StrixTUIApp(App): # type: ignore[misc] agents_tree.guide_style = "dashed" stats_display = Static("", id="stats_display") - stats_display.ALLOW_SELECT = False + stats_scroll = VerticalScroll(stats_display, id="stats_scroll") vulnerabilities_panel = VulnerabilitiesPanel(id="vulnerabilities_panel") - sidebar = Vertical(agents_tree, vulnerabilities_panel, stats_display, id="sidebar") + sidebar = Vertical(agents_tree, vulnerabilities_panel, stats_scroll, id="sidebar") content_container.mount(chat_area_container) content_container.mount(sidebar) @@ -1272,6 +1272,9 @@ class StrixTUIApp(App): # type: ignore[misc] if not self._is_widget_safe(stats_display): return + if self.screen.selections: + return + stats_content = Text() stats_text = build_tui_stats_text(self.tracer, self.agent_config) @@ -1281,15 +1284,7 @@ class StrixTUIApp(App): # type: ignore[misc] version = get_package_version() stats_content.append(f"\nv{version}", style="white") - from rich.panel import Panel - - stats_panel = Panel( - stats_content, - border_style="#333333", - padding=(0, 1), - ) - - self._safe_widget_operation(stats_display.update, stats_panel) + self._safe_widget_operation(stats_display.update, stats_content) def _update_vulnerabilities_panel(self) -> None: """Update the vulnerabilities panel with current vulnerability data.""" diff --git a/strix/interface/utils.py b/strix/interface/utils.py index fe5bdfc..5b9e52b 100644 --- a/strix/interface/utils.py +++ b/strix/interface/utils.py @@ -390,6 +390,12 @@ def build_tui_stats_text(tracer: Any, agent_config: dict[str, Any] | None = None stats_text.append(" · ", style="white") stats_text.append(f"${total_stats['cost']:.2f}", style="white") + caido_url = getattr(tracer, "caido_url", None) + if caido_url: + stats_text.append("\n") + stats_text.append("Caido: ", style="bold white") + stats_text.append(caido_url, style="white") + return stats_text diff --git a/strix/runtime/docker_runtime.py b/strix/runtime/docker_runtime.py index b783dcc..d57d358 100644 --- a/strix/runtime/docker_runtime.py +++ b/strix/runtime/docker_runtime.py @@ -22,6 +22,7 @@ from .runtime import AbstractRuntime, SandboxInfo HOST_GATEWAY_HOSTNAME = "host.docker.internal" DOCKER_TIMEOUT = 60 CONTAINER_TOOL_SERVER_PORT = 48081 +CONTAINER_CAIDO_PORT = 48080 class DockerRuntime(AbstractRuntime): @@ -37,6 +38,7 @@ class DockerRuntime(AbstractRuntime): self._scan_container: Container | None = None self._tool_server_port: int | None = None self._tool_server_token: str | None = None + self._caido_port: int | None = None def _find_available_port(self) -> int: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: @@ -78,6 +80,10 @@ class DockerRuntime(AbstractRuntime): if port_bindings.get(port_key): self._tool_server_port = int(port_bindings[port_key][0]["HostPort"]) + caido_port_key = f"{CONTAINER_CAIDO_PORT}/tcp" + if port_bindings.get(caido_port_key): + self._caido_port = int(port_bindings[caido_port_key][0]["HostPort"]) + def _wait_for_tool_server(self, max_retries: int = 30, timeout: int = 5) -> None: host = self._resolve_docker_host() health_url = f"http://{host}:{self._tool_server_port}/health" @@ -121,6 +127,7 @@ class DockerRuntime(AbstractRuntime): time.sleep(1) self._tool_server_port = self._find_available_port() + self._caido_port = self._find_available_port() self._tool_server_token = secrets.token_urlsafe(32) execution_timeout = Config.get("strix_sandbox_execution_timeout") or "120" @@ -130,7 +137,10 @@ class DockerRuntime(AbstractRuntime): detach=True, name=container_name, hostname=container_name, - ports={f"{CONTAINER_TOOL_SERVER_PORT}/tcp": self._tool_server_port}, + ports={ + f"{CONTAINER_TOOL_SERVER_PORT}/tcp": self._tool_server_port, + f"{CONTAINER_CAIDO_PORT}/tcp": self._caido_port, + }, cap_add=["NET_ADMIN", "NET_RAW"], labels={"strix-scan-id": scan_id}, environment={ @@ -152,6 +162,7 @@ class DockerRuntime(AbstractRuntime): if attempt < max_retries: self._tool_server_port = None self._tool_server_token = None + self._caido_port = None time.sleep(2**attempt) else: return container @@ -173,6 +184,7 @@ class DockerRuntime(AbstractRuntime): self._scan_container = None self._tool_server_port = None self._tool_server_token = None + self._caido_port = None try: container = self.client.containers.get(container_name) @@ -260,7 +272,7 @@ class DockerRuntime(AbstractRuntime): raise RuntimeError("Docker container ID is unexpectedly None") token = existing_token or self._tool_server_token - if self._tool_server_port is None or token is None: + if self._tool_server_port is None or self._caido_port is None or token is None: raise RuntimeError("Tool server not initialized") host = self._resolve_docker_host() @@ -273,6 +285,7 @@ class DockerRuntime(AbstractRuntime): "api_url": api_url, "auth_token": token, "tool_server_port": self._tool_server_port, + "caido_port": self._caido_port, "agent_id": agent_id, } @@ -314,6 +327,7 @@ class DockerRuntime(AbstractRuntime): self._scan_container = None self._tool_server_port = None self._tool_server_token = None + self._caido_port = None except (NotFound, DockerException): pass @@ -323,6 +337,7 @@ class DockerRuntime(AbstractRuntime): self._scan_container = None self._tool_server_port = None self._tool_server_token = None + self._caido_port = None if container_name is None: return diff --git a/strix/runtime/runtime.py b/strix/runtime/runtime.py index e33c08d..e523d51 100644 --- a/strix/runtime/runtime.py +++ b/strix/runtime/runtime.py @@ -7,6 +7,7 @@ class SandboxInfo(TypedDict): api_url: str auth_token: str | None tool_server_port: int + caido_port: int agent_id: str diff --git a/strix/telemetry/tracer.py b/strix/telemetry/tracer.py index 8bbb412..ef97ab6 100644 --- a/strix/telemetry/tracer.py +++ b/strix/telemetry/tracer.py @@ -56,6 +56,7 @@ class Tracer: self._next_message_id = 1 self._saved_vuln_ids: set[str] = set() + self.caido_url: str | None = None self.vulnerability_found_callback: Callable[[dict[str, Any]], None] | None = None def set_run_name(self, run_name: str) -> None: