From e5f664815fd624885fff7e8cfa28bfac0c5529fa Mon Sep 17 00:00:00 2001 From: shum Date: Thu, 2 Apr 2026 12:43:27 +0000 Subject: [PATCH] fix: escape double quotes in COPY CSV status field The status field (e.g. "blocked,reason=spam,notice={...}") is quoted in CSV for COPY protocol, but embedded double quotes from BlockingInfo notice (JSON) were not escaped. This could break CSV parsing during import. Now double quotes are escaped as "" per CSV spec. --- src/Simplex/FileTransfer/Server/Store/Postgres.hs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Simplex/FileTransfer/Server/Store/Postgres.hs b/src/Simplex/FileTransfer/Server/Store/Postgres.hs index 9677b646b..1be3c228b 100644 --- a/src/Simplex/FileTransfer/Server/Store/Postgres.hs +++ b/src/Simplex/FileTransfer/Server/Store/Postgres.hs @@ -26,6 +26,7 @@ import Control.Monad.Except import Control.Monad.IO.Class import Control.Monad.Trans.Except (throwE) import Data.ByteString (ByteString) +import qualified Data.ByteString.Char8 as B import Data.ByteString.Builder (Builder) import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy as LB @@ -331,7 +332,7 @@ fileRecToCSV sId FileRec {fileInfo = FileInfo {sndKey, size, digest}, filePath, renderField (toField (Binary (C.encodePubKey sndKey))), nullable (toField <$> path), renderField (toField createdAt), - BB.char7 '"' <> renderField (toField status) <> BB.char7 '"' + quotedField (toField status) ] recipientToCSV :: RecipientId -> SenderId -> RcvPublicAuthKey -> ByteString @@ -354,3 +355,10 @@ renderField = \case nullable :: Maybe Action -> Builder nullable = maybe mempty renderField + +quotedField :: Action -> Builder +quotedField a = BB.char7 '"' <> escapeQuotes (renderField a) <> BB.char7 '"' + where + escapeQuotes bld = + let bs = LB.toStrict $ BB.toLazyByteString bld + in BB.byteString $ B.concatMap (\c -> if c == '"' then "\"\"" else B.singleton c) bs