OpenClaw Security Audit Check IDs Reference
Reference catalog of checkIds emitted by the openclaw security audit, including severity, impact, and fix paths. Essential for developers and operators interpreting audit findings and hardening gateway deployments.
Read this 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
openclaw security audit produces structured findings that are identified by checkId. This page serves as the reference catalog for those IDs. For the high-level threat model and hardening guidance, see Security.
Certain checks require openclaw security audit --deep: plugin and skill code scans (plugins.code_safety*, skills.code_safety*) as well as live Gateway probe checks (gateway.probe_*). All other entries in this table operate on a standard openclaw security audit.
A severity such as warn/critical indicates that the same checkId can be issued at either level, depending on configuration (for instance, whether the Gateway is exposed remotely). High-signal values you are most likely to encounter in actual deployments (not exhaustive):
checkId | Severity | Why it matters | Primary fix key/path | Auto-fix |
|---|---|---|---|---|
fs.state_dir.perms_world_writable | critical | Full OpenClaw state can be altered by other users or processes | filesystem perms on ~/.openclaw | yes |
fs.state_dir.perms_group_writable | warn | Group members have write access to the entire OpenClaw state | filesystem perms on ~/.openclaw | yes |
fs.state_dir.perms_readable | warn | State directory is readable by anyone outside the owner | filesystem perms on ~/.openclaw | yes |
fs.state_dir.symlink | warn | State dir target shifts into a separate trust domain | state dir filesystem layout | no |
fs.config.perms_writable | critical | Auth, tool policy, or config can be changed by outsiders | filesystem perms on ~/.openclaw/openclaw.json | yes |
fs.config.symlink | warn | Symlinked config files block writes and introduce a new trust boundary | use a regular config file or set OPENCLAW_CONFIG_PATH to the actual file | no |
fs.config.perms_group_readable | warn | Config tokens and settings are readable by group users | filesystem perms on config file | yes |
fs.config.perms_world_readable | critical | Tokens and settings may be exposed through the config | filesystem perms on config file | yes |
fs.config_include.perms_writable | critical | Others can tamper with the config include file | include-file perms referenced from openclaw.json | yes |
fs.config_include.perms_group_readable | warn | Group users can view secrets and settings pulled in via includes | include-file perms referenced from openclaw.json | yes |
fs.config_include.perms_world_readable | critical | Included secrets and settings are open to all users | include-file perms referenced from openclaw.json | yes |
fs.auth_profiles.perms_writable | critical | Stored model credentials can be injected or swapped by others | agents/<agentId>/agent/auth-profiles.json perms | yes |
fs.auth_profiles.perms_readable | warn | API keys and OAuth tokens are readable by others | agents/<agentId>/agent/auth-profiles.json perms | yes |
fs.credentials_dir.perms_writable | critical | Channel pairing and credential state can be changed by others | filesystem perms on ~/.openclaw/credentials | yes |
fs.credentials_dir.perms_readable | warn | Channel credential state is readable by others | filesystem perms on ~/.openclaw/credentials | yes |
fs.sessions_store.perms_readable | warn | Session transcripts and metadata can be read by others | session store perms | yes |
fs.log_file.perms_readable | warn | Redacted logs still hold sensitive data and are readable by others | gateway log file perms | yes |
fs.synced_dir | warn | iCloud/Dropbox/Drive storage of state/config widens token and transcript exposure | relocate config and state outside synced folders | no |
gateway.bind_no_auth | critical | Remote bind active without a shared secret | gateway.bind, gateway.auth.* | no |
gateway.loopback_no_auth | critical | Loopback behind a reverse proxy may lose authentication | gateway.auth.*, proxy setup | no |
gateway.trusted_proxies_missing | warn | Reverse-proxy headers exist but are not trusted | gateway.trustedProxies | no |
gateway.http.no_auth | warn/critical | Gateway HTTP APIs are exposed with auth.mode="none" | gateway.auth.mode, gateway.http.endpoints.*, plugins.entries.admin-http-rpc | 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 | Dangerous tools become available again over HTTP API for owner/admin callers | gateway.tools.allow | no |
gateway.nodes.allow_commands_dangerous | warn/critical | High-impact node commands (desktop input/camera/screen/contacts/calendar/SMS) get enabled | gateway.nodes.commands.allow | no |
gateway.nodes.deny_commands_ineffective | warn | Pattern-like deny entries fail to match shell text or groups | gateway.nodes.commands.deny | no |
gateway.tailscale_funnel | critical | Direct exposure to the public internet | gateway.tailscale.mode | no |
gateway.tailscale_serve | info | Tailnet exposure is turned on through Serve | gateway.tailscale.mode | no |
gateway.control_ui.allowed_origins_required | critical | Non-loopback Control UI without an explicit browser-origin allowlist | gateway.controlUi.allowedOrigins | no |
gateway.control_ui.allowed_origins_wildcard | warn/critical | Browser-origin allowlisting gets switched off by allowedOrigins=["*"] | gateway.controlUi.allowedOrigins | no |
gateway.control_ui.host_header_origin_fallback | warn/critical | Host-header origin fallback becomes active, weakening DNS rebinding defenses | gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback | no |
gateway.real_ip_fallback_enabled | warn/critical | Source-IP spoofing becomes possible when X-Real-IP fallback is trusted due to proxy misconfig | gateway.allowRealIpFallback, gateway.trustedProxies | no |
gateway.token_too_short | warn | Brute forcing a short shared token is less difficult | gateway.auth.token | no |
gateway.auth_no_rate_limit | warn | Brute-force risk climbs when auth is exposed without rate limiting | gateway.auth.rateLimit | no |
gateway.trusted_proxy_auth | critical | The auth boundary shifts to proxy identity | gateway.auth.mode="trusted-proxy" | no |
gateway.trusted_proxy_no_proxies | critical | Trusted-proxy auth becomes unsafe without trusted proxy IPs | gateway.trustedProxies | no |
gateway.trusted_proxy_no_user_header | critical | User identity cannot be safely determined through trusted-proxy auth | gateway.auth.trustedProxy.userHeader | no |
gateway.trusted_proxy_no_allowlist | warn | Any authenticated upstream user is accepted by trusted-proxy auth | gateway.auth.trustedProxy.allowUsers | no |
gateway.trusted_proxy_allow_loopback | warn | Explicitly allowed loopback proxy sources are accepted by trusted-proxy auth | gateway.auth.trustedProxy.allowLoopback | no |
gateway.probe_auth_secretref_unavailable | warn | Auth SecretRefs could not be resolved by the deep probe in this command path | deep-probe auth source / SecretRef availability | no |
gateway.probe_failed | warn | The live Gateway probe did not succeed (--deep only) | gateway reachability/auth | no |
discovery.mdns_full_mode | warn/critical | mDNS full mode puts cliPath/sshPort metadata on the local network | discovery.mdns.mode, gateway.bind | no |
config.insecure_or_dangerous_flags | warn | One insecure or dangerous debug flag is turned on | key named in finding detail | no |
security.audit.suppressions.active | info | Suppressions configured in audit output can cause filtering | security.audit.suppressions | no |
config.secrets.gateway_password_in_config | warn | The Gateway password sits in config as plain text | gateway.auth.password | no |
config.secrets.hooks_token_in_config | warn | The hook bearer token sits in config as plain text | hooks.token | no |
hooks.token_reuse_gateway_token | critical | Gateway auth is also opened by the hook ingress token | hooks.token, gateway.auth.token, gateway.auth.password | no |
hooks.token_too_short | warn | Brute force on hook ingress becomes simpler | hooks.token | no |
hooks.default_session_key_unset | warn | Hook agent runs spread into generated per-request sessions | hooks.defaultSessionKey | no |
hooks.allowed_agent_ids_unrestricted | warn/critical | Authenticated hook callers can reach any configured agent | hooks.allowedAgentIds | no |
hooks.request_session_key_enabled | warn/critical | sessionKey selection is left to the external caller | hooks.allowRequestSessionKey | no |
hooks.request_session_key_prefixes_missing | warn/critical | External session key shapes have no bound | hooks.allowedSessionKeyPrefixes | no |
hooks.path_root | critical | Ingress collisions or misroutes get easier because the hook path is / | hooks.path | no |
hooks.installs_unpinned_npm_specs | warn | Hook install records lack pinning to immutable npm specs | hook install metadata | no |
hooks.installs_missing_integrity | warn | Integrity metadata is missing from hook install records | hook install metadata | no |
hooks.installs_version_drift | warn | Installed packages and hook install records fall out of sync | hook install metadata | no |
browser.control_invalid_config | warn | Browser control config is invalid before runtime | browser.* | no |
browser.control_no_auth | critical | Token or password authentication is absent from exposed browser control | gateway.auth.* | no |
browser.remote_cdp_http | warn | Transport encryption is missing for remote CDP over plain HTTP | browser profile cdpUrl | no |
browser.remote_cdp_private_host | warn | Remote CDP points at a private or internal host | browser profile cdpUrl, browser.ssrfPolicy.* | no |
browser.extension_relay_legacy_auth | warn | Legacy extension relay still has bearer, Basic, or token authentication active | browser.extensionRelay.allowLegacyAuth | no |
sandbox.docker_config_mode_off | warn | Sandbox Docker configuration exists but is not in use | agents.*.sandbox.mode | no |
sandbox.bind_mount_non_absolute | warn | Relative bind mounts may resolve in unexpected ways | agents.*.sandbox.docker.binds[] | no |
sandbox.dangerous_bind_mount | critical | Bind mounts in the sandbox target blocked system, credential, or Docker socket paths | agents.*.sandbox.docker.binds[] | no |
sandbox.dangerous_network_mode | critical | Sandbox Docker network joins the host or container:* namespace | agents.*.sandbox.docker.network | no |
sandbox.dangerous_seccomp_profile | critical | Container isolation is weakened by the sandbox seccomp profile | agents.*.sandbox.docker.securityOpt | no |
sandbox.dangerous_apparmor_profile | critical | Container isolation is weakened by the sandbox AppArmor profile | agents.*.sandbox.docker.securityOpt | no |
sandbox.browser_cdp_bridge_unrestricted | warn | Sandbox browser bridge has no source-range restriction on its exposure | sandbox.browser.cdpSourceRange | no |
sandbox.browser_container.non_loopback_publish | critical | CDP is published on non-loopback interfaces by the existing browser container | browser sandbox container publish config | no |
sandbox.browser_container.hash_label_missing | warn | Current config-hash labels are missing from the existing browser container | openclaw sandbox recreate --browser --all | no |
sandbox.browser_container.hash_epoch_stale | warn | The existing browser container is older than the current browser config epoch | openclaw sandbox recreate --browser --all | no |
sandbox.browser_container.docker_probe_timeout | warn | The Docker label probe for the browser container did not respond in time | Docker daemon reachability | no |
tools.exec.host_sandbox_no_sandbox_defaults | warn | exec host=sandbox closes off when sandbox is disabled | tools.exec.host, agents.defaults.sandbox.mode | no |
tools.exec.host_sandbox_no_sandbox_agents | warn | Per-agent exec host=sandbox closes off when sandbox is disabled | agents.entries.*.tools.exec.host, agents.entries.*.sandbox.mode | no |
tools.exec.security_full_configured | warn/critical | Host exec operates with security="full" | tools.exec.security, agents.entries.*.tools.exec.security | no |
tools.exec.agent_skill_mcp_boundary_drift | warn | Agent skill allowlists exist while host exec can access MCP clients or registries | agents.entries.*.tools.exec.*, sandbox/OS isolation, MCP server credentials | no |
tools.exec.fs_tools_disabled_but_exec_enabled | warn | Shell execution is not forced read-only by the filesystem tool policy | tools.deny, agents.entries.*.tools.deny, agents.*.sandbox.workspaceAccess | no |
tools.exec.auto_allow_skills_enabled | warn | Skill bins are trusted without question by exec approvals | host approvals file | no |
tools.exec.allowlist_interpreter_without_strict_inline_eval | warn | Inline eval is allowed by interpreter allowlists without mandatory reapproval | tools.exec.strictInlineEval, agents.entries.*.tools.exec.strictInlineEval, exec approvals allowlist | no |
tools.exec.safe_bins_interpreter_unprofiled | warn | Interpreter and runtime bins in safeBins lack explicit profiles, raising exec risk | tools.exec.safeBins, tools.exec.safeBinProfiles, agents.entries.*.tools.exec.* | no |
tools.exec.safe_bins_broad_behavior | warn | The low-risk stdin-filter trust model is undermined by broad-behavior tools in safeBins | tools.exec.safeBins, agents.entries.*.tools.exec.safeBins | no |
tools.exec.safe_bin_trusted_dirs_risky | warn | Mutable or risky directories are included by safeBinTrustedDirs | tools.exec.safeBinTrustedDirs, agents.entries.*.tools.exec.safeBinTrustedDirs | no |
tools.elevated.allowFrom.<provider>.wildcard | critical | Every sender gets approval because tools.elevated.allowFrom.<provider> includes "*" | tools.elevated.allowFrom.<provider> | no |
tools.elevated.allowFrom.<provider>.large | warn | More than 25 entries sit in the elevated allowlist for <provider> | tools.elevated.allowFrom.<provider> | no |
skills.workspace.symlink_escape | warn | Workspace skills/**/SKILL.md lands outside the workspace root due to symlink-chain drift | workspace skills/** filesystem state | no |
skills.workspace.scan_truncated | warn | The workspace skill scan stopped early after hitting its directory-visit cap | flatten/simplify the workspace skills/ directory tree | no |
plugins.extensions_no_allowlist | warn | Plugins get installed with no explicit plugin allowlist in place | plugins.allowlist | no |
plugins.allow_phantom_entries | warn | An ID in plugins.allow has no installed plugin that matches it | plugins.allow | no |
plugins.installs_unpinned_npm_specs | warn | Plugin index records are not pinned down to immutable npm specs | plugin install metadata | no |
plugins.installs_missing_integrity | warn | Integrity metadata is missing from plugin index records | plugin install metadata | no |
plugins.installs_version_drift | warn | Installed packages drift from what plugin index records describe | plugin install metadata | no |
plugins.code_safety | warn/critical | Suspicious or dangerous patterns turned up in the plugin code scan (--deep only) | plugin code / install source | no |
plugins.code_safety.entry_path | warn | The plugin entry path heads into hidden or node_modules spots | plugin manifest entry | no |
plugins.code_safety.entry_escape | critical | The plugin entry breaks out of the plugin directory | plugin manifest entry | no |
plugins.code_safety.manifest_parse_error | warn | Parsing the plugin manifest failed during the code-safety scan | plugin manifest file | no |
plugins.code_safety.scan_failed | warn | The plugin code scan never finished (--deep only) | plugin path / scan environment | no |
plugins.<pluginId>.security_audit_failed | warn | A security audit collector owned by a plugin raised an error | that plugin's security-audit collector | no |
skills.code_safety | warn/critical | Suspicious or dangerous patterns appear in skill installer metadata/code (--deep only) | skill install source | no |
skills.code_safety.scan_failed | warn | The skill code scan could not wrap up (--deep only) | skill scan environment | no |
channels.discord.allowlisted_groups.broad_members | warn | Allowlisted Discord guild/channel targets carry no member or role restriction | channels.discord.guilds.*.users/roles, per-channel users/roles | no |
security.exposure.open_channels_with_exec | warn/critical | Exec-enabled agents are reachable from shared/public rooms | channels.*.dmPolicy, channels.*.groupPolicy, tools.exec.*, agents.entries.*.tools.exec.* | no |
security.exposure.open_groups_with_elevated | critical | High-impact prompt-injection paths emerge from open DMs/groups plus elevated tools | top-level or nested DM policy paths, account overrides, channels.*.groupPolicy | no |
security.exposure.open_groups_with_runtime_or_fs | critical/warn | Command/file tools are reachable from open DMs/groups without sandbox/workspace guards | DM/group policy paths, tools.profile/deny, tools.fs.workspaceOnly, agents.*.sandbox.mode | no |
security.exposure.open_groups_with_control_plane_tools | critical | Gateway and cron control-plane tools are reachable through open DMs or groups | DM/group policy paths, tools.allow, tools.alsoAllow, tools.profile, gateway, cron | no |
security.trust_model.multi_user_heuristic | warn | Configuration suggests multi-user setup while gateway trust model assumes a personal assistant | split trust boundaries, or shared-user hardening (sandbox.mode, tool deny/workspace scoping) | no |
tools.profile_minimal_overridden | warn | Global minimal profile is bypassed by agent overrides | agents.entries.*.tools.profile | no |
plugins.tools_reachable_permissive_policy | warn | Extension tools are accessible in permissive contexts | tools.profile + tool allow/deny | no |
models.legacy | warn | Legacy model families remain in configuration | model selection | no |
models.weak_tier | warn | Configured models fall below current recommended tiers | model selection | no |
models.small_params | critical/info | Injection risk rises with small models and unsafe tool surfaces raise injection risk | model choice + sandbox/tool policy | no |
channels.<provider>.dm.open | critical | <provider> DM policy is "open"; the bot can be DMed by anyone | channels.<provider>.dmPolicy, .allowFrom | no |
channels.<provider>.dm.open_invalid | warn | dmPolicy="open" without "*" in allowFrom is inconsistent | channels.<provider>.allowFrom | no |
channels.<provider>.dm.scope_main_multiuser | warn | The main session is currently shared by multiple DM senders | session.dmScope | no |
channels.<provider>.allowFrom.dangerous_name_matching_enabled | info | dangerouslyAllowNameMatching re-enables mutable name/email/tag sender matching | disable dangerouslyAllowNameMatching, use stable sender IDs | no |
channels.<provider>.account.read_only_resolution | warn | A channel account could not be fully resolved for audit (missing secret/gateway) | ensure referenced secrets are resolvable, or run against a live gateway snapshot | no |
channels.<provider>.warning.<n> | info/warn/critical | Provider-specific security warning, classified from free-form plugin text | see finding detail | no |
summary.attack_surface | info | Roll-up summary of auth, channel, tool, and exposure posture | multiple keys (see finding detail) | no |
channels.<provider>.* and tools.elevated.allowFrom.<provider>.* checkIds are
generated per configured channel/provider, so <provider> is a real channel id
(for example telegram, discord) in actual output, not a literal string.