hledger-web 1.52.2 → 1.52.3
raw patch · 7 files changed
+108/−16 lines, 7 filesdep ~aesondep ~hledgerdep ~hledger-libPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: aeson, hledger, hledger-lib
API changes (from Hackage documentation)
Files
- CHANGES.md +32/−0
- Hledger/Web/Handler/AddR.hs +29/−3
- Hledger/Web/Test.hs +34/−0
- hledger-web.1 +2/−2
- hledger-web.cabal +8/−8
- hledger-web.info +1/−1
- hledger-web.txt +2/−2
CHANGES.md view
@@ -23,6 +23,38 @@ See also the hledger changelog. +# 1.52.3 2026-08-27++Fixes++- Another XSS (cross-site scripting) vulnerability has been fixed, in+ the add transaction form's error message. Any web page visited while+ hledger-web was running could use it to run javascript in+ hledger-web's origin, and from there read the whole journal, or+ alter it. All hledger-web users should upgrade. See also:+ GHSA-vq7r-8w52-jv84. (Arthur Cinader, Simon Michael, [#2700])++- A newline submitted in a transaction's description, code or account+ name is no longer written into the journal file. This removes the+ possibility of the user inserting an include directive, which could+ expose system files readable by the hledger-web server. See also:+ GHSA-vq7r-8w52-jv84. [#2704]++ Note: as with #2698 in 1.52.2, these fixes were backported from+ AI-assisted fixes in hledger 2, under the security exception in+ https://hledger.org/AI.html; they have been reviewed and tested.++- hledger-web's official binaries, and builds from the hledger source+ tree, now use aeson 2.3, avoiding a denial of service bug in that+ library. (With older aeson and /add enabled, hledger-web is+ vulnerable to HTTP requests which can trigger memory/CPU exhaustion.)+ aeson 2.3 is not yet in stackage, so hledger-web installed from+ hackage will normally still use the older aeson.+ (<https://haskell.github.io/security-advisories/advisory/HSEC-2026-0007.html>)++[#2700]: https://github.com/simonmichael/hledger/issues/2700+[#2704]: https://github.com/simonmichael/hledger/issues/2704+ # 1.52.2 2026-08-24 Fixes
Hledger/Web/Handler/AddR.hs view
@@ -11,6 +11,7 @@ ) where import Data.Aeson.Types (Result(..))+import Data.List (intersperse) import Data.Text qualified as T import Network.HTTP.Types.Status (status400) import Text.Blaze.Html (preEscapedToHtml)@@ -22,6 +23,27 @@ import Hledger.Web.WebOptions (WebOpts(..)) import Hledger.Web.Widget.AddForm (addForm) +-- | Replace newlines with spaces in the transaction fields which are written+-- to the journal file verbatim: the description, the code, and the posting+-- account names. A newline in one of these would split the rendered entry+-- across lines, letting arbitrary journal directives (eg an include) be+-- written into the file. The journal format can't represent a newline in+-- these fields anyway - their parsers stop at end of line - and the CSV+-- reader collapses them likewise, so nothing that could round trip is lost.+-- Comments are left alone: they are rendered as one ";" line per line, so+-- newlines in them are safe.+transactionCollapseNewlines :: Transaction -> Transaction+transactionCollapseNewlines t = t+ { tdescription = collapse $ tdescription t+ , tcode = collapse $ tcode t+ , tpostings = map collapseacct $ tpostings t+ }+ where+ collapse = T.map (\c -> if c == '\n' || c == '\r' then ' ' else c)+ -- Account names are also normalised to single spaces, since two spaces+ -- would end the account name when the entry is read back.+ collapseacct p = p{paccount = T.unwords . T.words $ paccount p}+ getAddR :: Handler () getAddR = do checkServerSideUiEnabled@@ -36,7 +58,7 @@ ((res, view), enctype) <- runFormPost $ addForm j today case res of FormSuccess (t,f) -> do- let t' = txnTieKnot t+ let t' = txnTieKnot $ transactionCollapseNewlines t liftIO $ do ensureJournalFileExists f appendToJournalFileOrStdout f (showTransaction t')@@ -44,7 +66,11 @@ redirect JournalR FormMissing -> showForm view enctype FormFailure errs -> do- mapM_ (setMessage . preEscapedToHtml . T.replace "\n" "<br>") errs+ -- Escape each error, then join the lines with <br>. An unbalanced+ -- transaction's error embeds an excerpt of the submitted entry (account+ -- names, amounts), so it must be escaped; only the <br> we insert is+ -- raw. (Cf EditR, which uses toHtml.)+ mapM_ (setMessage . mconcat . intersperse (preEscapedToHtml ("<br>" :: T.Text)) . map toHtml . T.lines) errs showForm view enctype where showForm view enctype =@@ -66,5 +92,5 @@ case r of Error err -> sendStatusJSON status400 ("could not parse json: " ++ err ::String) Success t -> do- void $ liftIO $ journalAddTransaction j (cliopts_ opts) t+ void $ liftIO $ journalAddTransaction j (cliopts_ opts) $ transactionCollapseNewlines t sendResponseCreated TransactionsR
Hledger/Web/Test.hs view
@@ -145,6 +145,40 @@ bodyContains "id=\"transaction-2-1\"" bodyContains "id=\"transaction-2-2\"" + -- Submitting an unbalanced transaction produces an error message that+ -- echoes the entry (account names, amounts). Those values must be rendered+ -- as text, not raw html. Note this echo happens on the FormFailure path,+ -- which yesod does not gate with the CSRF token, so no token is sent here -+ -- the vector is reachable cross-origin.+ aj <- fmap (either error' id) . runExceptT . journalFinalise iopts "add.journal" "" =<<+ readJournal'' (T.pack $ unlines -- PARTIAL: readJournal'' should not fail+ ["2025-01-01 opening"+ ," assets:bank:checking 100"+ ," equity:opening"])+ runTests "hledger-web add form" [("allow","add")] aj $ do++ yit "escapes submitted values in an add-form error message" $ do+ get JournalR+ statusIs 200+ -- Payloads in the two fields that reach the excerpt: the account name,+ -- and the (unvalidated) description. Distinct payloads so that escaping+ -- one field but not the other is caught. The entry parses but does not+ -- balance, so its excerpt - which includes both fields - is echoed.+ -- (Date and amount are validated and cannot carry raw html into it.)+ request $ do+ setMethod "POST"+ setUrl AddR+ addPostParam "_formid" "identify-add"+ addPostParam "date" "2025-02-02"+ addPostParam "description" "d<img src=x onerror=alert(1)>"+ addPostParam "account" "a<img src=x onerror=alert(2)>"+ addPostParam "amount" "5"+ addPostParam "account" "equity:opening"+ addPostParam "amount" "-3"+ bodyContains "d<img src=x onerror=alert(1)>" -- description, escaped+ bodyContains "a<img src=x onerror=alert(2)>" -- account, escaped+ bodyNotContains "<img src=x onerror" -- neither as raw html+ -- #2127 -- XXX I'm pretty sure this test lies, ie does not match production behaviour. -- (test with curl -s http://localhost:5000/journal | rg '(href)="[\w/].*?"' -o )
hledger-web.1 view
@@ -1,5 +1,5 @@ -.TH "HLEDGER\-WEB" "1" "March 2026" "hledger-web-1.52.1 " "hledger User Manuals"+.TH "HLEDGER\-WEB" "1" "August 2026" "hledger-web-1.52.3 " "hledger User Manuals" @@ -17,7 +17,7 @@ .PD \f[CR]hledger web [OPTS] [QUERY]\f[R] .SH DESCRIPTION-This manual is for hledger\(aqs web interface, version 1.52.1.+This manual is for hledger\(aqs web interface, version 1.52.3. See also the hledger manual for common concepts and file formats. .PP hledger is a robust, user\-friendly, cross\-platform set of programs for
hledger-web.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: hledger-web-version: 1.52.2+version: 1.52.3 synopsis: Web user interface for the hledger accounting system description: A simple web user interface for the hledger accounting system, providing a more modern UI than the command-line or terminal interfaces.@@ -28,7 +28,7 @@ license-file: LICENSE build-type: Simple tested-with:- ghc==9.6.7, ghc==9.8.4, ghc==9.10.2, ghc==9.12.2+ ghc==9.6.7, ghc==9.8.4, ghc==9.10.2, ghc==9.12.4 extra-source-files: CHANGES.md README.md@@ -154,10 +154,10 @@ hs-source-dirs: ./ ghc-options: -Wall -Wno-incomplete-uni-patterns -Wno-missing-signatures -Wno-orphans -Wno-type-defaults -Wno-unused-do-bind -threaded- cpp-options: -DVERSION="1.52.2"+ cpp-options: -DVERSION="1.52.3" build-depends: Decimal >=0.5.1- , aeson >=1 && <2.3+ , aeson >=1 && <2.4 , base >=4.18 && <4.23 , base64 , blaze-html@@ -176,8 +176,8 @@ , filepath , githash >=0.1.6.2 , hjsmin- , hledger >=1.52.2 && <1.53- , hledger-lib >=1.52.2 && <1.53+ , hledger >=1.52.3 && <1.53+ , hledger-lib >=1.52.3 && <1.53 , hspec , http-client , http-conduit@@ -222,7 +222,7 @@ hs-source-dirs: app ghc-options: -Wall -Wno-incomplete-uni-patterns -Wno-missing-signatures -Wno-orphans -Wno-type-defaults -Wno-unused-do-bind -threaded- cpp-options: -DVERSION="1.52.2"+ cpp-options: -DVERSION="1.52.3" build-depends: base >=4.18 && <4.23 , hledger-web@@ -242,7 +242,7 @@ hs-source-dirs: test ghc-options: -Wall -Wno-incomplete-uni-patterns -Wno-missing-signatures -Wno-orphans -Wno-type-defaults -Wno-unused-do-bind -threaded- cpp-options: -DVERSION="1.52.2"+ cpp-options: -DVERSION="1.52.3" build-depends: base >=4.18 && <4.23 , hledger-web
hledger-web.info view
@@ -18,7 +18,7 @@ or 'hledger web [OPTS] [QUERY]' - This manual is for hledger's web interface, version 1.52.1. See also+ This manual is for hledger's web interface, version 1.52.3. See also the hledger manual for common concepts and file formats. hledger is a robust, user-friendly, cross-platform set of programs
hledger-web.txt view
@@ -11,7 +11,7 @@ hledger web [OPTS] [QUERY] DESCRIPTION- This manual is for hledger's web interface, version 1.52.1. See also the+ This manual is for hledger's web interface, version 1.52.3. See also the hledger manual for common concepts and file formats. hledger is a robust, user-friendly, cross-platform set of programs for@@ -470,4 +470,4 @@ SEE ALSO hledger(1), hledger-ui(1), hledger-web(1), ledger(1) -hledger-web-1.52.1 March 2026 HLEDGER-WEB(1)+hledger-web-1.52.3 August 2026 HLEDGER-WEB(1)