T09 · Insecure Skill Coding Practices
- Location
scripts/template.mjs:86- Finding
Path Traversal Allows Deletion of JSON Files Outside the Templates Directory
- Content
View full analysis
Vulnerability Details
File Location:
scripts/template.mjs, lines 86-93 and 234-237
Vulnerability Type: Unvalidated path traversal in a file deletion operation
Risk Level: MediumVulnerable Code
js // Delete template function deleteTemplate(id) { const file = path.join(TEMPLATES_DIR, `${id}.json`); if (fs.existsSync(file)) { fs.unlinkSync(file); console.log(`✅ Template "${id}" deleted`); } else { console.log(`❌ Template "${id}" does not exist`); } }The value passed to this function comes directly from a command-line argument:
js } else if (deleteIndex > -1) { deleteTemplate(args[deleteIndex + 1]); } else { listTemplates(); }Technical Analysis
The
--deleteargument is used as the template identifier without validation. The application appends.jsonand passes the result topath.join():js path.join(TEMPLATES_DIR, `${id}.json`)path.join()normalizes traversal components such as../; it does not guarantee that the resulting path remains underTEMPLATES_DIR. An identifier containing enough parent-directory components can therefore resolve to a JSON file outside the intended templates directory.Once the path is resolved,
fs.unlinkSync()deletes the selected file using the privileges of the user running the process. There is no canonical-path containment check, allowlist validation, or confirmation prompt.The mandatory
.jsonsuffix limits direct exploitation to paths ending in.json, but potentially sensitive JSON configuration, credential, wallet, application-state, and package files remain exposed.Attack Path
- An attacker identifies a JSON file accessible to the victim's operating-system account.
- The attacker constructs a relative path from the project's
templatesdirectory to that file, omitting its.jsonsuffix. - The attacker convinces a user or aut ...[truncated 1095 chars]
- Remediation
View remediation
Remediation Suggestions
Restrict template identifiers to a conservative allowlist and verify that the resolved path remains inside the templates directory.
js function resolveTemplatePath(id) { if (typeof id !== 'string' || !/^[A-Za-z0-9_-]+$/.test(id)) { throw new Error('Invalid template ID'); } const base = path.resolve(TEMPLATES_DIR); const target = path.resolve(base, `${id}.json`); if (path.dirname(target) !== base) { throw new Error('Template path escapes the templates directory'); } return target; } function deleteTemplate(id) { const file = resolveTemplatePath(id); if (!fs.existsSync(file)) { console.log(`Template "${id}" does not exist`); return; } fs.unlinkSync(file); console.log(`Template "${id}" deleted`); }Additional hardening should include:
- Rejecting missing command-line values explicitly.
- Applying the same path-validation helper to all template read and write operations.
- Avoiding symlink traversal by inspecting the target with
lstatSync()and refusing symbolic links. - Considering an interactive confirmation before destructive deletion.
- Running the application with only the filesystem permissions it requires.
- Adding tests covering
../, absolute paths, encoded separators, platform-specific separators, and symlink targets.
