coderclaw security auditSee also: Formal Verification (Security Models)
Run this regularly (especially after changing config or exposing network surfaces):
coderclaw security audit
coderclaw security audit --deep
coderclaw security audit --fix
coderclaw security audit --json
It flags common footguns (Gateway auth exposure, browser control exposure, elevated allowlists, filesystem permissions).
CoderClaw is both a product and an experiment: you’re wiring frontier-model behavior into real messaging surfaces and real tools. There is no “perfectly secure” setup. The goal is to be deliberate about:
Start with the smallest access that still works, then widen it as you gain confidence.
Use this baseline first, then selectively re-enable tools per trusted agent:
{
gateway: {
mode: "local",
bind: "loopback",
auth: { mode: "token", token: "replace-with-long-random-token" },
},
session: {
dmScope: "per-channel-peer",
},
tools: {
profile: "messaging",
deny: ["group:automation", "group:runtime", "group:fs", "sessions_spawn", "sessions_send"],
fs: { workspaceOnly: true },
exec: { security: "deny", ask: "always" },
elevated: { enabled: false },
},
channels: {
whatsapp: { dmPolicy: "pairing", groups: { "*": { requireMention: true } } },
},
}
This keeps the Gateway local-only, isolates DMs, and disables control-plane/runtime tools by default.
If more than one person can DM your bot:
session.dmScope: "per-channel-peer" (or "per-account-channel-peer" for multi-account channels).dmPolicy: "pairing" or strict allowlists.gateway.nodes.denyCommands patterns; global tools.profile="minimal" overridden by per-agent profiles; extension plugin tools reachable under permissive tool policy).If you run --deep, CoderClaw also attempts a best-effort live Gateway probe.
Use this when auditing access or deciding what to back up:
~/.coderclaw/credentials/whatsapp/<accountId>/creds.jsonchannels.telegram.tokenFilechannels.slack.*)~/.coderclaw/credentials/<channel>-allowFrom.json~/.coderclaw/agents/<agentId>/agent/auth-profiles.json~/.coderclaw/credentials/oauth.jsonWhen the audit prints findings, treat this as a priority order:
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 CoderClaw state | filesystem perms on ~/.coderclaw |
yes |
fs.config.perms_writable |
critical | Others can change auth/tool policy/config | filesystem perms on ~/.coderclaw/coderclaw.json |
yes |
fs.config.perms_world_readable |
critical | Config can expose tokens/settings | filesystem perms on config file | yes |
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.tools_invoke_http.dangerous_allow |
warn/critical | Re-enables dangerous tools over HTTP API | gateway.tools.allow |
no |
gateway.tailscale_funnel |
critical | Public internet exposure | gateway.tailscale.mode |
no |
gateway.control_ui.insecure_auth |
critical | Token-only over HTTP, no device identity | gateway.controlUi.allowInsecureAuth |
no |
gateway.control_ui.device_auth_disabled |
critical | Disables device identity check | gateway.controlUi.dangerouslyDisableDeviceAuth |
no |
hooks.token_too_short |
warn | Easier brute force on hook ingress | hooks.token |
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 |
logging.redact_off |
warn | Sensitive values leak to logs/status | logging.redactSensitive |
yes |
sandbox.docker_config_mode_off |
warn | Sandbox Docker config present but inactive | agents.*.sandbox.mode |
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.small_params |
critical/info | Small models + unsafe tool surfaces raise injection risk | model choice + sandbox/tool policy | no |
The Control UI needs a secure context (HTTPS or localhost) to generate device
identity. If you enable gateway.controlUi.allowInsecureAuth, the UI falls back
to token-only auth and skips device pairing when device identity is omitted. This is a security
downgrade—prefer HTTPS (Tailscale Serve) or open the UI on 127.0.0.1.
For break-glass scenarios only, gateway.controlUi.dangerouslyDisableDeviceAuth
disables device identity checks entirely. This is a severe security downgrade;
keep it off unless you are actively debugging and can revert quickly.
coderclaw security audit warns when this setting is enabled.
If you run the Gateway behind a reverse proxy (nginx, Caddy, Traefik, etc.), you should configure gateway.trustedProxies for proper client IP detection.
When the Gateway detects proxy headers (X-Forwarded-For or X-Real-IP) from an address that is not in trustedProxies, it will not treat connections as local clients. If gateway auth is disabled, those connections are rejected. This prevents authentication bypass where proxied connections would otherwise appear to come from localhost and receive automatic trust.
gateway:
trustedProxies:
- "127.0.0.1" # if your proxy runs on localhost
auth:
mode: password
password: ${CODERCLAW_GATEWAY_PASSWORD}
When trustedProxies is configured, the Gateway will use X-Forwarded-For headers to determine the real client IP for local client detection. Make sure your proxy overwrites (not appends to) incoming X-Forwarded-For headers to prevent spoofing.
CoderClaw stores session transcripts on disk under ~/.coderclaw/agents/<agentId>/sessions/*.jsonl.
This is required for session continuity and (optionally) session memory indexing, but it also means
any process/user with filesystem access can read those logs. Treat disk access as the trust
boundary and lock down permissions on ~/.coderclaw (see the audit section below). If you need
stronger isolation between agents, run them under separate OS users or separate hosts.
If a macOS node is paired, the Gateway can invoke system.run on that node. This is remote code execution on the Mac:
CoderClaw can refresh the skills list mid-session:
SKILL.md can update the skills snapshot on the next agent turn.Treat skill folders as trusted code and restrict who can modify them.
Your AI assistant can:
People who message you can:
Most failures here are not fancy exploits — they’re “someone messaged the bot and the bot did what they asked.”
CoderClaw’s stance:
Slash commands and directives are only honored for authorized senders. Authorization is derived from
channel allowlists/pairing plus commands.useAccessGroups (see Configuration
and Slash commands). If a channel allowlist is empty or includes "*",
commands are effectively open for that channel.
/exec is a session-only convenience for authorized operators. It does not write config or
change other sessions.
Two built-in tools can make persistent control-plane changes:
gateway can call config.apply, config.patch, and update.run.cron can create scheduled jobs that keep running after the original chat/task ends.For any agent/surface that handles untrusted content, deny these by default:
{
tools: {
deny: ["gateway", "cron", "sessions_spawn", "sessions_send"],
},
}
commands.restart=false only blocks restart actions. It does not disable gateway config/update actions.
Plugins run in-process with the Gateway. Treat them as trusted code:
plugins.allow allowlists.coderclaw plugins install <npm-spec>), treat it like running untrusted code:
~/.coderclaw/extensions/<pluginId>/ (or $CODERCLAW_STATE_DIR/extensions/<pluginId>/).npm pack and then runs npm install --omit=dev in that directory (npm lifecycle scripts can execute code during install).@scope/[email protected]), and inspect the unpacked code on disk before enabling.Details: Plugins
All current DM-capable channels support a DM policy (dmPolicy or *.dm.policy) that gates inbound DMs before the message is processed:
pairing (default): unknown senders receive a short pairing code and the bot ignores their message until approved. Codes expire after 1 hour; repeated DMs won’t resend a code until a new request is created. Pending requests are capped at 3 per channel by default.allowlist: unknown senders are blocked (no pairing handshake).open: allow anyone to DM (public). Requires the channel allowlist to include "*" (explicit opt-in).disabled: ignore inbound DMs entirely.Approve via CLI:
coderclaw pairing list <channel>
coderclaw pairing approve <channel> <code>
Details + files on disk: Pairing
By default, CoderClaw routes all DMs into the main session so your assistant has continuity across devices and channels. If multiple people can DM the bot (open DMs or a multi-person allowlist), consider isolating DM sessions:
{
session: { dmScope: "per-channel-peer" },
}
This prevents cross-user context leakage while keeping group chats isolated.
Treat the snippet above as secure DM mode:
session.dmScope: "main" (all DMs share one session for continuity).session.dmScope: "per-channel-peer" (each channel+sender pair gets an isolated DM context).If you run multiple accounts on the same channel, use per-account-channel-peer instead. If the same person contacts you on multiple channels, use session.identityLinks to collapse those DM sessions into one canonical identity. See Session Management and Configuration.
CoderClaw has two separate “who can trigger me?” layers:
allowFrom / channels.discord.allowFrom / channels.slack.allowFrom; legacy: channels.discord.dm.allowFrom, channels.slack.dm.allowFrom): who is allowed to talk to the bot in direct messages.
dmPolicy="pairing", approvals are written to ~/.coderclaw/credentials/<channel>-allowFrom.json (merged with config allowlists).channels.whatsapp.groups, channels.telegram.groups, channels.imessage.groups: per-group defaults like requireMention; when set, it also acts as a group allowlist (include "*" to keep allow-all behavior).groupPolicy="allowlist" + groupAllowFrom: restrict who can trigger the bot inside a group session (WhatsApp/Telegram/Signal/iMessage/Microsoft Teams).channels.discord.guilds / channels.slack.channels: per-surface allowlists + mention defaults.dmPolicy="open" and groupPolicy="open" as last-resort settings. They should be barely used; prefer pairing + allowlists unless you fully trust every member of the room.Details: Configuration and Groups
Prompt injection is when an attacker crafts a message that manipulates the model into doing something unsafe (“ignore your instructions”, “dump your filesystem”, “follow this link and run commands”, etc.).
Even with strong system prompts, prompt injection is not solved. System prompt guardrails are soft guidance only; hard enforcement comes from tool policy, exec approvals, sandboxing, and channel allowlists (and operators can disable these by design). What helps in practice:
exec, browser, web_fetch, web_search) to trusted agents or explicit allowlists.Red flags to treat as untrusted:
CoderClaw includes explicit bypass flags that disable external-content safety wrapping:
hooks.mappings[].allowUnsafeExternalContenthooks.gmail.allowUnsafeExternalContentallowUnsafeExternalContentGuidance:
Even if only you can message the bot, prompt injection can still happen via any untrusted content the bot reads (web search/fetch results, browser pages, emails, docs, attachments, pasted logs/code). In other words: the sender is not the only threat surface; the content itself can carry adversarial instructions.
When tools are enabled, the typical risk is exfiltrating context or triggering tool calls. Reduce the blast radius by:
web_search / web_fetch / browser off for tool-enabled agents unless needed.input_file / input_image), set tight
gateway.http.endpoints.responses.files.urlAllowlist and
gateway.http.endpoints.responses.images.urlAllowlist, and keep maxUrlParts low.Prompt injection resistance is not uniform across model tiers. Smaller/cheaper models are generally more susceptible to tool misuse and instruction hijacking, especially under adversarial prompts.
Recommendations:
/reasoning and /verbose can expose internal reasoning or tool output that
was not meant for a public channel. In group settings, treat them as debug
only and keep them off unless you explicitly need them.
Guidance:
/reasoning and /verbose disabled in public rooms.Keep config + state private on the gateway host:
~/.coderclaw/coderclaw.json: 600 (user read/write only)~/.coderclaw: 700 (user only)coderclaw doctor can warn and offer to tighten these permissions.
The Gateway multiplexes WebSocket + HTTP on a single port:
18789gateway.port, --port, CODERCLAW_GATEWAY_PORTThis HTTP surface includes the Control UI and the canvas host:
/)/__coderclaw__/canvas/ and /__coderclaw__/a2ui/ (arbitrary HTML/JS; treat as untrusted content)If you load canvas content in a normal browser, treat it like any other untrusted web page:
Bind mode controls where the Gateway listens:
gateway.bind: "loopback" (default): only local clients can connect."lan", "tailnet", "custom") expand the attack surface. Only use them with a shared token/password and a real firewall.Rules of thumb:
0.0.0.0.The Gateway broadcasts its presence via mDNS (_coderclaw-gw._tcp on port 5353) for local device discovery. In full mode, this includes TXT records that may expose operational details:
cliPath: full filesystem path to the CLI binary (reveals username and install location)sshPort: advertises SSH availability on the hostdisplayName, lanHost: hostname informationOperational security consideration: Broadcasting infrastructure details makes reconnaissance easier for anyone on the local network. Even “harmless” info like filesystem paths and SSH availability helps attackers map your environment.
Recommendations:
Minimal mode (default, recommended for exposed gateways): omit sensitive fields from mDNS broadcasts:
{
discovery: {
mdns: { mode: "minimal" },
},
}
Disable entirely if you don’t need local device discovery:
{
discovery: {
mdns: { mode: "off" },
},
}
Full mode (opt-in): include cliPath + sshPort in TXT records:
{
discovery: {
mdns: { mode: "full" },
},
}
Environment variable (alternative): set CODERCLAW_DISABLE_BONJOUR=1 to disable mDNS without config changes.
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.
Gateway auth is required by default. If no token/password is configured, the Gateway refuses WebSocket connections (fail‑closed).
The onboarding wizard generates a token by default (even for loopback) so local clients must authenticate.
Set a token so all WS clients must authenticate:
{
gateway: {
auth: { mode: "token", token: "your-token" },
},
}
Doctor can generate one for you: coderclaw doctor --generate-gateway-token.
Note: gateway.remote.token is only for remote CLI calls; it does not
protect local WS access.
Optional: pin remote TLS with gateway.remote.tlsFingerprint when using wss://.
Local device pairing:
Auth modes:
gateway.auth.mode: "token": shared bearer token (recommended for most setups).gateway.auth.mode: "password": password auth (prefer setting via env: CODERCLAW_GATEWAY_PASSWORD).gateway.auth.mode: "trusted-proxy": trust an identity-aware reverse proxy to authenticate users and pass identity via headers (see Trusted Proxy Auth).Rotation checklist (token/password):
gateway.auth.token or CODERCLAW_GATEWAY_PASSWORD).gateway.remote.token / .password on machines that call into the Gateway).When gateway.auth.allowTailscale is true (default for Serve), CoderClaw
accepts Tailscale Serve identity headers (tailscale-user-login) as
authentication. CoderClaw verifies the identity by resolving the
x-forwarded-for address through the local Tailscale daemon (tailscale whois)
and matching it to the header. This only triggers for requests that hit loopback
and include x-forwarded-for, x-forwarded-proto, and x-forwarded-host as
injected by Tailscale.
Security rule: do not forward these headers from your own reverse proxy. If
you terminate TLS or proxy in front of the gateway, disable
gateway.auth.allowTailscale and use token/password auth (or Trusted Proxy Auth) instead.
Trusted proxies:
gateway.trustedProxies to your proxy IPs.x-forwarded-for (or x-real-ip) from those IPs to determine the client IP for local pairing checks and HTTP auth/local checks.x-forwarded-for and blocks direct access to the Gateway port.See Tailscale and Web overview.
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). Treat node pairing like admin access.
Recommended pattern:
Avoid:
Assume anything under ~/.coderclaw/ (or $CODERCLAW_STATE_DIR/) may contain secrets or private data:
coderclaw.json: config may include tokens (gateway, remote gateway), provider settings, and allowlists.credentials/**: channel credentials (example: WhatsApp creds), pairing allowlists, legacy OAuth imports.agents/<agentId>/agent/auth-profiles.json: API keys + OAuth tokens (imported from legacy credentials/oauth.json).agents/<agentId>/sessions/**: session transcripts (*.jsonl) + routing metadata (sessions.json) that can contain private messages and tool output.extensions/**: installed plugins (plus their node_modules/).sandboxes/**: tool sandbox workspaces; can accumulate copies of files you read/write inside the sandbox.Hardening tips:
700 on dirs, 600 on files).Logs and transcripts can leak sensitive info even when access controls are correct:
Recommendations:
logging.redactSensitive: "tools"; default).logging.redactPatterns (tokens, hostnames, internal URLs).coderclaw status --all (pasteable, secrets redacted) over raw logs.Details: Logging
{
channels: { whatsapp: { dmPolicy: "pairing" } },
}
{
"channels": {
"whatsapp": {
"groups": {
"*": { "requireMention": true }
}
}
},
"agents": {
"list": [
{
"id": "main",
"groupChat": { "mentionPatterns": ["@coderclaw", "@mybot"] }
}
]
}
}
In group chats, only respond when explicitly mentioned.
Consider running your AI on a separate phone number from your personal one:
You can already build a read-only profile by combining:
agents.defaults.sandbox.workspaceAccess: "ro" (or "none" for no workspace access)write, edit, apply_patch, exec, process, etc.We may add a single readOnlyMode flag later to simplify this configuration.
Additional hardening options:
tools.exec.applyPatch.workspaceOnly: true (default): ensures apply_patch cannot write/delete outside the workspace directory even when sandboxing is off. Set to false only if you intentionally want apply_patch to touch files outside the workspace.tools.fs.workspaceOnly: true (optional): restricts read/write/edit/apply_patch paths to the workspace directory (useful if you allow absolute paths today and want a single guardrail).One “safe default” config that keeps the Gateway private, requires DM pairing, and avoids always-on group bots:
{
gateway: {
mode: "local",
bind: "loopback",
port: 18789,
auth: { mode: "token", token: "your-long-random-token" },
},
channels: {
whatsapp: {
dmPolicy: "pairing",
groups: { "*": { requireMention: true } },
},
},
}
If you want “safer by default” tool execution too, add a sandbox + deny dangerous tools for any non-owner agent (example below under “Per-agent access profiles”).
Dedicated doc: Sandboxing
Two complementary approaches:
agents.defaults.sandbox, host gateway + Docker-isolated tools): SandboxingNote: to prevent cross-agent access, keep agents.defaults.sandbox.scope at "agent" (default)
or "session" for stricter per-session isolation. scope: "shared" uses a
single container/workspace.
Also consider agent workspace access inside the sandbox:
agents.defaults.sandbox.workspaceAccess: "none" (default) keeps the agent workspace off-limits; tools run against a sandbox workspace under ~/.coderclaw/sandboxesagents.defaults.sandbox.workspaceAccess: "ro" mounts the agent workspace read-only at /agent (disables write/edit/apply_patch)agents.defaults.sandbox.workspaceAccess: "rw" mounts the agent workspace read/write at /workspaceImportant: tools.elevated is the global baseline escape hatch that runs exec on the host. Keep tools.elevated.allowFrom tight and don’t enable it for strangers. You can further restrict elevated per agent via agents.list[].tools.elevated. See Elevated Mode.
Enabling browser control gives the model the ability to drive a real browser. If that browser profile already contains logged-in sessions, the model can access those accounts and data. Treat browser profiles as sensitive state:
coderclaw profile).gateway.nodes.browser.mode="off").With multi-agent routing, each agent can have its own sandbox + tool policy: use this to give full access, read-only, or no access per agent. See Multi-Agent Sandbox & Tools for full details and precedence rules.
Common use cases:
{
agents: {
list: [
{
id: "personal",
workspace: "~/.coderclaw/workspace-personal",
sandbox: { mode: "off" },
},
],
},
}
{
agents: {
list: [
{
id: "family",
workspace: "~/.coderclaw/workspace-family",
sandbox: {
mode: "all",
scope: "agent",
workspaceAccess: "ro",
},
tools: {
allow: ["read"],
deny: ["write", "edit", "apply_patch", "exec", "process", "browser"],
},
},
],
},
}
{
agents: {
list: [
{
id: "public",
workspace: "~/.coderclaw/workspace-public",
sandbox: {
mode: "all",
scope: "agent",
workspaceAccess: "none",
},
// Session tools can reveal sensitive data from transcripts. By default CoderClaw limits these tools
// to the current session + spawned subagent sessions, but you can clamp further if needed.
// See `tools.sessions.visibility` in the configuration reference.
tools: {
sessions: { visibility: "tree" }, // self | tree | agent | all
allow: [
"sessions_list",
"sessions_history",
"sessions_send",
"sessions_spawn",
"session_status",
"whatsapp",
"telegram",
"slack",
"discord",
],
deny: [
"read",
"write",
"edit",
"apply_patch",
"exec",
"process",
"browser",
"canvas",
"nodes",
"cron",
"gateway",
"image",
],
},
},
],
},
}
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
If your AI does something bad:
coderclaw gateway process.gateway.bind: "loopback" (or disable Tailscale Funnel/Serve) until you understand what happened.dmPolicy: "disabled" / require mentions, and remove "*" allow-all entries if you had them.gateway.auth.token / CODERCLAW_GATEWAY_PASSWORD) and restart.gateway.remote.token / .password) on any machine that can call the Gateway.auth-profiles.json)./tmp/coderclaw/coderclaw-YYYY-MM-DD.log (or logging.file).~/.coderclaw/agents/<agentId>/sessions/*.jsonl.gateway.bind, gateway.auth, dm/group policies, tools.elevated, plugin changes).coderclaw security audit --deep and confirm critical findings are resolved.CI runs detect-secrets scan --baseline .secrets.baseline in the secrets job.
If it fails, there are new candidates not yet in the baseline.
Reproduce locally:
detect-secrets scan --baseline .secrets.baseline
detect-secrets scan finds candidates and compares them to the baseline.detect-secrets audit opens an interactive review to mark each baseline
item as real or false positive.For false positives: run the interactive audit and mark them as false:
detect-secrets audit .secrets.baseline
.detect-secrets.cfg and regenerate the
baseline with matching --exclude-files / --exclude-lines flags (the config
file is reference-only; detect-secrets doesn’t read it automatically).Commit the updated .secrets.baseline once it reflects the intended state.
Found a vulnerability in CoderClaw? Please report responsibly: