dont open a public issue. use github security advisories or reach out to the maintainers directly.
include what the vulnerability is, how to reproduce it, and what the impact is. well acknowledge within 48 hours and aim to fix critical issues within 2 weeks.
security isnt a single layer here, its everywhere. heres what we do and why.
three modes, defaulting to the safest useful one:
- readonly - can observe but cant act. no shell, no file writes
- supervised - can act within allowlists (this is the default)
- full - autonomous within workspace sandbox
workspace_only = true by default. this means:
- all file operations are confined to the workspace directory
- absolute paths are rejected
- path traversal (
../../../etc/passwd) is blocked - symlink escape detection via canonical path verification
- null byte injection blocked (prevents c-level path truncation)
14 system directories are always blocked: /etc, /root, /home, /usr, /bin, /sbin, /lib, /opt, /boot, /dev, /proc, /sys, /var, /tmp
4 sensitive dotfile paths are always blocked: ~/.ssh, ~/.gnupg, ~/.aws, ~/.config
only explicitly approved commands can execute. the default list is: git, npm, cargo, ls, cat, grep, find, echo, pwd, wc, head, tail
we also block:
- subshell operators (backticks,
$(,${) that hide arbitrary execution - output redirections (
>,>>) that could write outside workspace - command chaining (
&&,||,;,|) is validated per-segment - env var prefix bypass (
FOO=bar rm -rf /) is caught
the gateway binds to 127.0.0.1 by default. it refuses to bind to 0.0.0.0 unless you have a tunnel configured or explicitly set allow_public_bind = true.
on first connect:
- server prints a 6-digit pairing code to the terminal
- client sends code via
X-Pairing-Codeheader onPOST /pair - server responds with a bearer token
- all subsequent requests require
Authorization: Bearer <token>
brute force protection: 5 wrong attempts triggers a 5-minute lockout. the pairing code comparison uses constant-time equality to prevent timing attacks.
api keys in config are encrypted with chacha20-poly1305 aead, not stored as plaintext. the encryption key lives in ~/.baihu/.secret_key with restrictive file permissions (0600 on unix, icacls restricted on windows).
each encryption generates a fresh random nonce so the same plaintext produces different ciphertext every time. the poly1305 tag prevents tampering.
legacy xor-encrypted secrets (enc: prefix) are auto-migrated to the secure format (enc2:) on decrypt. the xor cipher is kept for backward compat but its deprecated.
provider urls are validated before requests go out (blocks private ips, cloud metadata endpoints, etc). additionally, a custom redirect policy validates every 3xx hop. this prevents redirect-to-localhost attacks where an attacker url returns 302 -> http://127.0.0.1/... to bypass the initial url check. ollama is intentionally exempt since its meant to be local.
config.toml is checked for permissions on load. on unix, it auto-fixes to 0600 (owner read/write only) and warns if it was too permissive. paired gateway tokens are encrypted through chacha20-poly1305 before writing to disk so they dont sit as plaintext in config files.
sliding window rate limiter caps actions per hour. configurable via max_actions_per_hour in config. prevents runaway agent loops.
consistent across telegram, discord, slack, whatsapp:
- empty allowlist = deny all (safe default)
"*"= allow all (explicit opt-in)- otherwise exact-match list
security is covered by automated tests:
cargo test --lib -- security
cargo test --lib -- tools::shell
cargo test --lib -- tools::file_read
cargo test --lib -- tools::file_writedocker images follow cis docker benchmark:
- runs as uid 65534 (distroless nonroot)
gcr.io/distroless/cc-debian12:nonrootbase (no shell, no package manager)- supports
--read-onlyfilesystem with/workspacevolume mount