T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/employee-manager.js:50
- Finding
- Directory Traversal Through Unsanitized Employee IDs and Template Paths<![CDATA[ ## Vulnerability Details **File Location**: `scripts/employee-manager.js:50-86`, `scripts/employee-manager.js:110-118`, and `scripts/employee-manager.js:198-218` **Vulnerability Type**: Path traversal leading to unauthorized local file reads and writes **Risk Level**: Medium ### Vulnerable Code Employee IDs are derived directly from the user-controlled `role` value and then used as filesystem path components: ```js const id = `${role.toLowerCase().replace(/\s+/g, '-')}-${Date.now()}`; const employee = { id, name, role, skills, model, status: 'idle', createdAt: new Date().toISOString(), currentTask: null, completedTasks: 0, memoryPath: `./memory/${id}` }; // 创建记忆目录 const memDir = path.join(MEMORY_DIR, id); if (!fs.existsSync(memDir)) { fs.mkdirSync(memDir, { recursive: true }); // 创建初始记忆文件 fs.writeFileSync( path.join(memDir, 'memory.md'), `# ${name} - 记忆存储\n\n创建时间: ${new Date().toISOString()}\n\n## 工作记录\n\n`, 'utf8' ); } ``` The optional template name is also joined to the templates directory without validation: ```js // 如果提供了模板,复制模板内容 if (template) { const templatePath = path.join(TEMPLATES_DIR, template); if (fs.existsSync(templatePath)) { const templateContent = fs.readFileSync(templatePath, 'utf8'); fs.writeFileSync( path.join(memDir, 'workflow.md'), templateContent, 'utf8' ); } } ``` The same unsafe employee identifier assumption is used by archive and memory operations: ```js // 归档记忆 const memDir = path.join(MEMORY_DIR, id); const archiveDir = path.join(MEMORY_DIR, 'archived'); if (!fs.existsSync(archiveDir)) { fs.mkdirSync(archiveDir, { recursive: true }); } if (fs.existsSync(memDir)) { fs.renameSync(memDir, path.join(archiveDir, id)); } ``` ```js writeMemory(id, content) { const employee = this.get(id); if (!employee) { throw new Error(`未找到员工: ${id}`); } const memFile = path.join(MEMORY_DIR, id, 'memory.md'); const timestamp = new Date().toI ...[truncated 3880 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Generate employee IDs independently from user-controlled fields. Use `crypto.randomUUID()` or another trusted identifier generator rather than incorporating `role` into a filesystem path. 2. If human-readable identifiers are required, enforce a strict allowlist such as: ```js function validateIdentifier(value) { if (!/^[a-zA-Z0-9_-]+$/.test(value)) { throw new Error('Invalid identifier'); } return value; } ``` 3. Resolve and verify every filesystem path before using it: ```js function resolveWithin(baseDir, untrustedPath) { const base = path.resolve(baseDir); const target = path.resolve(base, untrustedPath); const relative = path.relative(base, target); if ( relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative)) ) { return target; } throw new Error('Path escapes the permitted directory'); } ``` 4. Restrict templates to an explicit allowlist of known filenames. Reject absolute paths, `..`, forward slashes, backslashes, null bytes, and unexpected file extensions. 5. Apply containment checks to all memory and archive paths, including `hire()`, `fire()`, `writeMemory()`, and `readMemory()`. 6. Do not trust IDs loaded from `employees.json`. Validate the complete configuration schema during loading and reject records containing unsafe IDs, invalid types, or unexpected fields. 7. Use restrictive filesystem permissions for generated directories and files. Run the application under a dedicated, least-privileged operating-system account. 8. Add automated tests covering traversal attempts with `../`, `..\`, absolute paths, repeated separators, encoded traversal input, and platform-specific path forms. ]]>
