CVE-2020-15262: Insufficient Verification of Data Authenticity (CWE-345)
value: In webpack-subresource-integrity before version 1.5.1, all dynamically loaded chunks receive an invalid integrity hash that is ignored by the browser, and therefore the browser cannot validate their integrity. This removes the additional level of protection offered by SRI for such chunks. Top-level chunks are unaffected. This issue is patched in version 1.5.1.
CWE description: The product does not sufficiently verify the origin or authenticity of data, in a way that causes it to accept invalid data.
CVSS v3 base score: 3.7
Published: 2020-10-19T20:15Z
Repository: https://github.com/waysact/webpack-subresource-integrity
Language: JavaScript
Files changed: examples/dynamic-modified/README.md, examples/dynamic-modified/corrupt.js, examples/dynamic-modified/index.js, examples/dynamic-modified/test.js, examples/dynamic-modified/webpack.config.js, jmtp.js, package.json
Commit date: 2020-10-18 18:23:32 +1300
Commit message: Fix hash lookup for dynamic chunks

Closes #131

Diff: diff --git a/examples/dynamic-modified/README.md b/examples/dynamic-modified/README.md
new file mode 100644
index 0000000..d0480bf
--- /dev/null
+++ b/examples/dynamic-modified/README.md
@@ -0,0 +1,3 @@
+# With a modified dynamically loaded chunk #hwp
+
+Ensure that when a chunk is modified, it fails to load.
diff --git a/examples/dynamic-modified/corrupt.js b/examples/dynamic-modified/corrupt.js
new file mode 100644
index 0000000..1b072a3
--- /dev/null
+++ b/examples/dynamic-modified/corrupt.js
@@ -0,0 +1 @@
+console.log('this should never load');
diff --git a/examples/dynamic-modified/index.js b/examples/dynamic-modified/index.js
new file mode 100644
index 0000000..3ac0327
--- /dev/null
+++ b/examples/dynamic-modified/index.js
@@ -0,0 +1,5 @@
+import("./corrupt").then(function error() {
+  console.log('error');
+}).catch(function ok() {
+  console.log('ok');
+});
diff --git a/examples/dynamic-modified/test.js b/examples/dynamic-modified/test.js
new file mode 100644
index 0000000..70b5fe7
--- /dev/null
+++ b/examples/dynamic-modified/test.js
@@ -0,0 +1,18 @@
+var webpackVersionComponents = require('webpack/package.json').version.split(
+  '.'
+);
+var webpackVersionMajor = Number(webpackVersionComponents[0]);
+
+var defaultCheck = require("../../test/defaultCheck");
+var fs = require('fs');
+
+module.exports.skip = function skip() {
+  return webpackVersionMajor < 2;
+};
+
+module.exports.check = function check(stats, url, browser) {
+  const otherAsset = Object.keys(stats.compilation.assets).find(key => key !== 'index.js' && key.endsWith(".js"));
+  fs.writeFileSync('dist/' + otherAsset, 'xxx');
+
+  return defaultCheck(stats, url, browser);
+};
diff --git a/examples/dynamic-modified/webpack.config.js b/examples/dynamic-modified/webpack.config.js
new file mode 100644
index 0000000..51b9b7a
--- /dev/null
+++ b/examples/dynamic-modified/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 79c99c8..bb51071 100644
--- a/jmtp.js
+++ b/jmtp.js
@@ -5,10 +5,14 @@
  * LICENSE file in the root directory of this source tree.
  */
 
 var Template = require('webpack/lib/Template');
 var util = require('./util');
+var webpackVersionComponents = require('webpack/package.json').version.split(
+  '.'
+);
+var webpackVersionMajor = Number(webpackVersionComponents[0]);
 
 function WebIntegrityJsonpMainTemplatePlugin(sriPlugin, compilation) {
   this.sriPlugin = sriPlugin;
   this.compilation = compilation;
 }
@@ -45,21 +49,24 @@ WebIntegrityJsonpMainTemplatePlugin.prototype.addSriHashes =
 
 /*
  *  Patch jsonp-script code to add the integrity attribute.
  */
 WebIntegrityJsonpMainTemplatePlugin.prototype.addAttribute =
-  function addAttribute(mainTemplate, elName, source, chunk) {
+  function addAttribute(mainTemplate, elName, source) {
     const outputOptions = this.compilation.outputOptions || mainTemplate.outputOptions;
     if (!outputOptions.crossOriginLoading) {
       this.sriPlugin.errorOnce(
         this.compilation,
         'webpack option output.crossOriginLoading not set, code splitting will not work!'
       );
     }
+
     return (Template.asString || mainTemplate.asString)([
       source,
-      elName + '.integrity = __webpack_require__.sriHashes[' + (chunk ? `'${chunk.id}'` : 'chunkId') + '];',
+      elName + '.integrity = __webpack_require__.sriHashes[' +
+        ((webpackVersionMajor >= 5 && elName === 'script') ? 'key.match(/^chunk-([0-9]+)$/)[1]' : 'chunkId') +
+        '];',
       elName + '.crossOrigin = ' + JSON.stringify(outputOptions.crossOriginLoading) + ';',
     ]);
   };
 
 WebIntegrityJsonpMainTemplatePlugin.prototype.apply = function apply(
diff --git a/package.json b/package.json
index 0ba6065..37dd2a0 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
   "name": "webpack-subresource-integrity",
-  "version": "1.5.0",
+  "version": "1.5.1",
   "description": "Webpack plugin for enabling Subresource Integrity",
   "engines": {
     "node": ">=4"
   },
   "main": "index",

Vulnerable code:   function addAttribute(mainTemplate, elName, source, chunk) {
      elName + '.integrity = __webpack_require__.sriHashes[' + (chunk ? `'${chunk.id}'` : 'chunkId') + '];',
  "version": "1.5.0",
Fixed code: # With a modified dynamically loaded chunk #hwp

Ensure that when a chunk is modified, it fails to load.
console.log('this should never load');
import("./corrupt").then(function error() {
  console.log('error');
}).catch(function ok() {
  console.log('ok');
});
var webpackVersionComponents = require('webpack/package.json').version.split(
  '.'
);
var webpackVersionMajor = Number(webpackVersionComponents[0]);

var defaultCheck = require("../../test/defaultCheck");
var fs = require('fs');

module.exports.skip = function skip() {
  return webpackVersionMajor < 2;
};

module.exports.check = function check(stats, url, browser) {
  const otherAsset = Object.keys(stats.compilation.assets).find(key => key !== 'index.js' && key.endsWith(".js"));
  fs.writeFileSync('dist/' + otherAsset, 'xxx');

  return defaultCheck(stats, url, browser);
};
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()
  ]
};
var webpackVersionComponents = require('webpack/package.json').version.split(
  '.'
);
var webpackVersionMajor = Number(webpackVersionComponents[0]);
  function addAttribute(mainTemplate, elName, source) {

      elName + '.integrity = __webpack_require__.sriHashes[' +
        ((webpackVersionMajor >= 5 && elName === 'script') ? 'key.match(/^chunk-([0-9]+)$/)[1]' : 'chunkId') +
        '];',
  "version": "1.5.1",