Cross-site Scripting (XSS) - Stored in kevinpapst/kimai2
Description
Kimai2 before 1.16.3 fails to escape customer, project, and activity names in JavaScript, enabling stored XSS attacks.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Kimai2 before 1.16.3 fails to escape customer, project, and activity names in JavaScript, enabling stored XSS attacks.
Vulnerability
Kimai2 versions prior to 1.16.3 are vulnerable to stored cross-site scripting (XSS) due to improper neutralization of user-controlled input when rendering customer, project, and activity names in JavaScript templates. The KimaiRecentActivities plugin used replace() with plain string substitution for %customer%, %project%, and %activity% placeholders, allowing injected HTML/JavaScript to be executed in the browser of any user viewing the recent activities panel. The fix introduced in commit 89bfa82c61da0d3639e4038e689e25467baac8a0 and released in version 1.16.3 adds an escape() method that encodes &, <, and > characters. The vulnerable code path exists in js-src/plugin/KimaiRecentActivities.js (renamed in the commit). Affected versions: all Kimai2 releases before 1.16.3 [1][3][4].
Exploitation
An attacker with the ability to create or edit customers, projects, or activities can inject malicious JavaScript as the name of one of these entities. No special network position is required; the attacker only needs permission to create or modify those records (e.g., a user with customer management rights). When any user loads the page that displays recent activities, the unsanitized name is inserted into the DOM via JavaScript, causing the injected script to execute. The attack is stored and can affect all users who view the recent activities, including administrators [1][3][4].
Impact
Successful exploitation allows an attacker to execute arbitrary JavaScript in the context of the victim’s session. This can lead to session hijacking, defacement, theft of sensitive data displayed in the application, or further actions performed as the victim user. The scope is within the browser session; the attacker gains the same privileges as the victim. Confidentiality, integrity, and availability of the application’s data may be compromised [1][3].
Mitigation
The vulnerability is fixed in Kimai2 version 1.16.3, released on 2021-11-30 [4]. Users should upgrade to 1.16.3 or later immediately. There is no known workaround short of restricting user permissions to prevent unauthorized entity creation, which does not eliminate the risk from existing malicious names. The fix ensures that customer, project, and activity names are properly HTML-escaped before insertion into the DOM [3][4].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
kevinpapst/kimai2Packagist | < 1.16.3 | 1.16.3 |
Affected products
2- kevinpapst/kevinpapst/kimai2v5Range: unspecified
Patches
189bfa82c61daescape customer, project and activity name in javascript (#2959)
7 files changed · +23 −7
assets/js/KimaiPlugin.js+15 −0 modified@@ -66,4 +66,19 @@ export default class KimaiPlugin { return this.getContainer().getPlugin(name); } + /** + * @param {string} title + * @returns {string} + */ + escape(title) { + const tagsToReplace = { + '&': '&', + '<': '<', + '>': '>', + }; + + return title.replace(/[&<>]/g, function(tag) { + return tagsToReplace[tag] || tag; + }); + }; }
assets/js/plugins/KimaiRecentActivities.js+4 −3 modified@@ -65,9 +65,10 @@ export default class KimaiRecentActivities extends KimaiPlugin { for (let timesheet of entries) { let label = this.attributes['template'] - .replace('%customer%', timesheet.project.customer.name) - .replace('%project%', timesheet.project.name) - .replace('%activity%', timesheet.activity.name); + .replace('%customer%', this.escape(timesheet.project.customer.name)) + .replace('%project%', this.escape(timesheet.project.name)) + .replace('%activity%', this.escape(timesheet.activity.name)) + ; htmlToInsert += `<li>` +
public/build/app.3947eaef.js+0 −2 removedpublic/build/app.9e8f68cf.js+2 −0 addedpublic/build/app.9e8f68cf.js.LICENSE.txt+0 −0 renamedpublic/build/entrypoints.json+1 −1 modified@@ -3,7 +3,7 @@ "app": { "js": [ "build/runtime.b8e7bb04.js", - "build/app.3947eaef.js" + "build/app.9e8f68cf.js" ], "css": [ "build/app.3bc2b4d9.css"
public/build/manifest.json+1 −1 modified@@ -1,6 +1,6 @@ { "build/app.css": "build/app.3bc2b4d9.css", - "build/app.js": "build/app.3947eaef.js", + "build/app.js": "build/app.9e8f68cf.js", "build/invoice.css": "build/invoice.ff32661a.css", "build/invoice.js": "build/invoice.19f36eca.js", "build/invoice-pdf.css": "build/invoice-pdf.9a7468ef.css",
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-67c7-5v9j-227rghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-3983ghsaADVISORY
- github.com/kevinpapst/kimai2/commit/89bfa82c61da0d3639e4038e689e25467baac8a0ghsax_refsource_MISCWEB
- github.com/kevinpapst/kimai2/releases/tag/1.16.3ghsaWEB
- huntr.dev/bounties/c96f3480-dccf-4cc2-99a4-d2b3a7462413ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.