Skip to main content

Security Patterns in Aragora

This document describes the security patterns, controls, and best practices used throughout the Aragora codebase. It serves as both documentation and a guide for contributors.

Executive Summary

Aragora implements defense-in-depth security with:

  • No unsafe dynamic code execution - Only 1 sandboxed exec() site
  • No eval() usage - Replaced with AST-based evaluators
  • No shell=True - All subprocess calls use list-based arguments
  • Input validation - Blocklist patterns for dangerous code
  • Process isolation - Subprocess sandboxing for untrusted code

Dynamic Code Execution

Sandboxed Exec (proofs.py)

Location: aragora/verification/proofs.py

Purpose: Execute user-provided proof assertions in a controlled environment.

Security Controls:

  1. Input validation: _validate_code_safety() checks for dangerous patterns before execution
  2. Builtin restriction: Uses {"__builtins__": SAFE_BUILTINS} whitelist
  3. Process isolation: Execution happens in a subprocess that can be killed
  4. Timeout enforcement: Hard timeout prevents infinite loops
  5. Pattern blocklist: Blocks __class__, __import__, exec(, eval(, open(, etc.
# Blocked patterns (proofs.py)
DANGEROUS_PATTERNS = [
"__class__", "__bases__", "__subclasses__", "__mro__",
"__globals__", "__code__", "__builtins__", "__import__",
"exec(", "eval(", "compile(", "open(", "getattr(",
"os.", "sys.", "subprocess", "importlib",
# ... and more
]

# Safe builtins whitelist (proofs.py)
SAFE_BUILTINS = {
"abs", "all", "any", "bool", "dict", "float", "int",
"len", "list", "max", "min", "range", "set", "str",
"sum", "tuple", "type", "zip",
# Explicitly excluded: __import__, open, exec, eval, compile
}

Risk Level: LOW (properly sandboxed)


AST-Based Condition Evaluation (policy/engine.py)

Location: aragora/policy/engine.py

Purpose: Evaluate policy condition expressions without using eval().

Security Controls:

  1. AST parsing: Uses ast.parse() to parse conditions
  2. Node whitelist: Only specific AST node types are allowed
  3. Explicit blocking: ast.Attribute, ast.Call, ast.Subscript are blocked
  4. Context isolation: Only provided context variables are accessible
# Blocked node types (engine.py:259-260)
elif isinstance(node, (ast.Attribute, ast.Call, ast.Subscript)):
raise ValueError(f"Blocked node type for security: {type(node).__name__}")

# Supported expressions:
# - Comparisons: x == 1, y != "foo", z < 10
# - Membership: x in ["a", "b"]
# - Boolean: x == 1 and y == 2
# - Negation: not x

Risk Level: MINIMAL (no code execution, AST-only)


Subprocess Execution

General Pattern

All subprocess calls in Aragora use the list-based argument form which prevents shell injection:

# CORRECT - list form (no shell injection)
subprocess.run(["git", "commit", "-m", message], shell=False)

# INCORRECT - shell form (vulnerable)
subprocess.run(f"git commit -m \{message\}", shell=True) # NEVER USED

Key Files Using Subprocess

FilePurposeshell=
utils/subprocess_runner.pyGeneral command executionFalse
tools/code.pyGit operationsFalse
broadcast/mixer.pyFFmpeg audio/videoFalse
verification/proofs.pySandboxed proof executionFalse
connectors/github.pyGitHub CLI operationsFalse
nomic/phases/commit.pyAuto-commit operationsFalse

Subprocess Runner (subprocess_runner.py)

Location: aragora/utils/subprocess_runner.py

Comment: shell=False, # Never use shell=True

This module provides the standard interface for subprocess execution throughout Aragora with:

  • Explicit shell=False enforcement
  • Timeout support
  • Output capture
  • Error handling

Content Security Policy

HTTP Headers (unified_server.py)

Location: aragora/server/unified_server.py

The server sets strict CSP headers:

# Security headers
self.send_header("X-Content-Type-Options", "nosniff")
self.send_header("X-Frame-Options", "DENY")
self.send_header("X-XSS-Protection", "1; mode=block")
self.send_header("Referrer-Policy", "strict-origin-when-cross-origin")

# CSP - 'unsafe-eval' removed for security
self.send_header(
"Content-Security-Policy",
"default-src 'self'; "
"script-src 'self'; " # No 'unsafe-eval' or 'unsafe-inline'
"style-src 'self' 'unsafe-inline'; " # Needed for CSS-in-JS
"img-src 'self' data: https:; "
# ...
)

Input Validation

API Input Validation

All API endpoints use Pydantic models for input validation:

# Example from handlers/debates.py
class CreateDebateRequest(BaseModel):
topic: str = Field(..., min_length=1, max_length=1000)
agents: list[str] = Field(..., min_items=2, max_items=10)
rounds: int = Field(default=3, ge=1, le=20)

SQL Injection Prevention

Database operations use parameterized queries via SQLAlchemy:

# Safe - parameterized query
session.execute(
select(Debate).where(Debate.id == debate_id)
)

# Never used - string interpolation
# f"SELECT * FROM debates WHERE id = \{debate_id\}"

Authentication & Authorization

Token Management

  • Tokens use cryptographically secure random generation
  • Tokens have configurable TTL
  • Token blacklist support for revocation
  • bcrypt for password hashing (not MD5/SHA1)

Session Security

  • Session cookies are HttpOnly and Secure
  • CSRF protection via SameSite cookie attribute
  • Session timeout enforcement

Environment & Secrets

Best Practices

  1. .env in .gitignore: Secrets never committed
  2. .env.example: Template without actual values
  3. pydantic-settings: Validates env vars at startup
  4. File permissions: .env should be mode 600

Required Environment Variables

See docs/ENVIRONMENT.md for the complete reference.


Security Testing

Automated Checks

The CI pipeline includes:

  • Bandit: Static security analysis for Python
  • Safety: Dependency vulnerability scanning
  • Ruff: Linting includes security rules
  • Codebase scanning: Dependency and CVE lookup via aragora.analysis.codebase

Manual Testing

For security-critical changes:

  1. Review all dynamic code execution paths
  2. Verify subprocess calls use list form
  3. Check input validation on new endpoints
  4. Test authentication bypass scenarios

Reporting Security Issues

If you discover a security vulnerability, please:

  1. Do not open a public issue
  2. Email security concerns to the maintainers
  3. Include steps to reproduce
  4. Allow reasonable time for a fix before disclosure

Changelog

DateChangeAuthor
2026-01-13Initial security audit and documentationClaude Code