docs: restructure gateway security page and extract audit checks reference

This commit is contained in:
Vincent Koc
2026-04-23 01:00:54 -07:00
parent 25039e973e
commit b7506521e6
3 changed files with 197 additions and 181 deletions

View File

@@ -1380,6 +1380,7 @@
"group": "Security and sandboxing",
"pages": [
"gateway/security/index",
"gateway/security/audit-checks",
"gateway/sandboxing",
"gateway/openshell",
"gateway/sandbox-vs-tool-policy-vs-elevated"

View File

@@ -0,0 +1,126 @@
---
summary: "Reference catalog of checkIds emitted by openclaw security audit"
read_when:
- You saw a specific `checkId` in `openclaw security audit` output and want to know what it means
- You need the fix key/path for a given finding
- You are triaging severity across a security audit run
title: "Security audit checks"
---
# Security audit checks
`openclaw security audit` emits structured findings keyed by `checkId`. This
page is the reference catalog for those IDs. For the high-level threat model
and hardening guidance, see [Security](/gateway/security).
High-signal `checkId` values you will most likely see in real deployments (not
exhaustive):
| `checkId` | Severity | Why it matters | Primary fix key/path | Auto-fix |
| ------------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- | -------- |
| `fs.state_dir.perms_world_writable` | critical | Other users/processes can modify full OpenClaw state | filesystem perms on `~/.openclaw` | yes |
| `fs.state_dir.perms_group_writable` | warn | Group users can modify full OpenClaw state | filesystem perms on `~/.openclaw` | yes |
| `fs.state_dir.perms_readable` | warn | State dir is readable by others | filesystem perms on `~/.openclaw` | yes |
| `fs.state_dir.symlink` | warn | State dir target becomes another trust boundary | state dir filesystem layout | no |
| `fs.config.perms_writable` | critical | Others can change auth/tool policy/config | filesystem perms on `~/.openclaw/openclaw.json` | yes |
| `fs.config.symlink` | warn | Symlinked config files are unsupported for writes and add another trust boundary | replace with a regular config file or point `OPENCLAW_CONFIG_PATH` at the real file | no |
| `fs.config.perms_group_readable` | warn | Group users can read config tokens/settings | filesystem perms on config file | yes |
| `fs.config.perms_world_readable` | critical | Config can expose tokens/settings | filesystem perms on config file | yes |
| `fs.config_include.perms_writable` | critical | Config include file can be modified by others | include-file perms referenced from `openclaw.json` | yes |
| `fs.config_include.perms_group_readable` | warn | Group users can read included secrets/settings | include-file perms referenced from `openclaw.json` | yes |
| `fs.config_include.perms_world_readable` | critical | Included secrets/settings are world-readable | include-file perms referenced from `openclaw.json` | yes |
| `fs.auth_profiles.perms_writable` | critical | Others can inject or replace stored model credentials | `agents/<agentId>/agent/auth-profiles.json` perms | yes |
| `fs.auth_profiles.perms_readable` | warn | Others can read API keys and OAuth tokens | `agents/<agentId>/agent/auth-profiles.json` perms | yes |
| `fs.credentials_dir.perms_writable` | critical | Others can modify channel pairing/credential state | filesystem perms on `~/.openclaw/credentials` | yes |
| `fs.credentials_dir.perms_readable` | warn | Others can read channel credential state | filesystem perms on `~/.openclaw/credentials` | yes |
| `fs.sessions_store.perms_readable` | warn | Others can read session transcripts/metadata | session store perms | yes |
| `fs.log_file.perms_readable` | warn | Others can read redacted-but-still-sensitive logs | gateway log file perms | yes |
| `fs.synced_dir` | warn | State/config in iCloud/Dropbox/Drive broadens token/transcript exposure | move config/state off synced folders | no |
| `gateway.bind_no_auth` | critical | Remote bind without shared secret | `gateway.bind`, `gateway.auth.*` | no |
| `gateway.loopback_no_auth` | critical | Reverse-proxied loopback may become unauthenticated | `gateway.auth.*`, proxy setup | no |
| `gateway.trusted_proxies_missing` | warn | Reverse-proxy headers are present but not trusted | `gateway.trustedProxies` | no |
| `gateway.http.no_auth` | warn/critical | Gateway HTTP APIs reachable with `auth.mode="none"` | `gateway.auth.mode`, `gateway.http.endpoints.*` | no |
| `gateway.http.session_key_override_enabled` | info | HTTP API callers can override `sessionKey` | `gateway.http.allowSessionKeyOverride` | no |
| `gateway.tools_invoke_http.dangerous_allow` | warn/critical | Re-enables dangerous tools over HTTP API | `gateway.tools.allow` | no |
| `gateway.nodes.allow_commands_dangerous` | warn/critical | Enables high-impact node commands (camera/screen/contacts/calendar/SMS) | `gateway.nodes.allowCommands` | no |
| `gateway.nodes.deny_commands_ineffective` | warn | Pattern-like deny entries do not match shell text or groups | `gateway.nodes.denyCommands` | no |
| `gateway.tailscale_funnel` | critical | Public internet exposure | `gateway.tailscale.mode` | no |
| `gateway.tailscale_serve` | info | Tailnet exposure is enabled via Serve | `gateway.tailscale.mode` | no |
| `gateway.control_ui.allowed_origins_required` | critical | Non-loopback Control UI without explicit browser-origin allowlist | `gateway.controlUi.allowedOrigins` | no |
| `gateway.control_ui.allowed_origins_wildcard` | warn/critical | `allowedOrigins=["*"]` disables browser-origin allowlisting | `gateway.controlUi.allowedOrigins` | no |
| `gateway.control_ui.host_header_origin_fallback` | warn/critical | Enables Host-header origin fallback (DNS rebinding hardening downgrade) | `gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback` | no |
| `gateway.control_ui.insecure_auth` | warn | Insecure-auth compatibility toggle enabled | `gateway.controlUi.allowInsecureAuth` | no |
| `gateway.control_ui.device_auth_disabled` | critical | Disables device identity check | `gateway.controlUi.dangerouslyDisableDeviceAuth` | no |
| `gateway.real_ip_fallback_enabled` | warn/critical | Trusting `X-Real-IP` fallback can enable source-IP spoofing via proxy misconfig | `gateway.allowRealIpFallback`, `gateway.trustedProxies` | no |
| `gateway.token_too_short` | warn | Short shared token is easier to brute force | `gateway.auth.token` | no |
| `gateway.auth_no_rate_limit` | warn | Exposed auth without rate limiting increases brute-force risk | `gateway.auth.rateLimit` | no |
| `gateway.trusted_proxy_auth` | critical | Proxy identity now becomes the auth boundary | `gateway.auth.mode="trusted-proxy"` | no |
| `gateway.trusted_proxy_no_proxies` | critical | Trusted-proxy auth without trusted proxy IPs is unsafe | `gateway.trustedProxies` | no |
| `gateway.trusted_proxy_no_user_header` | critical | Trusted-proxy auth cannot resolve user identity safely | `gateway.auth.trustedProxy.userHeader` | no |
| `gateway.trusted_proxy_no_allowlist` | warn | Trusted-proxy auth accepts any authenticated upstream user | `gateway.auth.trustedProxy.allowUsers` | no |
| `gateway.probe_auth_secretref_unavailable` | warn | Deep probe could not resolve auth SecretRefs in this command path | deep-probe auth source / SecretRef availability | no |
| `gateway.probe_failed` | warn/critical | Live Gateway probe failed | gateway reachability/auth | no |
| `discovery.mdns_full_mode` | warn/critical | mDNS full mode advertises `cliPath`/`sshPort` metadata on local network | `discovery.mdns.mode`, `gateway.bind` | no |
| `config.insecure_or_dangerous_flags` | warn | Any insecure/dangerous debug flags enabled | multiple keys (see finding detail) | no |
| `config.secrets.gateway_password_in_config` | warn | Gateway password is stored directly in config | `gateway.auth.password` | no |
| `config.secrets.hooks_token_in_config` | warn | Hook bearer token is stored directly in config | `hooks.token` | no |
| `hooks.token_reuse_gateway_token` | critical | Hook ingress token also unlocks Gateway auth | `hooks.token`, `gateway.auth.token` | no |
| `hooks.token_too_short` | warn | Easier brute force on hook ingress | `hooks.token` | no |
| `hooks.default_session_key_unset` | warn | Hook agent runs fan out into generated per-request sessions | `hooks.defaultSessionKey` | no |
| `hooks.allowed_agent_ids_unrestricted` | warn/critical | Authenticated hook callers may route to any configured agent | `hooks.allowedAgentIds` | no |
| `hooks.request_session_key_enabled` | warn/critical | External caller can choose sessionKey | `hooks.allowRequestSessionKey` | no |
| `hooks.request_session_key_prefixes_missing` | warn/critical | No bound on external session key shapes | `hooks.allowedSessionKeyPrefixes` | no |
| `hooks.path_root` | critical | Hook path is `/`, making ingress easier to collide or misroute | `hooks.path` | no |
| `hooks.installs_unpinned_npm_specs` | warn | Hook install records are not pinned to immutable npm specs | hook install metadata | no |
| `hooks.installs_missing_integrity` | warn | Hook install records lack integrity metadata | hook install metadata | no |
| `hooks.installs_version_drift` | warn | Hook install records drift from installed packages | hook install metadata | no |
| `logging.redact_off` | warn | Sensitive values leak to logs/status | `logging.redactSensitive` | yes |
| `browser.control_invalid_config` | warn | Browser control config is invalid before runtime | `browser.*` | no |
| `browser.control_no_auth` | critical | Browser control exposed without token/password auth | `gateway.auth.*` | no |
| `browser.remote_cdp_http` | warn | Remote CDP over plain HTTP lacks transport encryption | browser profile `cdpUrl` | no |
| `browser.remote_cdp_private_host` | warn | Remote CDP targets a private/internal host | browser profile `cdpUrl`, `browser.ssrfPolicy.*` | no |
| `sandbox.docker_config_mode_off` | warn | Sandbox Docker config present but inactive | `agents.*.sandbox.mode` | no |
| `sandbox.bind_mount_non_absolute` | warn | Relative bind mounts can resolve unpredictably | `agents.*.sandbox.docker.binds[]` | no |
| `sandbox.dangerous_bind_mount` | critical | Sandbox bind mount targets blocked system, credential, or Docker socket paths | `agents.*.sandbox.docker.binds[]` | no |
| `sandbox.dangerous_network_mode` | critical | Sandbox Docker network uses `host` or `container:*` namespace-join mode | `agents.*.sandbox.docker.network` | no |
| `sandbox.dangerous_seccomp_profile` | critical | Sandbox seccomp profile weakens container isolation | `agents.*.sandbox.docker.securityOpt` | no |
| `sandbox.dangerous_apparmor_profile` | critical | Sandbox AppArmor profile weakens container isolation | `agents.*.sandbox.docker.securityOpt` | no |
| `sandbox.browser_cdp_bridge_unrestricted` | warn | Sandbox browser bridge is exposed without source-range restriction | `sandbox.browser.cdpSourceRange` | no |
| `sandbox.browser_container.non_loopback_publish` | critical | Existing browser container publishes CDP on non-loopback interfaces | browser sandbox container publish config | no |
| `sandbox.browser_container.hash_label_missing` | warn | Existing browser container predates current config-hash labels | `openclaw sandbox recreate --browser --all` | no |
| `sandbox.browser_container.hash_epoch_stale` | warn | Existing browser container predates current browser config epoch | `openclaw sandbox recreate --browser --all` | no |
| `tools.exec.host_sandbox_no_sandbox_defaults` | warn | `exec host=sandbox` fails closed when sandbox is off | `tools.exec.host`, `agents.defaults.sandbox.mode` | no |
| `tools.exec.host_sandbox_no_sandbox_agents` | warn | Per-agent `exec host=sandbox` fails closed when sandbox is off | `agents.list[].tools.exec.host`, `agents.list[].sandbox.mode` | no |
| `tools.exec.security_full_configured` | warn/critical | Host exec is running with `security="full"` | `tools.exec.security`, `agents.list[].tools.exec.security` | no |
| `tools.exec.auto_allow_skills_enabled` | warn | Exec approvals trust skill bins implicitly | `~/.openclaw/exec-approvals.json` | no |
| `tools.exec.allowlist_interpreter_without_strict_inline_eval` | warn | Interpreter allowlists permit inline eval without forced reapproval | `tools.exec.strictInlineEval`, `agents.list[].tools.exec.strictInlineEval`, exec approvals allowlist | no |
| `tools.exec.safe_bins_interpreter_unprofiled` | warn | Interpreter/runtime bins in `safeBins` without explicit profiles broaden exec risk | `tools.exec.safeBins`, `tools.exec.safeBinProfiles`, `agents.list[].tools.exec.*` | no |
| `tools.exec.safe_bins_broad_behavior` | warn | Broad-behavior tools in `safeBins` weaken the low-risk stdin-filter trust model | `tools.exec.safeBins`, `agents.list[].tools.exec.safeBins` | no |
| `tools.exec.safe_bin_trusted_dirs_risky` | warn | `safeBinTrustedDirs` includes mutable or risky directories | `tools.exec.safeBinTrustedDirs`, `agents.list[].tools.exec.safeBinTrustedDirs` | no |
| `skills.workspace.symlink_escape` | warn | Workspace `skills/**/SKILL.md` resolves outside workspace root (symlink-chain drift) | workspace `skills/**` filesystem state | no |
| `plugins.extensions_no_allowlist` | warn | Plugins are installed without an explicit plugin allowlist | `plugins.allowlist` | no |
| `plugins.installs_unpinned_npm_specs` | warn | Plugin install records are not pinned to immutable npm specs | plugin install metadata | no |
| `plugins.installs_missing_integrity` | warn | Plugin install records lack integrity metadata | plugin install metadata | no |
| `plugins.installs_version_drift` | warn | Plugin install records drift from installed packages | plugin install metadata | no |
| `plugins.code_safety` | warn/critical | Plugin code scan found suspicious or dangerous patterns | plugin code / install source | no |
| `plugins.code_safety.entry_path` | warn | Plugin entry path points into hidden or `node_modules` locations | plugin manifest `entry` | no |
| `plugins.code_safety.entry_escape` | critical | Plugin entry escapes the plugin directory | plugin manifest `entry` | no |
| `plugins.code_safety.scan_failed` | warn | Plugin code scan could not complete | plugin path / scan environment | no |
| `skills.code_safety` | warn/critical | Skill installer metadata/code contains suspicious or dangerous patterns | skill install source | no |
| `skills.code_safety.scan_failed` | warn | Skill code scan could not complete | skill scan environment | no |
| `security.exposure.open_channels_with_exec` | warn/critical | Shared/public rooms can reach exec-enabled agents | `channels.*.dmPolicy`, `channels.*.groupPolicy`, `tools.exec.*`, `agents.list[].tools.exec.*` | no |
| `security.exposure.open_groups_with_elevated` | critical | Open groups + elevated tools create high-impact prompt-injection paths | `channels.*.groupPolicy`, `tools.elevated.*` | no |
| `security.exposure.open_groups_with_runtime_or_fs` | critical/warn | Open groups can reach command/file tools without sandbox/workspace guards | `channels.*.groupPolicy`, `tools.profile/deny`, `tools.fs.workspaceOnly`, `agents.*.sandbox.mode` | no |
| `security.trust_model.multi_user_heuristic` | warn | Config looks multi-user while gateway trust model is personal-assistant | split trust boundaries, or shared-user hardening (`sandbox.mode`, tool deny/workspace scoping`) | no |
| `tools.profile_minimal_overridden` | warn | Agent overrides bypass global minimal profile | `agents.list[].tools.profile` | no |
| `plugins.tools_reachable_permissive_policy` | warn | Extension tools reachable in permissive contexts | `tools.profile` + tool allow/deny | no |
| `models.legacy` | warn | Legacy model families are still configured | model selection | no |
| `models.weak_tier` | warn | Configured models are below current recommended tiers | model selection | no |
| `models.small_params` | critical/info | Small models + unsafe tool surfaces raise injection risk | model choice + sandbox/tool policy | no |
| `summary.attack_surface` | info | Roll-up summary of auth, channel, tool, and exposure posture | multiple keys (see finding detail) | no |
## Related
- [Security](/gateway/security)
- [Configuration](/gateway/configuration)
- [Trusted proxy auth](/gateway/trusted-proxy-auth)

View File

@@ -125,17 +125,6 @@ These patterns are commonly reported and are usually closed as no-action unless
- Reports that treat node pairing metadata as a hidden second per-command approval layer for `system.run`, when the real execution boundary is still the gateway's global node command policy plus the node's own exec approvals.
- "Missing per-user authorization" findings that treat `sessionKey` as an auth token.
## Researcher preflight checklist
Before opening a GHSA, verify all of these:
1. Repro still works on latest `main` or latest release.
2. Report includes exact code path (`file`, function, line range) and tested version/commit.
3. Impact crosses a documented trust boundary (not just prompt injection).
4. Claim is not listed in [Out of Scope](https://github.com/openclaw/openclaw/blob/main/SECURITY.md#out-of-scope).
5. Existing advisories were checked for duplicates (reuse canonical GHSA when applicable).
6. Deployment assumptions are explicit (loopback/local vs exposed, trusted vs untrusted operators).
## Hardened baseline in 60 seconds
Use this baseline first, then selectively re-enable tools per trusted agent:
@@ -238,110 +227,18 @@ When the audit prints findings, treat this as a priority order:
## Security audit glossary
High-signal `checkId` values you will most likely see in real deployments (not exhaustive):
Each audit finding is keyed by a structured `checkId` (for example
`gateway.bind_no_auth` or `tools.exec.security_full_configured`). Common
critical severity classes:
| `checkId` | Severity | Why it matters | Primary fix key/path | Auto-fix |
| ------------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- | -------- |
| `fs.state_dir.perms_world_writable` | critical | Other users/processes can modify full OpenClaw state | filesystem perms on `~/.openclaw` | yes |
| `fs.state_dir.perms_group_writable` | warn | Group users can modify full OpenClaw state | filesystem perms on `~/.openclaw` | yes |
| `fs.state_dir.perms_readable` | warn | State dir is readable by others | filesystem perms on `~/.openclaw` | yes |
| `fs.state_dir.symlink` | warn | State dir target becomes another trust boundary | state dir filesystem layout | no |
| `fs.config.perms_writable` | critical | Others can change auth/tool policy/config | filesystem perms on `~/.openclaw/openclaw.json` | yes |
| `fs.config.symlink` | warn | Symlinked config files are unsupported for writes and add another trust boundary | replace with a regular config file or point `OPENCLAW_CONFIG_PATH` at the real file | no |
| `fs.config.perms_group_readable` | warn | Group users can read config tokens/settings | filesystem perms on config file | yes |
| `fs.config.perms_world_readable` | critical | Config can expose tokens/settings | filesystem perms on config file | yes |
| `fs.config_include.perms_writable` | critical | Config include file can be modified by others | include-file perms referenced from `openclaw.json` | yes |
| `fs.config_include.perms_group_readable` | warn | Group users can read included secrets/settings | include-file perms referenced from `openclaw.json` | yes |
| `fs.config_include.perms_world_readable` | critical | Included secrets/settings are world-readable | include-file perms referenced from `openclaw.json` | yes |
| `fs.auth_profiles.perms_writable` | critical | Others can inject or replace stored model credentials | `agents/<agentId>/agent/auth-profiles.json` perms | yes |
| `fs.auth_profiles.perms_readable` | warn | Others can read API keys and OAuth tokens | `agents/<agentId>/agent/auth-profiles.json` perms | yes |
| `fs.credentials_dir.perms_writable` | critical | Others can modify channel pairing/credential state | filesystem perms on `~/.openclaw/credentials` | yes |
| `fs.credentials_dir.perms_readable` | warn | Others can read channel credential state | filesystem perms on `~/.openclaw/credentials` | yes |
| `fs.sessions_store.perms_readable` | warn | Others can read session transcripts/metadata | session store perms | yes |
| `fs.log_file.perms_readable` | warn | Others can read redacted-but-still-sensitive logs | gateway log file perms | yes |
| `fs.synced_dir` | warn | State/config in iCloud/Dropbox/Drive broadens token/transcript exposure | move config/state off synced folders | no |
| `gateway.bind_no_auth` | critical | Remote bind without shared secret | `gateway.bind`, `gateway.auth.*` | no |
| `gateway.loopback_no_auth` | critical | Reverse-proxied loopback may become unauthenticated | `gateway.auth.*`, proxy setup | no |
| `gateway.trusted_proxies_missing` | warn | Reverse-proxy headers are present but not trusted | `gateway.trustedProxies` | no |
| `gateway.http.no_auth` | warn/critical | Gateway HTTP APIs reachable with `auth.mode="none"` | `gateway.auth.mode`, `gateway.http.endpoints.*` | no |
| `gateway.http.session_key_override_enabled` | info | HTTP API callers can override `sessionKey` | `gateway.http.allowSessionKeyOverride` | no |
| `gateway.tools_invoke_http.dangerous_allow` | warn/critical | Re-enables dangerous tools over HTTP API | `gateway.tools.allow` | no |
| `gateway.nodes.allow_commands_dangerous` | warn/critical | Enables high-impact node commands (camera/screen/contacts/calendar/SMS) | `gateway.nodes.allowCommands` | no |
| `gateway.nodes.deny_commands_ineffective` | warn | Pattern-like deny entries do not match shell text or groups | `gateway.nodes.denyCommands` | no |
| `gateway.tailscale_funnel` | critical | Public internet exposure | `gateway.tailscale.mode` | no |
| `gateway.tailscale_serve` | info | Tailnet exposure is enabled via Serve | `gateway.tailscale.mode` | no |
| `gateway.control_ui.allowed_origins_required` | critical | Non-loopback Control UI without explicit browser-origin allowlist | `gateway.controlUi.allowedOrigins` | no |
| `gateway.control_ui.allowed_origins_wildcard` | warn/critical | `allowedOrigins=["*"]` disables browser-origin allowlisting | `gateway.controlUi.allowedOrigins` | no |
| `gateway.control_ui.host_header_origin_fallback` | warn/critical | Enables Host-header origin fallback (DNS rebinding hardening downgrade) | `gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback` | no |
| `gateway.control_ui.insecure_auth` | warn | Insecure-auth compatibility toggle enabled | `gateway.controlUi.allowInsecureAuth` | no |
| `gateway.control_ui.device_auth_disabled` | critical | Disables device identity check | `gateway.controlUi.dangerouslyDisableDeviceAuth` | no |
| `gateway.real_ip_fallback_enabled` | warn/critical | Trusting `X-Real-IP` fallback can enable source-IP spoofing via proxy misconfig | `gateway.allowRealIpFallback`, `gateway.trustedProxies` | no |
| `gateway.token_too_short` | warn | Short shared token is easier to brute force | `gateway.auth.token` | no |
| `gateway.auth_no_rate_limit` | warn | Exposed auth without rate limiting increases brute-force risk | `gateway.auth.rateLimit` | no |
| `gateway.trusted_proxy_auth` | critical | Proxy identity now becomes the auth boundary | `gateway.auth.mode="trusted-proxy"` | no |
| `gateway.trusted_proxy_no_proxies` | critical | Trusted-proxy auth without trusted proxy IPs is unsafe | `gateway.trustedProxies` | no |
| `gateway.trusted_proxy_no_user_header` | critical | Trusted-proxy auth cannot resolve user identity safely | `gateway.auth.trustedProxy.userHeader` | no |
| `gateway.trusted_proxy_no_allowlist` | warn | Trusted-proxy auth accepts any authenticated upstream user | `gateway.auth.trustedProxy.allowUsers` | no |
| `gateway.probe_auth_secretref_unavailable` | warn | Deep probe could not resolve auth SecretRefs in this command path | deep-probe auth source / SecretRef availability | no |
| `gateway.probe_failed` | warn/critical | Live Gateway probe failed | gateway reachability/auth | no |
| `discovery.mdns_full_mode` | warn/critical | mDNS full mode advertises `cliPath`/`sshPort` metadata on local network | `discovery.mdns.mode`, `gateway.bind` | no |
| `config.insecure_or_dangerous_flags` | warn | Any insecure/dangerous debug flags enabled | multiple keys (see finding detail) | no |
| `config.secrets.gateway_password_in_config` | warn | Gateway password is stored directly in config | `gateway.auth.password` | no |
| `config.secrets.hooks_token_in_config` | warn | Hook bearer token is stored directly in config | `hooks.token` | no |
| `hooks.token_reuse_gateway_token` | critical | Hook ingress token also unlocks Gateway auth | `hooks.token`, `gateway.auth.token` | no |
| `hooks.token_too_short` | warn | Easier brute force on hook ingress | `hooks.token` | no |
| `hooks.default_session_key_unset` | warn | Hook agent runs fan out into generated per-request sessions | `hooks.defaultSessionKey` | no |
| `hooks.allowed_agent_ids_unrestricted` | warn/critical | Authenticated hook callers may route to any configured agent | `hooks.allowedAgentIds` | no |
| `hooks.request_session_key_enabled` | warn/critical | External caller can choose sessionKey | `hooks.allowRequestSessionKey` | no |
| `hooks.request_session_key_prefixes_missing` | warn/critical | No bound on external session key shapes | `hooks.allowedSessionKeyPrefixes` | no |
| `hooks.path_root` | critical | Hook path is `/`, making ingress easier to collide or misroute | `hooks.path` | no |
| `hooks.installs_unpinned_npm_specs` | warn | Hook install records are not pinned to immutable npm specs | hook install metadata | no |
| `hooks.installs_missing_integrity` | warn | Hook install records lack integrity metadata | hook install metadata | no |
| `hooks.installs_version_drift` | warn | Hook install records drift from installed packages | hook install metadata | no |
| `logging.redact_off` | warn | Sensitive values leak to logs/status | `logging.redactSensitive` | yes |
| `browser.control_invalid_config` | warn | Browser control config is invalid before runtime | `browser.*` | no |
| `browser.control_no_auth` | critical | Browser control exposed without token/password auth | `gateway.auth.*` | no |
| `browser.remote_cdp_http` | warn | Remote CDP over plain HTTP lacks transport encryption | browser profile `cdpUrl` | no |
| `browser.remote_cdp_private_host` | warn | Remote CDP targets a private/internal host | browser profile `cdpUrl`, `browser.ssrfPolicy.*` | no |
| `sandbox.docker_config_mode_off` | warn | Sandbox Docker config present but inactive | `agents.*.sandbox.mode` | no |
| `sandbox.bind_mount_non_absolute` | warn | Relative bind mounts can resolve unpredictably | `agents.*.sandbox.docker.binds[]` | no |
| `sandbox.dangerous_bind_mount` | critical | Sandbox bind mount targets blocked system, credential, or Docker socket paths | `agents.*.sandbox.docker.binds[]` | no |
| `sandbox.dangerous_network_mode` | critical | Sandbox Docker network uses `host` or `container:*` namespace-join mode | `agents.*.sandbox.docker.network` | no |
| `sandbox.dangerous_seccomp_profile` | critical | Sandbox seccomp profile weakens container isolation | `agents.*.sandbox.docker.securityOpt` | no |
| `sandbox.dangerous_apparmor_profile` | critical | Sandbox AppArmor profile weakens container isolation | `agents.*.sandbox.docker.securityOpt` | no |
| `sandbox.browser_cdp_bridge_unrestricted` | warn | Sandbox browser bridge is exposed without source-range restriction | `sandbox.browser.cdpSourceRange` | no |
| `sandbox.browser_container.non_loopback_publish` | critical | Existing browser container publishes CDP on non-loopback interfaces | browser sandbox container publish config | no |
| `sandbox.browser_container.hash_label_missing` | warn | Existing browser container predates current config-hash labels | `openclaw sandbox recreate --browser --all` | no |
| `sandbox.browser_container.hash_epoch_stale` | warn | Existing browser container predates current browser config epoch | `openclaw sandbox recreate --browser --all` | no |
| `tools.exec.host_sandbox_no_sandbox_defaults` | warn | `exec host=sandbox` fails closed when sandbox is off | `tools.exec.host`, `agents.defaults.sandbox.mode` | no |
| `tools.exec.host_sandbox_no_sandbox_agents` | warn | Per-agent `exec host=sandbox` fails closed when sandbox is off | `agents.list[].tools.exec.host`, `agents.list[].sandbox.mode` | no |
| `tools.exec.security_full_configured` | warn/critical | Host exec is running with `security="full"` | `tools.exec.security`, `agents.list[].tools.exec.security` | no |
| `tools.exec.auto_allow_skills_enabled` | warn | Exec approvals trust skill bins implicitly | `~/.openclaw/exec-approvals.json` | no |
| `tools.exec.allowlist_interpreter_without_strict_inline_eval` | warn | Interpreter allowlists permit inline eval without forced reapproval | `tools.exec.strictInlineEval`, `agents.list[].tools.exec.strictInlineEval`, exec approvals allowlist | no |
| `tools.exec.safe_bins_interpreter_unprofiled` | warn | Interpreter/runtime bins in `safeBins` without explicit profiles broaden exec risk | `tools.exec.safeBins`, `tools.exec.safeBinProfiles`, `agents.list[].tools.exec.*` | no |
| `tools.exec.safe_bins_broad_behavior` | warn | Broad-behavior tools in `safeBins` weaken the low-risk stdin-filter trust model | `tools.exec.safeBins`, `agents.list[].tools.exec.safeBins` | no |
| `tools.exec.safe_bin_trusted_dirs_risky` | warn | `safeBinTrustedDirs` includes mutable or risky directories | `tools.exec.safeBinTrustedDirs`, `agents.list[].tools.exec.safeBinTrustedDirs` | no |
| `skills.workspace.symlink_escape` | warn | Workspace `skills/**/SKILL.md` resolves outside workspace root (symlink-chain drift) | workspace `skills/**` filesystem state | no |
| `plugins.extensions_no_allowlist` | warn | Plugins are installed without an explicit plugin allowlist | `plugins.allowlist` | no |
| `plugins.installs_unpinned_npm_specs` | warn | Plugin install records are not pinned to immutable npm specs | plugin install metadata | no |
| `plugins.installs_missing_integrity` | warn | Plugin install records lack integrity metadata | plugin install metadata | no |
| `plugins.installs_version_drift` | warn | Plugin install records drift from installed packages | plugin install metadata | no |
| `plugins.code_safety` | warn/critical | Plugin code scan found suspicious or dangerous patterns | plugin code / install source | no |
| `plugins.code_safety.entry_path` | warn | Plugin entry path points into hidden or `node_modules` locations | plugin manifest `entry` | no |
| `plugins.code_safety.entry_escape` | critical | Plugin entry escapes the plugin directory | plugin manifest `entry` | no |
| `plugins.code_safety.scan_failed` | warn | Plugin code scan could not complete | plugin path / scan environment | no |
| `skills.code_safety` | warn/critical | Skill installer metadata/code contains suspicious or dangerous patterns | skill install source | no |
| `skills.code_safety.scan_failed` | warn | Skill code scan could not complete | skill scan environment | no |
| `security.exposure.open_channels_with_exec` | warn/critical | Shared/public rooms can reach exec-enabled agents | `channels.*.dmPolicy`, `channels.*.groupPolicy`, `tools.exec.*`, `agents.list[].tools.exec.*` | no |
| `security.exposure.open_groups_with_elevated` | critical | Open groups + elevated tools create high-impact prompt-injection paths | `channels.*.groupPolicy`, `tools.elevated.*` | no |
| `security.exposure.open_groups_with_runtime_or_fs` | critical/warn | Open groups can reach command/file tools without sandbox/workspace guards | `channels.*.groupPolicy`, `tools.profile/deny`, `tools.fs.workspaceOnly`, `agents.*.sandbox.mode` | no |
| `security.trust_model.multi_user_heuristic` | warn | Config looks multi-user while gateway trust model is personal-assistant | split trust boundaries, or shared-user hardening (`sandbox.mode`, tool deny/workspace scoping) | no |
| `tools.profile_minimal_overridden` | warn | Agent overrides bypass global minimal profile | `agents.list[].tools.profile` | no |
| `plugins.tools_reachable_permissive_policy` | warn | Extension tools reachable in permissive contexts | `tools.profile` + tool allow/deny | no |
| `models.legacy` | warn | Legacy model families are still configured | model selection | no |
| `models.weak_tier` | warn | Configured models are below current recommended tiers | model selection | no |
| `models.small_params` | critical/info | Small models + unsafe tool surfaces raise injection risk | model choice + sandbox/tool policy | no |
| `summary.attack_surface` | info | Roll-up summary of auth, channel, tool, and exposure posture | multiple keys (see finding detail) | no |
- `fs.*` — filesystem permissions on state, config, credentials, auth profiles.
- `gateway.*` — bind mode, auth, Tailscale, Control UI, trusted-proxy setup.
- `hooks.*`, `browser.*`, `sandbox.*`, `tools.exec.*` — per-surface hardening.
- `plugins.*`, `skills.*` — plugin/skill supply chain and scan findings.
- `security.exposure.*` — cross-cutting checks where access policy meets tool blast radius.
See the full catalog with severity levels, fix keys, and auto-fix support at
[Security audit checks](/gateway/security/audit-checks).
## Control UI over HTTP
@@ -368,48 +265,53 @@ does not extend to node-role Control UI sessions.
## Insecure or dangerous flags summary
`openclaw security audit` includes `config.insecure_or_dangerous_flags` when
known insecure/dangerous debug switches are enabled. That check currently
aggregates:
`openclaw security audit` raises `config.insecure_or_dangerous_flags` when
known insecure/dangerous debug switches are enabled. Keep these unset in
production.
- `gateway.controlUi.allowInsecureAuth=true`
- `gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback=true`
- `gateway.controlUi.dangerouslyDisableDeviceAuth=true`
- `hooks.gmail.allowUnsafeExternalContent=true`
- `hooks.mappings[<index>].allowUnsafeExternalContent=true`
- `tools.exec.applyPatch.workspaceOnly=false`
- `plugins.entries.acpx.config.permissionMode=approve-all`
<AccordionGroup>
<Accordion title="Flags tracked by the audit today">
- `gateway.controlUi.allowInsecureAuth=true`
- `gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback=true`
- `gateway.controlUi.dangerouslyDisableDeviceAuth=true`
- `hooks.gmail.allowUnsafeExternalContent=true`
- `hooks.mappings[<index>].allowUnsafeExternalContent=true`
- `tools.exec.applyPatch.workspaceOnly=false`
- `plugins.entries.acpx.config.permissionMode=approve-all`
</Accordion>
Complete `dangerous*` / `dangerously*` config keys defined in OpenClaw config
schema:
<Accordion title="All `dangerous*` / `dangerously*` keys in the config schema">
Control UI and browser:
- `gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback`
- `gateway.controlUi.dangerouslyDisableDeviceAuth`
- `browser.ssrfPolicy.dangerouslyAllowPrivateNetwork`
- `channels.discord.dangerouslyAllowNameMatching`
- `channels.discord.accounts.<accountId>.dangerouslyAllowNameMatching`
- `channels.slack.dangerouslyAllowNameMatching`
- `channels.slack.accounts.<accountId>.dangerouslyAllowNameMatching`
- `channels.googlechat.dangerouslyAllowNameMatching`
- `channels.googlechat.accounts.<accountId>.dangerouslyAllowNameMatching`
- `channels.msteams.dangerouslyAllowNameMatching`
- `channels.synology-chat.dangerouslyAllowNameMatching` (plugin channel)
- `channels.synology-chat.accounts.<accountId>.dangerouslyAllowNameMatching` (plugin channel)
- `channels.synology-chat.dangerouslyAllowInheritedWebhookPath` (plugin channel)
- `channels.zalouser.dangerouslyAllowNameMatching` (plugin channel)
- `channels.zalouser.accounts.<accountId>.dangerouslyAllowNameMatching` (plugin channel)
- `channels.irc.dangerouslyAllowNameMatching` (plugin channel)
- `channels.irc.accounts.<accountId>.dangerouslyAllowNameMatching` (plugin channel)
- `channels.mattermost.dangerouslyAllowNameMatching` (plugin channel)
- `channels.mattermost.accounts.<accountId>.dangerouslyAllowNameMatching` (plugin channel)
- `channels.telegram.network.dangerouslyAllowPrivateNetwork`
- `channels.telegram.accounts.<accountId>.network.dangerouslyAllowPrivateNetwork`
- `agents.defaults.sandbox.docker.dangerouslyAllowReservedContainerTargets`
- `agents.defaults.sandbox.docker.dangerouslyAllowExternalBindSources`
- `agents.defaults.sandbox.docker.dangerouslyAllowContainerNamespaceJoin`
- `agents.list[<index>].sandbox.docker.dangerouslyAllowReservedContainerTargets`
- `agents.list[<index>].sandbox.docker.dangerouslyAllowExternalBindSources`
- `agents.list[<index>].sandbox.docker.dangerouslyAllowContainerNamespaceJoin`
- `gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback`
- `gateway.controlUi.dangerouslyDisableDeviceAuth`
- `browser.ssrfPolicy.dangerouslyAllowPrivateNetwork`
Channel name-matching (bundled and plugin channels; also available per
`accounts.<accountId>` where applicable):
- `channels.discord.dangerouslyAllowNameMatching`
- `channels.slack.dangerouslyAllowNameMatching`
- `channels.googlechat.dangerouslyAllowNameMatching`
- `channels.msteams.dangerouslyAllowNameMatching`
- `channels.synology-chat.dangerouslyAllowNameMatching` (plugin channel)
- `channels.synology-chat.dangerouslyAllowInheritedWebhookPath` (plugin channel)
- `channels.zalouser.dangerouslyAllowNameMatching` (plugin channel)
- `channels.irc.dangerouslyAllowNameMatching` (plugin channel)
- `channels.mattermost.dangerouslyAllowNameMatching` (plugin channel)
Network exposure:
- `channels.telegram.network.dangerouslyAllowPrivateNetwork` (also per account)
Sandbox Docker (defaults + per-agent):
- `agents.defaults.sandbox.docker.dangerouslyAllowReservedContainerTargets`
- `agents.defaults.sandbox.docker.dangerouslyAllowExternalBindSources`
- `agents.defaults.sandbox.docker.dangerouslyAllowContainerNamespaceJoin`
</Accordion>
</AccordionGroup>
## Reverse Proxy Configuration
@@ -771,7 +673,7 @@ Guidance:
## Configuration Hardening (examples)
### 0) File permissions
### File permissions
Keep config + state private on the gateway host:
@@ -780,7 +682,7 @@ Keep config + state private on the gateway host:
`openclaw doctor` can warn and offer to tighten these permissions.
### 0.4) Network exposure (bind + port + firewall)
### Network exposure (bind, port, firewall)
The Gateway multiplexes **WebSocket + HTTP** on a single port:
@@ -808,7 +710,7 @@ Rules of thumb:
- If you must bind to LAN, firewall the port to a tight allowlist of source IPs; do not port-forward it broadly.
- Never expose the Gateway unauthenticated on `0.0.0.0`.
### 0.4.1) Docker port publishing + UFW (`DOCKER-USER`)
### Docker port publishing with UFW
If you run OpenClaw with Docker on a VPS, remember that published container ports
(`-p HOST:CONTAINER` or Compose `ports:`) are routed through Docker's forwarding
@@ -857,7 +759,7 @@ nmap -sT -p 1-65535 <public-ip> --open
Expected external ports should be only what you intentionally expose (for most
setups: SSH + your reverse proxy ports).
### 0.4.2) mDNS/Bonjour discovery (information disclosure)
### mDNS/Bonjour discovery
The Gateway broadcasts its presence via mDNS (`_openclaw-gw._tcp` on port 5353) for local device discovery. In full mode, this includes TXT records that may expose operational details:
@@ -903,7 +805,7 @@ The Gateway broadcasts its presence via mDNS (`_openclaw-gw._tcp` on port 5353)
In minimal mode, the Gateway still broadcasts enough for device discovery (`role`, `gatewayPort`, `transport`) but omits `cliPath` and `sshPort`. Apps that need CLI path information can fetch it via the authenticated WebSocket connection instead.
### 0.5) Lock down the Gateway WebSocket (local auth)
### Lock down the Gateway WebSocket (local auth)
Gateway auth is **required by default**. If no valid gateway auth path is configured,
the Gateway refuses WebSocket connections (failclosed).
@@ -958,7 +860,7 @@ Rotation checklist (token/password):
3. Update any remote clients (`gateway.remote.token` / `.password` on machines that call into the Gateway).
4. Verify you can no longer connect with the old credentials.
### 0.6) Tailscale Serve identity headers
### Tailscale Serve identity headers
When `gateway.auth.allowTailscale` is `true` (default for Serve), OpenClaw
accepts Tailscale Serve identity headers (`tailscale-user-login`) for Control
@@ -1005,7 +907,7 @@ Trusted proxies:
See [Tailscale](/gateway/tailscale) and [Web overview](/web).
### 0.6.1) Browser control via node host (recommended)
### Browser control via node host (recommended)
If your Gateway is remote but the browser runs on another machine, run a **node host**
on the browser machine and let the Gateway proxy browser actions (see [Browser tool](/tools/browser)).
@@ -1021,7 +923,7 @@ Avoid:
- Exposing relay/control ports over LAN or public Internet.
- Tailscale Funnel for browser control endpoints (public exposure).
### 0.7) Secrets on disk (sensitive data)
### Secrets on disk
Assume anything under `~/.openclaw/` (or `$OPENCLAW_STATE_DIR/`) may contain secrets or private data:
@@ -1040,7 +942,7 @@ Hardening tips:
- Use full-disk encryption on the gateway host.
- Prefer a dedicated OS user account for the Gateway if the host is shared.
### 0.8) Workspace `.env` files
### Workspace `.env` files
OpenClaw loads workspace-local `.env` files for agents and tools, but never lets those files silently override gateway runtime controls.
@@ -1051,7 +953,7 @@ OpenClaw loads workspace-local `.env` files for agents and tools, but never lets
Why: workspace `.env` files frequently live next to agent code, get committed by accident, or get written by tools. Blocking the whole `OPENCLAW_*` prefix means adding a new `OPENCLAW_*` flag later can never regress into silent inheritance from workspace state.
### 0.9) Logs + transcripts (redaction + retention)
### Logs and transcripts (redaction and retention)
Logs and transcripts can leak sensitive info even when access controls are correct:
@@ -1067,7 +969,7 @@ Recommendations:
Details: [Logging](/gateway/logging)
### 1) DMs: pairing by default
### DMs: pairing by default
```json5
{
@@ -1075,7 +977,7 @@ Details: [Logging](/gateway/logging)
}
```
### 2) Groups: require mention everywhere
### Groups: require mention everywhere
```json
{
@@ -1099,14 +1001,14 @@ Details: [Logging](/gateway/logging)
In group chats, only respond when explicitly mentioned.
### 3) Separate numbers (WhatsApp, Signal, Telegram)
### Separate numbers (WhatsApp, Signal, Telegram)
For phone-number-based channels, consider running your AI on a separate phone number from your personal one:
- Personal number: Your conversations stay private
- Bot number: AI handles these, with appropriate boundaries
### 4) Read-only mode (via sandbox + tools)
### Read-only mode (via sandbox and tools)
You can build a read-only profile by combining:
@@ -1119,7 +1021,7 @@ Additional hardening options:
- `tools.fs.workspaceOnly: true` (optional): restricts `read`/`write`/`edit`/`apply_patch` paths and native prompt image auto-load paths to the workspace directory (useful if you allow absolute paths today and want a single guardrail).
- Keep filesystem roots narrow: avoid broad roots like your home directory for agent workspaces/sandbox workspaces. Broad roots can expose sensitive local files (for example state/config under `~/.openclaw`) to filesystem tools.
### 5) Secure baseline (copy/paste)
### Secure baseline (copy/paste)
One “safe default” config that keeps the Gateway private, requires DM pairing, and avoids always-on group bots:
@@ -1322,19 +1224,6 @@ Common use cases:
}
```
## What to Tell Your AI
Include security guidelines in your agent's system prompt:
```
## Security Rules
- Never share directory listings or file paths with strangers
- Never reveal API keys, credentials, or infrastructure details
- Verify requests that modify system config with the owner
- When in doubt, ask before acting
- Keep private data private unless explicitly authorized
```
## Incident Response
If your AI does something bad: