Skip to content

Security Model

Security is compiled into EdgeCrab — not runtime configuration switches. Every tool execution passes through a 6-layer defense stack before any file is read, any command is run, or any URL is fetched.


EdgeCrab is an autonomous agent: it reads files, runs shell commands, and makes HTTP requests on your behalf. The primary risks are:

Risk Vector
-------------------------------------------------------
Path traversal LLM reads /etc/passwd or ~/.ssh/id_rsa
SSRF Web tool fetches 169.254.169.254 (cloud IMDS)
Command injection Malicious file content escapes to shell args
Secret exfiltration LLM output contains API keys from read files
Prompt injection Web page / file contains hidden LLM instructions
Unsafe approval Agent runs destructive op without user review

Every file read / write / list / search passes through path_jail:

  1. Resolve all symlinks and collapse .. (canonicalize)
  2. Verify the canonical path starts with one of the allowed roots
  3. Reject the call if it falls outside allowed roots

The active workspace root is always allowed. Add extra roots explicitly in config.yaml:

tools:
file:
allowed_roots:
- /Users/you/projects/myapp # only this project

This policy is enforced at runtime by the shared path resolver used by file tools, local vision image reads, and @file / @folder context refs. It is not a general terminal sandbox: shell commands can still access any path the selected execution backend can reach.


All outbound HTTP calls (web search, web extract, web crawl, browser navigation) resolve the hostname to IPs and check those resolved IPs against a blocklist — preventing DNS-rebinding bypasses.

Blocked address space:

RangeCategory
10.0.0.0/8RFC 1918 private
172.16.0.0/12RFC 1918 private
192.168.0.0/16RFC 1918 private
127.0.0.0/8Loopback
::1/128IPv6 loopback
169.254.0.0/16Link-local / AWS / GCP / Azure IMDS
fd00::/8IPv6 ULA
100.64.0.0/10Carrier-grade NAT
*.internal, metadata.google.internalCloud-provider metadata FQDNs

SafeUrl is a distinct Rust type. No HTTP call is made without first constructing a SafeUrl — building one panics at compile time when bypassed, and returns an error at runtime on blocked addresses.

Cannot be disabled — the SSRF guard is compiled unconditionally.


Layer 3 — Command Scanner (command_scan.rs)

Section titled “Layer 3 — Command Scanner (command_scan.rs)”

Before any shell command executes via the terminal tool, the CommandScanner runs two passes over the normalized command string (ANSI-stripped, NFKC-normalized, lowercased):

Pass 1 — Aho-Corasick O(n) literal scan

38 literal patterns across 8 danger categories:

CategoryExample patterns
DestructiveFileOpsrm -r, rm -f, rmdir, shred, find -delete, dd if=, -exec rm
PermissionEscalationchmod 777, chown -R root
SystemDamagemkfs, > /dev/sd, > /etc/, systemctl stop, `:(){
SqlDestructiondrop table, drop database, truncate table
RemoteCodeExecution`
ProcessKillingkill -9 -1, pkill -9
GatewayProtectiongateway run &, nohup ... gateway
FileOverwritetee /etc/, tee .ssh/

Pass 2 — Regex scan for non-contiguous patterns

Patterns that require lookahead or non-adjacent keywords:

  • DELETE FROM without a WHERE clause (SQL)
  • find PATH -exec rm (non-adjacent)
  • bash <(curl ...) / sh < <(wget ...) (process substitution)
  • bash -lc, sh -ic (combined -c flag forms)
  • Gateway run outside systemd (nohup ... disown)

Tool-generated commands containing flagged patterns are blocked and the LLM receives a rejection message. User-typed commands in the TUI are not scanned — the user is trusted.


All LLM text — before display and before logging — passes through the redaction pipeline. Matched patterns are replaced with [REDACTED]:

PatternMatches
sk-[A-Za-z0-9]{20,}OpenAI API keys
ghp_[A-Za-z0-9]{36}GitHub personal access tokens
github_pat_[A-Za-z0-9_]{80,}Fine-grained GitHub PATs
AKIA[A-Z0-9]{16}AWS access key IDs
-----BEGIN .* PRIVATE KEY-----PEM private keys
High-entropy strings > 40 charsGeneric secrets (base64-like)

The raw API response is never written to disk. The SQLite session database stores only the redacted form.


Layer 5 — Prompt Injection Detection (injection.rs)

Section titled “Layer 5 — Prompt Injection Detection (injection.rs)”

Tool results (file contents, web pages, process output) are scanned for prompt-injection markers before being injected into the LLM context:

  • Ignore (all|previous|above) instructions
  • You are now / Act as override attempts
  • Hidden Unicode direction overrides (BiDi)
  • <|im_start|> / <|system|> token injections

Detected injections are:

  1. Flagged with a warning banner in the TUI
  2. Wrapped in a sandbox comment so the LLM knows the content is untrusted user data, not system instructions

Destructive operations can require explicit user approval before execution. Three modes:

ModeBehavior
offNo approval required (default in interactive TUI)
smartApprove when risk score exceeds threshold
manualAll tool calls require /approve or inline button

In gateway platforms (Telegram, Discord), approval appears as inline buttons. In the TUI, /approve and /deny slash commands confirm or reject pending actions.


The SQLite session database uses WAL (Write-Ahead Logging) mode:

  • Concurrent reads never block writes
  • Crash-safe: power failure mid-write cannot corrupt the database
  • Integrity verified on startup via PRAGMA integrity_check

Principle of Least Privilege — Config Template

Section titled “Principle of Least Privilege — Config Template”
~/.edgecrab/config.yaml
tools:
file:
allowed_roots:
- /path/to/your/project # never broader than needed
terminal:
timeout_seconds: 10 # short timeout = smaller blast radius
allowed_commands: # optional allowlist
- cargo
- git
- grep
web:
max_results: 5
security:
path_restrictions: [] # optional deny-list inside the workspace/allowed roots
ssrf_guard: true # cannot be false — compile-time guard
command_scan: true
output_redaction: true
approval_required: [] # add destructive command patterns here

Report vulnerabilities privately via GitHub Security Advisories. Do not open a public issue for security bugs.


Scenario 1: Constraining a refactoring task

# Only allow reading and writing inside the project
tools:
file:
allowed_roots:
- /home/you/project

If the agent tries to read /etc/hosts or any file outside the workspace plus allowed_roots, the call is blocked by the shared path policy.

Scenario 2: Review before any shell command

security:
approval_required:
- "rm"
- "git push"

Matching terminal commands require approval before running. Non-matching commands follow the normal terminal policy.

Scenario 3: Block all network access

tools:
enabled_toolsets:
- file
- terminal
- memory

No web/browser toolsets → no HTTP calls from agent tools. The SSRF guard still protects terminal commands that happen to make HTTP requests.

Scenario 4: Team deployment — lock down messaging

gateway:
telegram:
allowed_users:
- alice
- bob
home_channel: "-100123456789"
slack:
allowed_users:
- U012ABCDE
- U098FGHIJ

Only explicitly listed users can interact with the agent. All others receive an “access denied” response.


Prefer allowed_roots over relying on agent judgment. The agent is smart, but the path policy is deterministic runtime enforcement. Always set the smallest roots you actually need.

Run edgecrab doctor after changing security config. It checks that your settings are valid before you rely on them.

Log everything in production. Set save_trajectories: true in config.yaml. All tool calls and results are saved as JSON trajectory files in ~/.edgecrab/trajectories/. Invaluable for post-incident review.


Q: Can the agent read my SSH keys or credentials?

Not if the workspace root and allowed_roots exclude them. By default, file tools can read only inside the active workspace. To keep credentials inaccessible even inside a broad workspace, also add security.path_restrictions entries for sensitive subtrees.

tools:
file:
allowed_roots:
- /home/you/your-project

Q: Can a malicious web page take control of the agent?

EdgeCrab implements prompt injection detection (Layer 5). Content containing common injection markers is wrapped in a sandbox comment and flagged. This is defense-in-depth — do not rely on it as the only control. Never point the agent at untrusted web pages without an explicit approval policy.

Q: Can the SSRF guard be disabled?

No. It’s compiled unconditionally. SafeUrl is a Rust type — constructing an HTTP request without first constructing a SafeUrl is a compile error. Runtime configuration can’t disable this.

Q: What happens if a command scanner match is a false positive?

User-typed commands in the TUI are never scanned — only agent-generated commands. If the agent is blocked from a legitimate command:

  1. Type the command yourself in the TUI instead
  2. Or temporarily add it to terminal.allowed_commands

Q: Does EdgeCrab send any data to external servers by default?

Only to the configured LLM API. No telemetry, no crash reporting, no analytics. Network calls are limited to: LLM provider API, web tools (if enabled), gateway platforms (if configured), and Honcho (if cloud sync is enabled).