atomic file updates, escape attributes

This commit is contained in:
Evgeny @ SimpleX Chat
2026-06-03 20:22:06 +00:00
parent c9209bc3cc
commit 56df8cec98
2 changed files with 23 additions and 10 deletions
+9 -5
View File
@@ -26,7 +26,7 @@ where
import Control.Concurrent.STM (check)
import Control.Exception (SomeException, catch)
import Control.Logger.Simple
import Control.Monad (forM_, unless, void, when)
import Control.Monad (forM_, void, when)
import Control.Monad.Except (runExceptT)
import Data.Either (rights)
import Data.Int (Int64)
@@ -77,7 +77,7 @@ import Simplex.Chat.Types
import Simplex.Messaging.Agent.Store.Common (withTransaction)
import Simplex.Messaging.Encoding.String (strEncode)
import Simplex.Messaging.Parsers (defaultJSON)
import System.Directory (createDirectoryIfMissing, listDirectory, removeFile)
import System.Directory (createDirectoryIfMissing, listDirectory, removeFile, renameFile)
import System.FilePath (takeExtension, (</>))
import UnliftIO.STM
@@ -261,7 +261,10 @@ renderGroupPreview WebPreviewConfig {webJsonDir, webPreviewItemCount} cc user gI
messages = msgs,
updatedAt = ts
}
LB.writeFile (webJsonDir </> fName) (J.encode preview)
let destPath = webJsonDir </> fName
tmpPath = destPath <> ".tmp"
LB.writeFile tmpPath (J.encode preview)
renameFile tmpPath destPath
pure $ corsEntry publicGroupId <$> publicGroupAccess
Nothing -> pure Nothing
where
@@ -395,8 +398,9 @@ writeCorsConfig entries path =
removeStaleFiles :: FilePath -> S.Set FilePath -> IO ()
removeStaleFiles dir activeFiles = do
let jsonFiles = S.filter (\f -> takeExtension f == ".json") . S.fromList
allFiles <- jsonFiles <$> listDirectory dir
let isPreviewFile f = takeExtension f == ".json" && all isBase64Url (takeWhile (/= '.') f)
isBase64Url c = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_'
allFiles <- S.filter isPreviewFile . S.fromList <$> listDirectory dir
mapM_ (\f -> removeFile (dir </> f)) $ S.difference allFiles activeFiles
toFormattedText :: Text -> Maybe MarkdownList
+14 -5
View File
@@ -14,6 +14,15 @@ function escapeHtml(text) {
.replace(/\n/g, "<br>");
}
function escapeAttr(text) {
return text
.replace(/&/g, "&amp;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");
}
function getSimplexLinkDescr(linkType) {
switch (linkType) {
case 'contact': return 'SimpleX contact address';
@@ -71,17 +80,17 @@ function renderMarkdown(fts) {
break;
case 'uri':
let href = text.startsWith('http://') || text.startsWith('https://') || text.startsWith('simplex:/') ? text : 'https://' + text;
html += `<a href="${href}"${targetBlank(href)}>${escapeHtml(text)}</a>`;
html += `<a href="${escapeAttr(href)}"${targetBlank(href)}>${escapeHtml(text)}</a>`;
break;
case 'hyperLink': {
const { showText, linkUri } = format;
html += `<a href="${linkUri}"${targetBlank(linkUri)}>${escapeHtml(showText ?? linkUri)}</a>`;
html += `<a href="${escapeAttr(linkUri)}"${targetBlank(linkUri)}>${escapeHtml(showText ?? linkUri)}</a>`;
break;
}
case 'simplexLink': {
const { showText, linkType, simplexUri, smpHosts } = format;
const linkText = showText ? escapeHtml(showText) : getSimplexLinkDescr(linkType);
html += `<a href="${platformSimplexUri(simplexUri)}" target="_blank">${linkText} <em>(${viaHost(smpHosts)})</em></a>`;
html += `<a href="${escapeAttr(platformSimplexUri(simplexUri))}" target="_blank">${linkText} <em>(${escapeHtml(viaHost(smpHosts))})</em></a>`;
break;
}
case 'command':
@@ -91,10 +100,10 @@ function renderMarkdown(fts) {
html += `<strong>${escapeHtml(text)}</strong>`;
break;
case 'email':
html += `<a href="mailto:${text}">${escapeHtml(text)}</a>`;
html += `<a href="mailto:${escapeAttr(text)}">${escapeHtml(text)}</a>`;
break;
case 'phone':
html += `<a href="tel:${text}">${escapeHtml(text)}</a>`;
html += `<a href="tel:${escapeAttr(text)}">${escapeHtml(text)}</a>`;
break;
case 'unknown':
html += escapeHtml(text);