# 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)**:

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

**After (SECURE)**:

```yaml
tools:
  - operation: uppercase
```

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

```python
from cleveragents.reactive.stream_router import SimpleToolAgent

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

### ReactiveStreamRouter: Named Transforms

**Before (INSECURE)**:

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

**After (SECURE)**:

```yaml
operators:
  - type: transform
    params:
      fn: uppercase
```

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

```python
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

```python
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:

```python
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

```bash
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 |
