T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/generate-ppt.js:66
- Finding
- Unescaped User Input Executes as Active Content During Slide Rendering<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate-ppt.js:66-68, 87-88, 105-110`; additional affected code in `scripts/canvas-generator.js:179-180` **Vulnerability Type**: HTML injection with browser-context script execution **Risk Level**: High ### Vulnerable Code ```javascript function genHTML(topic, s, pn, total) { const isCover = pn===1, isEnd = pn > total-2; const title = isCover ? topic : (isEnd ? 'Thank You' : 'Part '+(pn-1)); const pts = isEnd ? ['Q&A','Contact'] : ['Key Point 1','Key Point 2','Key Point 3']; const tc = s.bg==='#FFFFFF'?'#333':'#fff'; return '<!DOCTYPE html><html><head><meta charset="UTF-8"><style>body{font-family:Inter,sans-serif;background:'+s.bg+';margin:0;width:1280px;height:720px;position:relative;overflow:hidden}.border{position:absolute;top:20px;left:20px;right:20px;bottom:20px;border:8px solid '+s.border+';border-radius:10px}.title{position:absolute;top:60px;left:80px;right:80px;height:100px;background:'+s.accent[0]+';border:6px solid '+s.border+';border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:48px;font-weight:bold;color:#000}.content{position:absolute;top:220px;left:80px;right:80px;bottom:80px;padding:30px;font-size:24px;line-height:1.8;color:'+tc+';background:rgba(0,0,0,0.3);border-radius:10px}.content ul{list-style:none;padding:0}.content li{padding:15px 0;border-bottom:2px dashed '+s.accent[1]+'}.footer{display:none}</style></head><body><div class="border"></div><div class="title">'+title+'</div><div class="content"><ul>'+pts.map(p=>'<li>'+p+'</li>').join('')+'</ul></div><div class="footer"></div></body></html>'; } ``` ```javascript if(args[i]==='--topic'&&args[i+1]) topic=args[++i]; ``` ```javascript for(let i=1;i<=total;i++) fs.writeFileSync( path.join(OUTPUT_DIR,'slide_'+i+'.html'), genHTML(topic,s,i,total) ); const puppeteer = require('/tmp/node_modules/puppeteer'); const browser = await puppeteer.launch({ head ...[truncated 3572 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Encode every user-controlled value before inserting it into HTML. At minimum, escape `&`, `<`, `>`, `"`, and `'`. ```javascript function escapeHTML(value) { return String(value) .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>') .replaceAll('"', '"') .replaceAll("'", '''); } const safeTitle = escapeHTML(title); ``` 2. Apply the same encoding to `title` and `subtitle` in `canvas-generator.js`. 3. Prefer DOM APIs such as `textContent` over string concatenation when constructing presentation content. 4. Disable JavaScript when rendering static presentation HTML: ```javascript await page.setJavaScriptEnabled(false); ``` 5. Use Puppeteer request interception to deny all network requests not strictly required for rendering: ```javascript await page.setRequestInterception(true); page.on('request', request => { const url = new URL(request.url()); if (url.protocol === 'file:') { request.continue(); } else { request.abort(); } }); ``` 6. Remove remote font imports or package required fonts locally so network access can remain disabled. 7. Run Chromium as a dedicated unprivileged user with its sandbox enabled. Remove `--no-sandbox` rather than relying on the browser process as a security boundary. 8. Validate input length and reject control characters or malformed values to reduce denial-of-service and parser edge cases. 9. Add tests using payloads containing HTML tags, event handlers, scripts, iframes, and remote resource URLs, confirming they appear only as literal slide text. ]]>
