From 9e08f3a6a8a75dc12252ed43295bcd0a70d42e10 Mon Sep 17 00:00:00 2001 From: Sebastian Serth Date: Sat, 3 Sep 2022 03:22:44 +0200 Subject: [PATCH] Enable Subresource Integrity --- app/helpers/exercise_helper.rb | 2 +- app/views/layouts/application.html.slim | 8 +++---- config/webpack/webpack.config.js | 21 +++++++++++++++-- lib/webpacker/sri_helper_extensions.rb | 27 ++++++++++++++++++++++ lib/webpacker/sri_manifest_extensions.rb | 29 ++++++++++++++++++++++++ package.json | 1 + yarn.lock | 12 ++++++++++ 7 files changed, 93 insertions(+), 7 deletions(-) create mode 100644 lib/webpacker/sri_helper_extensions.rb create mode 100644 lib/webpacker/sri_manifest_extensions.rb diff --git a/app/helpers/exercise_helper.rb b/app/helpers/exercise_helper.rb index 26343917..4302a3fb 100644 --- a/app/helpers/exercise_helper.rb +++ b/app/helpers/exercise_helper.rb @@ -8,7 +8,7 @@ module ExerciseHelper end def qa_js_tag - javascript_include_tag "#{qa_url}/assets/qa_api.js" + javascript_include_tag "#{qa_url}/assets/qa_api.js", integrity: true, crossorigin: 'anonymous' end def qa_url diff --git a/app/views/layouts/application.html.slim b/app/views/layouts/application.html.slim index bd59b19f..5bd6fdff 100644 --- a/app/views/layouts/application.html.slim +++ b/app/views/layouts/application.html.slim @@ -9,10 +9,10 @@ html lang="#{I18n.locale || I18n.default_locale}" = favicon_link_tag('/favicon.png', type: 'image/png') = favicon_link_tag('/favicon.png', rel: 'apple-touch-icon', type: 'image/png') = action_cable_meta_tag - = stylesheet_pack_tag('application', 'stylesheets', media: 'all', 'data-turbolinks-track': true) - = stylesheet_link_tag('application', media: 'all', 'data-turbolinks-track': true) - = javascript_pack_tag('application', 'data-turbolinks-track': true, defer: false) - = javascript_include_tag('application', 'data-turbolinks-track': true) + = stylesheet_pack_tag('application', 'stylesheets', media: 'all', 'data-turbolinks-track': true, integrity: true, crossorigin: 'anonymous') + = stylesheet_link_tag('application', media: 'all', 'data-turbolinks-track': true, integrity: true, crossorigin: 'anonymous') + = javascript_pack_tag('application', 'data-turbolinks-track': true, defer: false, integrity: true, crossorigin: 'anonymous') + = javascript_include_tag('application', 'data-turbolinks-track': true, integrity: true, crossorigin: 'anonymous') = yield(:head) = csrf_meta_tags = timeago_script_tag nonce: true diff --git a/config/webpack/webpack.config.js b/config/webpack/webpack.config.js index 355c3b3e..3ea79661 100644 --- a/config/webpack/webpack.config.js +++ b/config/webpack/webpack.config.js @@ -1,12 +1,14 @@ // See the shakacode/shakapacker README and docs directory for advice on customizing your webpackConfig. -const { webpackConfig, merge } = require('shakapacker') +const { webpackConfig, config, merge } = require('shakapacker') const webpack = require('webpack'); const CompressionPlugin = require("compression-webpack-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const TerserPlugin = require("terser-webpack-plugin"); +const WebpackAssetsManifest = require('webpack-assets-manifest'); +const { SubresourceIntegrityPlugin } = require("webpack-subresource-integrity"); // Custom ERB loader to disable Spring and prevent crashes const erb = require("./loaders/erb"); @@ -30,7 +32,9 @@ const envConfig = module.exports = { ], }, output: { - publicPath: relative_url_root + public_output_path + publicPath: relative_url_root + public_output_path, + // the following setting is required for SRI to work: + crossOriginLoading: 'anonymous', }, performance: { // Turn off size warnings for large assets @@ -53,6 +57,15 @@ const envConfig = module.exports = { }), new CompressionPlugin(), new MiniCssExtractPlugin(), + new SubresourceIntegrityPlugin(), + new WebpackAssetsManifest({ + entrypoints: true, + integrity: false, + writeToDisk: true, + entrypointsUseAssets: true, + publicPath: true, + output: config.manifestPath, + }) ], resolve: { extensions: ['.css', '.ts', '.tsx'], @@ -65,4 +78,8 @@ const envConfig = module.exports = { stats: 'minimal', } +// Use the two lines below to remove the original WebpackAssetsManifest and replace it with our custom config. +const filteredPlugins = webpackConfig.plugins.filter((plugin) => !(plugin instanceof WebpackAssetsManifest)) +webpackConfig.plugins = filteredPlugins; + module.exports = merge(webpackConfig, envConfig) diff --git a/lib/webpacker/sri_helper_extensions.rb b/lib/webpacker/sri_helper_extensions.rb new file mode 100644 index 00000000..74b7bdfc --- /dev/null +++ b/lib/webpacker/sri_helper_extensions.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Webpacker::SriHelperExtensions + def stylesheet_link_tag(*sources, **options) + tags = sources.map do |stylesheet| + if stylesheet.is_a?(Hash) + super(stylesheet[:src], options.merge(integrity: stylesheet[:integrity])) + else + super(stylesheet, options) + end + end + safe_join(tags) + end + + def javascript_include_tag(*sources, **options) + tags = sources.map do |javascript| + if javascript.is_a?(Hash) + super(javascript[:src], options.merge(integrity: javascript[:integrity])) + else + super(javascript, options) + end + end + safe_join(tags) + end +end + +Sprockets::Rails::Helper.prepend(Webpacker::SriHelperExtensions) diff --git a/lib/webpacker/sri_manifest_extensions.rb b/lib/webpacker/sri_manifest_extensions.rb new file mode 100644 index 00000000..143bf7a5 --- /dev/null +++ b/lib/webpacker/sri_manifest_extensions.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Webpacker::SriManifestExtensions + def lookup(name, pack_type = {}) + asset = super + + augment_with_integrity asset, pack_type + end + + def lookup_pack_with_chunks(name, pack_type = {}) + assets = super + + assets.map do |asset| + augment_with_integrity asset, pack_type + end + end + + def augment_with_integrity(asset, _pack_type = {}) + if asset.respond_to?(:dig) && asset['integrity'] + {src: asset['src'], integrity: asset['integrity']} + elsif asset.respond_to?(:dig) + asset['src'] + else + asset + end + end +end + +Webpacker::Manifest.prepend(Webpacker::SriManifestExtensions) diff --git a/package.json b/package.json index 80881b39..a9b2943f 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "webpack-cli": "^4.10.0", "webpack-merge": "^5.8.0", "webpack-sources": "^3.2.3", + "webpack-subresource-integrity": "^5.1.0", "xss": "^1.0.14" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index d717f7a6..7213fe82 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4199,6 +4199,11 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +typed-assert@^1.0.8: + version "1.0.9" + resolved "https://registry.yarnpkg.com/typed-assert/-/typed-assert-1.0.9.tgz#8af9d4f93432c4970ec717e3006f33f135b06213" + integrity sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg== + underscore@^1.13.4: version "1.13.4" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.4.tgz#7886b46bbdf07f768e0052f1828e1dcab40c0dee" @@ -4387,6 +4392,13 @@ webpack-sources@^3.2.3: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== +webpack-subresource-integrity@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz#8b7606b033c6ccac14e684267cb7fb1f5c2a132a" + integrity sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q== + dependencies: + typed-assert "^1.0.8" + webpack@^5.74.0: version "5.74.0" resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.74.0.tgz#02a5dac19a17e0bb47093f2be67c695102a55980"