T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/generate.js:96
- Finding
- Unescaped Invoice Data Allows HTML and Script Injection During PDF Rendering<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate.js:96-127` and `scripts/generate.js:197-223` **Vulnerability Type**: HTML injection leading to browser-side script execution **Risk Level**: High ### Vulnerable Code Quotation item data and template variables are inserted directly into HTML: ```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 generator 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 ...[truncated 3315 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Introduce a centralized HTML-escaping function that encodes at least `&`, `<`, `>`, `"`, and `'`. 2. Escape every scalar value before inserting it into an HTML text context, including supplier fields, client fields, item names, units, notes, dates, and invoice type. 3. Replace manual string concatenation and chained `replace()` calls with a template engine that enables automatic escaping by default. 4. Keep trusted template fragments, such as generated table-row structure, separate from untrusted text values. 5. Validate fields according to strict schemas: - Dates must match the expected date format. - Invoice type should be selected from an allowlist. - Numeric fields must be finite integers within acceptable business limits. - Text fields should have reasonable length limits. 6. Disable JavaScript while rendering invoices if scripts are not required: ```js await page.setJavaScriptEnabled(false); ``` 7. Add a restrictive Content Security Policy to both templates, for example one that denies scripts and external connections. 8. Consider request interception in Puppeteer and reject all network requests not required for local invoice rendering. 9. Add tests using payloads containing closing tags, script elements, event-handler attributes, and malformed table markup to verify that they are rendered only as text. ]]>
