SalmAlm is a local single-user personal AI tool. Default bind is 127.0.0.1 (loopback only). External exposure (0.0.0.0) requires explicit opt-in and triggers additional safety checks.
| Feature | Env Variable | Default | Risk |
|---|---|---|---|
| Network bind | SALMALM_BIND=0.0.0.0 |
127.0.0.1 |
Network exposure |
| Shell operators (pipe, redirect, chain) | SALMALM_ALLOW_SHELL=1 |
OFF | Command injection |
| Home directory file read | SALMALM_ALLOW_HOME_READ=1 |
OFF | Data exfiltration |
| Vault fallback (HMAC-CTR) | SALMALM_VAULT_FALLBACK=1 |
OFF (AES-256-GCM is always available; fallback exists for emergency recovery only) | Weaker encryption |
| Plugin system | SALMALM_PLUGINS=1 |
OFF | Arbitrary code execution |
| CLI OAuth token reuse | SALMALM_CLI_OAUTH=1 |
OFF | Third-party token access |
| Elevated exec on external bind | SALMALM_ALLOW_ELEVATED=1 |
OFF | Privilege escalation |
| Trusted proxy (XFF headers) | SALMALM_TRUST_PROXY=1 |
OFF | IP spoofing |
| Strict CSP (nonce mode) | Default ON | SALMALM_CSP_COMPAT=1 for legacy |
XSS surface |
| HTTP header permissive mode | SALMALM_HEADER_PERMISSIVE=1 |
OFF (allowlist) | Header injection |
| Dangerous exec flags | N/A (always blocked) | OFF | Code execution via allowed commands |
Tools are classified by risk level. Critical tools are blocked on external bind without authentication:
| Tier | Tools |
|---|---|
| 🔴 Critical | exec, exec_session, write_file, edit_file, python_eval, sandbox_exec, browser, email_send, gmail, google_calendar, calendar_delete, calendar_add, node_manage, plugin_manage |
| 🟡 High | http_request, read_file, memory_write, mesh, sub_agent, cron_manage, screenshot, tts, stt |
| 🟢 Normal | Everything else |
Tier names are verified against actual registered tool names via TestToolTierAlignment in CI.
Destructive operations require explicit _confirmed=true parameter:
gmail(action:send,delete)google_calendar(action:create,delete)email_send(all actions)calendar_delete(all actions)calendar_add(all actions)
- Web tools (
http_request,web_fetch): DNS pinning + private IP block on every redirect hop + scheme allowlist - Browser tool:
_is_internal_url()DNS resolution check — blocks private/loopback/link-local/metadata IPs on external bind - Both share the same private IP detection logic
- Centralized auth gate: All
/api/routes require authentication unless in_PUBLIC_PATHSset - CSRF defense: Origin validation +
X-Requested-With: SalmAlmcustom header (CORS preflight enforcement) - JWT tokens:
kidkey rotation,jtirevocation support - Password hashing: PBKDF2-200K iterations
- Login lockout: Persistent DB-backed brute-force protection
- Rate limiting: Token bucket per-IP rate limiter
- Multi-user session isolation:
session_store.user_idcolumn, queries filtered by user - Export security: Vault export requires admin role
- Audit log redaction: 9 secret patterns (OpenAI, Anthropic, xAI, Google, Slack, GitHub, AWS, JWT, key=value) scrubbed from tool args before logging (
security/redact.py) - Memory scrubbing: Same patterns applied before memory write
- Secret isolation: API keys stripped from subprocess environments (
exec,python_eval, background sessions) - Output redaction: Tool outputs scanned for API key patterns, replaced with
[REDACTED]
- All file path checks use
Path.resolve()+Path.is_relative_to()(not stringstartswith) - Write tools blocked outside allowed roots even for non-existent paths
- Allowed roots:
WORKSPACE_DIR,DATA_DIR,/tmp ..traversal blocked at string level as secondary defense
- Gateway registration requires auth token (
_gateway_token) - Tool dispatch payloads are HMAC-SHA256 signed with timestamp + nonce
- Response size capped at 5MB, strict dict validation
- JSON decode errors caught separately
OAuth tokens (web/oauth.py) are stored with the following priority:
- Vault (AES-256-GCM) — when vault is unlocked (recommended, secure)
- XOR obfuscation — stdlib-only fallback when vault is locked (
NOT encryption, just basic obfuscation to avoid plaintext on disk)
The XOR fallback emits a loud [OAUTH] ⚠️ warning to the log. If you use OAuth integrations, keep the vault unlocked. Future work: block OAuth storage unless vault is available.
- Primary: PBKDF2-HMAC-SHA256 (200K iterations) + AES-256-GCM (requires
cryptographypackage) - Fallback: PBKDF2-HMAC-SHA256 (200K iterations) + HMAC-CTR (pure Python, requires
SALMALM_VAULT_FALLBACK=1) - Password held in memory only while process runs, never written to disk
~/SalmAlm/.vault_autofor keychain-less environments (WSL)
Please report security issues to: https://github.com/hyunjun6928-netizen/salmalm/issues
- 150+ security regression tests (
test_security_regression.py,test_security_p0p1.py,test_security.py) - Tool tier alignment verified against registry in CI
- Exec bypass vectors (find -exec, tar --to-command, awk system(), xargs bash, curl metadata) pinned in tests