diff --git a/CHANGES.md b/CHANGES.md
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -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
diff --git a/Hledger/Web/Handler/AddR.hs b/Hledger/Web/Handler/AddR.hs
--- a/Hledger/Web/Handler/AddR.hs
+++ b/Hledger/Web/Handler/AddR.hs
@@ -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
diff --git a/Hledger/Web/Test.hs b/Hledger/Web/Test.hs
--- a/Hledger/Web/Test.hs
+++ b/Hledger/Web/Test.hs
@@ -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&lt;img src=x onerror=alert(1)&gt;"   -- description, escaped
+      bodyContains "a&lt;img src=x onerror=alert(2)&gt;"   -- 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 )
diff --git a/hledger-web.1 b/hledger-web.1
--- a/hledger-web.1
+++ b/hledger-web.1
@@ -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
diff --git a/hledger-web.cabal b/hledger-web.cabal
--- a/hledger-web.cabal
+++ b/hledger-web.cabal
@@ -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
diff --git a/hledger-web.info b/hledger-web.info
--- a/hledger-web.info
+++ b/hledger-web.info
@@ -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
diff --git a/hledger-web.txt b/hledger-web.txt
--- a/hledger-web.txt
+++ b/hledger-web.txt
@@ -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)
