Skip to content

XSS via unsanitized add-form autocomplete suggestions in hledger-web

High
simonmichael published GHSA-538p-cvc4-4qjm Aug 24, 2026

Software

hledger-web

Affected versions

0.24-1.52.1, 1.99.1-1.99.3

Patched versions

1.52.2

Description

Typeahead's default suggestion template concatenates each completion value into HTML, unescaped. An account name or transaction description containing markup is rendered as markup wherever it appears in the add form's completion list.

Attack path. Journal data isn't always written by the person reading it - it can arrive via CSV import, a shared file, or another tool. A counterparty who can put arbitrary text into a payment (a bank transfer memo, an invoice payee name) can get that text into the victim's journal via CSV import. The victim doesn't need to seek out the malicious entry: typeahead renders every matching suggestion as soon as the dropdown opens, not just the one selected, so ordinary use of the add form (typing the start of a common word) can trigger it. The resulting JS runs same-origin with hledger-web and can read /journal//register, exfiltrating the full journal, and POST to AddR (forging transactions) or EditR (overwriting the journal, if edit access is enabled).

Why this matters despite hledger-web having no built-in auth. The manual has always said hledger-web has no access control and must go behind a proxy if shared, so an operator who exposes it unprotected, against that advice, isn't made meaningfully worse off here - direct reading was already possible. The real impact is elsewhere:

  • Local-only instances (the default, and the configuration the manual treats as safe on its own). The attacker needs no access to the victim's machine or network at all - only the ability to get text into a payment that later gets imported. This defeats the premise of "local-only", which assumes only the person at the keyboard can act on their own instance.
  • Instances properly put behind a proxy/VPN per the manual's advice. A proxy or VPN gates network reachability - who can open a connection. This XSS runs inside the already-authenticated victim's own browser session, so it never needs to pass that gate; it rides along as the legitimate user. The documented mitigation doesn't stop it.

Access level. --allow has defaulted to add (append-only) access, not edit, since 2018, for both local and non-local --host bindings (no local/non-local distinction existed before mid-2026). Edit access has always required an explicit --allow=edit. So the forgery path (AddR) applies to essentially all default installs; the journal-overwrite path (EditR) requires the operator to have explicitly opted into edit access.

Approximate CVSS v3.1: AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N (~8.1), or AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N (~7.1) if Attack Vector is scored Local, since exploitation targets a localhost server by default. Both land in the High band. Integrity is High mainly because a fabricated transaction silently inserted into someone's financial records is a serious consequence on its own; whole-file overwrite is an additional capability only where edit access is enabled.

Workaround if you can't upgrade immediately: run with --allow=view; if bound to anything other than 127.0.0.1/::1/localhost, rebind to loopback or put it behind an authenticated proxy. (This fully blocks this specific bug, not just its write consequences: the add form - including the vulnerable completer and the journal data it embeds - is only rendered server-side when add access is granted, so under --allow=view none of the vulnerable code or data reaches the browser.)

Fix released. hledger-web 1.52.2 is out with the fix: https://github.com/simonmichael/hledger/releases/tag/1.52.2 (2026-08-24). The fix passes an explicit escaping template at all three places a completer is attached - the description field, the account fields, and a newly added posting row. See hledger-web/templates/add-form.hamlet and hledger-web/static/hledger.js. The same fix is also merged on the main branch (the upcoming 2.x line) ahead of its next release.

Severity

High

CVE ID

No known CVE

Weaknesses

Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users. Learn more on MITRE.