GHSA-m3mh-3mpg-37hw CVE-2026-35641
OpenClaw has an Arbitrary Malicious Code Execution Vulnerability
- Severity
- 0.0 / 10
- High · CVSS 4.0
- Exploit chance (EPSS)
- 0.14%
- Chance of exploitation in the next 30 days. Higher than 3.03% of scored vulnerabilities.
- Known exploited
- No
- Not in CISA's catalog
- Weakness
- CWE-349 Acceptance of Extraneous Untrusted Data With Trusted Data
- CWE-426 Untrusted Search Path
- Fix
2026.3.24- Upgrade openclaw to this version or later
Description
Fixed in OpenClaw 2026.3.24, the current shipping release.
Summary
During the installation phase of OpenClaw local plugins/hooks, the Git executable can be hijacked by a project-level .npmrc file, leading to arbitrary code execution during installation.
Details
Please note that the source code locations mentioned below are based on version openclaw-2026.3.13-1, but the issue has been confirmed to still exist in the current latest version, 2026.3.23.
When installing a local plugin directory, local plugin archive, local hook pack directory, or local hook pack archive, OpenClaw first copies the source directory to a temporary stageDir, then executes the following in that directory:
npm install --omit=dev --silent --ignore-scripts
See src/infra/install-package-dir.ts:176-199.
Since this process does not strip the project root .npmrc, and npm reads the project-level .npmrc during local project installation, an attacker could use a .npmrc file in a malicious plugin or hook directory to override npm’s git executable path. By leveraging a Git dependency, the attacker could trigger npm to call this malicious program, thereby executing arbitrary local code during the installation phase.
Affected Paths
- Plugin CLI entry point:
src/cli/plugins-cli.ts:199-255 - Hook CLI entry point:
src/cli/hooks-cli.ts:573-676 - Plugin local directory / archive installation:
src/plugins/install.ts:379-405,src/plugins/install.ts:541-565 - Hook local directory / archive installation:
src/hooks/install.ts:380-403,src/hooks/install.ts:443-470 - Actual execution of
npm install --ignore-scripts:src/infra/install-package-dir.ts:176-199
Vulnerability Trigger Flow
-
The user executes one of the following commands:
openclaw plugins install <path-or-spec>openclaw hooks install <path-or-spec>
-
If the argument is a local directory or local archive, OpenClaw navigates to the local installation path.
-
OpenClaw copies the source directory to a temporary
stageDir. Seesrc/infra/install-package-dir.ts:176-177. -
If
dependenciesare present inpackage.json, OpenClaw executes the following instageDir:
npm install --omit=dev --silent --ignore-scripts
See src/infra/install-package-dir.ts:188-199.
- npm reads the project-level
.npmrcfile in this directory. Official documentation:.npmrc - If
.npmrcis set togit=<path to malicious program>and there is a git dependency in the dependency tree, npm will invoke thatgitprogram when resolving the dependency. Official documentation:npm config gitGit dependency documentation:package.json - Consequently, an attacker can execute arbitrary local programs during the plugin/hook installation phase without waiting for the plugin or hook to be loaded later.
Triggering Commands
- Plugin installation command:
openclaw plugins install <path-or-spec>
- Hook installation command:
openclaw hooks install <path-or-spec>
When <path-or-spec> is a local directory or local archive, it will be resolved to the path used by the npm install --omit=dev --silent --ignore-scripts command mentioned above.
PoC
Currently, testpoc/ is a minimal PoC directory used to verify that “when installing local packages, OpenClaw enters the npm install --ignore-scripts path.” It is divided into two core sections:
testpoc/pkg/
Purpose: Simulates the local package directory installed by openclaw plugins install ... or openclaw hooks install ...
testpoc/repo/
Purpose: Simulates a Git dependency repository within the npm dependency tree
Directory Structure
testpoc/ ├─ pkg/ │ ├─ .npmrc │ ├─ package.json │ └─ sample-hook/ │ ├─ HOOK.md │ └─ handler.js └─ repo/ ├─ package.json └─ .git/... Function of Each Component
testpoc/pkg/.npmrc
Current content: git=calc.exe Function: Overrides npm’s Git executable configuration. Meaning: When npm encounters a git dependency during installation, it will not call the system git but will attempt to call the program specified here. This is the core trigger point of this PoC. See testpoc/pkg/.npmrc:1 testpoc/pkg/package.json
Currently, this is a “mixed-use” manifest that includes both plugin and hook fields: { “name”: “probe-host”, “version”: “1.0.0”, “private”: true, “openclaw”: { “extensions”: [“./dist/index.js”], “hooks”: [“./sample-hook”] }, “dependencies”: { “probe-git-dep”: “git+file:///D:/AI Agent Source/OpenClaw/openclaw-2026.3.13-1/.testpoc/repo” } } Its functionality is divided into three layers: openclaw.extensions: Allows it to be validated as a plugin package openclaw.hooks: Enables it to be validated as a hook package The Git URL in dependencies: Forces npm to enter the Git dependency resolution path during installation See testpoc/pkg/package.json:1 testpoc/pkg/sample-hook/HOOK.md
Purpose: To meet the minimum metadata requirements for a hook package.
This is the key file that allows openclaw hooks install pkg to pass the pre-check. See testpoc/pkg/sample-hook/HOOK.md:1
testpoc/pkg/sample-hook/handler.js
Current content: export default async function handler() { return { ok: true }; } Purpose: Meets the requirement that the hook directory must contain a handler entry file. It is not a usage point in itself; its sole purpose is to allow OpenClaw to proceed to the dependency installation phase. See testpoc/pkg/sample-hook/handler.js:1 testpoc/repo/package.json
Current content: {“name”:“probe-git-dep”,‘version’:“1.0.0”} Purpose: Serves as the minimum repository content corresponding to a Git dependency. The focus is not on the repository code itself, but on the fact that “it is a Git repository,” allowing npm to perform Git-related operations on it. See testpoc/repo/package.json:1 testpoc/repo/.git/
Purpose: Makes testpoc/repo/ a real Git repository rather than a regular directory. When npm resolves git+file://... When installing dependencies, this is treated as the Git source. How the current PoC works
If installing via hooks:
openclaw hooks install testpoc/pkg The trigger chain is:
OpenClaw identifies testpoc/pkg as the local hook package path Through pre-validation in openclaw.hooks, HOOK.md, and handler.js Proceeds to src/infra/install-package-dir.ts:188-199 Executes: npm install --omit=dev --silent --ignore-scripts npm reads testpoc/pkg/.npmrc npm processes the git dependency in package.json npm attempts to call the git=calc.exe specified in .npmrc
Impact
It is best described as an installation-time local command execution / unsafe package-install configuration issue.
More precisely:
OpenClaw installs local plugin and hook packs by running npm install --omit=dev --silent --ignore-scripts inside the staged package directory, see src/infra/install-package-dir.ts:188-199. If that local package directory contains an attacker-controlled .npmrc, npm will still read it. If .npmrc overrides npm’s git executable and the package has a git dependency, npm can invoke the attacker-chosen program during install.
Who is impacted
Users who run:
openclaw plugins install <local path/archive> openclaw hooks install <local path/archive>
And who install a malicious or untrusted local package that includes:
a controlled .npmrc a git dependency a runnable attacker-controlled git target on that platform
This should be treated as a security issue, not just “malicious plugin behavior,” because the code execution happens during OpenClaw’s install workflow, before the plugin or hook is ever loaded as trusted runtime code.
The important distinction is:
A normal “trusted plugin” case is: the operator installs a plugin, enables it, and later that plugin runs with plugin privileges. This issue is different: OpenClaw’s installer executes npm install --omit=dev --silent --ignore-scripts inside an attacker-controlled package directory, and npm still honors attacker-controlled project config from .npmrc.
That means an untrusted local plugin or hook package can influence the package manager itself and reach arbitrary program execution at install time, via npm’s git setting and a git dependency, even though --ignore-scripts is present.
Why this matters from a security perspective:
It is install-time execution, not post-install trusted execution.
The execution is triggered by OpenClaw’s installer in src/infra/install-package-dir.ts:188-199.
This occurs before the package is accepted as a trusted loaded plugin/hook in the usual sense.
It defeats an expected safety boundary.
The code explicitly uses --ignore-scripts, which strongly suggests an intent to make installation safer.
But the installer still allows attacker-controlled package-manager configuration from .npmrc to affect execution.
So the current mitigation is incomplete in a security-relevant way.
The dangerous input is part of a supported user flow.
OpenClaw explicitly supports installing plugins and hook packs from local directories and archives:
src/cli/plugins-cli.ts:199-255 src/cli/hooks-cli.ts:573-676
That makes “download a package/archive, then install it” a realistic operator action, not an artificial lab setup.
The issue is broader than plugin trust.
The problem is not “plugins can do bad things once trusted.”
The problem is “the installer consumes attacker-controlled package-manager config before trust is established.”
That is much closer to an unsafe install / supply-chain execution flaw than to ordinary trusted-plugin behavior.
Hooks are affected too.
The same installer path is used for hook packs, not only plugins.
So this is a shared install-surface issue, not an isolated plugin-runtime concern.
Affected versions
npm
| Package | Vulnerable | Fixed in |
|---|---|---|
| openclaw | < 2026.3.24 |
2026.3.24 |
How to fix
Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.
openclaw npm → 2026.3.24
- npm
npm install openclaw@2026.3.24- yarn
yarn add openclaw@2026.3.24- pnpm
pnpm add openclaw@2026.3.24
Severity breakdown
CVSS 4.0 0.0
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
- Attack vector
- Local
- Attack complexity
- Low
- Attack requirements
- None
- Privileges required
- None
- User interaction
- Active
- Confidentiality (vulnerable system)
- High
- Integrity (vulnerable system)
- High
- Availability (vulnerable system)
- High
- Confidentiality (subsequent systems)
- None
- Integrity (subsequent systems)
- None
- Availability (subsequent systems)
- None
CVSS 3.1 8.6
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
- Attack vector
- Local
- Attack complexity
- Low
- Privileges required
- None
- User interaction
- Required
- Scope
- Changed
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Weakness
CWE-349: Acceptance of Extraneous Untrusted Data With Trusted Data
The product, when processing trusted data, accepts any untrusted data that is also included with the trusted data, treating the untrusted data as if it were trusted.
CWE-426: Untrusted Search Path
The product searches for critical resources using an externally-supplied search path that can point to resources that are not under the product's direct control.
How to prevent it
- Architecture and Design, Implementation: Hard-code the search path to a set of known-safe values (such as system directories), or only allow them to be specified by the administrator in a configuration file. Do not allow these settings to be modified by an external party. Be careful to avoid related weaknesses such as CWE-426 and CWE-428.
- Implementation: When invoking other programs, specify those programs using fully-qualified pathnames. While this is an effective approach, code that uses fully-qualified pathnames might not be portable to other systems that do not use the same pathnames. The portability can be improved by locating the full-qualified paths in a centralized, easily-modifiable location within the source code, and having the code refer to these paths.
- Implementation: Remove or restrict all environment settings before invoking other programs. This includes the PATH environment variable, LD_LIBRARY_PATH, and other settings that identify the location of code libraries, and any application-specific search paths.
- Implementation: Check your search path before use and remove any elements that are likely to be unsafe, such as the current working directory or a temporary files directory.
- Implementation: Use other functions that require explicit paths. Making use of any of the other readily available functions that require explicit paths is a safe way to avoid this problem. For example, system() in C does not require a full path since the shell can take care of it, while execl() and execv() require a full path.
References
Advisories
Packages
Other links
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why it's related |
|---|---|---|
| GHSA-3x84-qq85-fj65 OpenClaw: Browser CDP discovery could accept blocked WebSocket URLs | High | Also affects openclaw |
| GHSA-9c3v-684m-579c OpenClaw MCP SSE redirects could forward Authorization headers | Medium | Also affects openclaw |
| GHSA-f397-5vjw-v2c2 OpenClaw: Shell inline-command parsing could miss an allowlist check | High | Also affects openclaw |
| GHSA-rx78-29qr-5hq8 OpenClaw: Workspace-derived service PATH could influence trash command selection | High | Also affects openclaw |
| GHSA-ccwh-wwpp-6wg5 OpenClaw: Host environment sanitizer missed two Node.js control variables | High | Also affects openclaw |
| GHSA-985f-72mj-8gf7 OpenClaw: Tool group policy callers could accept unvalidated group IDs | Medium | Also affects openclaw |
| GHSA-9v8j-9c9g-w66c OpenClaw: Bootstrap token replay could widen pending pairing scopes | Medium | Also affects openclaw |
| GHSA-c226-q6fx-6j6c OpenClaw: macOS Swift exec allowlist missed combined POSIX inline flags | Medium | Also affects openclaw |
MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.
Check your repos with MergeHarbor