Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ DOCKER_READ_ONLY=true
MAX_EXECUTION_TIME=120
MAX_MEMORY_MB=512
MAX_CPUS=1
MAX_CPU_QUOTA=50000 #Deprecated
MAX_PIDS=512
MAX_OPEN_FILES=1024

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v6
with:
python-version: "3.11"
cache: "pip"
Expand Down
2 changes: 1 addition & 1 deletion docker/c-cpp.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1.4
# C/C++ execution environment with BuildKit optimizations
# Pin to specific version for reproducibility
FROM gcc:13-bookworm
FROM gcc:15-bookworm

# Install essential development tools and libraries
RUN apt-get update && apt-get install -y --no-install-recommends \
Expand Down
4 changes: 2 additions & 2 deletions docker/go.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1.4
# Go execution environment with BuildKit optimizations
FROM golang:1.23-alpine
FROM golang:1.25-alpine

# Install common tools
RUN apk add --no-cache \
Expand Down Expand Up @@ -37,5 +37,5 @@ ENV GO111MODULE=on \
GOSUMDB=sum.golang.org

# Default command with sanitized environment
ENTRYPOINT ["/usr/bin/env","-i","PATH=/usr/local/go/bin:/usr/local/bin:/usr/bin:/bin","HOME=/tmp","TMPDIR=/tmp","GO111MODULE=on","GOPROXY=https://proxy.golang.org,direct","GOSUMDB=sum.golang.org","GOCACHE=/mnt/data/go-build"]
ENTRYPOINT ["/usr/bin/env","-i","PATH=/usr/local/go/bin:/usr/local/bin:/usr/bin:/bin","HOME=/tmp","TMPDIR=/tmp","GO111MODULE=on","GOPROXY=https://proxy.golang.org,direct","GOSUMDB=sum.golang.org","GOCACHE=/tmp/go-build"]
CMD ["go"]
2 changes: 1 addition & 1 deletion docker/r.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1.4
# R execution environment with BuildKit optimizations
FROM r-base:4.3.0
FROM r-base:4.4.3

# Install system dependencies for R packages (including Cairo)
RUN apt-get update && apt-get install -y --no-install-recommends \
Expand Down
2 changes: 1 addition & 1 deletion docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ Docker is used for secure code execution in containers.
| -------------------- | ------- | ---------------------------------------------------------------- |
| `MAX_EXECUTION_TIME` | `30` | Maximum code execution time (seconds) |
| `MAX_MEMORY_MB` | `512` | Maximum memory per execution (MB) |
| `MAX_CPU_QUOTA` | `50000` | CPU quota (100000 = 1 CPU) |
| `MAX_CPUS` | `4.0` | Maximum CPU cores available to execution containers |
| `MAX_PIDS` | `512` | Per-container process limit (cgroup pids_limit, prevents fork bombs) |
| `MAX_OPEN_FILES` | `1024` | Maximum open files per container |

Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[tool.pytest.ini_options]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "session"
18 changes: 9 additions & 9 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
requests>=2.31.0,<3
# Core API framework
fastapi==0.127.1
uvicorn[standard]==0.30.6
uvicorn[standard]==0.40.0

# Data validation and serialization
pydantic==2.12.5
pydantic-settings==2.5.0
pydantic-settings==2.12.0

# HTTP client for external requests
httpx==0.27.2
httpx==0.28.1

# Redis for session management
redis==5.1.0
redis==7.1.0

# SQLite async support for metrics
aiosqlite>=0.19.0

# MinIO/S3 client
minio==7.2.15
minio==7.2.20

# Docker client for container management
docker==7.1.0
Expand All @@ -29,7 +29,7 @@ python-dateutil==2.9.0.post0

# Testing framework
pytest==9.0.2
pytest-asyncio==0.21.1
pytest-asyncio==1.3.0
pytest-cov==4.1.0
pytest-mock==3.12.0

Expand All @@ -42,14 +42,14 @@ mypy==1.7.1
python-dotenv==1.0.0

# Logging
structlog==23.2.0
structlog==25.5.0

# File handling
python-multipart>=0.0.18
Unidecode==1.3.8
Unidecode==1.4.0

# System monitoring for performance tests
psutil==5.9.6

# Stress testing
locust==2.42.6
locust==2.43.0
8 changes: 8 additions & 0 deletions src/api/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,14 @@ async def upload_file(
detail=f"Too many files. Maximum {settings.max_files_per_session} files allowed",
)

# Check file type restrictions
for file in upload_files:
if not settings.is_file_allowed(file.filename or ""):
raise HTTPException(
status_code=415,
detail=f"File type not allowed: {file.filename}",
)

uploaded_files = []

# Create a session ID for this upload
Expand Down
90 changes: 56 additions & 34 deletions src/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,6 @@ class Settings(BaseSettings):
le=16.0,
description="Maximum CPU cores available to execution containers",
)
max_cpu_quota: int = Field(
default=50000, ge=10000, le=100000
) # Deprecated, use max_cpus
max_pids: int = Field(
default=512,
ge=64,
Expand Down Expand Up @@ -179,16 +176,6 @@ class Settings(BaseSettings):
container_pool_enabled: bool = Field(default=True)
container_pool_warmup_on_startup: bool = Field(default=True)

# Session Container Reuse - DEPRECATED
# These settings are no longer used. Containers are now stateless:
# - Each execution gets a fresh container from pool
# - Containers are destroyed immediately after execution
# Kept for backward compatibility with existing configs
session_container_reuse_enabled: bool = Field(default=False) # Deprecated, not used
session_container_ttl_seconds: int = Field(
default=0, ge=0, le=1800
) # Deprecated, not used

# Per-language pool sizes (0 = on-demand only, no pre-warming)
container_pool_py: int = Field(
default=5, ge=0, le=50, description="Python pool size"
Expand Down Expand Up @@ -365,7 +352,48 @@ class Settings(BaseSettings):
# Security Configuration
allowed_file_extensions: List[str] = Field(
default_factory=lambda: [
# Text and documentation
".txt",
".md",
".rtf",
".pdf",
# Microsoft Office
".doc",
".docx",
".dotx",
".xls",
".xlsx",
".xltx",
".ppt",
".pptx",
".potx",
".ppsx",
# OpenDocument formats
".odt",
".ods",
".odp",
".odg",
# Data formats
".json",
".csv",
".xml",
".yaml",
".yml",
".sql",
# Images
".png",
".jpg",
".jpeg",
".gif",
".svg",
".bmp",
".webp",
".ico",
# Web
".html",
".htm",
".css",
# Code files
".py",
".js",
".ts",
Expand All @@ -380,18 +408,24 @@ class Settings(BaseSettings):
".r",
".f90",
".d",
".json",
".csv",
".xml",
".yaml",
".yml",
".md",
".sql",
# Scripts and config
".sh",
".bat",
".ps1",
".dockerfile",
".makefile",
".ini",
".cfg",
".conf",
".log",
# Archives
".zip",
# Email and calendar
".eml",
".msg",
".mbox",
".ics",
".vcf",
]
)
blocked_file_patterns: List[str] = Field(
Expand Down Expand Up @@ -532,8 +566,6 @@ def security(self) -> SecurityConfig:
api_keys=self.api_keys if isinstance(self.api_keys, str) else None,
api_key_header=self.api_key_header,
api_key_cache_ttl=self.api_key_cache_ttl,
allowed_file_extensions=self.allowed_file_extensions,
blocked_file_patterns=self.blocked_file_patterns,
enable_network_isolation=self.enable_network_isolation,
enable_filesystem_isolation=self.enable_filesystem_isolation,
enable_security_logs=self.enable_security_logs,
Expand All @@ -546,7 +578,6 @@ def resources(self) -> ResourcesConfig:
max_execution_time=self.max_execution_time,
max_memory_mb=self.max_memory_mb,
max_cpus=self.max_cpus,
max_cpu_quota=self.max_cpu_quota,
max_pids=self.max_pids,
max_open_files=self.max_open_files,
max_file_size_mb=self.max_file_size_mb,
Expand Down Expand Up @@ -590,20 +621,11 @@ def validate_ssl_files(self) -> bool:

def get_redis_url(self) -> str:
"""Get Redis connection URL."""
if self.redis_url:
return self.redis_url
password_part = f":{self.redis_password}@" if self.redis_password else ""
return f"redis://{password_part}{self.redis_host}:{self.redis_port}/{self.redis_db}"
return self.redis.get_url()

def get_valid_api_keys(self) -> List[str]:
"""Get all valid API keys including the primary key."""
keys = [self.api_key]
if self.api_keys:
if isinstance(self.api_keys, list):
keys.extend(self.api_keys)
elif isinstance(self.api_keys, str):
keys.extend([k.strip() for k in self.api_keys.split(",") if k.strip()])
return list(set(keys))
return self.security.get_valid_api_keys()

def get_language_config(self, language: str) -> Dict[str, Any]:
"""Get configuration for a specific language."""
Expand Down
3 changes: 0 additions & 3 deletions src/config/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ class ResourcesConfig(BaseSettings):
le=16.0,
description="Maximum CPU cores available to execution containers",
)
max_cpu_quota: int = Field(
default=50000, ge=10000, le=100000
) # Deprecated, use max_cpus
max_pids: int = Field(
default=512,
ge=64,
Expand Down
83 changes: 0 additions & 83 deletions src/config/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,89 +14,6 @@ class SecurityConfig(BaseSettings):
api_key_header: str = Field(default="x-api-key")
api_key_cache_ttl: int = Field(default=300, ge=60)

# File Security
allowed_file_extensions: List[str] = Field(
default_factory=lambda: [
# Text and documentation
".txt",
".md",
".rtf",
".pdf",
# Microsoft Office
".doc",
".docx",
".dotx",
".xls",
".xlsx",
".xltx",
".ppt",
".pptx",
".potx",
".ppsx",
# OpenDocument formats
".odt",
".ods",
".odp",
".odg",
# Data formats
".json",
".csv",
".xml",
".yaml",
".yml",
".sql",
# Images
".png",
".jpg",
".jpeg",
".gif",
".svg",
".bmp",
".webp",
".ico",
# Web
".html",
".htm",
".css",
# Code files
".py",
".js",
".ts",
".go",
".java",
".c",
".cpp",
".h",
".hpp",
".php",
".rs",
".r",
".f90",
".d",
# Scripts and config
".sh",
".bat",
".ps1",
".dockerfile",
".makefile",
".ini",
".cfg",
".conf",
".log",
# Archives
".zip",
# Email and calendar
".eml",
".msg",
".mbox",
".ics",
".vcf",
]
)
blocked_file_patterns: List[str] = Field(
default_factory=lambda: ["*.exe", "*.dll", "*.so", "*.dylib", "*.bin"]
)

# Container Isolation
enable_network_isolation: bool = Field(default=True)
enable_filesystem_isolation: bool = Field(default=True)
Expand Down
Loading
Loading