Fix docker container creation issue

This commit is contained in:
Ahmed Allam
2025-09-09 00:02:39 -07:00
parent 138c5a9023
commit 500b987ed4
3 changed files with 57 additions and 132 deletions

View File

@@ -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

View File

@@ -21,8 +21,6 @@ INTER-AGENT MESSAGES:
USER INTERACTION: USER INTERACTION:
- Work autonomously by default - 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 - NEVER be redundant or repeat information - say it once and move on
- If you need user input, IMMEDIATELY call wait_for_message tool - If you need user input, IMMEDIATELY call wait_for_message tool
- Never ask questions without calling wait_for_message in the same response - Never ask questions without calling wait_for_message in the same response

View File

@@ -1,3 +1,4 @@
import contextlib
import logging import logging
import os import os
import secrets import secrets
@@ -78,11 +79,24 @@ class DockerRuntime(AbstractRuntime):
def _create_container_with_retry(self, scan_id: str, max_retries: int = 3) -> Container: def _create_container_with_retry(self, scan_id: str, max_retries: int = 3) -> Container:
last_exception = None last_exception = None
container_name = f"strix-scan-{scan_id}"
for attempt in range(max_retries): for attempt in range(max_retries):
try: try:
self._verify_image_available(STRIX_IMAGE) 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() caido_port = self._find_available_port()
tool_server_port = self._find_available_port() tool_server_port = self._find_available_port()
tool_server_token = self._generate_sandbox_token() tool_server_token = self._generate_sandbox_token()
@@ -94,7 +108,7 @@ class DockerRuntime(AbstractRuntime):
STRIX_IMAGE, STRIX_IMAGE,
command="sleep infinity", command="sleep infinity",
detach=True, detach=True,
name=f"strix-scan-{scan_id}", name=container_name,
hostname=f"strix-scan-{scan_id}", hostname=f"strix-scan-{scan_id}",
ports={ ports={
f"{caido_port}/tcp": caido_port, 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}" f"Failed to create Docker container after {max_retries} attempts: {last_exception}"
) from 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: if self._scan_container:
try: try:
self._scan_container.reload() self._scan_container.reload()
@@ -149,7 +165,43 @@ class DockerRuntime(AbstractRuntime):
self._tool_server_token = None self._tool_server_token = None
try: 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: if containers:
container = cast("Container", containers[0]) container = cast("Container", containers[0])
if container.status != "running": if container.status != "running":
@@ -163,9 +215,10 @@ class DockerRuntime(AbstractRuntime):
elif env_var.startswith("TOOL_SERVER_TOKEN="): elif env_var.startswith("TOOL_SERVER_TOKEN="):
self._tool_server_token = env_var.split("=")[1] self._tool_server_token = env_var.split("=")[1]
logger.info(f"Found existing container by label for scan {scan_id}")
return container return container
except DockerException as e: 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) logger.info("Creating new Docker container for scan %s", scan_id)
return self._create_container_with_retry(scan_id) return self._create_container_with_retry(scan_id)