diff --git a/app/assets/javascripts/template_include.js b/app/assets/javascripts/template_include.js new file mode 100644 index 00000000000..461d711ea31 --- /dev/null +++ b/app/assets/javascripts/template_include.js @@ -0,0 +1 @@ +//= require handlebars.runtime diff --git a/app/assets/javascripts/template_include.js.erb b/app/assets/javascripts/template_include.js.erb index ff7c323aea7..e69de29bb2d 100644 --- a/app/assets/javascripts/template_include.js.erb +++ b/app/assets/javascripts/template_include.js.erb @@ -1,8 +0,0 @@ -<% -if Rails.env.development? || Rails.env.test? - require_asset ("handlebars.js") - require_asset ("ember-template-compiler.js") -else - require_asset ("handlebars.runtime.js") -end -%> diff --git a/config/environments/development.rb b/config/environments/development.rb index 55a8f8e6c93..5ee92a55aba 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -30,7 +30,7 @@ Discourse::Application.configure do config.active_record.migration_error = :page_load config.watchable_dirs['lib'] = [:rb] - config.handlebars.precompile = false + config.handlebars.precompile = true # we recommend you use mailcatcher https://github.com/sj26/mailcatcher config.action_mailer.smtp_settings = { address: "localhost", port: 1025 } diff --git a/config/initializers/010-discourse_iife.rb b/config/initializers/010-discourse_iife.rb index 7e50904fa89..3405a62c4b2 100644 --- a/config/initializers/010-discourse_iife.rb +++ b/config/initializers/010-discourse_iife.rb @@ -5,7 +5,7 @@ require 'discourse_iife' Rails.application.config.assets.configure do |env| env.register_preprocessor('application/javascript', DiscourseIIFE) - unless Rails.env.production? || ENV["DISABLE_EVAL"] + unless Rails.env.production? require 'source_url' env.register_postprocessor('application/javascript', SourceURL) end diff --git a/lib/content_security_policy.rb b/lib/content_security_policy.rb index 87ad1947ce4..3a6227b5d3a 100644 --- a/lib/content_security_policy.rb +++ b/lib/content_security_policy.rb @@ -4,8 +4,8 @@ require 'content_security_policy/extension' class ContentSecurityPolicy class << self - def policy(theme_ids = []) - new.build(theme_ids) + def policy(theme_ids = [], path_info: "/") + new.build(theme_ids, path_info: path_info) end def base_url @@ -14,12 +14,13 @@ class ContentSecurityPolicy attr_writer :base_url end - def build(theme_ids) + def build(theme_ids, path_info: "/") builder = Builder.new Extension.theme_extensions(theme_ids).each { |extension| builder << extension } Extension.plugin_extensions.each { |extension| builder << extension } builder << Extension.site_setting_extension + builder << Extension.path_specific_extension(path_info) builder.build end diff --git a/lib/content_security_policy/default.rb b/lib/content_security_policy/default.rb index d0c8203dd56..7a6f1d24a5f 100644 --- a/lib/content_security_policy/default.rb +++ b/lib/content_security_policy/default.rb @@ -51,7 +51,6 @@ class ContentSecurityPolicy "#{base_url}/mini-profiler-resources/", *script_assets ].tap do |sources| - sources << :unsafe_eval if Rails.env.development? # TODO Remove this when we stop using `eval` in development mode sources << 'https://www.google-analytics.com/analytics.js' if SiteSetting.ga_universal_tracking_code.present? sources << 'https://www.googletagmanager.com/gtm.js' if SiteSetting.gtm_container_id.present? end diff --git a/lib/content_security_policy/extension.rb b/lib/content_security_policy/extension.rb index 60917888e58..f782f91e26f 100644 --- a/lib/content_security_policy/extension.rb +++ b/lib/content_security_policy/extension.rb @@ -7,6 +7,13 @@ class ContentSecurityPolicy { script_src: SiteSetting.content_security_policy_script_src.split('|') } end + def path_specific_extension(path_info) + {}.tap do |obj| + for_qunit_route = !Rails.env.production? && ["/qunit", "/wizard/qunit"].include?(path_info) + obj[:script_src] = :unsafe_eval if for_qunit_route + end + end + def plugin_extensions [].tap do |extensions| Discourse.plugins.each do |plugin| diff --git a/lib/content_security_policy/middleware.rb b/lib/content_security_policy/middleware.rb index d47babe35ad..3dfb2594406 100644 --- a/lib/content_security_policy/middleware.rb +++ b/lib/content_security_policy/middleware.rb @@ -15,8 +15,9 @@ class ContentSecurityPolicy ContentSecurityPolicy.base_url = request.host_with_port if Rails.env.development? theme_ids = env[:resolved_theme_ids] - headers['Content-Security-Policy'] = policy(theme_ids) if SiteSetting.content_security_policy - headers['Content-Security-Policy-Report-Only'] = policy(theme_ids) if SiteSetting.content_security_policy_report_only + + headers['Content-Security-Policy'] = policy(theme_ids, path_info: env["PATH_INFO"]) if SiteSetting.content_security_policy + headers['Content-Security-Policy-Report-Only'] = policy(theme_ids, path_info: env["PATH_INFO"]) if SiteSetting.content_security_policy_report_only response end diff --git a/lib/source_url.rb b/lib/source_url.rb index 8613dcc6b39..3e724e45d04 100644 --- a/lib/source_url.rb +++ b/lib/source_url.rb @@ -16,10 +16,9 @@ class SourceURL < Tilt::Template end def evaluate(scope, locals, &block) - code = +"eval(" - code << data.inspect - code << " + \"\\n//# sourceURL=#{scope.logical_path}\"" - code << ");\n" + code = +"\n//# BEGIN sourceURL=#{scope.logical_path}\n" + code << data + code << "\n//# END sourceURL=#{scope.logical_path}\n" code end end diff --git a/spec/lib/content_security_policy_spec.rb b/spec/lib/content_security_policy_spec.rb index 3ad2df28fe4..a40865a4814 100644 --- a/spec/lib/content_security_policy_spec.rb +++ b/spec/lib/content_security_policy_spec.rb @@ -123,6 +123,12 @@ describe ContentSecurityPolicy do Discourse.plugins.pop end + it 'only includes unsafe-inline for qunit paths' do + expect(parse(policy(path_info: "/qunit"))['script-src']).to include("'unsafe-eval'") + expect(parse(policy(path_info: "/wizard/qunit"))['script-src']).to include("'unsafe-eval'") + expect(parse(policy(path_info: "/"))['script-src']).to_not include("'unsafe-eval'") + end + context "with a theme" do let!(:theme) { Fabricate(:theme).tap do |t| @@ -174,7 +180,7 @@ describe ContentSecurityPolicy do end.to_h end - def policy(theme_ids = []) - ContentSecurityPolicy.policy(theme_ids) + def policy(theme_ids = [], path_info: "/") + ContentSecurityPolicy.policy(theme_ids, path_info: path_info) end end diff --git a/test/javascripts/test_helper.js b/test/javascripts/test_helper.js index de63b47b730..696f8d0a91a 100644 --- a/test/javascripts/test_helper.js +++ b/test/javascripts/test_helper.js @@ -3,9 +3,7 @@ //= require env //= require jquery.debug //= require jquery.ui.widget -//= require handlebars //= require ember.debug -//= require ember-template-compiler //= require message-bus //= require qunit/qunit/qunit //= require ember-qunit @@ -26,6 +24,11 @@ //= require application //= require admin +// These are not loaded in prod or development +// But we need them for testing handlebars templates in qunit +//= require handlebars +//= require ember-template-compiler + //= require sinon/pkg/sinon //= require helpers/assertions