T09 · Insecure Skill Coding Practices
- Location
scripts/convert-ap-report.mjs:145- Finding
OS Command Injection Through the PDF Output Path
- Content
View full analysis
&1 || true`, { stdio: 'pipe', timeout: 30000 }); const generated = tmpHtml.replace(/\.html$/, '.pdf'); if (existsSync(generated)) { renameSync(generated, resolve(outputPdf)); console.log('OK'); } else { // Fallback: try node with puppeteer directly console.log('Fallback: trying direct puppeteer render...'); execSync(`node -e " const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch({headless:'new'}); const page = await browser.newPage(); await page.goto('file://${tmpHtml}', {waitUntil:'networkidle0'}); await page.pdf({path:'${resolve(outputPdf)}', format:'A4', margin:{top:'18mm',bottom:'18mm',left:'18mm',right:'18mm'}, printBackground:true}); await browser.close(); console.log('OK'); })(); "`, { stdio: 'inherit', timeout: 30000 }); } ``` ### Technical Analysis The `outputPdf` value comes directly from `process.argv`. It is used to derive `tmpHtml` and is subsequently interpolated into command strings passed to `execSync`. The first invocation places `tmpHtml` inside double quotes, but shell substitutions such as `$(command)` remain active inside double-quoted shell arguments. The fallback also embeds both `tmpHtml` and `resolve(outputPdf)` in a shell command containing inline JavaScript. Crafted quote characters, command substitutions, or JavaScript delimiters can therefore escape the intended path context. Because `execSync` executes through a shell, string interpolation converts an otherwise ordinary output filename into a command-execution primitive. No filename validation or restriction to an approved output directory is p ...[truncated 1443 chars]- Remediation
View remediation
