CVE-2026-53813
Description
OpenClaw before 2026.4.25 has a path traversal vulnerability in memory-core artifact loading that can lead to arbitrary code execution or data access.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
OpenClaw before 2026.4.25 has a path traversal vulnerability in memory-core artifact loading that can lead to arbitrary code execution or data access.
Vulnerability
OpenClaw versions before 2026.4.25 contain a path traversal vulnerability (CWE-427) in memory-core artifact loading. The vulnerability arises because workspace state influences local package root resolution, allowing an attacker with access to an affected workspace to load memory-core artifacts from unintended local locations. This can lead to execution of malicious code or access to sensitive data [1][2].
Exploitation
An attacker must have access to a workspace that uses the affected memory-core artifact loading feature. No special authentication beyond normal workspace access is required. The attacker can craft a malicious package root or manipulate workspace state to point to an unintended local path. By doing so, when the system loads a memory-core artifact, it reads from the attacker-selected path, enabling the loading of arbitrary content [1][2].
Impact
Successful exploitation allows an attacker to load arbitrary memory-core artifacts from the local filesystem. This can result in arbitrary code execution in the context of the OpenClaw process, or unauthorized access to sensitive data stored on the host. The privilege level achieved depends on the running process, typically with high impact to confidentiality, integrity, and availability as per CVSS v4 [2].
Mitigation
The vulnerability is fixed in OpenClaw version 2026.4.25 [1]. Until patched, users should run memory-core flows only from trusted workspaces. Additional mitigations include keeping channel and tool allowlists narrow, avoiding sharing a Gateway between mutually untrusted users, and disabling the affected feature when not needed [1]. The vulnerability is not listed in the KEV as of the publication date.
AI Insight generated on Jun 11, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2Patches
1aa36ee670b76fix(gateway): stage startup plugin deps before load
2 files changed · +100 −13
src/gateway/server-startup-plugins.test.ts+22 −13 modified@@ -1,5 +1,10 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; +type ConfiguredDeferredChannelPluginIdsResolver = + typeof import("../plugins/channel-plugin-ids.js").resolveConfiguredDeferredChannelPluginIds; +type GatewayStartupPluginIdsResolver = + typeof import("../plugins/channel-plugin-ids.js").resolveGatewayStartupPluginIds; + const applyPluginAutoEnable = vi.hoisted(() => vi.fn((params: { config: unknown }) => ({ config: params.config, @@ -14,14 +19,16 @@ const loadGatewayStartupPlugins = vi.hoisted(() => gatewayMethods: ["ping"], })), ); -const repairBundledRuntimeDepsInstallRootAsync = vi.hoisted(() => - vi.fn(async (_params: unknown) => ({})), -); +const repairBundledRuntimeDepsInstallRoot = vi.hoisted(() => vi.fn((_params: unknown) => ({}))); const resolveBundledRuntimeDependencyPackageInstallRoot = vi.hoisted(() => vi.fn((_packageRoot: string, _params: unknown) => "/runtime"), ); -const resolveConfiguredDeferredChannelPluginIds = vi.hoisted(() => vi.fn(() => [])); -const resolveGatewayStartupPluginIds = vi.hoisted(() => vi.fn(() => ["memory-core"])); +const resolveConfiguredDeferredChannelPluginIds = vi.hoisted(() => + vi.fn<ConfiguredDeferredChannelPluginIdsResolver>(() => []), +); +const resolveGatewayStartupPluginIds = vi.hoisted(() => + vi.fn<GatewayStartupPluginIdsResolver>(() => ["memory-core"]), +); const resolveOpenClawPackageRootSync = vi.hoisted(() => vi.fn((_params: unknown) => "/package")); const runChannelPluginStartupMaintenance = vi.hoisted(() => vi.fn(async (_params: unknown) => undefined), @@ -61,17 +68,19 @@ vi.mock("../infra/openclaw-root.js", () => ({ })); vi.mock("../plugins/bundled-runtime-deps.js", () => ({ - repairBundledRuntimeDepsInstallRootAsync: (params: unknown) => - repairBundledRuntimeDepsInstallRootAsync(params), + repairBundledRuntimeDepsInstallRoot: (params: unknown) => + repairBundledRuntimeDepsInstallRoot(params), resolveBundledRuntimeDependencyPackageInstallRoot: (packageRoot: string, params: unknown) => resolveBundledRuntimeDependencyPackageInstallRoot(packageRoot, params), scanBundledPluginRuntimeDeps: (params: unknown) => scanBundledPluginRuntimeDeps(params), })); vi.mock("../plugins/channel-plugin-ids.js", () => ({ - resolveConfiguredDeferredChannelPluginIds: (params: unknown) => - resolveConfiguredDeferredChannelPluginIds(params), - resolveGatewayStartupPluginIds: (params: unknown) => resolveGatewayStartupPluginIds(params), + resolveConfiguredDeferredChannelPluginIds: ( + ...args: Parameters<ConfiguredDeferredChannelPluginIdsResolver> + ) => resolveConfiguredDeferredChannelPluginIds(...args), + resolveGatewayStartupPluginIds: (...args: Parameters<GatewayStartupPluginIdsResolver>) => + resolveGatewayStartupPluginIds(...args), })); vi.mock("../plugins/registry.js", () => ({ @@ -113,7 +122,7 @@ describe("prepareGatewayPluginBootstrap runtime-deps staging", () => { applyPluginAutoEnable.mockClear(); initSubagentRegistry.mockClear(); loadGatewayStartupPlugins.mockClear(); - repairBundledRuntimeDepsInstallRootAsync.mockReset().mockResolvedValue({}); + repairBundledRuntimeDepsInstallRoot.mockReset().mockReturnValue({}); resolveBundledRuntimeDependencyPackageInstallRoot.mockClear(); resolveConfiguredDeferredChannelPluginIds.mockClear().mockReturnValue([]); resolveGatewayStartupPluginIds.mockClear().mockReturnValue(["memory-core"]); @@ -151,14 +160,14 @@ describe("prepareGatewayPluginBootstrap runtime-deps staging", () => { pluginIds: ["memory-core"], }), ); - expect(repairBundledRuntimeDepsInstallRootAsync).toHaveBeenCalledWith( + expect(repairBundledRuntimeDepsInstallRoot).toHaveBeenCalledWith( expect.objectContaining({ installRoot: "/runtime", missingSpecs: ["chokidar@^5.0.0"], installSpecs: expect.arrayContaining(["chokidar@^5.0.0", "typebox@^1.0.0"]), }), ); - expect(repairBundledRuntimeDepsInstallRootAsync.mock.invocationCallOrder[0]).toBeLessThan( + expect(repairBundledRuntimeDepsInstallRoot.mock.invocationCallOrder[0]).toBeLessThan( loadGatewayStartupPlugins.mock.invocationCallOrder[0] ?? Number.POSITIVE_INFINITY, ); });
src/gateway/server-startup-plugins.ts+78 −0 modified@@ -3,6 +3,12 @@ import { initSubagentRegistry } from "../agents/subagent-registry.js"; import { runChannelPluginStartupMaintenance } from "../channels/plugins/lifecycle-startup.js"; import { applyPluginAutoEnable } from "../config/plugin-auto-enable.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; +import { resolveOpenClawPackageRootSync } from "../infra/openclaw-root.js"; +import { + repairBundledRuntimeDepsInstallRoot, + resolveBundledRuntimeDependencyPackageInstallRoot, + scanBundledPluginRuntimeDeps, +} from "../plugins/bundled-runtime-deps.js"; import { resolveConfiguredDeferredChannelPluginIds, resolveGatewayStartupPluginIds, @@ -21,6 +27,73 @@ type GatewayPluginBootstrapLog = { debug: (message: string) => void; }; +function prestageGatewayBundledRuntimeDeps(params: { + cfg: OpenClawConfig; + pluginIds: readonly string[]; + log: GatewayPluginBootstrapLog; +}): void { + if (params.pluginIds.length === 0) { + return; + } + const packageRoot = resolveOpenClawPackageRootSync({ + argv1: process.argv[1], + cwd: process.cwd(), + moduleUrl: import.meta.url, + }); + if (!packageRoot) { + return; + } + let scanResult: ReturnType<typeof scanBundledPluginRuntimeDeps>; + try { + scanResult = scanBundledPluginRuntimeDeps({ + packageRoot, + config: params.cfg, + pluginIds: [...params.pluginIds], + env: process.env, + }); + } catch (error) { + params.log.warn( + `[plugins] failed to scan bundled runtime deps before gateway startup; gateway startup will continue with per-plugin runtime-deps installs: ${String(error)}`, + ); + return; + } + const { deps, missing, conflicts } = scanResult; + if (conflicts.length > 0) { + params.log.warn( + `[plugins] bundled runtime deps have version conflicts: ${conflicts.map((conflict) => `${conflict.name} (${conflict.versions.join(", ")})`).join("; ")}`, + ); + } + if (missing.length === 0) { + return; + } + const missingSpecs = missing.map((dep) => `${dep.name}@${dep.version}`); + const installSpecs = deps.map((dep) => `${dep.name}@${dep.version}`); + const installRoot = resolveBundledRuntimeDependencyPackageInstallRoot(packageRoot, { + env: process.env, + }); + const startedAt = Date.now(); + params.log.info( + `[plugins] staging bundled runtime deps before gateway startup (${missingSpecs.length} missing, ${installSpecs.length} install specs): ${missingSpecs.join(", ")}`, + ); + try { + repairBundledRuntimeDepsInstallRoot({ + installRoot, + missingSpecs, + installSpecs, + env: process.env, + warn: (message) => params.log.warn(`[plugins] ${message}`), + }); + } catch (error) { + params.log.warn( + `[plugins] failed to stage bundled runtime deps before gateway startup after ${Date.now() - startedAt}ms; gateway startup will continue with per-plugin runtime-deps installs: ${String(error)}`, + ); + return; + } + params.log.info( + `[plugins] installed bundled runtime deps before gateway startup in ${Date.now() - startedAt}ms: ${missingSpecs.join(", ")}`, + ); +} + export async function prepareGatewayPluginBootstrap(params: { cfgAtStart: OpenClawConfig; startupRuntimeConfig: OpenClawConfig; @@ -89,6 +162,11 @@ export async function prepareGatewayPluginBootstrap(params: { let baseGatewayMethods = baseMethods; if (!params.minimalTestGateway) { + prestageGatewayBundledRuntimeDeps({ + cfg: gatewayPluginConfigAtStart, + pluginIds: startupPluginIds, + log: params.log, + }); ({ pluginRegistry, gatewayMethods: baseGatewayMethods } = loadGatewayStartupPlugins({ cfg: gatewayPluginConfigAtStart, activationSourceConfig: params.cfgAtStart,
Vulnerability mechanics
Root cause
"Path traversal in memory-core artifact loading caused by workspace state influencing local package root resolution before runtime dependencies are staged."
Attack vector
An attacker with local access to an affected OpenClaw workspace can manipulate workspace state (e.g., configuration files or environment variables) to alter the package root resolution used during plugin loading. Because runtime dependencies were installed per-plugin after startup, the attacker could cause the gateway to load memory-core artifacts from an attacker-controlled location, leading to arbitrary code execution or unauthorized data access. The attack requires local user privileges but no authentication beyond workspace access.
Affected code
The vulnerability resides in the gateway startup plugin loading sequence (`src/gateway/server-startup-plugins.ts`). Before the patch, bundled runtime dependencies were installed **after** plugins were loaded, allowing a malicious workspace to influence the local package root resolution and load memory-core artifacts from unintended paths.
What the fix does
The patch introduces `prestageGatewayBundledRuntimeDeps()`, which scans and installs all bundled runtime dependencies **before** `loadGatewayStartupPlugins()` is called. By moving the dependency installation earlier in the startup sequence, the fix ensures that the package root resolution is not influenced by untrusted workspace state at the time plugins are loaded. The commit message explicitly states this is a fix for "stage startup plugin deps before load."
Preconditions
- authAttacker must have local access to an affected OpenClaw workspace.
- configAttacker must be able to modify workspace state (e.g., config files or environment) that influences package root resolution.
- configThe workspace must have memory-core artifacts configured as startup plugins.
Generated on Jun 11, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
2News mentions
0No linked articles in our index yet.