security: simplify remediation steps with copy-paste CLI commands
This commit is contained in:
parent
83de980d6c
commit
5ae78f5513
@ -138,8 +138,7 @@ export function collectSecretsInConfigFindings(cfg: ClawdbotConfig): SecurityAud
|
||||
title: "Gateway password is stored in config",
|
||||
detail:
|
||||
"gateway.auth.password is set in the config file; prefer environment variables for secrets when possible.",
|
||||
remediation:
|
||||
"Prefer CLAWDBOT_GATEWAY_PASSWORD (env) and remove gateway.auth.password from disk.",
|
||||
remediation: `Run: export CLAWDBOT_GATEWAY_PASSWORD="your-password" && clawdbot config unset gateway.auth.password`,
|
||||
});
|
||||
}
|
||||
|
||||
@ -188,7 +187,7 @@ export function collectHooksHardeningFindings(cfg: ClawdbotConfig): SecurityAudi
|
||||
title: "Hooks token reuses the Gateway token",
|
||||
detail:
|
||||
"hooks.token matches gateway.auth token; compromise of hooks expands blast radius to the Gateway API.",
|
||||
remediation: "Use a separate hooks.token dedicated to hook ingress.",
|
||||
remediation: `Run: clawdbot config set hooks.token "$(openssl rand -hex 32)"`,
|
||||
});
|
||||
}
|
||||
|
||||
@ -199,7 +198,7 @@ export function collectHooksHardeningFindings(cfg: ClawdbotConfig): SecurityAudi
|
||||
severity: "critical",
|
||||
title: "Hooks base path is '/'",
|
||||
detail: "hooks.path='/' would shadow other HTTP endpoints and is unsafe.",
|
||||
remediation: "Use a dedicated path like '/hooks'.",
|
||||
remediation: `Run: clawdbot config set hooks.path /hooks`,
|
||||
});
|
||||
}
|
||||
|
||||
@ -338,7 +337,7 @@ export function collectModelHygieneFindings(cfg: ClawdbotConfig): SecurityAuditF
|
||||
"Older/legacy models can be less robust against prompt injection and tool misuse.\n" +
|
||||
lines +
|
||||
more,
|
||||
remediation: "Prefer modern, instruction-hardened models for any bot that can run tools.",
|
||||
remediation: `Run: clawdbot config set agents.defaults.model.primary claude-sonnet-4-20250514 (or gpt-5)`,
|
||||
});
|
||||
}
|
||||
|
||||
@ -356,8 +355,7 @@ export function collectModelHygieneFindings(cfg: ClawdbotConfig): SecurityAuditF
|
||||
"Smaller/older models are generally more susceptible to prompt injection and tool misuse.\n" +
|
||||
lines +
|
||||
more,
|
||||
remediation:
|
||||
"Use the latest, top-tier model for any bot with tools or untrusted inboxes. Avoid Haiku tiers; prefer GPT-5+ and Claude 4.5+.",
|
||||
remediation: `Run: clawdbot config set agents.defaults.model.primary claude-sonnet-4-20250514 (avoid Haiku; prefer Claude 4.5+ or GPT-5+)`,
|
||||
});
|
||||
}
|
||||
|
||||
@ -506,8 +504,7 @@ export function collectSmallModelRiskFindings(params: {
|
||||
exposureDetail +
|
||||
`\n` +
|
||||
"Small models are not recommended for untrusted inputs.",
|
||||
remediation:
|
||||
'If you must use small models, enable sandboxing for all sessions (agents.defaults.sandbox.mode="all") and disable web_search/web_fetch/browser (tools.deny=["group:web","browser"]).',
|
||||
remediation: `Run: clawdbot config set agents.defaults.sandbox.mode all && clawdbot config set tools.deny '["group:web","browser"]'`,
|
||||
});
|
||||
|
||||
return findings;
|
||||
@ -602,7 +599,7 @@ export async function collectPluginsTrustFindings(params: {
|
||||
(skillCommandsLikelyExposed
|
||||
? "\nNative skill commands are enabled on at least one configured chat surface; treat unpinned/unallowlisted extensions as high risk."
|
||||
: ""),
|
||||
remediation: "Set plugins.allow to an explicit list of plugin ids you trust.",
|
||||
remediation: `Run: clawdbot config set plugins.allow '["plugin-id-1","plugin-id-2"]' (list only trusted plugin IDs)`,
|
||||
});
|
||||
}
|
||||
|
||||
@ -937,7 +934,7 @@ export function collectExposureMatrixFindings(cfg: ClawdbotConfig): SecurityAudi
|
||||
detail:
|
||||
`Found groupPolicy="open" at:\n${openGroups.map((p) => `- ${p}`).join("\n")}\n` +
|
||||
"With tools.elevated enabled, a prompt injection in those rooms can become a high-impact incident.",
|
||||
remediation: `Set groupPolicy="allowlist" and keep elevated allowlists extremely tight.`,
|
||||
remediation: `Run: clawdbot security audit --fix (auto-sets groupPolicy to allowlist)`,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -273,7 +273,7 @@ function collectGatewayConfigFindings(
|
||||
severity: "critical",
|
||||
title: "Gateway binds beyond loopback without auth",
|
||||
detail: `gateway.bind="${bind}" but no gateway.auth token/password is configured.`,
|
||||
remediation: `Set gateway.auth (token recommended) or bind to loopback.`,
|
||||
remediation: `Run: clawdbot config set gateway.auth.mode token && clawdbot config set gateway.auth.token "$(openssl rand -hex 32)"`,
|
||||
});
|
||||
}
|
||||
|
||||
@ -286,8 +286,7 @@ function collectGatewayConfigFindings(
|
||||
"gateway.bind is loopback and gateway.trustedProxies is empty. " +
|
||||
"If you expose the Control UI through a reverse proxy, configure trusted proxies " +
|
||||
"so local-client checks cannot be spoofed.",
|
||||
remediation:
|
||||
"Set gateway.trustedProxies to your proxy IPs or keep the Control UI local-only.",
|
||||
remediation: `Run: clawdbot config set gateway.trustedProxies '["127.0.0.1"]' (replace with your proxy IPs)`,
|
||||
});
|
||||
}
|
||||
|
||||
@ -299,7 +298,7 @@ function collectGatewayConfigFindings(
|
||||
detail:
|
||||
"gateway.bind is loopback but no gateway auth secret is configured. " +
|
||||
"If the Control UI is exposed through a reverse proxy, unauthenticated access is possible.",
|
||||
remediation: "Set gateway.auth (token recommended) or keep the Control UI local-only.",
|
||||
remediation: `Run: clawdbot config set gateway.auth.mode token && clawdbot config set gateway.auth.token "$(openssl rand -hex 32)"`,
|
||||
});
|
||||
}
|
||||
|
||||
@ -309,7 +308,7 @@ function collectGatewayConfigFindings(
|
||||
severity: "critical",
|
||||
title: "Tailscale Funnel exposure enabled",
|
||||
detail: `gateway.tailscale.mode="funnel" exposes the Gateway publicly; keep auth strict and treat it as internet-facing.`,
|
||||
remediation: `Prefer tailscale.mode="serve" (tailnet-only) or set tailscale.mode="off".`,
|
||||
remediation: `Run: clawdbot config set gateway.tailscale.mode serve (or "off" to disable)`,
|
||||
});
|
||||
} else if (tailscaleMode === "serve") {
|
||||
findings.push({
|
||||
@ -327,7 +326,7 @@ function collectGatewayConfigFindings(
|
||||
title: "Control UI allows insecure HTTP auth",
|
||||
detail:
|
||||
"gateway.controlUi.allowInsecureAuth=true allows token-only auth over HTTP and skips device identity.",
|
||||
remediation: "Disable it or switch to HTTPS (Tailscale Serve) or localhost.",
|
||||
remediation: `Run: clawdbot config set gateway.controlUi.allowInsecureAuth false`,
|
||||
});
|
||||
}
|
||||
|
||||
@ -338,7 +337,7 @@ function collectGatewayConfigFindings(
|
||||
title: "DANGEROUS: Control UI device auth disabled",
|
||||
detail:
|
||||
"gateway.controlUi.dangerouslyDisableDeviceAuth=true disables device identity checks for the Control UI.",
|
||||
remediation: "Disable it unless you are in a short-lived break-glass scenario.",
|
||||
remediation: `Run: clawdbot config set gateway.controlUi.dangerouslyDisableDeviceAuth false`,
|
||||
});
|
||||
}
|
||||
|
||||
@ -490,7 +489,7 @@ async function collectChannelSecurityFindings(params: {
|
||||
severity: "critical",
|
||||
title: `${input.label} DMs are open`,
|
||||
detail: `${policyPath}="open" allows anyone to DM the bot.`,
|
||||
remediation: `Use pairing/allowlist; if you really need open DMs, ensure ${allowFromKey} includes "*".`,
|
||||
remediation: `Run: clawdbot config set ${policyPath} allowlist (or add "*" to ${allowFromKey} if open DMs are required)`,
|
||||
});
|
||||
if (!hasWildcard) {
|
||||
findings.push({
|
||||
@ -519,7 +518,7 @@ async function collectChannelSecurityFindings(params: {
|
||||
title: `${input.label} DMs share the main session`,
|
||||
detail:
|
||||
"Multiple DM senders currently share the main session, which can leak context across users.",
|
||||
remediation: 'Set session.dmScope="per-channel-peer" to isolate DM sessions per sender.',
|
||||
remediation: `Run: clawdbot config set session.dmScope per-channel-peer`,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -596,8 +595,7 @@ async function collectChannelSecurityFindings(params: {
|
||||
title: "Discord slash commands are unrestricted",
|
||||
detail:
|
||||
"commands.useAccessGroups=false disables sender allowlists for Discord slash commands unless a per-guild/channel users allowlist is configured; with no users allowlist, any user in allowed guild channels can invoke /… commands.",
|
||||
remediation:
|
||||
"Set commands.useAccessGroups=true (recommended), or configure channels.discord.guilds.<id>.users (or channels.discord.guilds.<id>.channels.<channel>.users).",
|
||||
remediation: `Run: clawdbot config set commands.useAccessGroups true`,
|
||||
});
|
||||
} else if (
|
||||
useAccessGroups &&
|
||||
@ -612,8 +610,7 @@ async function collectChannelSecurityFindings(params: {
|
||||
title: "Discord slash commands have no allowlists",
|
||||
detail:
|
||||
"Discord slash commands are enabled, but neither an owner allowFrom list nor any per-guild/channel users allowlist is configured; /… commands will be rejected for everyone.",
|
||||
remediation:
|
||||
"Add your user id to channels.discord.dm.allowFrom (or approve yourself via pairing), or configure channels.discord.guilds.<id>.users.",
|
||||
remediation: `Run: clawdbot pair discord (recommended), or: clawdbot config set channels.discord.dm.allowFrom '["YOUR_USER_ID"]'`,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -650,7 +647,7 @@ async function collectChannelSecurityFindings(params: {
|
||||
title: "Slack slash commands bypass access groups",
|
||||
detail:
|
||||
"Slack slash/native commands are enabled while commands.useAccessGroups=false; this can allow unrestricted /… command execution from channels/users you didn't explicitly authorize.",
|
||||
remediation: "Set commands.useAccessGroups=true (recommended).",
|
||||
remediation: `Run: clawdbot config set commands.useAccessGroups true`,
|
||||
});
|
||||
} else {
|
||||
const dmAllowFromRaw = (account as { dm?: { allowFrom?: unknown } } | null)?.dm
|
||||
@ -672,8 +669,7 @@ async function collectChannelSecurityFindings(params: {
|
||||
title: "Slack slash commands have no allowlists",
|
||||
detail:
|
||||
"Slack slash/native commands are enabled, but neither an owner allowFrom list nor any channels.<id>.users allowlist is configured; /… commands will be rejected for everyone.",
|
||||
remediation:
|
||||
"Approve yourself via pairing (recommended), or set channels.slack.dm.allowFrom and/or channels.slack.channels.<id>.users.",
|
||||
remediation: `Run: clawdbot pair slack (recommended), or: clawdbot config set channels.slack.dm.allowFrom '["YOUR_USER_ID"]'`,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -765,8 +761,7 @@ async function collectChannelSecurityFindings(params: {
|
||||
title: "Telegram group allowlist contains wildcard",
|
||||
detail:
|
||||
'Telegram group sender allowlist contains "*", which allows any group member to run /… commands and control directives.',
|
||||
remediation:
|
||||
'Remove "*" from channels.telegram.groupAllowFrom and pairing store; prefer explicit user ids/usernames.',
|
||||
remediation: `Run: clawdbot config set channels.telegram.groupAllowFrom '["YOUR_USER_ID"]' (remove "*" and use explicit IDs)`,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@ -786,8 +781,7 @@ async function collectChannelSecurityFindings(params: {
|
||||
detail:
|
||||
`Telegram group access is enabled but no sender allowlist is configured; this allows any group member to invoke /… commands` +
|
||||
(skillsEnabled ? " (including skill commands)." : "."),
|
||||
remediation:
|
||||
"Approve yourself via pairing (recommended), or set channels.telegram.groupAllowFrom (or per-group groups.<id>.allowFrom).",
|
||||
remediation: `Run: clawdbot pair telegram (recommended), or: clawdbot config set channels.telegram.groupAllowFrom '["YOUR_USER_ID"]'`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user