T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/generate-image.mjs:38
- Finding
- Unrestricted Output Path Allows Arbitrary Writable-File Overwrite## Vulnerability Details **File Location**: `scripts/generate-image.mjs`, lines 38 and 70-71 **Vulnerability Type**: Unrestricted file write and overwrite **Risk Level**: Medium ### Vulnerable Code ```js const outputPath = getArg('--output', `${defaultOutputDir}/minimax-image-${Date.now()}.png`); ``` ```js mkdirSync(dirname(outputPath), { recursive: true }); writeFileSync(outputPath, buffer); ``` ### Technical Analysis The `--output` argument is accepted as a caller-controlled filesystem path. The script does not normalize the path or verify that its canonical destination remains beneath the intended `~/.openclaw/workspace/images` directory. It also does not reject symbolic links, path traversal, absolute paths, or existing files. `writeFileSync()` uses overwrite behavior by default. Therefore, after downloading an image, the script can replace any file writable by the account running the process. Creating the parent directory does not provide containment and can also create attacker-selected directory trees where permissions allow. Exploitation requires influence over the script's command-line arguments. The downloaded content is controlled indirectly through the MiniMax generation response rather than being arbitrary attacker-supplied local bytes, but it can still corrupt or replace targeted files. ### Attack Path 1. An attacker or untrusted automation gains influence over arguments passed to `generate-image.mjs`. 2. The attacker supplies `--output` with an absolute path, traversal path, or path resolving through a symbolic link to a sensitive writable file. 3. The script requests image generation from MiniMax and downloads the first returned image URL. 4. `writeFileSync(outputPath, buffer)` opens the selected destination with overwrite semantics. 5. The target file is replaced with the downloaded image bytes. Example invocation: ```bash node scripts/generate-image.mjs "test image" \ --output "$HOM ...[truncated 710 chars]
- Remediation
- ## Remediation Suggestions 1. Resolve both the trusted output root and requested destination with `realpath` or `resolve`, then verify that the destination remains beneath the trusted root using `relative()`. 2. Reject absolute paths and traversal outside the dedicated image directory unless a trusted operator explicitly authorizes them. 3. Inspect every existing path component with `lstat()` and reject symbolic links. Use platform-supported no-follow behavior where available to reduce time-of-check/time-of-use risks. 4. Prevent silent replacement by opening the destination with exclusive creation, such as `writeFileSync(path, buffer, { flag: 'wx' })`. 5. Generate server-side filenames rather than accepting complete caller-controlled paths. If customization is needed, accept only a sanitized basename. 6. Permit only expected image extensions and verify the downloaded response's content type and file signature before writing. 7. Run the script with a least-privileged account that cannot modify sensitive configuration or executable locations. 8. Add tests covering absolute paths, `../` traversal, existing destinations, nested symlinks, and paths outside the approved output directory.
