T09 · Insecure Skill Coding Practices
Warning
- Location
- index.js:41
- Finding
- Output Directory Traversal Through Untrusted Feishu Folder Names<![CDATA[ ## Vulnerability Details **File Location**: `index.js:41-45` and `index.js:100-105` **Vulnerability Type**: Path traversal through remotely supplied directory names **Risk Level**: Medium ### Vulnerable Code ```js if (recursive) { for (const item of [...items]) { if (item.type === 'folder') { const subItems = await listFolder(item.token, true); items = items.concat(subItems.map(subItem => ({ ...subItem, path: path.join(item.name, subItem.path || subItem.name) }))); } } } ``` ```js const content = await readDocument(item.token); const title = content.title || item.name; const outputSubPath = item.path ? path.join(options.output, path.dirname(item.path)) : options.output; const filePath = saveMarkdown(content.content, outputSubPath, title); console.log(`✅ 已保存到: ${filePath}`); ``` The final filesystem write occurs in `saveMarkdown`: ```js function saveMarkdown(content, outputPath, title) { const filePath = path.join(outputPath, `${title.replace(/[\/\\:*?"<>|]/g, '_')}.md`); fs.mkdirSync(path.dirname(filePath), { recursive: true }); fs.writeFileSync(filePath, content); return filePath; } ``` ### Technical Analysis Folder names and paths returned by the Feishu Drive tool are remotely controlled metadata. During recursive enumeration, the implementation passes `item.name` directly to `path.join`. It later combines the resulting `item.path` with the user-selected output directory. Although document titles are partially sanitized before being used as filenames, directory components are neither sanitized nor checked against the intended export root. Node.js normalizes `..` path components when evaluating `path.join`. Consequently, a folder name such as `../../target` can cause the resolved destination to escape `options.output`. This issue applies to recursive folder exports where an attacker can influence the names of folders included in an export. The application does not verify that the fin ...[truncated 1708 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Treat every remotely supplied folder name as an untrusted path segment. 1. Resolve the configured export directory to an absolute canonical root. 2. Sanitize or reject each remote folder component independently. 3. Reject `.`, `..`, absolute paths, NUL bytes, forward slashes, and backslashes in folder names. 4. Resolve the complete destination and confirm that it remains inside the export root before creating directories or writing files. 5. Apply the containment check immediately before every filesystem write to avoid relying solely on earlier validation. 6. Consider replacing unsafe remote names with deterministic escaped names instead of silently normalizing them. Example containment validation: ```js function resolveSafeDestination(exportRoot, relativeDirectory) { const root = path.resolve(exportRoot); const destination = path.resolve(root, relativeDirectory); if ( destination !== root && !destination.startsWith(root + path.sep) ) { throw new Error('Unsafe folder path'); } return destination; } ``` Folder components should also be validated before path construction: ```js function sanitizeFolderSegment(name) { if ( typeof name !== 'string' || name === '.' || name === '..' || name.includes('/') || name.includes('\\') || name.includes('\0') || path.isAbsolute(name) ) { throw new Error('Invalid remote folder name'); } return name; } ``` Add automated tests covering `..`, nested traversal, absolute paths, mixed separators, NUL bytes, and valid similarly named folders. Tests should assert that no generated destination can escape the resolved export root. ]]>
