T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- scripts/discord-push.js:123
- Finding
- Arbitrary Local File Disclosure Through Discord Push Instructions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/discord-push.js`, lines 123-129 and 152-166 **Vulnerability Type**: Unrestricted local file read followed by external-delivery instruction generation **Risk Level**: High ### Vulnerable Code ```js } else if (opts.report) { // 指定报告文件 if (!fs.existsSync(opts.report)) { console.error(`报告文件不存在: ${opts.report}`); process.exit(1); } content = fs.readFileSync(opts.report, 'utf-8'); } ``` ```js const chunks = splitMessage(content); const instructions = generatePushInstructions(chunks, pushType); if (opts.dryRun) { console.log('[DRY-RUN] 推送预览:'); console.log(JSON.stringify(instructions, null, 2)); return; } // 输出 JSON 指令供 agent 读取 console.log(JSON.stringify(instructions)); ``` The generated instruction identifies the requested operation as a Discord push: ```js const instructions = { action: 'discord_push', type, channel: '#🧠-hq-指挥中心', chunks: chunks.map((c, i) => ({ index: i + 1, total: chunks.length, content: c })), total_chunks: chunks.length, generated_at: new Date().toISOString() }; ``` ### Technical Analysis The `--report` argument is treated as an unrestricted filesystem path. The script only checks whether the path exists and then reads it with the privileges of the Node.js process. It does not require the target to be beneath `data/reports`, verify that it is a regular Markdown report, reject symbolic links, or enforce an approved filename pattern. The complete file contents are split into chunks and embedded in an Agent-consumable `discord_push` instruction. Although this script does not directly connect to Discord, its declared integration model expects another Agent component to execute the generated instruction. Therefore, the unrestricted local read can become an external data-disclosure channel. ### Attack Path 1. An attacker or untrusted workflow invokes the script with a sensitive readable path: ```bash node scripts/discord-pu ...[truncated 982 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Resolve the requested path and require it to remain under `REPORTS_DIR`: ```js const reportsRoot = fs.realpathSync(REPORTS_DIR); const requested = fs.realpathSync(path.resolve(REPORTS_DIR, opts.report)); if ( requested !== reportsRoot && !requested.startsWith(reportsRoot + path.sep) ) { throw new Error('Report path is outside the approved report directory'); } ``` 2. Require an approved filename pattern, such as: ```js /^(weekly|monthly)-\d{4}-\d{2}-\d{2}\.md$/ ``` 3. Use `fs.lstatSync()` and reject symbolic links and non-regular files. 4. Accept a report identifier or basename rather than an arbitrary path. 5. Require explicit authorization or confirmation before report content is transmitted externally. 6. Apply content-size limits and optionally scan generated reports for credentials or other sensitive patterns before constructing push instructions. ]]>
