Skip to content

Commit 3075123

Browse files
fix: blocked bg-images do not trigger external content warning
refs #284
1 parent eb2a2a3 commit 3075123

File tree

2 files changed

+119
-13
lines changed

2 files changed

+119
-13
lines changed

src/view/mail/message/reader/MessageViewController.js

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* conjoon
33
* extjs-app-webmail
4-
* Copyright (C) 2019-2022 Thorsten Suckow-Homberg https://github.com/conjoon/extjs-app-webmail
4+
* Copyright (C) 2019-2023 Thorsten Suckow-Homberg https://github.com/conjoon/extjs-app-webmail
55
*
66
* Permission is hereby granted, free of charge, to any person
77
* obtaining a copy of this software and associated documentation
@@ -98,7 +98,8 @@ Ext.define("conjoon.cn_mail.view.mail.message.reader.MessageViewController", {
9898
* iframe is currently processing.
9999
*
100100
* @param {conjoon.cn_mail.view.mail.message.reader.MessageViewIframe} iframe
101-
* @param {String} src
101+
* @param {Object} src
102+
* @param {String} src.srcDoc
102103
*/
103104
onBeforeSrcDoc: function (iframe, src) {
104105
const me = this;
@@ -114,28 +115,50 @@ Ext.define("conjoon.cn_mail.view.mail.message.reader.MessageViewController", {
114115
* sanitizeImages will only be called if the iframe's getImagesAllowed()-method returns
115116
* false.
116117
* Will set the viewodel's hasImages if any images where detected in the message's html.
118+
* If the method requires check for css images, the srcDoc of the iframe will be updated.
117119
*
118120
* @param {conjoon.cn_mail.view.mail.message.reader.MessageViewIframe} iframe
119121
*
120122
* @see #sanitizeLinks
121123
* @see #sanitizeImages
122124
*/
123-
onIframeLoad: function (iframe) {
125+
onIframeLoad (iframe) {
124126

125-
const me = this,
127+
const me = this;
128+
129+
me.cssImageCheck = me.cssImageCheck || {};
130+
131+
// will only step into this method if the check was not finished,
132+
// to prevent recursion because of setSrcDoc and the load-event
133+
// associated with it, this method will return and be recalled
134+
// once setSrcDoc was updated.
135+
if (!me.cssImageCheck.finished && !iframe.getImagesAllowed()) {
136+
let result = me.sanitizeCssImages();
137+
me.cssImageCheck.finished = true;
138+
me.cssImageCheck.result = result;
139+
if (result) {
140+
return me.cssImageCheck;
141+
}
142+
}
143+
144+
const
126145
view = me.getView(),
127146
vm = view.getViewModel(),
128147
body = iframe.getBody(),
129148
cnt = view.down("#msgBodyContainer"),
130-
bars = Ext.getScrollbarSize();
149+
bars = Ext.getScrollbarSize(),
150+
viewerWindow = iframe.cn_iframeEl.dom.contentWindow;
131151

132-
me.sanitizeLinks(iframe.cn_iframeEl.dom.contentWindow.document.getElementsByTagName("a"));
152+
const imgs = viewerWindow.document.getElementsByTagName("img");
133153

134-
let imgs = iframe.cn_iframeEl.dom.contentWindow.document.getElementsByTagName("img");
135-
vm.set("hasImages", imgs.length > 0);
154+
vm.set("hasImages", me.cssImageCheck.result === true || imgs.length > 0);
155+
156+
me.cssImageCheck = {};
157+
158+
me.sanitizeLinks(viewerWindow.document.getElementsByTagName("a"));
136159

137160
if (!iframe.getImagesAllowed()) {
138-
me.sanitizeImages(imgs);
161+
imgs.length && me.sanitizeImages(imgs);
139162
}
140163

141164
cnt.setScrollY(0);
@@ -146,6 +169,8 @@ Ext.define("conjoon.cn_mail.view.mail.message.reader.MessageViewController", {
146169

147170
iframe.setSize(cnt.getWidth() - bars.width, cnt.getHeight() - bars.height);
148171
iframe.setSize(body.scrollWidth, body.scrollHeight);
172+
173+
return true;
149174
},
150175

151176

@@ -215,12 +240,14 @@ Ext.define("conjoon.cn_mail.view.mail.message.reader.MessageViewController", {
215240
* - the images border ist set to 1px solid black
216241
* - the images src is set to img_block.png out of this package's resource path
217242
*
218-
* @param {Array} an array of HTMLElements representing links (<a>).
243+
* @param {Array} elements array of HTMLElements representing links (<a>).
219244
*
220245
* @private
221246
*/
222247
sanitizeImages: function (elements) {
223248

249+
const me = this;
250+
224251
let img, i, len = elements.length;
225252

226253
for (i = 0; i < len; i++) {
@@ -230,13 +257,41 @@ Ext.define("conjoon.cn_mail.view.mail.message.reader.MessageViewController", {
230257
// isolated tests will not have resources property set
231258
img.setAttribute(
232259
"src",
233-
coon.core.Environment.getPathForResource("resources/images/img_block.png","extjs-app-webmail")
260+
me.getProxyImage()
234261
);
235262

236263
}
237264
},
238265

239266

267+
getCssImages (viewerWindow) {
268+
return viewerWindow.performance.getEntriesByType("resource").filter(
269+
resource => ["css"].includes(resource.initiatorType)
270+
).map(resource => resource.name);
271+
},
272+
273+
274+
sanitizeCssImages () {
275+
276+
const
277+
me = this,
278+
iframe = me.getIframe(),
279+
cssImgs = me.getCssImages(iframe.cn_iframeEl.dom.contentWindow);
280+
281+
if (cssImgs.length) {
282+
let srcDoc = iframe.getSrcDoc();
283+
srcDoc = l8.replace(cssImgs, me.getProxyImage(), srcDoc, "gmi");
284+
iframe.setSrcDoc(srcDoc, false);
285+
return true;
286+
}
287+
288+
return false;
289+
},
290+
291+
getProxyImage () {
292+
return coon.core.Environment.getPathForResource("resources/images/img_block.png", "extjs-app-webmail");
293+
},
294+
240295
/**
241296
* @private
242297
*/

tests/src/view/mail/message/reader/MessageViewControllerTest.js

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* conjoon
33
* extjs-app-webmail
4-
* Copyright (C) 2017-2022 Thorsten Suckow-Homberg https://github.com/conjoon/extjs-app-webmail
4+
* Copyright (C) 2017-2023 Thorsten Suckow-Homberg https://github.com/conjoon/extjs-app-webmail
55
*
66
* Permission is hereby granted, free of charge, to any person
77
* obtaining a copy of this software and associated documentation
@@ -298,12 +298,14 @@ StartTest(async t => {
298298
};
299299
};
300300

301-
t.isCalledNTimes("sanitizeLinks", controller, 3);
301+
t.isCalledNTimes("sanitizeLinks", controller, 4);
302302
t.isCalledNTimes("sanitizeImages", controller, 1);
303303

304304
IMAGESALLOWED = false;
305305
TAGS["img"] = [{setAttribute: function (){}, style: {}}];
306306

307+
let sanitizeCssImagesSpy = t.spyOn(controller, "sanitizeCssImages").and.callFake(() => false);
308+
307309
controller.onIframeLoad(IFRAME);
308310

309311
t.expect(VM["hasImages"]).toBe(true);
@@ -325,7 +327,21 @@ StartTest(async t => {
325327
controller.onIframeLoad(IFRAME);
326328
t.expect(VM["hasImages"]).toBe(true);
327329

330+
sanitizeCssImagesSpy.remove();
331+
sanitizeCssImagesSpy = t.spyOn(controller, "sanitizeCssImages").and.callFake(() => true);
332+
333+
IMAGESALLOWED = false;
334+
TAGS["img"] = [];
335+
t.expect(controller.cssImageCheck.finished).toBeUndefined();
336+
337+
t.expect(controller.onIframeLoad(IFRAME)).toBe(controller.cssImageCheck);
338+
t.expect(controller.cssImageCheck.finished).toBe(true);
339+
t.expect(controller.cssImageCheck.result).toBe(true);
340+
341+
t.expect(controller.onIframeLoad(IFRAME)).toBe(true);
342+
t.expect(controller.cssImageCheck).toEqual({});
328343

344+
sanitizeCssImagesSpy.remove();
329345
Ext.getScrollbarSize = tmp;
330346
});
331347

@@ -381,4 +397,39 @@ StartTest(async t => {
381397
t.expect(IMAGES[1].style.border).toBe("1px solid black");
382398
});
383399

400+
401+
t.it("sanitizeCssImages", t => {
402+
403+
controller = createController();
404+
405+
let CSSIMAGES = [
406+
"A", "B"
407+
];
408+
409+
const FAKE_IFRAME = {
410+
getSrcDoc: () => "A aa B B C",
411+
setSrcDoc () {}
412+
};
413+
414+
l8.chain("cn_iframeEl.dom.contentWindow", FAKE_IFRAME, {});
415+
const
416+
sanitizeSpy = t.spyOn(controller, "getCssImages").and.callFake(() => CSSIMAGES),
417+
getIframeSpy = t.spyOn(controller, "getIframe").and.callFake(() => FAKE_IFRAME),
418+
setSrcDocSpy = t.spyOn(FAKE_IFRAME, "setSrcDoc");
419+
420+
t.expect(controller.sanitizeCssImages()).toBe(true);
421+
t.expect(setSrcDocSpy.calls.mostRecent().args).toEqual(
422+
[
423+
[`${controller.getProxyImage()} `,
424+
`${controller.getProxyImage()}${controller.getProxyImage()} `,
425+
`${controller.getProxyImage()} ${controller.getProxyImage()} C`].join("")
426+
427+
, false]
428+
);
429+
430+
[sanitizeSpy, getIframeSpy, setSrcDocSpy].map(spy => spy.remove);
431+
432+
});
433+
434+
384435
});});

0 commit comments

Comments
 (0)