T09 · Insecure Skill Coding Practices
- Location
index.js:17- Finding
Directory Symlink Traversal and Recursive Denial of Service
- Content
View full analysis
Vulnerability Details
File Location:
index.js, lines 17-30
Vulnerability Type: Unrestricted symlink traversal and unbounded recursive directory scanning
Risk Level: MediumVulnerable Code
javascript try { const list = fs.readdirSync(dir); for (const file of list) { if (ignore.includes(file)) continue; const filePath = path.join(dir, file); let stat; try { stat = fs.statSync(filePath); } catch (e) { continue; } if (stat && stat.isDirectory()) { results = results.concat(findMarkdownFiles(filePath, ignore));Technical Analysis
The recursive scanner uses
fs.statSync()to inspect workspace entries. This function follows symbolic links, so a symbolic link to a directory is treated as the target directory rather than as a link. The code neither verifies that the canonical target remains under the requested scan root nor tracks previously visited directories.Consequently, a symbolic link placed inside the scanned workspace can redirect traversal to an arbitrary directory accessible to the process. If the link points to an ancestor or otherwise creates a directory cycle, the same directory tree can be traversed repeatedly. The synchronous, unbounded recursion may then exhaust stack, CPU, or memory resources.
Attack Path
- An attacker with the ability to add files or symbolic links to the scanned workspace creates a directory symlink.
- The symlink points either outside the workspace to a sensitive, readable directory or back to an ancestor directory to form a cycle.
- A user or agent invokes
scan()against the affected workspace. fs.statSync()follows the symlink and reports its target as a directory.findMarkdownFiles()recursively enters the target without checking its canonical path.- For an external target, the scanner reads Markdown files outside the intended root and returns file ...[truncated 736 chars]
- Remediation
View remediation
Remediation Suggestions
-
Use
fs.lstatSync()when inspecting directory entries and skip symbolic links by default:javascript const stat = fs.lstatSync(filePath); if (stat.isSymbolicLink()) continue; -
Canonicalize the scan root with
fs.realpathSync()and verify that every traversed path remains within that root. Path-boundary checks should usepath.relative()rather than a simple string-prefix comparison. -
Maintain a set of visited canonical directory paths, or device-and-inode identifiers, and refuse to revisit a directory. This prevents cycles even if symbolic-link traversal is intentionally enabled.
-
Add configurable maximum traversal depth and file-count limits to bound CPU, memory, and filesystem usage.
-
Return root-relative paths where possible so reports do not unnecessarily disclose absolute filesystem locations.
-
Add tests covering symlinks to external directories, links to ancestor directories, directory cycles, depth limits, and canonical root-boundary enforcement.
-
