High severity7.8NVD Advisory· Published Apr 23, 2026· Updated Apr 28, 2026
CVE-2026-41336
CVE-2026-41336
Description
OpenClaw before 2026.3.31 allows workspace .env files to override the OPENCLAW_BUNDLED_HOOKS_DIR environment variable, enabling loading of attacker-controlled hook code. Attackers can replace trusted default-on bundled hooks from untrusted workspaces to execute arbitrary code.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
openclawnpm | < 2026.3.31 | 2026.3.31 |
Affected products
1Patches
1330a9f98cb29fix(config): block workspace bundled-root dotenv overrides (#58170)
3 files changed · +54 −0
CHANGELOG.md+1 −0 modified@@ -242,6 +242,7 @@ Docs: https://docs.openclaw.ai - Agents/compaction: trigger timeout recovery compaction before retrying high-context LLM timeouts so embedded runs stop repeating oversized requests. (#46417) thanks @joeykrug. - Agents/compaction: reconcile `sessions.json.compactionCount` after a late embedded auto-compaction success so persisted session counts catch up once the handler reports completion. (#45493) Thanks @jackal092927. - Agents/failover: classify Codex accountId token extraction failures as auth errors so model fallback continues to the next configured candidate. (#55206) Thanks @cosmicnet. +- Hooks/plugins/skills: block workspace `.env` overrides for bundled root directories so workspace startup cannot redirect bundled trust roots away from the packaged defaults. Thanks @nexrin and @vincentkoc. - Plugins/runtime: reuse only compatible active plugin registries across tools, providers, web search, and channel bootstrap, align `/tools/invoke` plugin loading with the session workspace, and retry outbound channel recovery when the pinned channel surface changes so plugin tools and channels stop disappearing or re-registering from mismatched runtime loads. Thanks @gumadeiras. - Talk/macOS: stop direct system-voice failures from replaying system speech, use app-locale fallback for shared watchdog timing, and add regression coverage for the macOS fallback route and language-aware timeout policy. (#53511) thanks @hongsw. - Discord/gateway cleanup: keep late Carbon reconnect-exhausted errors suppressed through startup/dispose cleanup so Discord monitor shutdown no longer crashes on late gateway close events. (#55373) Thanks @Takhoffman.
src/infra/dotenv.test.ts+51 −0 modified@@ -236,6 +236,31 @@ describe("loadDotEnv", () => { }); }); + it("blocks bundled trust-root vars from workspace .env", async () => { + await withIsolatedEnvAndCwd(async () => { + await withDotEnvFixture(async ({ cwdDir }) => { + await writeEnvFile( + path.join(cwdDir, ".env"), + [ + "OPENCLAW_BUNDLED_HOOKS_DIR=./attacker-hooks", + "OPENCLAW_BUNDLED_PLUGINS_DIR=./attacker-plugins", + "OPENCLAW_BUNDLED_SKILLS_DIR=./attacker-skills", + ].join("\n"), + ); + + delete process.env.OPENCLAW_BUNDLED_HOOKS_DIR; + delete process.env.OPENCLAW_BUNDLED_PLUGINS_DIR; + delete process.env.OPENCLAW_BUNDLED_SKILLS_DIR; + + loadWorkspaceDotEnvFile(path.join(cwdDir, ".env"), { quiet: true }); + + expect(process.env.OPENCLAW_BUNDLED_HOOKS_DIR).toBeUndefined(); + expect(process.env.OPENCLAW_BUNDLED_PLUGINS_DIR).toBeUndefined(); + expect(process.env.OPENCLAW_BUNDLED_SKILLS_DIR).toBeUndefined(); + }); + }); + }); + it("still allows trusted global .env to set non-workspace runtime vars", async () => { await withIsolatedEnvAndCwd(async () => { await withDotEnvFixture(async ({ cwdDir, stateDir }) => { @@ -348,6 +373,32 @@ describe("loadCliDotEnv", () => { }); }); + it("blocks bundled trust-root vars from workspace .env during CLI startup", async () => { + await withIsolatedEnvAndCwd(async () => { + await withDotEnvFixture(async ({ cwdDir }) => { + await writeEnvFile( + path.join(cwdDir, ".env"), + [ + "OPENCLAW_BUNDLED_HOOKS_DIR=./attacker-hooks", + "OPENCLAW_BUNDLED_PLUGINS_DIR=./attacker-plugins", + "OPENCLAW_BUNDLED_SKILLS_DIR=./attacker-skills", + ].join("\n"), + ); + + delete process.env.OPENCLAW_BUNDLED_HOOKS_DIR; + delete process.env.OPENCLAW_BUNDLED_PLUGINS_DIR; + delete process.env.OPENCLAW_BUNDLED_SKILLS_DIR; + vi.spyOn(process, "cwd").mockReturnValue(cwdDir); + + loadCliDotEnv({ quiet: true }); + + expect(process.env.OPENCLAW_BUNDLED_HOOKS_DIR).toBeUndefined(); + expect(process.env.OPENCLAW_BUNDLED_PLUGINS_DIR).toBeUndefined(); + expect(process.env.OPENCLAW_BUNDLED_SKILLS_DIR).toBeUndefined(); + }); + }); + }); + it("blocks workspace .env takeover vars before loading the global fallback", async () => { await withIsolatedEnvAndCwd(async () => { await withDotEnvFixture(async ({ base, cwdDir, stateDir }) => {
src/infra/dotenv.ts+2 −0 modified@@ -17,7 +17,9 @@ const BLOCKED_WORKSPACE_DOTENV_KEYS = new Set([ "NODE_TLS_REJECT_UNAUTHORIZED", "NO_PROXY", "OPENCLAW_AGENT_DIR", + "OPENCLAW_BUNDLED_HOOKS_DIR", "OPENCLAW_BUNDLED_PLUGINS_DIR", + "OPENCLAW_BUNDLED_SKILLS_DIR", "OPENCLAW_CONFIG_PATH", "OPENCLAW_GATEWAY_PASSWORD", "OPENCLAW_GATEWAY_SECRET",
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/openclaw/openclaw/commit/330a9f98cb29c79b1c16a2117e03d6276a0d6289nvdPatchWEB
- github.com/advisories/GHSA-3qpv-xf3v-mm45ghsaADVISORY
- github.com/openclaw/openclaw/security/advisories/GHSA-3qpv-xf3v-mm45nvdVendor AdvisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2026-41336ghsaADVISORY
- www.vulncheck.com/advisories/openclaw-arbitrary-hook-code-execution-via-openclaw-bundled-hooks-dir-environment-variable-overridenvdThird Party AdvisoryWEB
- github.com/openclaw/openclaw/releases/tag/v2026.3.31ghsaWEB
News mentions
0No linked articles in our index yet.