waysact / webpack-subresource-integrity

Webpack plugin for enabling Subresource Integrity.
MIT License
357 stars 46 forks source link

Compressed files and integrity issues #166

Closed danrevah closed 3 years ago

danrevah commented 3 years ago

There's seem to be an issue with webpack-subresource-integrity while using it combined with compression-webpack-plugin. I saw that there was a similar issue that was closed (#125) with this repo as POC: https://github.com/jscheid/wsi-issue-125.

Trying to use the SriPlugin with a single hash function still throwed an integrity error for me.

new SriPlugin({
      hashFuncNames: ["sha512"],
      enabled: true,
 }),

Appreciate your thoughts on this one.

EDIT: After playing with it a bit further it seems like it's really unstable, while sometimes it works and sometimes it doesn't.

This was usually failing:

webpack.config.babel.js:

import webpack from "webpack";
import SriPlugin from "webpack-subresource-integrity";
import HtmlWebpackPlugin from "html-webpack-plugin";
import CompressionPlugin from "compression-webpack-plugin";
import zlib from "zlib";

export default {
  mode: "production",
  entry: {
    index: "./index.js",
  },
  output: {
    crossOriginLoading: "anonymous",
    filename: "[name].bundle.js"
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: "Code Splitting",
    }),
    new CompressionPlugin({
      filename: "[path].gz[query]",
      algorithm: "gzip",
      test: /\.(js|css)$/,
      threshold: 0,
      minRatio: 10,
    }),
    new CompressionPlugin({
      filename: "[path].br[query]",
      algorithm: "brotliCompress",
      test: /\.(js|css)$/,
      compressionOptions: {
        level: 11,
      },
      threshold: 0,
      minRatio: 10,
    }),
    new SriPlugin({
      hashFuncNames: ["sha256"],
      enabled: true,
    }),
  ],
};

Command:

npm run clean && npm run build && npm run server

Dev Console:

Failed to find a valid digest in the 'integrity' attribute for resource 'http://127.0.0.1:8081/index.bundle.js' with computed SHA-256 integrity 'plsjHG0cN5pwn241Rpy/BGHfYfnztMdZh+eqhpae0+U='. The resource has been blocked.

This feels like a race-condition.

danrevah commented 3 years ago

Yep, after digging into it a bit further it does seem to be a race-condition.

I'm using compression-webpack-plugin@6.1.1 & webpack-subresource-integrity@1.5.2, and it seems that if I change the tapPromise calls (2 occurances on v6.1.1) to tap - it works as expected.

https://github.com/webpack-contrib/compression-webpack-plugin/blob/master/src/index.js

jscheid commented 3 years ago

Which Webpack version are you using?

danrevah commented 3 years ago

4.41.5

jscheid commented 3 years ago

If I use these package versions and your webpack config from above, I'm getting WARNING in Conflict: Multiple assets emit different content to the same filename .gz and other warnings, which makes me think there is an incompatibility somewhere.

It would be best if you could fork my sample to create a new demo repository. Make sure it contains a package-lock.json or yarn.lock file so I can test with the exact same package versions.

danrevah commented 3 years ago

After your question regarding the Webpack version, I tried upgrading to latest Webpack (v4), and it seems like upgrading webpack from v4.41.5 to v44.4.2 currently solves it.

i'll continue testing this tommorow and will update the ticket accordingly.

Thanks!

danrevah commented 3 years ago

This was a really nice issue to investigate, it came from the fact we were using the compression plugin to override the .js files instead of generating it them separately.

This caused a race condition between webpack-subresource-integrity and compression-webpack-files, it seems like the compression sometimes took longer and the subresource-integrity was signing the file before the compression, and sometimes it was signing it after the compression - causing this race condition.

Changing (overriding JS files):

    new CompressionPlugin({
      filename: "[file][query]",
      test: /\.(js|css)$/
    })

to (generating both .js and .gz files):

    new CompressionPlugin({
      test: /\.(js|css)$/
    })

solved it for me.

jscheid commented 3 years ago

Ah yes, that's an interesting one. Thanks for following up.

Perhaps compression-webpack-plugin should warn or error when overwriting files, since clearly it isn't designed to do that (considering that, at least on the Webpack 4 code path, it's using the emit hook which isn't the place for transforming existing assets.)