T09 · Insecure Skill Coding Practices
Error
- Location
- orchestrator.js:296
- Finding
- Arbitrary File Write Through Model-Controlled Path Traversal## Vulnerability Details **File Location**: `orchestrator.js`, lines 296–306 **Vulnerability Type**: Path traversal leading to arbitrary file creation or overwrite **Risk Level**: High ```js function parseWorkerOutput(output, roleDir) { const fileRegex = /=== FILE: (.+?)\s*===\n([\s\S]*?)\n=== END FILE ===/g; let match; const files = []; while ((match = fileRegex.exec(output)) !== null) { const filePath = match[1].replace(/^\.?\//, ''); const content = match[2]; const fullPath = path.join(roleDir, filePath); fs.mkdirSync(path.dirname(fullPath), { recursive: true }); fs.writeFileSync(fullPath, content); files.push(filePath); } if (files.length === 0) throw new Error('No file blocks found'); return files; } ``` ### Technical Analysis `parseWorkerOutput()` treats filenames supplied in an OpenRouter model response as trusted filesystem paths. The normalization only removes one leading slash or `./`; it does not reject `..` path components or confirm that the resolved destination remains beneath `roleDir`. In Node.js, `path.join(roleDir, "../../../../target")` resolves traversal components and can identify a destination outside the intended generated-project directory. The subsequent `fs.mkdirSync()` and `fs.writeFileSync()` calls then create or overwrite that destination using the privileges of the orchestrator process. Because worker output is influenced by the user's prompt and generated by an external model, it must be treated as untrusted input. Prompt injection or unexpected model behavior could produce a malicious file block even though the system prompt requests relative paths. Related planner-generated values, including role IDs and declared output paths, are also used in path construction without strict validation. This expands the untrusted path surface, although the direct arbitrary-write sink is shown above. ### Attack Path 1. An attacker supplies a crafted project prompt containing instructions intended to ...[truncated 1701 chars]
- Remediation
- ## Remediation Suggestions 1. Canonicalize and validate every generated filename before performing any filesystem operation: ```js function safePathWithin(baseDir, untrustedPath) { if (typeof untrustedPath !== 'string' || untrustedPath.includes('\0')) { throw new Error('Invalid generated file path'); } if (path.isAbsolute(untrustedPath)) { throw new Error('Absolute paths are not permitted'); } const base = path.resolve(baseDir); const destination = path.resolve(base, untrustedPath); if (destination !== base && !destination.startsWith(base + path.sep)) { throw new Error('Generated file path escapes its assigned directory'); } return destination; } ``` 2. Replace the vulnerable path construction with the validated destination: ```js const filePath = match[1].trim(); const fullPath = safePathWithin(roleDir, filePath); fs.mkdirSync(path.dirname(fullPath), { recursive: true }); fs.writeFileSync(fullPath, content, { flag: 'wx' }); ``` Use an overwrite policy appropriate to the application; `flag: "wx"` prevents silent replacement of existing files. 3. Reject paths containing empty components, `.` or `..` components, drive prefixes, UNC paths, null bytes, and platform-specific separators where they are not expected. 4. Validate planner-generated role IDs and output paths against strict schemas. Role IDs should use a narrow pattern such as `^[a-z0-9-]+$`, and every output path should undergo the same containment check. 5. Apply limits to the number and size of generated files to reduce denial-of-service risks. 6. Run the orchestrator under a dedicated, least-privileged operating-system account in an isolated workspace or container. Mount only the intended output directory as writable. 7. Treat all external model responses as hostile data. Do not rely on prompt instructions such as “use relative paths” as a security boundary. 8. Add automated tests covering trave ...[truncated 289 chars]
