Navigation: Main Guide | Security Audit Reference | CVEs/GHSAs | Issue #1796 | Medium Article | ZeroLeeks | Post-merge Hardening | Open Issues | Open PRs | Ecosystem Threats | SecurityScorecard | Cisco AI Defense | Model Poisoning | Hudson Rock | Cline Supply Chain | ClawJacked | Model Comparison
Source:
src/cli/security-cli.ts:45-51,src/security/audit.ts:1380-1517,src/security/fix.ts:387-473,src/security/audit-extra.sync.ts,src/security/audit-extra.async.ts,src/security/audit-channel.tsThe built-in security audit scans your local config, filesystem permissions, and channel policies for common misconfigurations. It does not scan source code for vulnerabilities.
| Command | Behavior |
|---|---|
openclaw security audit |
Read-only scan across config, filesystem, channels, models, plugins, hooks, gateway, browser, and exposure matrix checks. No live gateway network probe. |
openclaw security audit --deep |
Everything above + static code-safety scan of installed plugins and skills (collectPluginsCodeSafetyFindings, collectInstalledSkillsCodeSafetyFindings); also maybeProbeGateway() connects to gateway WebSocket (5 s timeout), verifies auth, adds gateway.probe_failed if unreachable. |
openclaw security audit --fix |
Runs fixSecurityFootguns() first, then full audit. Report reflects post-fix state. Also accepts --deep. |
openclaw security audit --json |
Any mode above with JSON output instead of formatted text. |
This table summarizes the 18 checks most likely to affect real-world deployments. The full audit includes 70+ check IDs across 14 categories — see the Check categories table below for the complete list.
| Check ID | Severity | Why it matters | Primary fix | Auto-fix? |
|---|---|---|---|---|
fs.state_dir.* |
critical | World-readable config/credentials leak secrets | chmod 700 ~/.openclaw |
Yes (--fix) |
fs.credentials_dir.* |
critical | OAuth tokens, bot tokens exposed | chmod 700 ~/.openclaw/credentials/ |
Yes (--fix) |
gateway.bind_no_auth |
critical | Non-loopback bind without authentication | Set gateway.auth.mode: token or password |
No |
gateway.loopback_no_auth |
critical | Loopback bind but auth disabled (reverse-proxy/local trust risk) | Set gateway.auth.mode: token |
No |
gateway.tailscale_funnel |
critical | Public internet exposure via Tailscale Funnel | Disable Funnel, use Serve instead | No |
gateway.control_ui.insecure_auth |
critical | Control UI allows insecure HTTP auth fallback | Disable gateway.controlUi.allowInsecureAuth; prefer HTTPS or localhost |
No |
gateway.control_ui.device_auth_disabled |
critical | Device verification bypassed | Remove dangerouslyDisableDeviceAuth |
No |
hooks.token_reuse_gateway_token |
critical | Hook token same as gateway token (privilege escalation) | Generate separate hook token | No |
hooks.path_root |
critical | Hook handler can access any path | Set hooks.path to specific directory |
No |
hooks.request_session_key_enabled |
critical | External hook payload can override session key | Disable hooks.allowRequestSessionKey (or constrain prefixes) |
No |
browser.control_no_auth |
critical | Browser control server has no auth | Set browser.control.auth: token |
No |
browser.remote_cdp_http |
warn | Remote CDP uses HTTP (OK if tailnet-only or behind encrypted tunnel) | Use HTTPS or restrict to tailnet | No |
sandbox.dangerous_bind_mount |
critical | Container can access host filesystem | Remove dangerous bind mounts | No |
sandbox.dangerous_network_mode |
critical | Container has full network access | Use bridge/none network mode | No |
tools.elevated.allowFrom.*.wildcard |
critical | Anyone can trigger elevated tools | Replace * with specific allowlist |
No |
security.exposure.open_groups_with_elevated |
critical | Public groups + dangerous tools = exploitation | Close groups or disable elevated tools | No |
channels.<provider>.dm.open |
critical | Anyone can DM the bot on that channel | Set dmPolicy: pairing or allowlist |
No |
logging.redact_off |
warn | Secrets visible in tool summaries | Set logging.redactSensitive: tools |
Yes (--fix) |
Tip: Run
openclaw security audit --fixfirst to auto-resolve safe issues (file permissions, group policy, redaction). Then address remaining critical items manually.
| # | Category | Check ID prefix | Severities | What it checks |
|---|---|---|---|---|
| 1 | Attack surface summary | summary.attack_surface |
info | Counts open groups, elevated tools, hooks (reports hooks.webhooks and hooks.internal as separate lines), browser control |
| 2 | Synced folders | fs.synced_dir |
warn | State/config in iCloud, Dropbox, OneDrive, Google Drive |
| 3 | Filesystem permissions | fs.state_dir.*, fs.config.*, fs.credentials_dir.*, fs.auth_profiles.*, fs.sessions_store.*, fs.log_file.* |
critical/warn | World/group-writable dirs, world/group-readable config/credentials, symlink detection |
| 4 | Config include files | fs.config_include.* |
critical/warn | Permissions on included config files |
| 5 | Gateway configuration | gateway.bind_no_auth, gateway.loopback_no_auth, gateway.tailscale_funnel, gateway.tailscale_serve, gateway.control_ui.*, gateway.token_too_short, gateway.trusted_proxies_missing, gateway.trusted_proxy_auth, gateway.trusted_proxy_no_proxies, gateway.trusted_proxy_no_user_header, gateway.trusted_proxy_no_allowlist, gateway.auth_no_rate_limit, gateway.http.no_auth, gateway.http.session_key_override_enabled, gateway.tools_invoke_http.dangerous_allow |
critical/warn/info | Non-loopback bind without auth, Tailscale Funnel public exposure, Control UI insecure auth/device auth, token length, missing trusted proxy, trusted-proxy mode hardening checks (missing proxy IPs/user-header/allowlist), auth brute-force protection (gateway.auth_no_rate_limit), and HTTP endpoint exposure: gateway.http.no_auth=warn/critical when auth.mode="none" (lists exposed HTTP endpoints; critical if remotely exposed); gateway.http.session_key_override_enabled=info; gateway.tools_invoke_http.dangerous_allow=warn/critical when gateway.tools.allow re-enables default-denied HTTP tools (sessions_spawn, sessions_send, gateway, whatsapp_login) |
| 5a | Gateway nodes | gateway.nodes.deny_commands_ineffective |
warn | denyCommands has pattern-like or unknown entries that won't match (exact command-name matching only) |
| 6 | Browser control | browser.remote_cdp_http, browser.control_invalid_config, browser.control_no_auth |
critical/warn | Remote CDP over plain HTTP, invalid CDP config, and browser control server with auth disabled |
| 7 | Logging | logging.redact_off |
warn | redactSensitive="off" leaks secrets in tool summaries |
| 8 | Elevated tools | tools.elevated.allowFrom.*.wildcard, tools.elevated.allowFrom.*.large |
critical/warn | Wildcard "*" in elevated allowlist, oversized allowlist (>25 entries) |
| 8a | Exec runtime / sandboxing | tools.exec.host_sandbox_no_sandbox_defaults, tools.exec.host_sandbox_no_sandbox_agents |
warn | Exec host sandbox not applied to default or agent profiles |
| 8b | Sandbox config | sandbox.docker_config_mode_off, sandbox.dangerous_bind_mount, sandbox.dangerous_network_mode, sandbox.dangerous_seccomp_profile, sandbox.dangerous_apparmor_profile, sandbox.bind_mount_non_absolute |
critical/warn | Docker config present but sandbox.mode=off; dangerous bind mounts, network modes, seccomp/apparmor profiles (critical); non-absolute bind mount path (warn) |
| 8c | Tools profile | tools.profile_minimal_overridden |
warn | Global tools.profile=minimal overridden by an agent-level profile |
| 9 | Hooks hardening | hooks.path_root, hooks.token_too_short, hooks.token_reuse_gateway_token, hooks.default_session_key_unset, hooks.request_session_key_enabled, hooks.request_session_key_prefixes_missing |
critical/warn | Hooks base path is "/", short token (<24 chars), token reuses gateway token (critical as of Feb 19 2026 sync); session key unset (warn); request session key enabled or prefixes missing (critical if remotely exposed, warn otherwise) |
| 10 | Model hygiene | models.legacy, models.weak_tier, models.small_params |
critical/warn | Legacy models (GPT-3.5, Claude 2), weak tier (Haiku, pre-GPT-5), small models (<=300B params) without sandboxing exposed to web tools |
| 11 | Config secrets | config.secrets.gateway_password_in_config, config.secrets.hooks_token_in_config |
warn/info | Secrets stored in config file instead of env vars |
| 12 | Plugins/extensions | plugins.extensions_no_allowlist, plugins.tools_reachable_permissive_policy, plugins.installs_unpinned_npm_specs, plugins.installs_missing_integrity, plugins.installs_version_drift, hooks.installs_unpinned_npm_specs, hooks.installs_missing_integrity, hooks.installs_version_drift |
critical/warn | Extensions present but plugins.allow not configured; permissive tool reachability policy; unpinned/missing-integrity/version-drifted npm specs in plugin or hook installs |
| 13 | Channel security | channels.discord.*, channels.slack.*, channels.telegram.*, channels.*.dm.* |
critical/warn/info | DM policies (open/disabled/scoped), group policies, slash command restrictions, sender allowlists, multi-user DM session isolation |
| 14 | Exposure matrix | security.exposure.open_groups_with_elevated |
critical | Dangerous combination: open groupPolicy + elevated tools enabled |
| — | Deep probe | gateway.probe_failed |
warn | --deep only: gateway WebSocket unreachable or auth failed |
| — | Plugin/skill code safety | plugins.code_safety.*, skills.code_safety.* |
critical/warn | --deep only: static pattern scan of installed plugin/skill JS/TS/JSON for dangerous patterns; entry_escape=critical, entry_path/scan_failed=warn |
Config changes (applyConfigFixes, line 276):
logging.redactSensitive:"off"→"tools"(prevents secrets in tool summaries)groupPolicy:"open"→"allowlist"for all 7 supported channels (telegram, whatsapp, discord, signal, imessage, slack, msteams), including per-account overrides- WhatsApp
groupAllowFrom: populated from pairing store when policy flipped (so existing paired contacts still work)
Filesystem hardening (chmod/icacls):
| Target | Mode | Purpose |
|---|---|---|
~/.openclaw/ (state dir) |
700 |
User-only access to all state |
| Config file | 600 |
User-only read/write (contains tokens) |
| Config include files | 600 |
Same protection for split configs |
~/.openclaw/credentials/ |
700 |
OAuth credential directory |
credentials/*.json |
600 |
Individual credential files |
agents/<id>/agent/ |
700 |
Per-agent directory |
agents/<id>/agent/auth-profiles.json |
600 |
API keys and tokens |
agents/<id>/sessions/ |
700 |
Session transcript directory |
agents/<id>/sessions/sessions.json |
600 |
Session metadata |
agents/<id>/sessions/*.jsonl |
600 |
Session transcript files |
On Windows: uses icacls ACL reset instead of chmod.
--fix skips symlinks, missing paths, and already-correct permissions (safe + idempotent).
The audit is a configuration and filesystem hardening tool. It detects misconfigurations but not code-level vulnerabilities.
| Issue | Severity | What the audit catches | Check ID |
|---|---|---|---|
| #9065 | LOW | ~/.openclaw group-writable after sudo install |
fs.state_dir.perms_group_writable (--fix applies chmod 700) |
| #7862 | MEDIUM | Session transcripts 644 instead of 600 | fs.sessions_store.perms_readable (--fix applies chmod 600) |
| #9627 | HIGH | Config secrets exposed in JSON | config.secrets.gateway_password_in_config (warns; recommends env var) |
| #6609 | HIGH | Browser bridge server optional auth | browser.control_invalid_config, browser.control_no_auth |
| General gateway exposure | — | Non-loopback bind, missing auth, Tailscale Funnel | gateway.bind_no_auth, gateway.loopback_no_auth, gateway.tailscale_funnel |
| Channel misconfiguration | — | Open DMs, open groups, missing allowlists | All channels.* checks + security.exposure.open_groups_with_elevated |
Note: The audit does not scan workspace bootstrap .md files (MEMORY.md, SOUL.md, AGENTS.md, TOOLS.md, IDENTITY.md, USER.md, HEARTBEAT.md, BOOTSTRAP.md, memory.md) or memory/*.md for prompt injection content. The bootstrap files are injected into the system prompt as trusted context with no content validation. Memory directory files (memory/*.md) go through a separate tool-call pipeline but are also unscanned. See Cisco AI Defense gap analysis.
| Issue | Severity | Why the audit cannot detect it |
|---|---|---|
| #8512 | CRITICAL | Plugin HTTP routes bypass — code-level auth gap in src/gateway/server/plugins-http.ts |
| #3277 | HIGH | Path traversal via startsWith — code-level validation bug |
| #4950 | HIGH | Browser evaluate default on — hardcoded constant, not configurable |
| #5052 | HIGH | Config validation fail-open — code-level bug in src/config/io.ts |
| #5255 | HIGH | Browser file upload arbitrary read — code-level |
| #8516 | HIGH | Browser download/trace arbitrary file write — code-level |
| #8586 | HIGH | Configurable exec bypass — code-level allowlist gap |
| #8590 | HIGH | Status endpoint info leak — code-level |
| #8591 | HIGH | Env vars exposed via shell — code-level |
| #8776 | HIGH | soul-evil hook hijacking — code-level |
| #9435 | Gateway token in URL query params — fixed in PR #9436 | |
| #9512 | HIGH | Skill archive path traversal (Zip Slip) — code-level |
| #9517 | HIGH | Canvas host auth bypass — code-level |
| #8696 | HIGH | Playwright download path traversal — code-level |
| #4949 | HIGH | Browser DNS rebinding — code-level (no Host header validation) |
| #4995 | HIGH | iMessage DM auto-responds with pairing codes — code-level |
| #5995 | HIGH | Secrets in session transcripts — by design |
| #6606 | HIGH | Telegram webhook binds 0.0.0.0 — code-level |
| #8054 | HIGH | Type coercion "undefined" credentials — code-level |
The audit catches ~6 of 37 documented issues (config/filesystem misconfigurations). The expanded check set (70+ check IDs) adds sandbox config, exec runtime sandboxing, gateway HTTP auth gaps, plugin/hook supply-chain checks, and
--deepplugin/skill code-safety scanning. The remaining ~31 issues are code-level vulnerabilities that require upstream patches. For defense-in-depth: runopenclaw security audit --fixand monitor the open upstream security issues list.