GHSA-gpxg-fx2g-qxj2 — kanidm
MEDIUMGHSA-gpxg-fx2g-qxj2 is a medium-severity (CVSS 6.1) vulnerability in kanidm. A fix is available for kanidm — see the affected versions and patch details below.
Kanidm: Stored HTML injection in "passkey-enrolment" partial via displayname → htmx-driven authenticated request forgery
Real-World Exposure
kanidmReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects crates.io packages — download data is not available via public APIs for these ecosystems.
Description
Summary
The kanidmd web UI renders the WebAuthn passkey-registration challenge as raw JSON inside an inline <script id="data"> element using the Askama |safe filter. The challenge embeds the account's displayname, which serde_json serialises without escaping </>. A displayname containing </script> therefore terminates the script element early and injects arbitrary HTML into the credential-update page. Because the page is htmx-driven and the server's CSP allows 'unsafe-eval', injected hx-* attributes can issue authenticated same-origin API requests with the viewer's bearer cookie.
Impact
An authenticated attacker who is a member of idm_people_admins can write the displayname of any Person entry — including high-privilege persons — because idm_acp_people_pii_manage carries no high-privilege exclusion filter. When the targeted high-privilege user later opens Add Passkey on their own credential-update page (/ui/reset), the injected markup is swapped into the DOM and htmx fires attacker-chosen same-origin requests authenticated as the victim. This allows a helpdesk-tier operator to escalate to idm_admins (e.g. by POSTing themselves into the group) or otherwise act with the victim's session. The self-write path (idm_people_self_name_write) is self-XSS only and is not counted toward impact. Even without the htmx vector, the breakout permits <meta http-equiv='refresh'> open-redirect and arbitrary defacement of the credential page.
Details
- https://github.com/kanidm/kanidm/blob/master/server/core/templates/credential_update_add_passkey_partial.html#L3 — the
|safesink - https://github.com/kanidm/kanidm/blob/master/server/core/src/https/views/reset.rs#L506-L509 —
serde_json::to_stringof the challenge - https://github.com/kanidm/kanidm/blob/master/server/lib/src/idm/credupdatesession.rs#L2453-L2460 —
displaynameflows intostart_passkey_registration
Affected versions
All releases shipping the htmx credential-update views
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🦀crates.io | kanidm | all versions | 1.9.3cargo update -p kanidm --precise 1.9.3 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for kanidm, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update kanidm to 1.9.3 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-gpxg-fx2g-qxj2 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like GHSA-gpxg-fx2g-qxj2 can be triaged on real exposure rather than presence alone.
Tailored to GHSA-gpxg-fx2g-qxj2. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-gpxg-fx2g-qxj2 in your dependencies?
O3 Security finds GHSA-gpxg-fx2g-qxj2 across crates.io dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.