T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/generate.js:92
- Finding
- Unescaped Invoice Data Allows HTML and Script Injection During PDF Rendering<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate.js:92-133`, `scripts/generate.js:181-210`, and `scripts/generate.js:232-241` **Vulnerability Type**: HTML injection into a browser-rendered document **Risk Level**: High ### Vulnerable Code ```js let itemRows = ''; itemList.forEach((item, idx) => { const amount = item.quantity * item.price; itemRows += ` <tr> <td>${idx + 1}</td> <td>${item.name}</td> <td>${item.unit}</td> <td>${formatCurrency(item.quantity)}</td> <td>${formatCurrency(item.price)}</td> <td>${formatCurrency(amount)}</td> </tr> `; }); const html = template .replace(/{{myCompanyName}}/g, myInfo.companyName || '') .replace(/{{myBusinessNumber}}/g, myInfo.businessNumber || '') .replace(/{{myCEO}}/g, myInfo.ceo || '') .replace(/{{myAddress}}/g, myInfo.address || '') .replace(/{{myPhone}}/g, myInfo.phone || '') .replace(/{{myEmail}}/g, myInfo.email || '') .replace(/{{clientName}}/g, client.name || '') .replace(/{{clientBusinessNumber}}/g, client.businessNumber || '') .replace(/{{clientCEO}}/g, client.ceo || '') .replace(/{{clientAddress}}/g, client.address || '') .replace(/{{clientPhone}}/g, client.phone || '') .replace(/{{issueDate}}/g, issueDate) .replace(/{{validUntil}}/g, validUntil) .replace(/{{itemRows}}/g, itemRows) .replace(/{{subtotal}}/g, formatCurrency(subtotal)) .replace(/{{vat}}/g, formatCurrency(vat)) .replace(/{{total}}/g, formatCurrency(total)) .replace(/{{notes}}/g, options.notes || ''); ``` The tax-invoice path has the same issue: ```js let itemRows = ''; itemList.forEach((item, idx) => { const amount = item.quantity * item.price; itemRows += ` <tr> <td>${issueDate}</td> <td>${item.name}</td> <td>${formatCurrency(item.quantity)}</td> <td>${formatCurrency(item.price)}</td> <td>${formatCurrency(amount)}</td> <td>${options.notes || ''}</td> </tr> `; }); const html = template . ...[truncated 3425 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Apply HTML escaping to every dynamic text value before inserting it into a template, including supplier data, client data, item names, units, notes, dates, and invoice types. 2. Replace manual string concatenation and `.replace()` templating with a maintained template engine that enables automatic escaping by default. 3. Treat `itemRows` as structured data rather than preassembled HTML. Generate rows through safe template iteration. 4. Validate fields according to their expected formats. For example, restrict invoice type to an explicit allowlist and enforce maximum lengths on names and notes. 5. Configure the rendering page defensively: - Disable JavaScript when it is not required. - Block HTTP and HTTPS requests using Puppeteer request interception. - Reject frames, scripts, plugins, and remote resources. - Use a dedicated, isolated browser profile with no authenticated sessions or unnecessary permissions. 6. Add tests using payloads such as `<script>`, `<img onerror>`, attribute-breaking strings, and malicious CSS to verify that generated output renders them only as text. ]]>
