Files
2026-02-21 16:32:03 +00:00

5.7 KiB

Security: eval/exec Removal (SEC1)

This document describes the security hardening applied to CleverAgents configuration parsing to eliminate all code-injection attack surfaces.

Background

Prior to SEC1, certain code paths in the reactive stream router allowed arbitrary Python execution through eval() and exec() calls:

  • reactive/stream_router.py line 113: exec(code, ...) — allowed tool agents to run arbitrary code blocks supplied in YAML configs.
  • reactive/stream_router.py line 340: eval(fn_str) — allowed transform operators to evaluate arbitrary expressions.

Both paths have been replaced with named registries that only permit pre-registered, audited operations.

Replacement Patterns

SimpleToolAgent: Named Operations

Before (INSECURE):

tools:
  - code: "import os; os.system('echo pwned')"

After (SECURE):

tools:
  - operation: uppercase

Only operations registered in SimpleToolAgent._SAFE_OPERATIONS are allowed. Custom operations are registered via:

from cleveragents.reactive.stream_router import SimpleToolAgent

SimpleToolAgent.register_operation(
    "my_op",
    lambda content, meta, ctx: content.upper(),
)

ReactiveStreamRouter: Named Transforms

Before (INSECURE):

operators:
  - type: transform
    params:
      fn: "lambda x: __import__('os').system('rm -rf /')"

After (SECURE):

operators:
  - type: transform
    params:
      fn: uppercase

Only transforms registered in ReactiveStreamRouter._TRANSFORM_REGISTRY are allowed. Custom transforms are registered via:

from cleveragents.reactive.stream_router import ReactiveStreamRouter

ReactiveStreamRouter.register_transform(
    "my_transform",
    lambda x: x.upper(),
)

Config Security Scanner

The cleveragents.config.security_scanner module provides automated scanning of configuration files for dangerous patterns.

Scanned Patterns

Token Severity Rationale
eval( CRITICAL Executes arbitrary Python
exec( CRITICAL Executes arbitrary Python
__import__ CRITICAL Dynamic module loading
os.system CRITICAL Shell command execution
subprocess.call CRITICAL Shell command execution
subprocess.run CRITICAL Shell command execution
subprocess.Popen CRITICAL Process spawning
subprocess.check_output CRITICAL Shell command execution
subprocess.check_call CRITICAL Shell command execution
compile( HIGH Code compilation
os.popen HIGH Shell command execution
importlib.import_module HIGH Dynamic module loading
pickle.loads HIGH Arbitrary code execution via deserialization
{{ MEDIUM Template directive (potential injection)
{% MEDIUM Template directive (potential injection)

Programmatic Usage

from cleveragents.config.security_scanner import scan_files, Severity

violations = scan_files(["config.yaml", "settings.toml"])
for v in violations:
    print(f"{v.file_path}:{v.line_number} [{v.severity.name}] {v.message}")

Validation Gate

Use validate_config_safety() to hard-fail if a config file contains dangerous patterns:

from cleveragents.config.security_scanner import validate_config_safety

content = open("config.yaml").read()
validate_config_safety(content, file_path="config.yaml")
# Raises ConfigurationError if any violations are detected.

CLI

python -m cleveragents.config.security_scanner path/to/config.yaml

Known Limitations

The scanner performs literal substring matching against a fixed set of tokens. This is intentional — the goal is fast, low-false-positive detection of the most common code-injection patterns in configuration files, not a full static-analysis engine.

The following bypass vectors are not detected by the current scanner:

Bypass technique Example Why not detected
Whitespace before paren eval ( Tokens match literal eval( only
Indirect calls getattr(os, 'system')('cmd') No semantic analysis
pickle.load (singular) pickle.load(f) Only pickle.loads is registered
shutil.rmtree shutil.rmtree('/') Destructive FS ops not in scope
pty.spawn pty.spawn('/bin/sh') Terminal spawning not in scope
ctypes FFI ctypes.CDLL('libc.so') Native code loading not in scope
String concatenation "ev" + "al(" No expression evaluation

These limitations are acceptable for a defence-in-depth config scanner. Production security relies on the removal of eval()/exec() from production code paths (SEC1), not solely on this scanner. Future iterations may extend the pattern list.

Security Rationale

  1. No backwards compatibility for unsafe features. The code block and expression-eval features were explicitly removed with no migration path. Users must switch to named operations/transforms.

  2. Defense in depth. Even after removing eval()/exec(), the config scanner provides an additional layer to catch any future regressions or accidental re-introduction of dangerous patterns.

  3. Fail-fast. validate_config_safety() raises immediately on first detection, preventing any partial loading of tainted configuration.

Audit Summary

File Findings Action
reactive/stream_router.py:113 exec() in SimpleToolAgent Fully removed; code blocks unconditionally rejected
reactive/stream_router.py:340 eval() in transform operator Replaced with named transform registry
config/config_parser.py re.compile() Safe — not code execution
All other production code No eval/exec/compile usage No action needed