aboutsummaryrefslogtreecommitdiffstats
path: root/shared/Notify.qml
diff options
context:
space:
mode:
Diffstat (limited to 'shared/Notify.qml')
-rw-r--r--shared/Notify.qml20
1 files changed, 13 insertions, 7 deletions
diff --git a/shared/Notify.qml b/shared/Notify.qml
index 237d8c9..1abe57d 100644
--- a/shared/Notify.qml
+++ b/shared/Notify.qml
@@ -70,9 +70,11 @@ Singleton {
// remote <img src> (http(s), protocol-relative //host, ftp, data, or an
// entity-encoded scheme) would make the shell fetch or embed something the
// sender chose, which leaks that the notification was shown. Deny by
- // default: a tag survives only if its src decodes to a file: URL or a
- // leading-slash absolute path. Entities are decoded before the test, so an
- // encoded scheme cannot slip past.
+ // default: a tag survives only if every src it carries decodes to a file:
+ // URL or a leading-slash absolute path. Every src assignment is checked,
+ // not just the first, so a decoy attribute cannot shadow a remote one;
+ // entities are decoded before the test, so an encoded scheme cannot slip
+ // past.
function sanitize(body) {
function decode(s) {
return s.replace(/&(?:#x([0-9a-f]+)|#(\d+)|(amp|colon|sol|tab|quot));/gi,
@@ -83,10 +85,14 @@ Singleton {
});
}
return (body || "").replace(/<img\b[^>]*>/gi, function (tag) {
- const m = tag.match(/\bsrc\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i);
- if (!m) return "";
- const src = decode(m[1] !== undefined ? m[1] : m[2] !== undefined ? m[2] : m[3]);
- return /^file:/i.test(src) || (src.charAt(0) === "/" && src.charAt(1) !== "/") ? tag : "";
+ const re = /(?:^|\s)src\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gi;
+ let m, found = false, local = true;
+ while ((m = re.exec(tag))) {
+ found = true;
+ const src = decode(m[1] !== undefined ? m[1] : m[2] !== undefined ? m[2] : m[3]);
+ if (!(/^file:/i.test(src) || (src.charAt(0) === "/" && src.charAt(1) !== "/"))) local = false;
+ }
+ return (found && local) ? tag : "";
});
}