diff --git a/docs/releases.md b/docs/releases.md
index 036b2120..1d4d8590 100644
--- a/docs/releases.md
+++ b/docs/releases.md
@@ -1898,6 +1898,7 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
**Bug fixes + maintenance:**
* Remove `stacktrace-js`, `stacktrace-gps`, `humanize-duration`, and `js-base64` from the web app to reduce dependency and security footprint
+* Restrict the publish dialog's local file preview to safe image types (png/jpg/gif/webp) to prevent same-origin script execution from blob URLs when previewing a crafted SVG ([GHSA-j8hr-p342-xrmh](https://github.com/binwiederhier/ntfy/security/advisories/GHSA-j8hr-p342-xrmh), thanks to [@Venukamatchi](https://github.com/Venukamatchi) for reporting)
## ntfy Android v1.25.x (UNRELEASED)
diff --git a/web/src/app/notificationUtils.js b/web/src/app/notificationUtils.js
index 070f8231..2453075a 100644
--- a/web/src/app/notificationUtils.js
+++ b/web/src/app/notificationUtils.js
@@ -35,7 +35,7 @@ export const formatMessage = (m) => {
return m.message || "";
};
-const imageRegex = /\.(png|jpe?g|gif|webp)$/i;
+export const imageRegex = /\.(png|jpe?g|gif|webp)$/i;
export const isImage = (attachment) => {
if (!attachment) return false;
diff --git a/web/src/app/utils.js b/web/src/app/utils.js
index a6fa07b0..2a62aa38 100644
--- a/web/src/app/utils.js
+++ b/web/src/app/utils.js
@@ -145,8 +145,7 @@ export const hashCode = (s) => {
* which is expected by `` and `Intl.DateTimeFormat`. Falls back to "en"
* if the input is missing or not a string.
*/
-export const getKebabCaseLangStr = (language) =>
- typeof language === "string" && language.length > 0 ? language.replace(/_/g, "-") : "en";
+export const getKebabCaseLangStr = (language) => (typeof language === "string" && language.length > 0 ? language.replace(/_/g, "-") : "en");
export const formatShortDateTime = (timestamp, language) =>
new Intl.DateTimeFormat(getKebabCaseLangStr(language), {
diff --git a/web/src/components/AttachmentIcon.jsx b/web/src/components/AttachmentIcon.jsx
index b7bf6b50..0cdcb0b0 100644
--- a/web/src/components/AttachmentIcon.jsx
+++ b/web/src/components/AttachmentIcon.jsx
@@ -31,18 +31,24 @@ const AttachmentIcon = (props) => {
imageFile = fileDocument;
imageLabel = t("notifications_attachment_file_document");
}
+ const icon = (
+
+ );
+ if (!props.href) {
+ return icon;
+ }
return (
-
-
+
+ {icon}
);
};
diff --git a/web/src/components/PublishDialog.jsx b/web/src/components/PublishDialog.jsx
index 912810b3..c263861a 100644
--- a/web/src/components/PublishDialog.jsx
+++ b/web/src/components/PublishDialog.jsx
@@ -30,6 +30,7 @@ import priority3 from "../img/priority-3.svg";
import priority4 from "../img/priority-4.svg";
import priority5 from "../img/priority-5.svg";
import { formatBytes, maybeWithAuth, topicShortUrl, topicUrl, validTopic, validUrl } from "../app/utils";
+import { imageRegex } from "../app/notificationUtils";
import AttachmentIcon from "./AttachmentIcon";
import DialogFooter from "./DialogFooter";
import api from "../app/Api";
@@ -805,7 +806,7 @@ const AttachmentBox = (props) => {
borderRadius: "4px",
}}
>
-
+