T09 · Insecure Skill Coding Practices
- Location
index.js:12- Finding
Unrestricted Symbolic-Link Traversal
- Content
View full analysis
Vulnerability Details
File Location:
index.js, lines 12–29
Vulnerability Type: Unrestricted symbolic-link traversal and recursive resource exhaustion
Risk Level: Mediumjs try { stats = fs.statSync(dirPath); } catch (err) { return { name, error: err.message }; } const item = { name, type: stats.isDirectory() ? 'directory' : 'file', path: dirPath }; if (stats.isDirectory()) { if (currentDepth < maxDepth) { try { const children = fs.readdirSync(dirPath) .filter(child => !ignore.includes(child)) .map(child => generateStructure(path.join(dirPath, child), { ...options, currentDepth: currentDepth + 1 })); item.children = children;Technical Analysis
fs.statSync()follows symbolic links. The recursive traversal does not usefs.lstatSync()to identify links, track visited filesystem objects, verify that resolved child paths remain under the initially selected directory, or impose a finite default depth or entry limit.Consequently, a symbolic link inside a scanned directory can redirect traversal to any filesystem location readable by the process. If a link points to the scanned directory or one of its ancestors, recursion can repeatedly revisit the same directory. The default
maxDepthisInfinity, so such a cycle can continue until the process exhausts stack, memory, file-processing capacity, or execution time.Attack Path
- An attacker creates or controls a directory that the victim or agent will scan.
- The attacker adds a symbolic link targeting either:
- a readable directory outside the intended scan root, or
- the current directory or an ancestor, creating a traversal cycle.
- The victim invokes the skill on the attacker-controlled directory without a finite
--depthvalue. fs.statSync()follows the link and classifies its target as a directory.- The recursive
readdirSync()and `g ...[truncated 943 chars]
- Remediation
View remediation
Remediation Suggestions
- Use
fs.lstatSync()orfs.readdirSync()with directory entries to detect symbolic links, and do not traverse them by default. - If following links is required, resolve each candidate with
fs.realpathSync()and verify that the resolved path remains within the canonical initial root using a separator-aware containment check. - Track visited filesystem objects by canonical path or device/inode pair to prevent cycles and repeated traversal.
- Set a conservative finite default recursion depth and enforce maximum node, output-size, and execution-time limits.
- Handle links explicitly in generated output, for example by returning an item with
type: "symlink"and its target without recursively scanning it. - Add tests covering links to ancestors, self-referential links, links outside the root, deeply nested trees, and traversal-limit enforcement.
- Use
