mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:20:43 +00:00
fix(security): keep plain audit off plugin runtimes
Keep routine security audit on config/filesystem checks by default, reserving plugin runtime collectors for deep audit paths.\n\nThanks @vincentkoc
This commit is contained in:
@@ -42,6 +42,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Discord/voice: lengthen the default voice join Ready wait, add configurable `voice.connectTimeoutMs`/`voice.reconnectGraceMs`, and warn before destroying unrecovered disconnected sessions so slow Discord voice handshakes and reconnects no longer fail silently. Fixes #63098; refs #39825 and #65039. Thanks @darealgege, @kzicherman, and @ayochim.
|
||||
- Gateway/health: refresh cached health RPC snapshots when channel runtime state diverges, so Discord and other channel status reads no longer report stale running or connected values until the cache TTL expires. (#75423) Thanks @clawsweeper.
|
||||
- Gateway/sessions: keep session-store reads from running stale prune and entry-count cap maintenance during startup, so oversized stores no longer block chat history readiness after updates while writes and `sessions cleanup --enforce` still preserve the cleanup safeguards. Fixes #70050. Thanks @tangda18.
|
||||
- Security/audit: keep plain `security audit` on the cold config/filesystem path and reserve plugin runtime security collectors for `--deep`, so large plugin installs cannot execute every plugin runtime during routine audits. Thanks @vincentkoc.
|
||||
- Discord/voice: merge configured media-understanding providers such as Deepgram into partial active provider registries, so follow-up voice turns keep transcribing after another media plugin is already active. Fixes #65687. Thanks @OneMintJulep.
|
||||
- WhatsApp: stage `qrcode` through root mirrored runtime dependencies so packaged QR pairing can render from staged plugin-runtime-deps installs. Fixes #75394. Thanks @FelipeX2001.
|
||||
- Discord/voice: apply per-channel Discord `systemPrompt` overrides to voice transcript turns by forwarding the trusted channel prompt through the voice agent run. Fixes #47095. Thanks @qearlyao.
|
||||
|
||||
@@ -25,6 +25,8 @@ openclaw security audit --fix
|
||||
openclaw security audit --json
|
||||
```
|
||||
|
||||
Plain `security audit` stays on the cold config/filesystem/read-only path. It does not discover plugin runtime security collectors by default, so routine audits do not load every installed plugin runtime. Use `--deep` to include best-effort live Gateway probes and plugin-owned security audit collectors; explicit internal callers may also opt into those plugin-owned collectors when they already have an appropriate runtime scope.
|
||||
|
||||
The audit warns when multiple DM senders share the main session and recommends **secure DM mode**: `session.dmScope="per-channel-peer"` (or `per-account-channel-peer` for multi-account channels) for shared inboxes.
|
||||
This is for cooperative/shared inbox hardening. A single Gateway shared by mutually untrusted/adversarial operators is not a recommended setup; split trust boundaries with separate gateways (or separate OS users/hosts).
|
||||
It also emits `security.trust_model.multi_user_heuristic` when config suggests likely shared-user ingress (for example open DM/group policy, configured group targets, or wildcard sender rules), and reminds you that OpenClaw is a personal-assistant trust model by default.
|
||||
|
||||
@@ -41,7 +41,10 @@ export function registerSecurityCli(program: Command) {
|
||||
() =>
|
||||
`\n${theme.heading("Examples:")}\n${formatHelpExamples([
|
||||
["openclaw security audit", "Run a local security audit."],
|
||||
["openclaw security audit --deep", "Include best-effort live Gateway probe checks."],
|
||||
[
|
||||
"openclaw security audit --deep",
|
||||
"Include best-effort live Gateway probes and plugin-owned security audit collectors.",
|
||||
],
|
||||
["openclaw security audit --deep --token <token>", "Use explicit token for deep probe."],
|
||||
[
|
||||
"openclaw security audit --deep --password <password>",
|
||||
@@ -55,7 +58,7 @@ export function registerSecurityCli(program: Command) {
|
||||
security
|
||||
.command("audit")
|
||||
.description("Audit config + local state for common security foot-guns")
|
||||
.option("--deep", "Attempt live Gateway probe (best-effort)", false)
|
||||
.option("--deep", "Attempt live Gateway probes and plugin-owned collector checks", false)
|
||||
.option("--token <token>", "Use explicit gateway token for deep probe auth")
|
||||
.option("--password <password>", "Use explicit gateway password for deep probe auth")
|
||||
.option("--fix", "Apply safe fixes (tighten defaults + chmod state/config)", false)
|
||||
|
||||
@@ -23,7 +23,7 @@ vi.mock("../plugins/runtime/metadata-registry-loader.js", () => ({
|
||||
loadPluginMetadataRegistrySnapshotMock(...args),
|
||||
}));
|
||||
|
||||
const { collectPluginSecurityAuditFindings } = await import("./audit.js");
|
||||
const { collectPluginSecurityAuditFindings, runSecurityAudit } = await import("./audit.js");
|
||||
|
||||
function createAuditContext(params: {
|
||||
sourceConfig: Parameters<typeof collectPluginSecurityAuditFindings>[0]["sourceConfig"];
|
||||
@@ -152,4 +152,26 @@ describe("security audit read-only plugin scope", () => {
|
||||
expect(applyPluginAutoEnableMock).not.toHaveBeenCalled();
|
||||
expect(loadPluginMetadataRegistrySnapshotMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("keeps plain security audit off plugin collector runtime discovery by default", async () => {
|
||||
const sourceConfig = {
|
||||
plugins: {
|
||||
allow: ["audit-plugin"],
|
||||
},
|
||||
};
|
||||
|
||||
await runSecurityAudit({
|
||||
config: sourceConfig,
|
||||
sourceConfig,
|
||||
env: {},
|
||||
includeFilesystem: false,
|
||||
includeChannelSecurity: false,
|
||||
stateDir: "/tmp/openclaw-test-state",
|
||||
configPath: "/tmp/openclaw-test-config.json",
|
||||
});
|
||||
|
||||
expect(getActivePluginRegistryMock).not.toHaveBeenCalled();
|
||||
expect(applyPluginAutoEnableMock).not.toHaveBeenCalled();
|
||||
expect(loadPluginMetadataRegistrySnapshotMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -946,7 +946,7 @@ async function createAuditExecutionContext(
|
||||
execDockerRawFn: opts.execDockerRawFn,
|
||||
probeGatewayFn: opts.probeGatewayFn,
|
||||
plugins: opts.plugins,
|
||||
loadPluginSecurityCollectors: opts.loadPluginSecurityCollectors !== false,
|
||||
loadPluginSecurityCollectors: opts.loadPluginSecurityCollectors ?? deep,
|
||||
workspaceDir,
|
||||
configSnapshot,
|
||||
codeSafetySummaryCache: opts.codeSafetySummaryCache ?? new Map<string, Promise<unknown>>(),
|
||||
|
||||
Reference in New Issue
Block a user