T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/export_pdf.js:153
- Finding
- Predictable Temporary Directory Is Recursively Deleted Without Ownership Validation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/export_pdf.js`, lines 153–155 and 215 **Vulnerability Type**: Unsafe temporary-file handling and uncontrolled recursive deletion **Risk Level**: Medium ### Vulnerable Code ```javascript const imgDir = path.resolve(process.cwd(), '_pdf_pages'); if (fs.existsSync(imgDir)) fs.rmSync(imgDir, { recursive: true }); fs.mkdirSync(imgDir); ``` Cleanup later repeats the recursive deletion: ```javascript await browser.close(); fs.rmSync(imgDir, { recursive: true }); fs.unlinkSync(tmpHtml); ``` ### Technical Analysis The exporter uses the fixed `_pdf_pages` path beneath the current working directory as temporary storage. Before export, it recursively deletes any existing object at that path without checking whether the directory was created by the current invocation or contains user-owned data. A predictable temporary path violates safe temporary-file handling principles. An existing legitimate directory with the same name will be destroyed. Depending on platform and filesystem behavior, directory junctions or other redirection mechanisms may increase the affected scope. The implementation also lacks a `try/finally` cleanup boundary, so failures may leave temporary slide images behind. The operation is performed with the filesystem permissions of the user running the exporter. It does not independently elevate privileges, but it can delete data accessible to that user. ### Attack Path 1. The exporter is run from a directory writable by another local process, collaborator, or untrusted build step. 2. The attacker or conflicting process creates `<working-directory>/_pdf_pages` and places data there. On platforms where directory junction behavior permits it, the attacker may attempt to redirect the path to another accessible directory. 3. The user runs `node export_pdf.js`. 4. The initialization code resolves the predictable path and calls `fs.rmSync(imgDir, { recursive: true })`. 5. Existing content is d ...[truncated 507 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Create a unique temporary directory with the operating system's secure temporary-directory API: ```javascript const os = require('os'); const imgDir = fs.mkdtempSync(path.join(os.tmpdir(), 'awesome-deck-pdf-')); ``` - Never recursively delete a predictable user-controlled path. - Keep the exact path returned by `mkdtempSync` and remove only that directory. - Put browser shutdown and temporary-file cleanup in a `finally` block: ```javascript let browser; const imgDir = fs.mkdtempSync( path.join(os.tmpdir(), 'awesome-deck-pdf-') ); try { browser = await launch(); // Perform export. } finally { if (browser) await browser.close(); fs.rmSync(imgDir, { recursive: true, force: true }); } ``` - Create temporary files using generated names and restrictive permissions where supported. - Avoid putting `_slides.html` in the working directory; place it inside the unique temporary directory as well. ]]>
