From ad4c5afd3b887908cddf3de7065d005465be6f71 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 6 Nov 2020 11:56:31 +0100 Subject: [PATCH] Add subresource integrity for JS and CSS assets (#15096) Fix #2744 --- app/views/about/more.html.haml | 2 +- app/views/admin/action_logs/index.html.haml | 2 +- app/views/admin/custom_emojis/index.html.haml | 2 +- app/views/admin/domain_allows/new.html.haml | 2 +- app/views/admin/domain_blocks/edit.html.haml | 2 +- app/views/admin/domain_blocks/new.html.haml | 2 +- app/views/admin/ip_blocks/index.html.haml | 2 +- .../admin/pending_accounts/index.html.haml | 2 +- app/views/admin/reports/show.html.haml | 2 +- app/views/admin/settings/edit.html.haml | 2 +- app/views/admin/statuses/index.html.haml | 2 +- app/views/admin/tags/index.html.haml | 2 +- app/views/auth/sessions/two_factor.html.haml | 2 +- app/views/home/index.html.haml | 10 +++++----- app/views/layouts/admin.html.haml | 2 +- app/views/layouts/application.html.haml | 8 ++++---- app/views/layouts/auth.html.haml | 2 +- app/views/layouts/embedded.html.haml | 4 ++-- app/views/layouts/error.html.haml | 8 ++++---- app/views/layouts/modal.html.haml | 2 +- app/views/layouts/public.html.haml | 2 +- app/views/media/player.html.haml | 2 +- app/views/public_timelines/show.html.haml | 2 +- app/views/relationships/show.html.haml | 2 +- .../webauthn_credentials/new.html.haml | 2 +- app/views/shares/show.html.haml | 2 +- app/views/tags/show.html.haml | 2 +- config/application.rb | 2 ++ config/webpack/shared.js | 3 ++- lib/webpacker/helper_extensions.rb | 20 +++++++++++++++++++ lib/webpacker/manifest_extensions.rb | 17 ++++++++++++++++ 31 files changed, 79 insertions(+), 39 deletions(-) create mode 100644 lib/webpacker/helper_extensions.rb create mode 100644 lib/webpacker/manifest_extensions.rb diff --git a/app/views/about/more.html.haml b/app/views/about/more.html.haml index e8f6041f89d1db..b34ff1a9f6d24d 100644 --- a/app/views/about/more.html.haml +++ b/app/views/about/more.html.haml @@ -2,7 +2,7 @@ = site_hostname - content_for :header_tags do - = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag 'public', crossorigin: 'anonymous' = render partial: 'shared/og' %meta{ name: 'robots', content: 'noindex' }/ diff --git a/app/views/admin/action_logs/index.html.haml b/app/views/admin/action_logs/index.html.haml index 99f756762958b9..e7d9054d9f8beb 100644 --- a/app/views/admin/action_logs/index.html.haml +++ b/app/views/admin/action_logs/index.html.haml @@ -2,7 +2,7 @@ = t('admin.action_logs.title') - content_for :header_tags do - = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + = javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous' = form_tag admin_action_logs_url, method: 'GET', class: 'simple_form' do = hidden_field_tag :target_account_id, params[:target_account_id] if params[:target_account_id].present? diff --git a/app/views/admin/custom_emojis/index.html.haml b/app/views/admin/custom_emojis/index.html.haml index 1cbc36f97ec465..bfec0407ef2aa4 100644 --- a/app/views/admin/custom_emojis/index.html.haml +++ b/app/views/admin/custom_emojis/index.html.haml @@ -2,7 +2,7 @@ = t('admin.custom_emojis.title') - content_for :header_tags do - = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + = javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous' - if can?(:create, :custom_emoji) - content_for :heading_actions do diff --git a/app/views/admin/domain_allows/new.html.haml b/app/views/admin/domain_allows/new.html.haml index 52599857aabc43..249a961cee85bb 100644 --- a/app/views/admin/domain_allows/new.html.haml +++ b/app/views/admin/domain_allows/new.html.haml @@ -1,5 +1,5 @@ - content_for :header_tags do - = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + = javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous' - content_for :page_title do = t('admin.domain_allows.add_new') diff --git a/app/views/admin/domain_blocks/edit.html.haml b/app/views/admin/domain_blocks/edit.html.haml index 29e47ef3bdd78a..d5868070a7c1e0 100644 --- a/app/views/admin/domain_blocks/edit.html.haml +++ b/app/views/admin/domain_blocks/edit.html.haml @@ -1,5 +1,5 @@ - content_for :header_tags do - = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + = javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous' - content_for :page_title do = t('admin.domain_blocks.edit') diff --git a/app/views/admin/domain_blocks/new.html.haml b/app/views/admin/domain_blocks/new.html.haml index ed1581936a8fac..f503f9b7721872 100644 --- a/app/views/admin/domain_blocks/new.html.haml +++ b/app/views/admin/domain_blocks/new.html.haml @@ -1,5 +1,5 @@ - content_for :header_tags do - = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + = javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous' - content_for :page_title do = t('.title') diff --git a/app/views/admin/ip_blocks/index.html.haml b/app/views/admin/ip_blocks/index.html.haml index a282a4cfefbe54..d5b983de9ec079 100644 --- a/app/views/admin/ip_blocks/index.html.haml +++ b/app/views/admin/ip_blocks/index.html.haml @@ -2,7 +2,7 @@ = t('admin.ip_blocks.title') - content_for :header_tags do - = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + = javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous' - if can?(:create, :ip_block) - content_for :heading_actions do diff --git a/app/views/admin/pending_accounts/index.html.haml b/app/views/admin/pending_accounts/index.html.haml index 79ae4a320fccc4..8384a1c9f012f5 100644 --- a/app/views/admin/pending_accounts/index.html.haml +++ b/app/views/admin/pending_accounts/index.html.haml @@ -2,7 +2,7 @@ = t('admin.pending_accounts.title', count: User.pending.count) - content_for :header_tags do - = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + = javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous' = form_for(@form, url: batch_admin_pending_accounts_path) do |f| = hidden_field_tag :page, params[:page] || 1 diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml index 0d563eea777495..2681419ca2fb62 100644 --- a/app/views/admin/reports/show.html.haml +++ b/app/views/admin/reports/show.html.haml @@ -1,5 +1,5 @@ - content_for :header_tags do - = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + = javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous' - content_for :page_title do = t('admin.reports.report', id: @report.id) diff --git a/app/views/admin/settings/edit.html.haml b/app/views/admin/settings/edit.html.haml index f37775aa985a7b..9e28766b1c161d 100644 --- a/app/views/admin/settings/edit.html.haml +++ b/app/views/admin/settings/edit.html.haml @@ -1,5 +1,5 @@ - content_for :header_tags do - = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + = javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous' - content_for :page_title do = t('admin.settings.title') diff --git a/app/views/admin/statuses/index.html.haml b/app/views/admin/statuses/index.html.haml index f1169a2fdecca4..c39ba9071fce14 100644 --- a/app/views/admin/statuses/index.html.haml +++ b/app/views/admin/statuses/index.html.haml @@ -1,5 +1,5 @@ - content_for :header_tags do - = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + = javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous' - content_for :page_title do = t('admin.statuses.title') diff --git a/app/views/admin/tags/index.html.haml b/app/views/admin/tags/index.html.haml index f888a311dbf8c5..d7719d45d62d1a 100644 --- a/app/views/admin/tags/index.html.haml +++ b/app/views/admin/tags/index.html.haml @@ -2,7 +2,7 @@ = t('admin.tags.title') - content_for :header_tags do - = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + = javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous' .filters .filter-subset diff --git a/app/views/auth/sessions/two_factor.html.haml b/app/views/auth/sessions/two_factor.html.haml index f2f6fe19d58d0b..b897a0422e23c1 100644 --- a/app/views/auth/sessions/two_factor.html.haml +++ b/app/views/auth/sessions/two_factor.html.haml @@ -1,7 +1,7 @@ - content_for :page_title do = t('auth.login') -=javascript_pack_tag 'two_factor_authentication', integrity: true, crossorigin: 'anonymous' +=javascript_pack_tag 'two_factor_authentication', crossorigin: 'anonymous' - if @webauthn_enabled = render partial: 'auth/sessions/two_factor/webauthn_form', locals: { hidden: @scheme_type != 'webauthn' } diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index 30c7aab194f3aa..94cc782b2b7fe0 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,12 +1,12 @@ - content_for :header_tags do - = preload_link_tag asset_pack_path('features/getting_started.js'), crossorigin: 'anonymous' - = preload_link_tag asset_pack_path('features/compose.js'), crossorigin: 'anonymous' - = preload_link_tag asset_pack_path('features/home_timeline.js'), crossorigin: 'anonymous' - = preload_link_tag asset_pack_path('features/notifications.js'), crossorigin: 'anonymous' + = preload_pack_asset 'features/getting_started.js', crossorigin: 'anonymous' + = preload_pack_asset 'features/compose.js', crossorigin: 'anonymous' + = preload_pack_asset 'features/home_timeline.js', crossorigin: 'anonymous' + = preload_pack_asset 'features/notifications.js', crossorigin: 'anonymous' %meta{name: 'applicationServerKey', content: Rails.configuration.x.vapid_public_key} = render_initial_state - = javascript_pack_tag 'application', integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag 'application', crossorigin: 'anonymous' .app-holder#mastodon{ data: { props: Oj.dump(default_props) } } %noscript diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml index b1a2d0617ff23e..62716ab1ee01b4 100644 --- a/app/views/layouts/admin.html.haml +++ b/app/views/layouts/admin.html.haml @@ -1,6 +1,6 @@ - content_for :header_tags do = render_initial_state - = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag 'public', crossorigin: 'anonymous' - content_for :content do .admin-wrapper diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 1f10f40c0a3925..9501207e0818b3 100755 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -21,10 +21,10 @@ %title= content_for?(:page_title) ? safe_join([yield(:page_title).chomp.html_safe, title], ' - ') : title - = stylesheet_pack_tag 'common', media: 'all' - = stylesheet_pack_tag current_theme, media: 'all' - = javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous' - = javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous' + = stylesheet_pack_tag 'common', media: 'all', crossorigin: 'anonymous' + = stylesheet_pack_tag current_theme, media: 'all', crossorigin: 'anonymous' + = javascript_pack_tag 'common', crossorigin: 'anonymous' + = javascript_pack_tag "locale_#{I18n.locale}", crossorigin: 'anonymous' = csrf_meta_tags %meta{ name: 'style-nonce', content: request.content_security_policy_nonce } diff --git a/app/views/layouts/auth.html.haml b/app/views/layouts/auth.html.haml index 585e246557a4de..0ea3bbe3b07069 100644 --- a/app/views/layouts/auth.html.haml +++ b/app/views/layouts/auth.html.haml @@ -1,5 +1,5 @@ - content_for :header_tags do - = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag 'public', crossorigin: 'anonymous' - content_for :content do .container-alt diff --git a/app/views/layouts/embedded.html.haml b/app/views/layouts/embedded.html.haml index 37051e70cf8dd2..e4311d342c82ab 100644 --- a/app/views/layouts/embedded.html.haml +++ b/app/views/layouts/embedded.html.haml @@ -11,8 +11,8 @@ - if storage_host? %link{ rel: 'dns-prefetch', href: storage_host }/ - = stylesheet_pack_tag 'common', media: 'all' - = stylesheet_pack_tag Setting.default_settings['theme'], media: 'all' + = stylesheet_pack_tag 'common', media: 'all', crossorigin: 'anonymous' + = stylesheet_pack_tag Setting.default_settings['theme'], media: 'all', crossorigin: 'anonymous' = javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous' = javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous' = render_initial_state diff --git a/app/views/layouts/error.html.haml b/app/views/layouts/error.html.haml index 25c85abf9e7964..852a0c69b6069f 100644 --- a/app/views/layouts/error.html.haml +++ b/app/views/layouts/error.html.haml @@ -5,10 +5,10 @@ %meta{ charset: 'utf-8' }/ %title= safe_join([yield(:page_title), Setting.default_settings['site_title']], ' - ') %meta{ content: 'width=device-width,initial-scale=1', name: 'viewport' }/ - = stylesheet_pack_tag 'common', media: 'all' - = stylesheet_pack_tag Setting.default_settings['theme'], media: 'all' - = javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous' - = javascript_pack_tag 'error', integrity: true, crossorigin: 'anonymous' + = stylesheet_pack_tag 'common', media: 'all', crossorigin: 'anonymous' + = stylesheet_pack_tag Setting.default_settings['theme'], media: 'all', crossorigin: 'anonymous' + = javascript_pack_tag 'common', crossorigin: 'anonymous' + = javascript_pack_tag 'error', crossorigin: 'anonymous' %body.error .dialog .dialog__illustration diff --git a/app/views/layouts/modal.html.haml b/app/views/layouts/modal.html.haml index 2ef49e413208ad..e74e2c0e35644e 100644 --- a/app/views/layouts/modal.html.haml +++ b/app/views/layouts/modal.html.haml @@ -1,5 +1,5 @@ - content_for :header_tags do - = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag 'public', crossorigin: 'anonymous' - content_for :content do - if user_signed_in? && !@hide_header diff --git a/app/views/layouts/public.html.haml b/app/views/layouts/public.html.haml index a2c4e5deb2d407..e63cf0848f0dc4 100644 --- a/app/views/layouts/public.html.haml +++ b/app/views/layouts/public.html.haml @@ -1,6 +1,6 @@ - content_for :header_tags do = render_initial_state - = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag 'public', crossorigin: 'anonymous' - content_for :content do .public-layout diff --git a/app/views/media/player.html.haml b/app/views/media/player.html.haml index ae47750e9ab0e6..92428ca94acf33 100644 --- a/app/views/media/player.html.haml +++ b/app/views/media/player.html.haml @@ -1,6 +1,6 @@ - content_for :header_tags do = render_initial_state - = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag 'public', crossorigin: 'anonymous' - if @media_attachment.video? = react_component :video, src: @media_attachment.file.url(:original), preview: @media_attachment.thumbnail.present? ? @media_attachment.thumbnail.url : @media_attachment.file.url(:small), blurhash: @media_attachment.blurhash, width: 670, height: 380, editable: true, detailed: true, inline: true, alt: @media_attachment.description do diff --git a/app/views/public_timelines/show.html.haml b/app/views/public_timelines/show.html.haml index 5e536a23561802..3325be5bf1c82d 100644 --- a/app/views/public_timelines/show.html.haml +++ b/app/views/public_timelines/show.html.haml @@ -3,7 +3,7 @@ - content_for :header_tags do %meta{ name: 'robots', content: 'noindex' }/ - = javascript_pack_tag 'about', integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag 'about', crossorigin: 'anonymous' .page-header %h1= t('about.see_whats_happening') diff --git a/app/views/relationships/show.html.haml b/app/views/relationships/show.html.haml index 099bb32024d111..4b1e4fd638d9c0 100644 --- a/app/views/relationships/show.html.haml +++ b/app/views/relationships/show.html.haml @@ -2,7 +2,7 @@ = t('settings.relationships') - content_for :header_tags do - = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + = javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous' .filters .filter-subset diff --git a/app/views/settings/two_factor_authentication/webauthn_credentials/new.html.haml b/app/views/settings/two_factor_authentication/webauthn_credentials/new.html.haml index 0b23bb6897a319..1148d5ed7e9f10 100644 --- a/app/views/settings/two_factor_authentication/webauthn_credentials/new.html.haml +++ b/app/views/settings/two_factor_authentication/webauthn_credentials/new.html.haml @@ -13,4 +13,4 @@ .actions = f.button :button, t('webauthn_credentials.add'), class: 'js-webauthn', type: :submit -= javascript_pack_tag 'two_factor_authentication', integrity: true, crossorigin: 'anonymous' += javascript_pack_tag 'two_factor_authentication', crossorigin: 'anonymous' diff --git a/app/views/shares/show.html.haml b/app/views/shares/show.html.haml index f2f5479a79c4a6..1c0bbf6765817d 100644 --- a/app/views/shares/show.html.haml +++ b/app/views/shares/show.html.haml @@ -1,5 +1,5 @@ - content_for :header_tags do = render_initial_state - = javascript_pack_tag 'share', integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag 'share', crossorigin: 'anonymous' #mastodon-compose{ data: { props: Oj.dump(default_props) } } diff --git a/app/views/tags/show.html.haml b/app/views/tags/show.html.haml index 19dadd36a552da..beeeb56f2ddf79 100644 --- a/app/views/tags/show.html.haml +++ b/app/views/tags/show.html.haml @@ -5,7 +5,7 @@ %meta{ name: 'robots', content: 'noindex' }/ %link{ rel: 'alternate', type: 'application/rss+xml', href: tag_url(@tag, format: 'rss') }/ - = javascript_pack_tag 'about', integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag 'about', crossorigin: 'anonymous' = render 'og' .page-header diff --git a/config/application.rb b/config/application.rb index af67ac8c72f876..0ef2abda7df482 100644 --- a/config/application.rb +++ b/config/application.rb @@ -22,6 +22,8 @@ require_relative '../lib/devise/two_factor_ldap_authenticatable' require_relative '../lib/devise/two_factor_pam_authenticatable' require_relative '../lib/chewy/strategy/custom_sidekiq' +require_relative '../lib/webpacker/manifest_extensions' +require_relative '../lib/webpacker/helper_extensions' Dotenv::Railtie.load diff --git a/config/webpack/shared.js b/config/webpack/shared.js index 6676528090b482..05828aebe076ef 100644 --- a/config/webpack/shared.js +++ b/config/webpack/shared.js @@ -79,7 +79,8 @@ module.exports = { chunkFilename: 'css/[name]-[contenthash:8].chunk.css', }), new AssetsManifestPlugin({ - integrity: false, + integrity: true, + integrityHashes: ['sha256'], entrypoints: true, writeToDisk: true, publicPath: true, diff --git a/lib/webpacker/helper_extensions.rb b/lib/webpacker/helper_extensions.rb new file mode 100644 index 00000000000000..8f46d763132b5d --- /dev/null +++ b/lib/webpacker/helper_extensions.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Webpacker::HelperExtensions + def javascript_pack_tag(name, **options) + src, integrity = current_webpacker_instance.manifest.lookup!(name, type: :javascript, with_integrity: true) + javascript_include_tag(src, options.merge(integrity: integrity)) + end + + def stylesheet_pack_tag(name, **options) + src, integrity = current_webpacker_instance.manifest.lookup!(name, type: :stylesheet, with_integrity: true) + stylesheet_link_tag(src, options.merge(integrity: integrity)) + end + + def preload_pack_asset(name, **options) + src, integrity = current_webpacker_instance.manifest.lookup!(name, with_integrity: true) + preload_link_tag(src, options.merge(integrity: integrity)) + end +end + +Webpacker::Helper.prepend(Webpacker::HelperExtensions) diff --git a/lib/webpacker/manifest_extensions.rb b/lib/webpacker/manifest_extensions.rb new file mode 100644 index 00000000000000..789eb81ccf3173 --- /dev/null +++ b/lib/webpacker/manifest_extensions.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Webpacker::ManifestExtensions + def lookup(name, pack_type = {}) + asset = super + + if pack_type[:with_integrity] && asset.respond_to?(:dig) + [asset.dig('src'), asset.dig('integrity')] + elsif asset.respond_to?(:dig) + asset.dig('src') + else + asset + end + end +end + +Webpacker::Manifest.prepend(Webpacker::ManifestExtensions)