mirror of
https://github.com/multipleof4/ntfy.git
synced 2026-10-15 16:07:55 +00:00
Low: Fix self-XSS in SVGs
This commit is contained in:
@@ -1898,6 +1898,7 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
|
|||||||
**Bug fixes + maintenance:**
|
**Bug fixes + maintenance:**
|
||||||
|
|
||||||
* Remove `stacktrace-js`, `stacktrace-gps`, `humanize-duration`, and `js-base64` from the web app to reduce dependency and security footprint
|
* 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)
|
## ntfy Android v1.25.x (UNRELEASED)
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export const formatMessage = (m) => {
|
|||||||
return m.message || "";
|
return m.message || "";
|
||||||
};
|
};
|
||||||
|
|
||||||
const imageRegex = /\.(png|jpe?g|gif|webp)$/i;
|
export const imageRegex = /\.(png|jpe?g|gif|webp)$/i;
|
||||||
export const isImage = (attachment) => {
|
export const isImage = (attachment) => {
|
||||||
if (!attachment) return false;
|
if (!attachment) return false;
|
||||||
|
|
||||||
|
|||||||
@@ -145,8 +145,7 @@ export const hashCode = (s) => {
|
|||||||
* which is expected by `<html lang>` and `Intl.DateTimeFormat`. Falls back to "en"
|
* which is expected by `<html lang>` and `Intl.DateTimeFormat`. Falls back to "en"
|
||||||
* if the input is missing or not a string.
|
* if the input is missing or not a string.
|
||||||
*/
|
*/
|
||||||
export const getKebabCaseLangStr = (language) =>
|
export const getKebabCaseLangStr = (language) => (typeof language === "string" && language.length > 0 ? language.replace(/_/g, "-") : "en");
|
||||||
typeof language === "string" && language.length > 0 ? language.replace(/_/g, "-") : "en";
|
|
||||||
|
|
||||||
export const formatShortDateTime = (timestamp, language) =>
|
export const formatShortDateTime = (timestamp, language) =>
|
||||||
new Intl.DateTimeFormat(getKebabCaseLangStr(language), {
|
new Intl.DateTimeFormat(getKebabCaseLangStr(language), {
|
||||||
|
|||||||
@@ -31,18 +31,24 @@ const AttachmentIcon = (props) => {
|
|||||||
imageFile = fileDocument;
|
imageFile = fileDocument;
|
||||||
imageLabel = t("notifications_attachment_file_document");
|
imageLabel = t("notifications_attachment_file_document");
|
||||||
}
|
}
|
||||||
|
const icon = (
|
||||||
|
<Box
|
||||||
|
component="img"
|
||||||
|
src={imageFile}
|
||||||
|
alt={imageLabel}
|
||||||
|
loading="lazy"
|
||||||
|
sx={{
|
||||||
|
width: "28px",
|
||||||
|
height: "28px",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
if (!props.href) {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Link href={props.href} target="_blank">
|
<Link href={props.href} target="_blank" rel="noopener noreferrer">
|
||||||
<Box
|
{icon}
|
||||||
component="img"
|
|
||||||
src={imageFile}
|
|
||||||
alt={imageLabel}
|
|
||||||
loading="lazy"
|
|
||||||
sx={{
|
|
||||||
width: "28px",
|
|
||||||
height: "28px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import priority3 from "../img/priority-3.svg";
|
|||||||
import priority4 from "../img/priority-4.svg";
|
import priority4 from "../img/priority-4.svg";
|
||||||
import priority5 from "../img/priority-5.svg";
|
import priority5 from "../img/priority-5.svg";
|
||||||
import { formatBytes, maybeWithAuth, topicShortUrl, topicUrl, validTopic, validUrl } from "../app/utils";
|
import { formatBytes, maybeWithAuth, topicShortUrl, topicUrl, validTopic, validUrl } from "../app/utils";
|
||||||
|
import { imageRegex } from "../app/notificationUtils";
|
||||||
import AttachmentIcon from "./AttachmentIcon";
|
import AttachmentIcon from "./AttachmentIcon";
|
||||||
import DialogFooter from "./DialogFooter";
|
import DialogFooter from "./DialogFooter";
|
||||||
import api from "../app/Api";
|
import api from "../app/Api";
|
||||||
@@ -805,7 +806,7 @@ const AttachmentBox = (props) => {
|
|||||||
borderRadius: "4px",
|
borderRadius: "4px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AttachmentIcon type={file.type} href={URL.createObjectURL(file)} />
|
<AttachmentIcon type={file.type} href={imageRegex.test(file.name) ? URL.createObjectURL(file) : undefined} />
|
||||||
<Box sx={{ marginLeft: 1, textAlign: "left" }}>
|
<Box sx={{ marginLeft: 1, textAlign: "left" }}>
|
||||||
<ExpandingTextField
|
<ExpandingTextField
|
||||||
minWidth={140}
|
minWidth={140}
|
||||||
|
|||||||
Reference in New Issue
Block a user