diff --git a/.cursor/rules/strix-project.mdc b/.cursor/rules/strix-project.mdc deleted file mode 100644 index ab9b84b..0000000 --- a/.cursor/rules/strix-project.mdc +++ /dev/null @@ -1,126 +0,0 @@ ---- -description: -globs: -alwaysApply: true ---- -# Strix Cybersecurity Agent - Project Rules - -## Project Overview - -### Goal and Purpose -Strix is a sophisticated cybersecurity agent specialized in vulnerability scanning and security assessment. It provides: -- Automated cybersecurity scans and assessments -- Web application security testing -- Infrastructure vulnerability analysis -- Comprehensive security reporting -- RESTful API for scan management -- CLI interface for direct usage - -The project implements an AI-powered ReAct (Reasoning and Acting) framework for autonomous security testing. - -## Project Structure - -### High-Level Architecture -``` -strix-agent/ -├── strix/ # Core application package -│ ├── agents/ # AI agent implementations -│ ├── api/ # FastAPI web service -│ ├── cli/ # Command-line interface -│ ├── llm/ # Language model configurations -│ └── tools/ # Security testing tools -├── tests/ # Test suite -├── evaluation/ # Evaluation framework -├── containers/ # Docker configuration -└── docs/ # Documentation -``` - -### Low-Level Structure - -#### Core Components -- **[strix/agents/StrixAgent/strix_agent.py](mdc:strix/agents/StrixAgent/strix_agent.py)** - Main cybersecurity agent -- **[strix/agents/base_agent.py](mdc:strix/agents/base_agent.py)** - Base agent framework -- **[strix/api/main.py](mdc:strix/api/main.py)** - FastAPI application entry point -- **[strix/cli/main.py](mdc:strix/cli/main.py)** - CLI entry point -- **[pyproject.toml](mdc:pyproject.toml)** - Project configuration and dependencies - -#### API Structure -- **[strix/api/routers/](mdc:strix/api/routers)** - API endpoint definitions -- **[strix/api/models/](mdc:strix/api/models)** - Pydantic data models -- **[strix/api/services/](mdc:strix/api/services)** - Business logic services - -#### Security Tools -- **[strix/tools/browser/](mdc:strix/tools/browser)** - Web browser automation -- **[strix/tools/terminal/](mdc:strix/tools/terminal)** - Terminal command execution -- **[strix/tools/python/](mdc:strix/tools/python)** - Python code execution -- **[strix/tools/web_search/](mdc:strix/tools/web_search)** - Web reconnaissance -- **[strix/tools/reporting/](mdc:strix/tools/reporting)** - Security report generation - -## Development Guidelines - -### Code Standards -- **Simplicity**: Write simple, clean, and modular code -- **Functionality**: Prefer functional programming patterns where appropriate -- **Efficiency**: Optimize for performance without premature optimization -- **No Bloat**: Avoid unnecessary complexity or over-engineering -- **Minimal Comments**: Code should be self-documenting; use comments sparingly for complex business logic only - -### Code Quality Requirements -- All code MUST pass `make pre-commit` checks -- All code MUST pass Ruff linting without warnings -- All code MUST pass MyPy type checking without errors -- Type hints are required for all function signatures -- Follow the strict configuration in [pyproject.toml](mdc:pyproject.toml) - -### Execution Environment -- **ALWAYS** use `poetry run` for executing Python scripts and commands -- **NEVER** run Python directly with `python` command -- Use `poetry run strix-agent` for CLI operations -- Use `poetry run uvicorn strix.api.main:app` for API server - -### File Management Rules -- **DO NOT** create or edit README.md or any .md documentation files unless explicitly requested -- Focus on code implementation, not documentation -- Keep docstrings concise and functional - -### Testing and Quality Assurance -- Run `make pre-commit` before any commits -- Ensure all tests pass with `poetry run pytest` -- Use `poetry run mypy .` for type checking -- Use `poetry run ruff check .` for linting - -### Dependencies -- All dependencies managed through [pyproject.toml](mdc:pyproject.toml) -- Use Poetry for dependency management -- Pin versions for production dependencies -- Keep dev dependencies in separate group - -### Configuration -- Application settings in [strix/api/core/config.py](mdc:strix/api/core/config.py) -- LLM configuration in [strix/llm/config.py](mdc:strix/llm/config.py) -- Agent system prompts in [strix/agents/StrixAgent/system_prompt.jinja](mdc:strix/agents/StrixAgent/system_prompt.jinja) - -## Key Implementation Patterns - -### Agent Framework -- Inherit from BaseAgent for new agent implementations -- Use ReAct pattern for reasoning and action loops -- Implement tools through the registry system in [strix/tools/registry.py](mdc:strix/tools/registry.py) - -### API Development -- Use FastAPI with Pydantic models -- Implement proper error handling and validation -- Follow REST conventions for endpoints -- Use Beanie ODM for MongoDB operations - -### Security Tools -- Implement tools as action classes with clear interfaces -- Use async/await for I/O operations -- Implement proper cleanup and resource management -- Follow principle of least privilege - -### Error Handling -- Use structured exception handling -- Provide meaningful error messages -- Log errors appropriately without exposing sensitive information -- Implement graceful degradation where possible diff --git a/strix/agents/StrixAgent/system_prompt.jinja b/strix/agents/StrixAgent/system_prompt.jinja index 8aeace4..7d1681c 100644 --- a/strix/agents/StrixAgent/system_prompt.jinja +++ b/strix/agents/StrixAgent/system_prompt.jinja @@ -21,8 +21,6 @@ INTER-AGENT MESSAGES: USER INTERACTION: - Work autonomously by default -- BRIEFLY update user about current state in ONE SENTENCE and don't be repetitive/redundant (e.g., "Scanning port 443 for SSL vulnerabilities..." or "Found SQLi in login form, validating...") -- Keep updates concise and informative - no lengthy explanations - NEVER be redundant or repeat information - say it once and move on - If you need user input, IMMEDIATELY call wait_for_message tool - Never ask questions without calling wait_for_message in the same response diff --git a/strix/runtime/docker_runtime.py b/strix/runtime/docker_runtime.py index 4b514ec..eef88fc 100644 --- a/strix/runtime/docker_runtime.py +++ b/strix/runtime/docker_runtime.py @@ -1,3 +1,4 @@ +import contextlib import logging import os import secrets @@ -78,11 +79,24 @@ class DockerRuntime(AbstractRuntime): def _create_container_with_retry(self, scan_id: str, max_retries: int = 3) -> Container: last_exception = None + container_name = f"strix-scan-{scan_id}" for attempt in range(max_retries): try: self._verify_image_available(STRIX_IMAGE) + try: + existing_container = self.client.containers.get(container_name) + logger.warning(f"Container {container_name} already exists, removing it") + with contextlib.suppress(Exception): + existing_container.stop(timeout=5) + existing_container.remove(force=True) + time.sleep(1) + except NotFound: + pass + except DockerException as e: + logger.warning(f"Error checking/removing existing container: {e}") + caido_port = self._find_available_port() tool_server_port = self._find_available_port() tool_server_token = self._generate_sandbox_token() @@ -94,7 +108,7 @@ class DockerRuntime(AbstractRuntime): STRIX_IMAGE, command="sleep infinity", detach=True, - name=f"strix-scan-{scan_id}", + name=container_name, hostname=f"strix-scan-{scan_id}", ports={ f"{caido_port}/tcp": caido_port, @@ -137,7 +151,9 @@ class DockerRuntime(AbstractRuntime): f"Failed to create Docker container after {max_retries} attempts: {last_exception}" ) from last_exception - def _get_or_create_scan_container(self, scan_id: str) -> Container: + def _get_or_create_scan_container(self, scan_id: str) -> Container: # noqa: PLR0912 + container_name = f"strix-scan-{scan_id}" + if self._scan_container: try: self._scan_container.reload() @@ -149,7 +165,43 @@ class DockerRuntime(AbstractRuntime): self._tool_server_token = None try: - containers = self.client.containers.list(filters={"label": f"strix-scan-id={scan_id}"}) + container = self.client.containers.get(container_name) + container.reload() + + if ( + "strix-scan-id" not in container.labels + or container.labels["strix-scan-id"] != scan_id + ): + logger.warning( + f"Container {container_name} exists but missing/wrong label, updating" + ) + + if container.status != "running": + logger.info(f"Starting existing container {container_name}") + container.start() + time.sleep(2) + + self._scan_container = container + + for env_var in container.attrs["Config"]["Env"]: + if env_var.startswith("TOOL_SERVER_PORT="): + self._tool_server_port = int(env_var.split("=")[1]) + elif env_var.startswith("TOOL_SERVER_TOKEN="): + self._tool_server_token = env_var.split("=")[1] + + logger.info(f"Reusing existing container {container_name}") + + except NotFound: + pass + except DockerException as e: + logger.warning(f"Failed to get container by name {container_name}: {e}") + else: + return container + + try: + containers = self.client.containers.list( + all=True, filters={"label": f"strix-scan-id={scan_id}"} + ) if containers: container = cast("Container", containers[0]) if container.status != "running": @@ -163,9 +215,10 @@ class DockerRuntime(AbstractRuntime): elif env_var.startswith("TOOL_SERVER_TOKEN="): self._tool_server_token = env_var.split("=")[1] + logger.info(f"Found existing container by label for scan {scan_id}") return container except DockerException as e: - logger.warning("Failed to find existing container for scan %s: %s", scan_id, e) + logger.warning("Failed to find existing container by label for scan %s: %s", scan_id, e) logger.info("Creating new Docker container for scan %s", scan_id) return self._create_container_with_retry(scan_id)