diff options
author | Jacques Erasmus <jerasmus@gitlab.com> | 2020-10-23 11:39:22 +0300 |
---|---|---|
committer | Jacques Erasmus <jerasmus@gitlab.com> | 2020-10-23 11:39:22 +0300 |
commit | fdd1fc948cd47c6b130e78e00b6b9e8f4e1b39eb (patch) | |
tree | ca1eab05519e5539429492c65f9d9d11a401ee55 | |
parent | 8ef6a7cd62318cc50f091133428f04ccaaa23df8 (diff) | |
parent | f494d1179980f1c116929f7e1c7196a99b4fcc9e (diff) |
Merge branch 'csp-headers' into 'master'
Add CSP meta tag
Closes #850
See merge request gitlab-org/gitlab-docs!1217
-rw-r--r-- | README.md | 54 | ||||
-rw-r--r-- | content/assets/javascripts/disqus.js | 26 | ||||
-rw-r--r-- | content/assets/javascripts/docsearch.js | 19 | ||||
-rw-r--r-- | content/assets/javascripts/facebook_analytics.js | 12 | ||||
-rw-r--r-- | content/assets/javascripts/google_tagmanager.js | 9 | ||||
-rw-r--r-- | content/assets/javascripts/gtag_analytics.js | 8 | ||||
-rw-r--r-- | content/assets/javascripts/linkedin_analytics.js | 12 | ||||
-rw-r--r-- | content/assets/javascripts/marketo_analytics.js | 24 | ||||
-rw-r--r-- | content/assets/javascripts/mermaid.js (renamed from layouts/mermaid.html) | 18 | ||||
-rw-r--r-- | content/assets/javascripts/toggle_popover.js | 10 | ||||
-rw-r--r-- | layouts/analytics.html | 53 | ||||
-rw-r--r-- | layouts/csp.html | 1 | ||||
-rw-r--r-- | layouts/default.html | 11 | ||||
-rw-r--r-- | layouts/docsearch.html | 18 | ||||
-rw-r--r-- | layouts/feedback.html | 25 | ||||
-rw-r--r-- | layouts/head.html | 13 |
16 files changed, 201 insertions, 112 deletions
@@ -484,3 +484,57 @@ want to receive weekly reports of the search usage, search the Google doc with title "Email, Slack, and GitLab Groups and Aliases", search for `docsearch`, and add a comment with your email to be added to the alias that gets the weekly reports. + +## CSP header + +The GitLab docs site uses a [Content Security Policy (CSP) header](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) +as an added layer of security that helps to detect and mitigate certain types of +attacks, including Cross Site Scripting (XSS) and data injection attacks. +Although it is a static site, and the potential for injection of scripts is very +low, there are customer concerns around not having this header applied. + +It's enabled by default on <https://docs.gitlab.com>, but if you want to build the +site on your own and disable the inclusion of the CSP header, you can do so with +the `DISABLE_CSP` environment variable: + +```shell +DISABLE_CSP=1 bundle exec nanoc compile +``` + +### Adding or updating domains in the CSP header + +The CSP header and the allowed domains can be found in the [`csp.html`](/layouts/csp.html) +layout. Every time a new font or Javascript file is added, or maybe updated in +case of a versioned file, you need to update the `csp.html` layout, otherwise +it can cause the site to misbehave and be broken. + +### No inline scripts allowed + +To avoid allowing `'unsafe-line'` in the CSP, we cannot use any inline scripts. +For example, this is prohibited: + +```html +<script> +$(function () { + $('[data-toggle="popover"]').popover(); + $('.popover-dismiss').popover({ + trigger: 'focus' + }) +}) +</script> +``` + +Instead, this should be extracted to its own files in the +[`/content/assets/javascripts/`](/content/assets/javascripts/) directory, +and then be included in the HTML file that you want. The example above lives +under `/content/assets/javascripts/toggle_popover.js`, and you would call +it with: + +```html +<script src="<%= @items['/assets/javascripts/toggle_popover.*'].path %>"></script> +``` + +### Testing the CSP header for conformity + +To test that the CSP header works as expected, you can visit +<https://cspvalidator.org/> and paste the URL that you want tested. diff --git a/content/assets/javascripts/disqus.js b/content/assets/javascripts/disqus.js new file mode 100644 index 00000000..de2c7a60 --- /dev/null +++ b/content/assets/javascripts/disqus.js @@ -0,0 +1,26 @@ +--- +version: 1 +--- + +var disqus_config = function () { + this.page.url = '<%= @config[:base_url] %><%= @item.identifier.without_ext + '.html' %>'; + this.page.title = '<%= @item.key?(:title) ? "#{item[:title]} - GitLab Documentation" : "GitLab Documentation" %>'; +<% if @item[:disqus_identifier] %> + this.page.identifier = '<%= @item[:disqus_identifier] %>'; +<% else %> + this.page.identifier = '<%= @config[:base_url] %><%= @item.identifier.without_ext + '.html' %>'; +<% end %> +}; + +var is_disqus_loaded = false; +window.loadDisqus = function() { + if (!is_disqus_loaded){ + is_disqus_loaded = true; + var disqusThread = document.getElementById('disqus_thread'); + var d = document, s = d.createElement('script'); + disqusThread.innerHTML = ''; + s.src = 'https://gitlab-docs.disqus.com/embed.js'; + s.setAttribute('data-timestamp', +new Date()); + (d.head || d.body).appendChild(s); + } +}; diff --git a/content/assets/javascripts/docsearch.js b/content/assets/javascripts/docsearch.js new file mode 100644 index 00000000..ac788034 --- /dev/null +++ b/content/assets/javascripts/docsearch.js @@ -0,0 +1,19 @@ +--- +version: 1 +--- + +var search = docsearch({ + apiKey: 'ce1690e1421303458a1fcbea0cc4a927', + indexName: 'gitlab', + inputSelector: '.docsearch', + algoliaOptions: { + // Filter by tags as described in https://github.com/algolia/docsearch-configs/blob/master/configs/gitlab.json + 'filters': "tags:gitlab<score=4> OR tags:omnibus<score=3> OR tags:runner<score=2> OR tags:charts<score=1>", + // Number of results shown in the search dropdown + 'hitsPerPage': 10 + }, + debug: false, // Set debug to true if you want to inspect the dropdown + autocompleteOptions: { + 'autoselect': false + } +}); diff --git a/content/assets/javascripts/facebook_analytics.js b/content/assets/javascripts/facebook_analytics.js new file mode 100644 index 00000000..2eb7121a --- /dev/null +++ b/content/assets/javascripts/facebook_analytics.js @@ -0,0 +1,12 @@ +--- +version: 1 +--- + +!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod? +n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n; +n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0; +t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window, +document,'script','//connect.facebook.net/en_US/fbevents.js'); + +fbq('init', '1689559637958103'); +fbq('track', "PageView"); diff --git a/content/assets/javascripts/google_tagmanager.js b/content/assets/javascripts/google_tagmanager.js new file mode 100644 index 00000000..6b5f2d00 --- /dev/null +++ b/content/assets/javascripts/google_tagmanager.js @@ -0,0 +1,9 @@ +--- +version: 1 +--- + +(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': +new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], +j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= +'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); +})(window,document,'script','dataLayer','GTM-WZCXKT5'); diff --git a/content/assets/javascripts/gtag_analytics.js b/content/assets/javascripts/gtag_analytics.js new file mode 100644 index 00000000..35fafbe9 --- /dev/null +++ b/content/assets/javascripts/gtag_analytics.js @@ -0,0 +1,8 @@ +--- +version: 1 +--- + +window.dataLayer = window.dataLayer || []; +function gtag(){dataLayer.push(arguments);} +gtag('js', new Date()); +gtag('config', 'AW-923339191'); diff --git a/content/assets/javascripts/linkedin_analytics.js b/content/assets/javascripts/linkedin_analytics.js new file mode 100644 index 00000000..57a62add --- /dev/null +++ b/content/assets/javascripts/linkedin_analytics.js @@ -0,0 +1,12 @@ +--- +version: 1 +--- + +_linkedin_partner_id = "30694"; +window._linkedin_data_partner_ids = window._linkedin_data_partner_ids || []; +window._linkedin_data_partner_ids.push(_linkedin_partner_id); + (function(){var s = document.getElementsByTagName("script")[0]; + var b = document.createElement("script"); + b.type = "text/javascript";b.async = true; + b.src = "https://snap.licdn.com/li.lms-analytics/insight.min.js"; + s.parentNode.insertBefore(b, s);})(); diff --git a/content/assets/javascripts/marketo_analytics.js b/content/assets/javascripts/marketo_analytics.js new file mode 100644 index 00000000..421f36fe --- /dev/null +++ b/content/assets/javascripts/marketo_analytics.js @@ -0,0 +1,24 @@ +--- +version: 1 +--- + +(function() { + var didInit = false; + function initMunchkin() { + if(didInit === false) { + didInit = true; + Munchkin.init('194-VVC-221'); + } + } + var s = document.createElement('script'); + s.type = 'text/javascript'; + s.async = true; + s.src = 'https://munchkin.marketo.net/munchkin.js'; + s.onreadystatechange = function() { + if (this.readyState == 'complete' || this.readyState == 'loaded') { + initMunchkin(); + } + }; + s.onload = initMunchkin; + document.getElementsByTagName('head')[0].appendChild(s); +})(); diff --git a/layouts/mermaid.html b/content/assets/javascripts/mermaid.js index fe282055..f22b67ee 100644 --- a/layouts/mermaid.html +++ b/content/assets/javascripts/mermaid.js @@ -1,18 +1,20 @@ -<script type="text/javascript"> - function loadMermaidJsIfNeeded() { +--- +version: 1 +--- + +function loadMermaidJsIfNeeded() { if (document.querySelector('.mermaid') === null) { - return; - } + return; + } var element = document.createElement("script"); element.src = "//cdnjs.cloudflare.com/ajax/libs/mermaid/8.8.0/mermaid.min.js"; element.onload = function(){mermaid.init();}; document.body.appendChild(element); - } +} if (window.addEventListener) - window.addEventListener("load", loadMermaidJsIfNeeded, false); + window.addEventListener("load", loadMermaidJsIfNeeded, false); else if (window.attachEvent) - window.attachEvent("onload", loadMermaidJsIfNeeded); + window.attachEvent("onload", loadMermaidJsIfNeeded); else window.onload = loadMermaidJsIfNeeded; -</script> diff --git a/content/assets/javascripts/toggle_popover.js b/content/assets/javascripts/toggle_popover.js new file mode 100644 index 00000000..0500ef31 --- /dev/null +++ b/content/assets/javascripts/toggle_popover.js @@ -0,0 +1,10 @@ +--- +version: 1 +--- + +$(function () { + $('[data-toggle="popover"]').popover(); + $('.popover-dismiss').popover({ + trigger: 'focus' + }) +}) diff --git a/layouts/analytics.html b/layouts/analytics.html index 0e49e3c4..3f8a9985 100644 --- a/layouts/analytics.html +++ b/layouts/analytics.html @@ -1,66 +1,21 @@ <!-- Facebook --> -<script> - !function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod? - n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n; - n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0; - t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window, - document,'script','//connect.facebook.net/en_US/fbevents.js'); - - fbq('init', '1689559637958103'); - fbq('track', "PageView"); -</script> +<script src="<%= @items['/assets/javascripts/facebook_analytics.*'].path %>"></script> <noscript><img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=1689559637958103&ev=PageView&noscript=1" /></noscript> <!-- Marketo --> -<script type="text/javascript"> - (function() { - var didInit = false; - function initMunchkin() { - if(didInit === false) { - didInit = true; - Munchkin.init('194-VVC-221'); - } - } - var s = document.createElement('script'); - s.type = 'text/javascript'; - s.async = true; - s.src = 'https://munchkin.marketo.net/munchkin.js'; - s.onreadystatechange = function() { - if (this.readyState == 'complete' || this.readyState == 'loaded') { - initMunchkin(); - } - }; - s.onload = initMunchkin; - document.getElementsByTagName('head')[0].appendChild(s); - })(); -</script> +<script src="<%= @items['/assets/javascripts/marketo_analytics.*'].path %>"></script> <!-- Bizible --> <script type="text/javascript" src="https://cdn.bizible.com/scripts/bizible.js" async=""></script> <!-- Global site tag (gtag.js) - Google Ads: 923339191 --> <script async src="https://www.googletagmanager.com/gtag/js?id=AW-923339191"></script> -<script> -window.dataLayer = window.dataLayer || []; -function gtag(){dataLayer.push(arguments);} -gtag('js', new Date()); -gtag('config', 'AW-923339191'); -</script> +<script src="<%= @items['/assets/javascripts/gtag_analytics.*'].path %>"></script> <!-- Linkedin --> -<script type="text/javascript"> -_linkedin_partner_id = "30694"; -window._linkedin_data_partner_ids = window._linkedin_data_partner_ids || []; -window._linkedin_data_partner_ids.push(_linkedin_partner_id); -</script><script type="text/javascript"> - (function(){var s = document.getElementsByTagName("script")[0]; - var b = document.createElement("script"); - b.type = "text/javascript";b.async = true; - b.src = "https://snap.licdn.com/li.lms-analytics/insight.min.js"; - s.parentNode.insertBefore(b, s);})(); -</script> +<script src="<%= @items['/assets/javascripts/linkedin_analytics.*'].path %>"></script> <noscript> <img height="1" width="1" style="display:none;" alt="" src="https://dc.ads.linkedin.com/collect/?pid=30694&fmt=gif" /> </noscript> diff --git a/layouts/csp.html b/layouts/csp.html new file mode 100644 index 00000000..38fa6aff --- /dev/null +++ b/layouts/csp.html @@ -0,0 +1 @@ +<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-eval' https://assets.gitlab-static.net/assets/snowplow/ https://cdn.bizible.com/scripts/bizible.js https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js https://cdnjs.cloudflare.com/ajax/libs/popper.js/ cdnjs.cloudflare.com/ajax/libs/mermaid/ connect.facebook.net/en_US/fbevents.js https://consent.cookiebot.com https://consentcdn.cookiebot.com https://googleads.g.doubleclick.net/pagead/viewthroughconversion/923339191/ https://munchkin.marketo.net/munchkin.js https://script.hotjar.com/ https://snap.licdn.com/li.lms-analytics/insight.min.js https://stackpath.bootstrapcdn.com/bootstrap/ https://static.hotjar.com/c/ https://www.google-analytics.com/analytics.js https://www.googleadservices.com/pagead/conversion_async.js https://www.googletagmanager.com/gtm.js https://cdn.jsdelivr.net/npm/jquery@3.5.1/ https://*.algolia.net https://*.algolianet.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://fonts.googleapis.com https://stackpath.bootstrapcdn.com; object-src 'none'; base-uri 'self'; connect-src 'self' https://*.algolia.net https://*.algolianet.com https://*.mktoresp.com https://snowplow.trx.gitlab.net https://stats.g.doubleclick.net https://www.google-analytics.com https://www.google.com; font-src 'self' https://cdnjs.cloudflare.com https://fonts.gstatic.com; frame-src 'self' https://bid.g.doubleclick.net https://consentcdn.cookiebot.com https://vars.hotjar.com; img-src 'self' https: data:; manifest-src 'self'; media-src 'self'; worker-src 'none';"> diff --git a/layouts/default.html b/layouts/default.html index 4ec6f3ca..9fd650f4 100644 --- a/layouts/default.html +++ b/layouts/default.html @@ -70,19 +70,12 @@ <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.1/umd/popper.min.js" integrity="sha512-ubuT8Z88WxezgSqf3RLuNi5lmjstiJcyezx34yIU2gAHonIi27Na7atqzUZCOoY4CExaoFumzOsFQ2Ch+I/HCw==" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script> - <script> - $(function () { - $('[data-toggle="popover"]').popover(); - $('.popover-dismiss').popover({ - trigger: 'focus' - }) - }) - </script> + <script src="<%= @items['/assets/javascripts/toggle_popover.*'].path %>"></script> <script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script> <script type="application/javascript" src="<%= @items['/assets/javascripts/clipboardjs.*'].path %>"></script> <script type="application/javascript" src="<%= @items['/assets/javascripts/badges.*'].path %>"></script> <%= render '/docsearch.*' %> - <%= render '/mermaid.*' %> + <script src="<%= @items['/assets/javascripts/mermaid.*'].path %>"></script> <% if is_production? %> <%# Add analytics only in production %> <%= render '/analytics.*' %> diff --git a/layouts/docsearch.html b/layouts/docsearch.html index 88dd752e..21037f14 100644 --- a/layouts/docsearch.html +++ b/layouts/docsearch.html @@ -1,19 +1,3 @@ <!-- Algolia docsearch https://community.algolia.com/docsearch/ --> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js"></script> -<script type="text/javascript"> -var search = docsearch({ - apiKey: 'ce1690e1421303458a1fcbea0cc4a927', - indexName: 'gitlab', - inputSelector: '.docsearch', - algoliaOptions: { - // Filter by tags as described in https://github.com/algolia/docsearch-configs/blob/master/configs/gitlab.json - 'filters': "tags:gitlab<score=4> OR tags:omnibus<score=3> OR tags:runner<score=2> OR tags:charts<score=1>", - // Number of results shown in the search dropdown - 'hitsPerPage': 10 - }, - debug: false, // Set debug to true if you want to inspect the dropdown - autocompleteOptions: { - 'autoselect': false - } -}); -</script> +<script src="<%= @items['/assets/javascripts/docsearch.*'].path %>"></script> diff --git a/layouts/feedback.html b/layouts/feedback.html index 5c69e7eb..235ffdd1 100644 --- a/layouts/feedback.html +++ b/layouts/feedback.html @@ -60,30 +60,7 @@ <div id="disqus_thread" class="disqus-comments-gitlab"></div> - <script> - var disqus_config = function () { - this.page.url = '<%= @config[:base_url] %><%= @item.identifier.without_ext + '.html' %>'; - this.page.title = '<%= @item.key?(:title) ? "#{item[:title]} - GitLab Documentation" : "GitLab Documentation" %>'; - <% if @item[:disqus_identifier] %> - this.page.identifier = '<%= @item[:disqus_identifier] %>'; - <% else %> - this.page.identifier = '<%= @config[:base_url] %><%= @item.identifier.without_ext + '.html' %>'; - <% end %> - }; - - var is_disqus_loaded = false; - window.loadDisqus = function() { - if (!is_disqus_loaded){ - is_disqus_loaded = true; - var disqusThread = document.getElementById('disqus_thread'); - var d = document, s = d.createElement('script'); - disqusThread.innerHTML = ''; - s.src = 'https://gitlab-docs.disqus.com/embed.js'; - s.setAttribute('data-timestamp', +new Date()); - (d.head || d.body).appendChild(s); - } - }; - </script> + <script src="<%= @items['/assets/javascripts/disqus.*'].path %>"></script> <script src="<%= @items['/frontend/feedback/feedback.*'].path %>"></script> <noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a></noscript> <% end %> diff --git a/layouts/head.html b/layouts/head.html index 71871884..bffb09fa 100644 --- a/layouts/head.html +++ b/layouts/head.html @@ -20,6 +20,13 @@ <% else %> <meta name="docsearch:version" content="master" /> <% end %> + +<!-- Enable CSP headers --> +<% unless ENV['DISABLE_CSP'] %> +<%= render '/csp.*' %> +<% end %> +<!-- End of CSP headers --> + <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous"> <link rel="stylesheet" href="<%= @items['/assets/stylesheets/stylesheet.*'].path %>"> <link rel="stylesheet" href="<%= @items['/assets/stylesheets/highlight.*'].path %>"> @@ -38,11 +45,7 @@ <script id="Cookiebot" src="https://consent.cookiebot.com/uc.js" data-cbid="36a06ac5-ddb4-4f91-8337-067ad19ad8d5" type="text/javascript"></script> <!-- Google Tag Manager --> - <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': - new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], - j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= - 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); - })(window,document,'script','dataLayer','GTM-WZCXKT5');</script> + <script src="<%= @items['/assets/javascripts/google_tagmanager.*'].path %>"></script> <!-- End Google Tag Manager --> <!-- Google webmasters verification --> |