T09 · Insecure Skill Coding Practices
- Location
index.js:14- Finding
Path Traversal Enables File Overwrite Outside the Skills Root
- Content
View full analysis
Vulnerability Details
File Location:
index.js, lines 7 and 14–16, with the write operation at line 63
Vulnerability Type: Path traversal and unsafe file overwrite
Risk Level: HighVulnerable Code
js const SKILLS_ROOT = path.resolve(__dirname, '..'); const TARGET_SKILL = process.argv[2];js const skillPath = path.join(SKILLS_ROOT, TARGET_SKILL); const indexJsPath = path.join(skillPath, 'index.js'); const testJsPath = path.join(skillPath, 'test.js'); if (!fs.existsSync(indexJsPath)) { console.error(`Skill ${TARGET_SKILL} not found or index.js missing.`); process.exit(1); }js fs.writeFileSync(testJsPath, testContent);Technical Analysis
TARGET_SKILLis accepted directly from the command line and passed topath.joinwithout validation or a containment check. Values containing traversal components such as..can causeskillPathto reference a directory outsideSKILLS_ROOT.The existence check only confirms that an
index.jsfile exists at the selected location. It does not establish that the target is an authorized skill directory or that its canonical path remains beneathSKILLS_ROOT. After this check, the program unconditionally writestest.js, overwriting an existing file with that name.Symbolic links can also undermine simple lexical path assumptions unless canonical paths are checked. Consequently, a caller who controls the CLI argument can select another accessible directory containing
index.jsand overwrite itstest.js.Attack Path
- The attacker identifies or creates a writable directory outside the intended skills root that contains an
index.jsfile. - The attacker invokes the generator with a traversal value such as
../../attacker-controlled-directory, adjusted for the actual directory layout. path.join(SKILLS_ROOT, TARGET_SKILL)resolves the traversal components and selects the external directory.- The ...[truncated 968 chars]
- The attacker identifies or creates a writable directory outside the intended skills root that contains an
- Remediation
View remediation
Remediation Suggestions
-
Restrict skill identifiers to a conservative allowlist:
js if (!/^[A-Za-z0-9_-]+$/.test(TARGET_SKILL)) { throw new Error('Invalid skill name'); } -
Resolve and verify the target path before accessing it:
js const skillPath = path.resolve(SKILLS_ROOT, TARGET_SKILL); const relative = path.relative(SKILLS_ROOT, skillPath); if ( relative === '' || relative.startsWith(`..${path.sep}`) || relative === '..' || path.isAbsolute(relative) ) { throw new Error('Target must be a child of the skills root'); } -
If symbolic links are permitted in the directory tree, compare canonical paths obtained with
fs.realpathSyncand ensure the canonical target remains inside the canonical skills root. -
Refuse to replace an existing
test.jsby default. Use an explicit overwrite option or an exclusive write flag such as{ flag: 'wx' }. -
Consider writing generated tests to a safely created temporary directory instead of modifying the target project.
-
Verify that the target is a direct child of the skills root if nested paths are not required.
-
