Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion extension/js/common/platform/xss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export class Xss {
'col',
];
private static ADD_ATTR = ['email', 'page', 'addurltext', 'longid', 'index', 'target', 'fingerprint', 'cryptup-data'];
private static FORBID_ATTR = ['background'];
private static FORBID_ATTR = ['background', 'srcset'];
Comment thread
martgil marked this conversation as resolved.
private static HREF_REGEX_CACHE: RegExp | undefined;
private static EMOJI_REGEX = /(?![*#0-9]+)[\p{Emoji}\p{Emoji_Modifier}\p{Emoji_Component}\p{Emoji_Modifier_Base}\p{Emoji_Presentation}]/gu;
/* eslint-disable @typescript-eslint/naming-convention */
Expand Down Expand Up @@ -201,6 +201,11 @@ export class Xss {
} else if (!src) {
img.remove(); // src that exists but is null is suspicious
} else if (imgHandling === 'IMG-KEEP' && checkValidURL(src)) {
const pathname = new URL(src).pathname.toLowerCase();
if (pathname.includes('/logout') || pathname.includes('/signout')) {
img.remove();
return;
}
// replace remote image with remote_image_container
const remoteImgEl = `
<div class="remote_image_container" data-src="${Xss.escape(src)}" data-test="remote-image-container">
Expand Down
39 changes: 39 additions & 0 deletions test/source/tests/browser-unit-tests/unit-Xss.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,42 @@ BROWSER_UNIT_TEST_NAME(`Xss.htmlSanitizeKeepBasicTags strips url() in CSS`);
}
return 'pass';
})();

BROWSER_UNIT_TEST_NAME(`Xss.htmlSanitizeKeepBasicTags strips srcset attribute from img`);
(async () => {
const dirty = `<img src="cid:test-image" srcset="https://accounts.google.com/Logout?me.png 1x, https://attacker.example/track.png 2x" width="200" height="100">`;
const clean = Xss.htmlSanitizeKeepBasicTags(dirty, 'IMG-KEEP');
if (/srcset/i.test(clean)) {
throw Error(`srcset was not stripped from sanitized HTML: ${clean}`);
}
if (!/src="cid:test-image"/.test(clean)) {
throw Error(`expected cid src to be preserved, got: ${clean}`);
}
return 'pass';
})();

BROWSER_UNIT_TEST_NAME(`Xss.htmlSanitize (used for CID replacement) strips srcset attribute from img`);
(async () => {
const dirty = `<img src="cid:test-image" srcset="https://accounts.google.com/Logout?me.png 1x" alt="inline">`;
const clean = Xss.htmlSanitize(dirty);
if (/srcset/i.test(clean)) {
throw Error(`srcset was not stripped from sanitized HTML: ${clean}`);
}
if (!/src="cid:test-image"/.test(clean)) {
throw Error(`expected cid src to be preserved, got: ${clean}`);
}
return 'pass';
})();

BROWSER_UNIT_TEST_NAME(`Xss.htmlSanitizeKeepBasicTags keeps srcset-less inline images and gates remote src`);
(async () => {
const dirty = `<img src="https://attacker.example/track.png" srcset="https://attacker.example/track2.png 1x">`;
const clean = Xss.htmlSanitizeKeepBasicTags(dirty, 'IMG-KEEP');
if (/srcset/i.test(clean)) {
throw Error(`srcset was not stripped from sanitized HTML: ${clean}`);
}
if (!/remote_image_container/.test(clean)) {
throw Error(`expected remote src to be replaced with remote_image_container, got: ${clean}`);
}
return 'pass';
})();
Loading