{"id":"GHSA-r736-2678-fcrx","summary":"FacturaScripts vulnerable to stored XSS via product reference in sales/purchases","details":"## Summary\n\nA stored Cross-Site Scripting (XSS) vulnerability exists in the product search modal of sales and purchases documents. An authenticated user with access to the warehouse module can create a product with a malicious reference that executes arbitrary JavaScript in the browser of any other user who opens the product search modal inside an invoice, order, or delivery note.\n\n## Affected files\n\n- `Core/Lib/AjaxForms/SalesModalHTML.php`\n- `Core/Lib/AjaxForms/PurchasesModalHTML.php`\n\n## Vulnerability details\n\nThe `referencia` field of a product variant is injected directly into an HTML `onclick` attribute string without JavaScript context escaping:\n\n```php\n// SalesModalHTML.php ~line 102\n$tbody .= '\u003ctr onclick=\"return salesFormAction(\\'add-product\\', \\''\n    . $row['referencia']   // no htmlspecialchars() applied\n    . '\\');\"\u003e';\n```\n\nWhen a product is saved, `noHtml()` encodes `'` → `&#39;`. This appears safe in static HTML context. However, the modal HTML is later returned as a JSON response and inserted into the DOM via `innerHTML`:\n\n```javascript\n// SalesDocument.html.twig line 118\ndocument.getElementById(\"findProductList\").innerHTML = data.products;\n```\n\nThe browser HTML parser decodes `&#39;` → `'` during the `innerHTML` assignment, breaking out of the JavaScript string literal in the `onclick` attribute and executing the injected code.\n\n**Attack payload stored in database:** `x&#39;+alert(1)+&#39;`\n\n**Resulting `onclick` after `innerHTML` decode:**\n```javascript\nreturn salesFormAction('add-product', 'x'+alert(1)+'')\n//                                        ^^^^^^^^^^ executes before the function call\n```\n\n## Steps to reproduce\n\n**Step 1 — Inject the payload**\n\n1. Log in as a user with write access to Warehouse → Products\n2. Navigate to `/EditProducto` and create a new product with the following values:\n\n| Field | Value |\n|---|---|\n| Reference | `x'+alert(1)+'` |\n| Description | `test` |\n\n3. Save the product\n\n**Step 2 — Trigger the XSS**\n\n1. Make sure at least one customer exists in the system (Sales → Customers)\n2. Navigate to `/EditFacturaCliente?codcliente=\u003ccustomer_code\u003e`\n3. In the invoice form, click the product search button next to the \"Referencia\" field\n4. Click on the 'malicious' product `alert(1)`\n\n\u003cimg width=\"1162\" height=\"536\" alt=\"image\" src=\"https://github.com/user-attachments/assets/aaa2879e-c1fb-4af9-8501-bac03ca24ffe\" /\u003e\n\n\n## Impact\n\nAlthough session cookies (`fsLogkey`, `fsNick`) have the `HttpOnly` flag set and cannot be read directly via `document.cookie`, the injected script runs in the victim's authenticated browser context, meaning the attacker can make arbitrary authenticated requests on their behalf, create new admin users via AJAX POST to `/EditUser`, exfiltrate any business data visible in the DOM, or redirect the user to an external site. The most critical scenario is privilege escalation: a low-privilege employee with only warehouse\naccess can execute JavaScript in an administrator's session without knowing their password.\n\n## Recommended fix\n\nApply `htmlspecialchars()` with `ENT_QUOTES` before inserting `referencia` into the `onclick` attribute in both affected files.\n\n**`Core/Lib/AjaxForms/SalesModalHTML.php`**\n\n```php\n// Before (vulnerable):\n$tbody .= '\u003ctr onclick=\"return salesFormAction(\\'add-product\\', \\''\n    . $row['referencia']\n    . '\\');\"\u003e';\n\n// After (safe):\n$tbody .= '\u003ctr onclick=\"return salesFormAction(\\'add-product\\', \\''\n    . htmlspecialchars($row['referencia'], ENT_QUOTES, 'UTF-8')\n    . '\\');\"\u003e';\n```\n\n**`Core/Lib/AjaxForms/PurchasesModalHTML.php`**\n\nApply the same change to the equivalent line.\n\n**Why `ENT_QUOTES` is required:** `ENT_QUOTES` encodes both `\"` and `'` characters. This ensures that `'` is stored as `&#39;` and — critically — remains `&#39;` after `innerHTML` assignment, because `htmlspecialchars` produces a form that the HTML parser does not decode back into a raw quote inside a JS string context.\n\n**Alternative mitigation:** replace `innerHTML` with `innerText` or a DOM-based rendering approach that never parses injected strings as HTML. This would eliminate the entire class of HTML-injection-via-innerHTML vulnerabilities in the sales and purchases\nforms.\n\n## Credits\nOmar Ramirez","aliases":["CVE-2026-42877"],"modified":"2026-05-07T19:48:56.794842Z","published":"2026-05-07T19:37:08Z","database_specific":{"severity":"MODERATE","nvd_published_at":null,"github_reviewed_at":"2026-05-07T19:37:08Z","github_reviewed":true,"cwe_ids":["CWE-79"]},"references":[{"type":"WEB","url":"https://github.com/NeoRazorX/facturascripts/security/advisories/GHSA-r736-2678-fcrx"},{"type":"PACKAGE","url":"https://github.com/NeoRazorX/facturascripts"}],"affected":[{"package":{"name":"facturascripts/facturascripts","ecosystem":"Packagist","purl":"pkg:composer/facturascripts/facturascripts"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"2025.92"}]}],"versions":["2018.03","2018.04","2018.05","2018.11","v2018.12","v2018.13","v2018.14","v2018.15","v2018.16","v2020.01","v2020.2","v2020.3","v2020.4","v2020.51","v2020.61","v2020.71","v2020.80","v2021","v2021.1","v2021.2","v2021.4","v2021.51","v2021.71","v2021.81","v2022.06","v2022.08","v2022.2","v2022.4","v2022.51","v2023.03","v2023.08","v2023.16","v2023.21","v2024","v2024.1","v2024.2","v2024.3","v2024.5","v2024.7","v2024.8","v2024.9","v2024.91","v2024.93","v2024.94","v2024.95","v2024.96","v2025","v2025.11","v2025.2","v2025.3","v2025.4","v2025.41","v2025.43","v2025.7","v2025.71","v2025.8","v2025.81"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/05/GHSA-r736-2678-fcrx/GHSA-r736-2678-fcrx.json"}}],"schema_version":"1.7.5","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N"}]}