From 3a9ba45801087dd71331d6f2a93c4a87647e41d1 Mon Sep 17 00:00:00 2001 From: Julian Scheid Date: Fri, 30 Aug 2019 20:31:59 +1200 Subject: [PATCH] Add integrity to link preload tags Closes #111 --- README.md | 12 +++++++----- examples/webpack-preload/README.md | 3 +++ examples/webpack-preload/index.js | 1 + examples/webpack-preload/lazy-chunk-1.js | 5 +++++ examples/webpack-preload/lazy-chunk-2.js | 16 ++++++++++++++++ examples/webpack-preload/test.js | 8 ++++++++ examples/webpack-preload/webpack.config.js | 18 ++++++++++++++++++ jmtp.js | 20 ++++++++++++++++++++ 8 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 examples/webpack-preload/README.md create mode 100644 examples/webpack-preload/index.js create mode 100644 examples/webpack-preload/lazy-chunk-1.js create mode 100644 examples/webpack-preload/lazy-chunk-2.js create mode 100644 examples/webpack-preload/test.js create mode 100644 examples/webpack-preload/webpack.config.js diff --git a/README.md b/README.md index 9916cf2..2ca0a22 100644 --- a/README.md +++ b/README.md @@ -155,11 +155,13 @@ Webpack >= 4). ### Preloading -`` doesn't work as expected in current Chrome versions, even -if the integrity attribute is added to the `link tag (which the current version -of webpack-subresource-integrity does _not_ do.) The resource will be loaded -twice, defeating the purpose of preloading. This issue doesn't appear to exist -in Firefox or Safari. See issue #111 for more information. +This plugin adds the integrity attribute to `` +tags, but preloading with SRI doesn't work as expected in current +Chrome versions. The resource will be loaded twice, defeating the +purpose of preloading. This problem doesn't appear to exist in +Firefox or Safari. See [issue +#111](https://github.com/waysact/webpack-subresource-integrity/issues/111) +for more information. ### Proxies diff --git a/examples/webpack-preload/README.md b/examples/webpack-preload/README.md new file mode 100644 index 0000000..4f18dd9 --- /dev/null +++ b/examples/webpack-preload/README.md @@ -0,0 +1,3 @@ +# With `/* webpackPreload: "true" */` + +https://github.com/waysact/webpack-subresource-integrity/issues/111 diff --git a/examples/webpack-preload/index.js b/examples/webpack-preload/index.js new file mode 100644 index 0000000..97f0403 --- /dev/null +++ b/examples/webpack-preload/index.js @@ -0,0 +1 @@ +import('./lazy-chunk-1.js'); diff --git a/examples/webpack-preload/lazy-chunk-1.js b/examples/webpack-preload/lazy-chunk-1.js new file mode 100644 index 0000000..4f767a1 --- /dev/null +++ b/examples/webpack-preload/lazy-chunk-1.js @@ -0,0 +1,5 @@ +setTimeout(() => { + import(/* webpackPreload: true */ './lazy-chunk-2.js').then(mod => + mod.test() + ); +}, 750); diff --git a/examples/webpack-preload/lazy-chunk-2.js b/examples/webpack-preload/lazy-chunk-2.js new file mode 100644 index 0000000..261c726 --- /dev/null +++ b/examples/webpack-preload/lazy-chunk-2.js @@ -0,0 +1,16 @@ +module.exports.test = () => { + const linkTag = Array.from(document.getElementsByTagName('link')).find( + el => el.rel === 'preload' + ); + const scriptTag = Array.from(document.getElementsByTagName('script')).find( + el => linkTag && el.src === linkTag.href + ); + console.log( + scriptTag && + linkTag && + scriptTag.integrity && + scriptTag.integrity === linkTag.integrity + ? 'ok' + : 'error' + ); +}; diff --git a/examples/webpack-preload/test.js b/examples/webpack-preload/test.js new file mode 100644 index 0000000..fa9fadc --- /dev/null +++ b/examples/webpack-preload/test.js @@ -0,0 +1,8 @@ +var webpackVersion = Number( + require('webpack/package.json').version.split('.')[0] +); + +module.exports.skip = function skip() { + // webpack-preload tag requires Webpack 4 + return webpackVersion < 4; +}; diff --git a/examples/webpack-preload/webpack.config.js b/examples/webpack-preload/webpack.config.js new file mode 100644 index 0000000..51b9b7a --- /dev/null +++ b/examples/webpack-preload/webpack.config.js @@ -0,0 +1,18 @@ +var SriPlugin = require('webpack-subresource-integrity'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); + +module.exports = { + entry: { + index: './index.js' + }, + output: { + crossOriginLoading: 'anonymous' + }, + plugins: [ + new SriPlugin({ + hashFuncNames: ['sha256', 'sha384'], + enabled: true + }), + new HtmlWebpackPlugin() + ] +}; diff --git a/jmtp.js b/jmtp.js index 55e3015..1808f94 100644 --- a/jmtp.js +++ b/jmtp.js @@ -43,6 +43,24 @@ WebIntegrityJsonpMainTemplatePlugin.prototype.addSriHashes = return source; }; +/* + * Patch jsonp-script code to add the integrity attribute. + */ +WebIntegrityJsonpMainTemplatePlugin.prototype.linkPreloadPlugin = + function linkPreloadPlugin(mainTemplate, source) { + if (!mainTemplate.outputOptions.crossOriginLoading) { + this.sriPlugin.errorOnce( + this.compilation, + 'webpack option output.crossOriginLoading not set, code splitting will not work!' + ); + } + return (Template.asString || mainTemplate.asString)([ + source, + 'link.integrity = sriHashes[chunkId];', + 'link.crossOrigin = ' + JSON.stringify(mainTemplate.outputOptions.crossOriginLoading) + ';', + ]); + }; + /* * Patch jsonp-script code to add the integrity attribute. */ @@ -65,6 +83,7 @@ WebIntegrityJsonpMainTemplatePlugin.prototype.apply = function apply( mainTemplate ) { var jsonpScriptPlugin = this.jsonpScriptPlugin.bind(this, mainTemplate); + var linkPreloadPlugin = this.linkPreloadPlugin.bind(this, mainTemplate); var addSriHashes = this.addSriHashes.bind(this, mainTemplate); if (!mainTemplate.hooks) { @@ -72,6 +91,7 @@ WebIntegrityJsonpMainTemplatePlugin.prototype.apply = function apply( mainTemplate.plugin('local-vars', addSriHashes); } else if (mainTemplate.hooks.jsonpScript && mainTemplate.hooks.localVars) { mainTemplate.hooks.jsonpScript.tap('SriPlugin', jsonpScriptPlugin); + mainTemplate.hooks.linkPreload.tap('SriPlugin', linkPreloadPlugin); mainTemplate.hooks.localVars.tap('SriPlugin', addSriHashes); } else { this.sriPlugin.warnOnce(