diff --git a/lib/resource-handler/html/html-source-element.js b/lib/resource-handler/html/html-source-element.js index 3376388a..c50bf3ad 100644 --- a/lib/resource-handler/html/html-source-element.js +++ b/lib/resource-handler/html/html-source-element.js @@ -43,6 +43,12 @@ class HtmlSourceElement { this.rule.attr ? this.el.attr(this.rule.attr, newData) : this.el.text(newData); } + removeIntegrityCheck () { + if (this.el.attr('integrity')) { + this.el.attr('integrity', null); + } + } + /** * Returns PathContainer instance for element * @returns {CssText|HtmlCommonTag|HtmlImgSrcSetTag|null} diff --git a/lib/resource-handler/html/index.js b/lib/resource-handler/html/index.js index 667810a2..0b0f32d5 100644 --- a/lib/resource-handler/html/index.js +++ b/lib/resource-handler/html/index.js @@ -25,8 +25,8 @@ class HtmlResourceHandler { loadResourcesForRule ($, parentResource, rule) { const self = this; - const promises = $(rule.selector).map(function loadForElement () { - const el = new HtmlSourceElement($(this), rule); + const promises = $(rule.selector).map((i, element) => { + const el = new HtmlSourceElement($(element), rule); const isRecursive = self.options.recursiveSources && Boolean(el.findMatchedRule(self.options.recursiveSources)); const isDepthGreaterThanMax = self.options.maxRecursiveDepth && parentResource.getDepth() >= self.options.maxRecursiveDepth; @@ -39,7 +39,10 @@ class HtmlResourceHandler { if (!pathContainer) { return Promise.resolve(); } - return self.handleChildrenPaths(pathContainer, parentResource).then(el.setData.bind(el)); + return self.handleChildrenPaths(pathContainer, parentResource).then((updatedText) => { + el.setData(updatedText); + el.removeIntegrityCheck(); + }); }).get(); return utils.waitAllFulfilled(promises); diff --git a/test/unit/resource-handler/html.test.js b/test/unit/resource-handler/html.test.js index c8f5b1d7..4c895c23 100644 --- a/test/unit/resource-handler/html.test.js +++ b/test/unit/resource-handler/html.test.js @@ -1,23 +1,25 @@ +'use strict'; + require('should'); -var Promise = require('bluebird'); -var sinon = require('sinon'); -var Resource = require('../../../lib/resource'); -var HtmlHandler = require('../../../lib/resource-handler/html'); +const Promise = require('bluebird'); +const sinon = require('sinon'); +const Resource = require('../../../lib/resource'); +const HtmlHandler = require('../../../lib/resource-handler/html'); -var HtmlImgSrcsetTag = require('../../../lib/resource-handler/path-containers/html-img-srcset-tag'); -var HtmlCommonTag = require('../../../lib/resource-handler/path-containers/html-common-tag'); -var CssText = require('../../../lib/resource-handler/path-containers/css-text'); +const HtmlImgSrcsetTag = require('../../../lib/resource-handler/path-containers/html-img-srcset-tag'); +const HtmlCommonTag = require('../../../lib/resource-handler/path-containers/html-common-tag'); +const CssText = require('../../../lib/resource-handler/path-containers/css-text'); -describe('ResourceHandler: Html', function () { - var htmlHandler; +describe('ResourceHandler: Html', () => { + let htmlHandler; - beforeEach(function() { + beforeEach(() => { htmlHandler = new HtmlHandler({ sources: [] }, sinon.stub().returns(Promise.resolve())); }); - describe(' tag', function () { - it('should remove base tag from text and update resource url for absolute href', function () { - var html = ` + describe(' tag', () => { + it('should remove base tag from text and update resource url for absolute href', () => { + const html = ` @@ -25,17 +27,17 @@ describe('ResourceHandler: Html', function () { `; - var resource = new Resource('http://example.com', 'index.html'); + const resource = new Resource('http://example.com', 'index.html'); resource.setText(html); - return htmlHandler.handle(resource).then(function() { + return htmlHandler.handle(resource).then(() =>{ resource.getUrl().should.be.eql('http://some-other-domain.com/src'); resource.getText().should.not.containEql(' { + const html = ` @@ -43,17 +45,17 @@ describe('ResourceHandler: Html', function () { `; - var resource = new Resource('http://example.com', 'index.html'); + const resource = new Resource('http://example.com', 'index.html'); resource.setText(html); - return htmlHandler.handle(resource).then(function() { + return htmlHandler.handle(resource).then(() => { resource.getUrl().should.be.eql('http://example.com/src'); resource.getText().should.not.containEql(' { + const html = ` @@ -61,18 +63,18 @@ describe('ResourceHandler: Html', function () { `; - var resource = new Resource('http://example.com', 'index.html'); + const resource = new Resource('http://example.com', 'index.html'); resource.setText(html); - return htmlHandler.handle(resource).then(function() { + return htmlHandler.handle(resource).then(() => { resource.getUrl().should.be.eql('http://example.com'); resource.getText().should.containEql(''); }); }); }); - it('should not encode text to html entities', function () { - var html = ` + it('should not encode text to html entities', () => { + const html = `

Этот текст не должен быть преобразован в html entities

@@ -80,18 +82,18 @@ describe('ResourceHandler: Html', function () { `; - var resource = new Resource('http://example.com', 'index.html'); + const resource = new Resource('http://example.com', 'index.html'); resource.setText(html); - return htmlHandler.handle(resource).then(function () { + return htmlHandler.handle(resource).then(() => { resource.getText().should.containEql('Этот текст не должен быть преобразован в html entities'); }); }); - it('should call handleChildrenResources for each source', function () { + it('should call handleChildrenResources for each source', () => { htmlHandler.options.sources.push({ selector: 'img', attr: 'src' }); - var html = ` + const html = ` @@ -102,38 +104,38 @@ describe('ResourceHandler: Html', function () { `; - var resource = new Resource('http://example.com', 'index.html'); + const resource = new Resource('http://example.com', 'index.html'); resource.setText(html); - return htmlHandler.handle(resource).then(function() { + return htmlHandler.handle(resource).then(() =>{ htmlHandler.handleChildrenPaths.calledThrice.should.be.eql(true); }); }); - it('should not call handleChildrenResources if source attr is empty', function () { + it('should not call handleChildrenResources if source attr is empty', () =>{ htmlHandler.options.sources.push({ selector: 'img', attr: 'src' }); - var html = ` + const html = ` `; - var resource = new Resource('http://example.com', 'index.html'); + const resource = new Resource('http://example.com', 'index.html'); resource.setText(html); - return htmlHandler.handle(resource).then(function() { + return htmlHandler.handle(resource).then(() =>{ htmlHandler.handleChildrenPaths.called.should.be.eql(false); }); }); - it('should use correct path containers based on tag', function() { + it('should use correct path containers based on tag', () =>{ htmlHandler.options.sources.push({ selector: 'img', attr: 'src' }); htmlHandler.options.sources.push({ selector: 'img', attr: 'srcset' }); htmlHandler.options.sources.push({ selector: '.styled', attr: 'style' }); - var html = ` + const html = ` @@ -144,14 +146,43 @@ describe('ResourceHandler: Html', function () { `; - var resource = new Resource('http://example.com', 'index.html'); + const resource = new Resource('http://example.com', 'index.html'); resource.setText(html); - return htmlHandler.handle(resource).then(function() { + return htmlHandler.handle(resource).then(() =>{ htmlHandler.handleChildrenPaths.calledThrice.should.be.eql(true); htmlHandler.handleChildrenPaths.args[0][0].should.be.instanceOf(HtmlCommonTag); htmlHandler.handleChildrenPaths.args[1][0].should.be.instanceOf(HtmlImgSrcsetTag); htmlHandler.handleChildrenPaths.args[2][0].should.be.instanceOf(CssText); }); }); + + it('should remove SRI check for loaded resources', () => { + htmlHandler.options.sources.push({ selector: 'script', attr: 'src' }); + + const html = ` + + + + + + + + + `; + + const resource = new Resource('http://example.com', 'index.html'); + resource.setText(html); + + // before handle should contain both integrity checks + resource.getText().should.containEql('integrity="sha256-gaWb8m2IHSkoZnT23u/necREOC//MiCFtQukVUYMyuU="'); + resource.getText().should.containEql('integrity="sha256-X+Q/xqnlEgxCczSjjpp2AUGGgqM5gcBzhRQ0p+EAUEk="'); + + return htmlHandler.handle(resource).then(() => { + // after handle should contain integrity check for styles + // but not contain integrity check for script because it was loaded + resource.getText().should.containEql('integrity="sha256-gaWb8m2IHSkoZnT23u/necREOC//MiCFtQukVUYMyuU="'); + resource.getText().should.not.containEql('integrity="sha256-X+Q/xqnlEgxCczSjjpp2AUGGgqM5gcBzhRQ0p+EAUEk="'); + }); + }); });