security: simplify remediation steps with copy-paste CLI commands
This commit is contained in:
parent
83de980d6c
commit
f4c8f1a266
@ -138,8 +138,7 @@ export function collectSecretsInConfigFindings(cfg: ClawdbotConfig): SecurityAud
|
|||||||
title: "Gateway password is stored in config",
|
title: "Gateway password is stored in config",
|
||||||
detail:
|
detail:
|
||||||
"gateway.auth.password is set in the config file; prefer environment variables for secrets when possible.",
|
"gateway.auth.password is set in the config file; prefer environment variables for secrets when possible.",
|
||||||
remediation:
|
remediation: `Run: export CLAWDBOT_GATEWAY_PASSWORD="your-password" && clawdbot config unset gateway.auth.password`,
|
||||||
"Prefer CLAWDBOT_GATEWAY_PASSWORD (env) and remove gateway.auth.password from disk.",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +187,7 @@ export function collectHooksHardeningFindings(cfg: ClawdbotConfig): SecurityAudi
|
|||||||
title: "Hooks token reuses the Gateway token",
|
title: "Hooks token reuses the Gateway token",
|
||||||
detail:
|
detail:
|
||||||
"hooks.token matches gateway.auth token; compromise of hooks expands blast radius to the Gateway API.",
|
"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",
|
severity: "critical",
|
||||||
title: "Hooks base path is '/'",
|
title: "Hooks base path is '/'",
|
||||||
detail: "hooks.path='/' would shadow other HTTP endpoints and is unsafe.",
|
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" +
|
"Older/legacy models can be less robust against prompt injection and tool misuse.\n" +
|
||||||
lines +
|
lines +
|
||||||
more,
|
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" +
|
"Smaller/older models are generally more susceptible to prompt injection and tool misuse.\n" +
|
||||||
lines +
|
lines +
|
||||||
more,
|
more,
|
||||||
remediation:
|
remediation: `Run: clawdbot config set agents.defaults.model.primary claude-sonnet-4-20250514 (avoid Haiku; prefer Claude 4.5+ or GPT-5+)`,
|
||||||
"Use the latest, top-tier model for any bot with tools or untrusted inboxes. Avoid Haiku tiers; prefer GPT-5+ and Claude 4.5+.",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -506,8 +504,7 @@ export function collectSmallModelRiskFindings(params: {
|
|||||||
exposureDetail +
|
exposureDetail +
|
||||||
`\n` +
|
`\n` +
|
||||||
"Small models are not recommended for untrusted inputs.",
|
"Small models are not recommended for untrusted inputs.",
|
||||||
remediation:
|
remediation: `Run: clawdbot config set agents.defaults.sandbox.mode all && clawdbot config set tools.deny '["group:web","browser"]'`,
|
||||||
'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"]).',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return findings;
|
return findings;
|
||||||
@ -602,7 +599,7 @@ export async function collectPluginsTrustFindings(params: {
|
|||||||
(skillCommandsLikelyExposed
|
(skillCommandsLikelyExposed
|
||||||
? "\nNative skill commands are enabled on at least one configured chat surface; treat unpinned/unallowlisted extensions as high risk."
|
? "\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:
|
detail:
|
||||||
`Found groupPolicy="open" at:\n${openGroups.map((p) => `- ${p}`).join("\n")}\n` +
|
`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.",
|
"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",
|
severity: "critical",
|
||||||
title: "Gateway binds beyond loopback without auth",
|
title: "Gateway binds beyond loopback without auth",
|
||||||
detail: `gateway.bind="${bind}" but no gateway.auth token/password is configured.`,
|
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. " +
|
"gateway.bind is loopback and gateway.trustedProxies is empty. " +
|
||||||
"If you expose the Control UI through a reverse proxy, configure trusted proxies " +
|
"If you expose the Control UI through a reverse proxy, configure trusted proxies " +
|
||||||
"so local-client checks cannot be spoofed.",
|
"so local-client checks cannot be spoofed.",
|
||||||
remediation:
|
remediation: `Run: clawdbot config set gateway.trustedProxies '["127.0.0.1"]' (replace with your proxy IPs)`,
|
||||||
"Set gateway.trustedProxies to your proxy IPs or keep the Control UI local-only.",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,7 +298,7 @@ function collectGatewayConfigFindings(
|
|||||||
detail:
|
detail:
|
||||||
"gateway.bind is loopback but no gateway auth secret is configured. " +
|
"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.",
|
"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",
|
severity: "critical",
|
||||||
title: "Tailscale Funnel exposure enabled",
|
title: "Tailscale Funnel exposure enabled",
|
||||||
detail: `gateway.tailscale.mode="funnel" exposes the Gateway publicly; keep auth strict and treat it as internet-facing.`,
|
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") {
|
} else if (tailscaleMode === "serve") {
|
||||||
findings.push({
|
findings.push({
|
||||||
@ -327,7 +326,7 @@ function collectGatewayConfigFindings(
|
|||||||
title: "Control UI allows insecure HTTP auth",
|
title: "Control UI allows insecure HTTP auth",
|
||||||
detail:
|
detail:
|
||||||
"gateway.controlUi.allowInsecureAuth=true allows token-only auth over HTTP and skips device identity.",
|
"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",
|
title: "DANGEROUS: Control UI device auth disabled",
|
||||||
detail:
|
detail:
|
||||||
"gateway.controlUi.dangerouslyDisableDeviceAuth=true disables device identity checks for the Control UI.",
|
"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",
|
severity: "critical",
|
||||||
title: `${input.label} DMs are open`,
|
title: `${input.label} DMs are open`,
|
||||||
detail: `${policyPath}="open" allows anyone to DM the bot.`,
|
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) {
|
if (!hasWildcard) {
|
||||||
findings.push({
|
findings.push({
|
||||||
@ -519,7 +518,7 @@ async function collectChannelSecurityFindings(params: {
|
|||||||
title: `${input.label} DMs share the main session`,
|
title: `${input.label} DMs share the main session`,
|
||||||
detail:
|
detail:
|
||||||
"Multiple DM senders currently share the main session, which can leak context across users.",
|
"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",
|
title: "Discord slash commands are unrestricted",
|
||||||
detail:
|
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.",
|
"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:
|
remediation: `Run: clawdbot config set commands.useAccessGroups true`,
|
||||||
"Set commands.useAccessGroups=true (recommended), or configure channels.discord.guilds.<id>.users (or channels.discord.guilds.<id>.channels.<channel>.users).",
|
|
||||||
});
|
});
|
||||||
} else if (
|
} else if (
|
||||||
useAccessGroups &&
|
useAccessGroups &&
|
||||||
@ -612,8 +610,7 @@ async function collectChannelSecurityFindings(params: {
|
|||||||
title: "Discord slash commands have no allowlists",
|
title: "Discord slash commands have no allowlists",
|
||||||
detail:
|
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.",
|
"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:
|
remediation: `Run: clawdbot pair discord (recommended), or: clawdbot config set channels.discord.dm.allowFrom '["YOUR_USER_ID"]'`,
|
||||||
"Add your user id to channels.discord.dm.allowFrom (or approve yourself via pairing), or configure channels.discord.guilds.<id>.users.",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -650,7 +647,7 @@ async function collectChannelSecurityFindings(params: {
|
|||||||
title: "Slack slash commands bypass access groups",
|
title: "Slack slash commands bypass access groups",
|
||||||
detail:
|
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.",
|
"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 {
|
} else {
|
||||||
const dmAllowFromRaw = (account as { dm?: { allowFrom?: unknown } } | null)?.dm
|
const dmAllowFromRaw = (account as { dm?: { allowFrom?: unknown } } | null)?.dm
|
||||||
@ -672,8 +669,7 @@ async function collectChannelSecurityFindings(params: {
|
|||||||
title: "Slack slash commands have no allowlists",
|
title: "Slack slash commands have no allowlists",
|
||||||
detail:
|
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.",
|
"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:
|
remediation: `Run: clawdbot pair slack (recommended), or: clawdbot config set channels.slack.dm.allowFrom '["YOUR_USER_ID"]'`,
|
||||||
"Approve yourself via pairing (recommended), or set channels.slack.dm.allowFrom and/or channels.slack.channels.<id>.users.",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -765,8 +761,7 @@ async function collectChannelSecurityFindings(params: {
|
|||||||
title: "Telegram group allowlist contains wildcard",
|
title: "Telegram group allowlist contains wildcard",
|
||||||
detail:
|
detail:
|
||||||
'Telegram group sender allowlist contains "*", which allows any group member to run /… commands and control directives.',
|
'Telegram group sender allowlist contains "*", which allows any group member to run /… commands and control directives.',
|
||||||
remediation:
|
remediation: `Run: clawdbot config set channels.telegram.groupAllowFrom '["YOUR_USER_ID"]' (remove "*" and use explicit IDs)`,
|
||||||
'Remove "*" from channels.telegram.groupAllowFrom and pairing store; prefer explicit user ids/usernames.',
|
|
||||||
});
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -786,8 +781,7 @@ async function collectChannelSecurityFindings(params: {
|
|||||||
detail:
|
detail:
|
||||||
`Telegram group access is enabled but no sender allowlist is configured; this allows any group member to invoke /… commands` +
|
`Telegram group access is enabled but no sender allowlist is configured; this allows any group member to invoke /… commands` +
|
||||||
(skillsEnabled ? " (including skill commands)." : "."),
|
(skillsEnabled ? " (including skill commands)." : "."),
|
||||||
remediation:
|
remediation: `Run: clawdbot pair telegram (recommended), or: clawdbot config set channels.telegram.groupAllowFrom '["YOUR_USER_ID"]'`,
|
||||||
"Approve yourself via pairing (recommended), or set channels.telegram.groupAllowFrom (or per-group groups.<id>.allowFrom).",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user