From b8137927a62a6991d680ac3049f68efd429aff7d Mon Sep 17 00:00:00 2001 From: Mehmet KOZAN Date: Sat, 16 May 2026 02:37:02 +0300 Subject: [PATCH] Security: Fix Critical XSS in NewsModal (CVE GHSA-rpr9-rxv7-x643) (#3932) Subject: Security Vulnerability Report: Critical XSS in OpenFront.io via sanitize-html (CVE GHSA-rpr9-rxv7-x643) Hello OpenFront Development Team, While reviewing the OpenFront.io project, I discovered a critical Cross-Site Scripting (XSS) vulnerability on the client side. I am responsibly disclosing this issue to you along with technical details and a remediation plan so it can be addressed. Vulnerability Summary - Vulnerability Type: Cross-Site Scripting (XSS) / Mutation XSS - Affected Components: src/client/NewsModal.ts, src/client/components/NewsBox.ts - Affected Dependency: sanitize-html v2.17.0 (imported via lit-markdown) - CVE Reference: GHSA-rpr9-rxv7-x643 (CVSS Score: 9.3) Technical Details The "News" (Changelog) modal in the game uses the lit-markdown package to parse markdown content. This package depends on sanitize-html v2.17.0. This specific version of sanitize-html has a known parsing flaw when handling the `` tag. When malicious HTML is wrapped inside an `<xmp>` tag, the sanitization filter misinterprets it and fails to properly strip the inner HTML. As a result, when the sanitized content is injected into the DOM, the browser executes the inner HTML. Proof of Concept (PoC) If the changelog.md file (or the network response) is manipulated to include the following payload, the malicious code bypasses sanitization and executes in the context of the application: `<xmp><img src=x onerror="alert('System compromised')">` In local testing, injecting this payload directly into the markdown property of the news-modal component resulted in the `` tag bypassing the filter and rendering successfully in the DOM. Impact This vulnerability introduces a high-risk Stored XSS vector. If an attacker compromises the server or the CDN hosting the changelog.md file, or performs a Man-in-the-Middle (MitM) attack: - Arbitrary JavaScript can be executed in the browsers of all players who open the News modal. - Session tokens and authentication data can be stolen. - Attackers can perform unauthorized actions on behalf of the players (e.g., disbanding clans or altering settings). Remediation The fix is straightforward and requires updating the sanitize-html library to version 2.17.4 or higher. You can enforce this update by adding an overrides block to your package.json: "overrides": { "sanitize-html": ">=2.17.4" } After updating the package.json, running npm install will apply the patch. I am disclosing this vulnerability responsibly and will keep the details private until a patch has been released. Please let me know if you need any further information or assistance with the fix. Best regards, Mehmet Kozan Security Researcher Email: twanske1@gmail.com --- ## Description: This PR addresses the critical XSS vulnerability detailed above. By enforcing `sanitize-html` to be version `>=2.17.4` via the `overrides` block in `package.json`, the `` tag parsing flaw is patched. No UI changes or new text strings were added. ## Please complete the following: - [ ] I have added screenshots for all UI updates *(N/A - Security patch in package.json)* - [ ] I process any text displayed to the user through translateText() and I've added it to the en.json file *(N/A)* - [ ] I have added relevant tests to the test directory *(N/A)* - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: hz.mehmetsultan --- package-lock.json | 79 +++++++++++++++++++++++++++++++++-------------- package.json | 3 ++ 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/package-lock.json b/package-lock.json index f86c89379..fd3d31773 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4627,6 +4627,13 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, + "node_modules/dayjs": { + "version": "1.11.20", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz", + "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==", + "dev": true, + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -5913,6 +5920,39 @@ "dev": true, "license": "MIT" }, + "node_modules/htmlparser2": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -6364,6 +6404,16 @@ "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", "license": "MIT" }, + "node_modules/launder": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/launder/-/launder-1.7.1.tgz", + "integrity": "sha512-mU6WRz5EusL9ZZuiZ5SO4Y6C0P9PAUR9iwdb6bzj4KDihm28DiHFw+/yk9DBH4f+Pv1wuzQ4e2jV3oQ7mkIqvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dayjs": "^1.11.7" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -8164,40 +8214,21 @@ "license": "MIT" }, "node_modules/sanitize-html": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.17.0.tgz", - "integrity": "sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA==", + "version": "2.17.4", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.17.4.tgz", + "integrity": "sha512-2HW7v2ol/uAM7sX4hbD8Z59OGWmAPrvjL8E71UWlBcj6m+kcF6ilQBLny+cIgY214QJeJT5tQuxKKqX0SQqjGQ==", "dev": true, "license": "MIT", "dependencies": { "deepmerge": "^4.2.2", "escape-string-regexp": "^4.0.0", - "htmlparser2": "^8.0.0", + "htmlparser2": "^10.1.0", "is-plain-object": "^5.0.0", + "launder": "^1.7.1", "parse-srcset": "^1.0.2", "postcss": "^8.3.11" } }, - "node_modules/sanitize-html/node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } - }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", diff --git a/package.json b/package.json index 7554e5634..3ed080276 100644 --- a/package.json +++ b/package.json @@ -115,5 +115,8 @@ "ws": "^8.20.0", "zod": "^4.4.2" }, + "overrides": { + "sanitize-html": ">=2.17.4" + }, "type": "module" }