From 5fbbd3dd6e965f76ecf1767373bddd236a78a4be Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 5 Aug 2019 23:14:32 -0700 Subject: Add support for Content-Security-Policy A nonce-based Content-Security-Policy thwarts XSS attacks by allowing inline JavaScript to execute if the script nonce matches the header value. Rails 5.2 supports nonce-based Content-Security-Policy headers, so provide configuration to enable this and make it work. To support this, we need to change all `:javascript` HAML filters to the following form: ``` = javascript_tag nonce: true do :plain ... ``` We use `%script` throughout our HAML to store JSON and other text, but since this doesn't execute, browsers don't appear to block this content from being used and require the nonce value to be present. --- app/views/layouts/_google_analytics.html.haml | 20 ++++++++-------- app/views/layouts/_head.html.haml | 3 ++- app/views/layouts/_init_auto_complete.html.haml | 10 ++++---- .../layouts/_init_client_detection_flags.html.haml | 8 +++---- app/views/layouts/_piwik.html.haml | 28 +++++++++++----------- app/views/layouts/errors.html.haml | 16 ++++++------- app/views/layouts/group.html.haml | 6 ++--- app/views/layouts/project.html.haml | 6 ++--- app/views/layouts/snippets.html.haml | 6 ++--- 9 files changed, 52 insertions(+), 51 deletions(-) (limited to 'app/views/layouts') diff --git a/app/views/layouts/_google_analytics.html.haml b/app/views/layouts/_google_analytics.html.haml index 98ea96b0b77..e8a5359e791 100644 --- a/app/views/layouts/_google_analytics.html.haml +++ b/app/views/layouts/_google_analytics.html.haml @@ -1,11 +1,11 @@ --# haml-lint:disable InlineJavaScript -:javascript - var _gaq = _gaq || []; - _gaq.push(['_setAccount', '#{extra_config.google_analytics_id}']); - _gaq.push(['_trackPageview']); += javascript_tag nonce: true do + :plain + var _gaq = _gaq || []; + _gaq.push(['_setAccount', '#{extra_config.google_analytics_id}']); + _gaq.push(['_trackPageview']); - (function() { - var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; - ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; - var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); - })(); + (function() { + var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; + ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; + var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); + })(); diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index ac774803f95..271b73326fa 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -40,7 +40,7 @@ = stylesheet_link_tag "highlight/themes/#{user_color_scheme}", media: "all" - = Gon::Base.render_data + = Gon::Base.render_data(nonce: content_security_policy_nonce) - if content_for?(:library_javascripts) = yield :library_javascripts @@ -56,6 +56,7 @@ = yield :project_javascripts = csrf_meta_tags + = csp_meta_tag - unless browser.safari? %meta{ name: 'referrer', content: 'origin-when-cross-origin' } diff --git a/app/views/layouts/_init_auto_complete.html.haml b/app/views/layouts/_init_auto_complete.html.haml index 240e03a5d53..82ec92988eb 100644 --- a/app/views/layouts/_init_auto_complete.html.haml +++ b/app/views/layouts/_init_auto_complete.html.haml @@ -4,8 +4,8 @@ - datasources = autocomplete_data_sources(object, noteable_type) - if object - -# haml-lint:disable InlineJavaScript - :javascript - gl = window.gl || {}; - gl.GfmAutoComplete = gl.GfmAutoComplete || {}; - gl.GfmAutoComplete.dataSources = #{datasources.to_json}; + = javascript_tag nonce: true do + :plain + gl = window.gl || {}; + gl.GfmAutoComplete = gl.GfmAutoComplete || {}; + gl.GfmAutoComplete.dataSources = #{datasources.to_json}; diff --git a/app/views/layouts/_init_client_detection_flags.html.haml b/app/views/layouts/_init_client_detection_flags.html.haml index c729f8aa696..6537b86085f 100644 --- a/app/views/layouts/_init_client_detection_flags.html.haml +++ b/app/views/layouts/_init_client_detection_flags.html.haml @@ -1,7 +1,7 @@ - client = client_js_flags - if client - -# haml-lint:disable InlineJavaScript - :javascript - gl = window.gl || {}; - gl.client = #{client.to_json}; + = javascript_tag nonce: true do + :plain + gl = window.gl || {}; + gl.client = #{client.to_json}; diff --git a/app/views/layouts/_piwik.html.haml b/app/views/layouts/_piwik.html.haml index 473b14ce626..2cb2e23433d 100644 --- a/app/views/layouts/_piwik.html.haml +++ b/app/views/layouts/_piwik.html.haml @@ -1,15 +1,15 @@ --# haml-lint:disable InlineJavaScript -:javascript - var _paq = _paq || []; - _paq.push(['trackPageView']); - _paq.push(['enableLinkTracking']); - (function() { - var u="//#{extra_config.piwik_url}/"; - _paq.push(['setTrackerUrl', u+'piwik.php']); - _paq.push(['setSiteId', "#{extra_config.piwik_site_id}"]); - var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; - g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s); - })(); - - += javascript_tag nonce: true do + :plain + var _paq = _paq || []; + _paq.push(['trackPageView']); + _paq.push(['enableLinkTracking']); + (function() { + var u="//#{extra_config.piwik_url}/"; + _paq.push(['setTrackerUrl', u+'piwik.php']); + _paq.push(['setSiteId', "#{extra_config.piwik_site_id}"]); + var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; + g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s); + })(); + + diff --git a/app/views/layouts/errors.html.haml b/app/views/layouts/errors.html.haml index 06069a72951..74484005b48 100644 --- a/app/views/layouts/errors.html.haml +++ b/app/views/layouts/errors.html.haml @@ -8,12 +8,12 @@ %body .page-container = yield - -# haml-lint:disable InlineJavaScript - :javascript - (function(){ - var goBackElement = document.querySelector('.js-go-back'); + = javascript_tag nonce: true do + :plain + (function(){ + var goBackElement = document.querySelector('.js-go-back'); - if (goBackElement && history.length > 1) { - goBackElement.style.display = 'block'; - } - }()); + if (goBackElement && history.length > 1) { + goBackElement.style.display = 'block'; + } + }()); diff --git a/app/views/layouts/group.html.haml b/app/views/layouts/group.html.haml index 1d40b78fa83..49de821f1c2 100644 --- a/app/views/layouts/group.html.haml +++ b/app/views/layouts/group.html.haml @@ -6,8 +6,8 @@ - content_for :page_specific_javascripts do - if current_user - -# haml-lint:disable InlineJavaScript - :javascript - window.uploads_path = "#{group_uploads_path(@group)}"; + = javascript_tag nonce: true do + :plain + window.uploads_path = "#{group_uploads_path(@group)}"; = render template: "layouts/application" diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml index 6b51483810e..b8ef38272fc 100644 --- a/app/views/layouts/project.html.haml +++ b/app/views/layouts/project.html.haml @@ -7,8 +7,8 @@ - content_for :project_javascripts do - project = @target_project || @project - if current_user - -# haml-lint:disable InlineJavaScript - :javascript - window.uploads_path = "#{project_uploads_path(project)}"; + = javascript_tag nonce: true do + :plain + window.uploads_path = "#{project_uploads_path(project)}"; = render template: "layouts/application" diff --git a/app/views/layouts/snippets.html.haml b/app/views/layouts/snippets.html.haml index 841b2a5e79c..cde2b467392 100644 --- a/app/views/layouts/snippets.html.haml +++ b/app/views/layouts/snippets.html.haml @@ -3,8 +3,8 @@ - content_for :page_specific_javascripts do - if snippets_upload_path - -# haml-lint:disable InlineJavaScript - :javascript - window.uploads_path = "#{snippets_upload_path}"; + = javascript_tag nonce: true do + :plain + window.uploads_path = "#{snippets_upload_path}"; = render template: "layouts/application" -- cgit v1.2.1