Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Unsafe inline scripts by adding nonce as CSP #3832

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contributing-frontend.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ A react root element (or the first container element within it) must include the
If your react component isn't dependent on any Knockout observables or Vue components you can integrate it by adding a small script and a component reference directly in the HTML code. The following example integrates MyComponent as a react root in an HTML/mako file.

```HTML
<script type="text/javascript">
<script type="text/javascript" nonce="${ request.csp_nonce }">
(function () {
window.createReactComponents('#some-container');
})();
Expand Down
15 changes: 15 additions & 0 deletions desktop/core/src/desktop/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from __future__ import absolute_import

from builtins import object
import base64
import inspect
import json
import logging
Expand Down Expand Up @@ -972,6 +973,20 @@ def __call__(self, request):
return self.get_response(request)


class CSPMiddleware:
def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
nonce = base64.b64encode(os.urandom(16)).decode('utf-8')
request.csp_nonce = nonce # Make nonce available in the request
response = self.get_response(request)
csp_policy = "script-src 'nonce-{}' 'strict-dynamic' 'unsafe-eval';".format(nonce)

response['Content-Security-Policy'] = csp_policy
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is configurable in the .ini, secure_content_security_policy, I believe you need to merge it somehow here instead. You could potentially just string replace 'nonce' with 'nonce-{}'...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And add 'strict-dynamic' to the default of the config value.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that there's already a def ContentSecurityPolicyMiddleware in the same file..

return response


class CacheControlMiddleware(MiddlewareMixin):
def __init__(self, get_response):
self.get_response = get_response
Expand Down
1 change: 1 addition & 0 deletions desktop/core/src/desktop/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@
# 'axes.middleware.FailedLoginMiddleware',
'desktop.middleware.MimeTypeJSFileFixStreamingMiddleware',
'crequest.middleware.CrequestMiddleware',
'desktop.middleware.CSPMiddleware',
]

# if os.environ.get(ENV_DESKTOP_DEBUG):
Expand Down
2 changes: 1 addition & 1 deletion desktop/core/src/desktop/templates/common_footer.mako
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ ${ smart_unicode(login_modal(request).content) | n,unicode }

<iframe id="zoomDetectFrame" style="width: 250px; display: none" ></iframe>

${ commonHeaderFooterComponents.footer(messages) }
${ commonHeaderFooterComponents.footer(messages, csp_nonce) }

</body>
</html>
32 changes: 17 additions & 15 deletions desktop/core/src/desktop/templates/common_header.mako
Original file line number Diff line number Diff line change
Expand Up @@ -142,24 +142,26 @@ if USE_NEW_EDITOR.get():
<%
global_constants_url = '/desktop/globalJsConstants.js?v=' + hue_version()
%>
<script src="${global_constants_url}"></script>
<script nonce="${ request.csp_nonce }" src="${global_constants_url}"></script>
% endif

% if not conf.DEV.get():
<script src="${ static('desktop/js/hue.errorcatcher.js') }"></script>
<script nonce="${ request.csp_nonce }" src="${ static('desktop/js/hue.errorcatcher.js') }"></script>
% endif

% for bundle in get_hue_bundles('login' if section == 'login' else 'hue', 'LOGIN' if section == 'login' else 'DEFAULT'):
${ render_bundle(bundle, config='LOGIN' if section == 'login' else 'DEFAULT') | n,unicode }
## Instead of trying to assign to a variable, directly operate within expressions.
${render_bundle(bundle, config='LOGIN' if section == 'login' else 'DEFAULT').replace('<script ', '<script nonce="' + request.csp_nonce + '" ') | n}
% endfor

<script src="${ static('desktop/js/bootstrap-tooltip.js') }"></script>
<script src="${ static('desktop/js/bootstrap-typeahead-touchscreen.js') }"></script>
<script src="${ static('desktop/ext/js/bootstrap-better-typeahead.min.js') }"></script>
<script src="${ static('desktop/js/popover-extra-placements.js') }"></script>
<script src="${ static('desktop/ext/js/moment-with-locales.min.js') }"></script>
<script src="${ static('desktop/ext/js/moment-timezone-with-data.min.js') }" type="text/javascript" charset="utf-8"></script>
<script src="${ static('desktop/ext/js/tzdetect.js') }" type="text/javascript" charset="utf-8"></script>

<script nonce="${ request.csp_nonce }" src="${ static('desktop/js/bootstrap-tooltip.js') }"></script>
<script nonce="${ request.csp_nonce }" src="${ static('desktop/js/bootstrap-typeahead-touchscreen.js') }"></script>
<script nonce="${ request.csp_nonce }" src="${ static('desktop/ext/js/bootstrap-better-typeahead.min.js') }"></script>
<script nonce="${ request.csp_nonce }" src="${ static('desktop/js/popover-extra-placements.js') }"></script>
<script nonce="${ request.csp_nonce }" src="${ static('desktop/ext/js/moment-with-locales.min.js') }"></script>
<script nonce="${ request.csp_nonce }" src="${ static('desktop/ext/js/moment-timezone-with-data.min.js') }" type="text/javascript" charset="utf-8"></script>
<script nonce="${ request.csp_nonce }" src="${ static('desktop/ext/js/tzdetect.js') }" type="text/javascript" charset="utf-8"></script>

% if user.is_authenticated:
${ hueAceAutocompleter.hueAceAutocompleter() }
Expand All @@ -168,9 +170,9 @@ if USE_NEW_EDITOR.get():
${ commonHeaderFooterComponents.header_pollers(user, is_s3_enabled, apps) }

% if user.is_authenticated:
<script src="${ static('desktop/ext/js/localforage.min.js') }"></script>
<script nonce="${ request.csp_nonce }" src="${ static('desktop/ext/js/localforage.min.js') }"></script>

<script type="text/javascript">
<script nonce="${ request.csp_nonce }" type="text/javascript">
$(document).ready(function () {
localforage.config({
version: 1.0,
Expand Down Expand Up @@ -200,7 +202,7 @@ ${ hueIcons.symbols() }


% if hasattr(request, 'environ') and request.environ.get("PATH_INFO").find("/hue/") < 0:
<script>
<script nonce="${ request.csp_nonce }">
window.location.replace(window.HUE_BASE_URL || "/");
</script>
% endif
Expand Down Expand Up @@ -556,8 +558,8 @@ ${ hueIcons.symbols() }
</ul>

<!-- UserVoice JavaScript SDK -->
<script>(function(){var uv=document.createElement('script');uv.type='text/javascript';uv.async=true;uv.src='//widget.uservoice.com/8YpsDfIl1Y2sNdONoLXhrg.js';var s=document.getElementsByTagName('script')[0];s.parentNode.insertBefore(uv,s)})()</script>
<script>
<script nonce="${ request.csp_nonce }" >(function(){var uv=document.createElement('script');uv.type='text/javascript';uv.async=true;uv.src='//widget.uservoice.com/8YpsDfIl1Y2sNdONoLXhrg.js';var s=document.getElementsByTagName('script')[0];s.parentNode.insertBefore(uv,s)})()</script>
<script nonce="${ request.csp_nonce }">
UserVoice = window.UserVoice || [];
function showClassicWidget() {
UserVoice.push(['showLightbox', 'classic_widget', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ else:
%>

<%def name="header_pollers(user, is_s3_enabled, apps)">
<script type="text/javascript">
<script nonce="${request.csp_nonce}" type="text/javascript">
Dropzone.autoDiscover = false;
moment.locale(window.navigator.userLanguage || window.navigator.language);
localeFormat = function (time) {
Expand Down Expand Up @@ -230,7 +230,7 @@ else:

</%def>

<%def name="footer(messages)">
<%def name="footer(messages, csp_nonce)">

<div id="progressStatus" class="uploadstatus well hide">
<h4>${ _('Upload progress') }</h4>
Expand Down Expand Up @@ -267,7 +267,7 @@ else:

<div class="clipboard-content"></div>

<script type="text/javascript">
<script nonce="${request.csp_nonce if request else csp_nonce}" type="text/javascript">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the 2 different options?


$(document).ready(function () {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ else:
</div>
</script>

<script type="text/javascript">
<script type="text/javascript" nonce="${request.csp_nonce}">
(function () {
var WHEEL_RADIUS = 75;
var PLUS_ICON_RADIUS = 27.859; // FA-5X
Expand Down Expand Up @@ -353,7 +353,7 @@ else:
</div>
</script>

<script type="text/javascript">
<script type="text/javascript" nonce="${request.csp_nonce}">
(function () {

function DownloadResultsViewModel (params, element) {
Expand Down Expand Up @@ -778,7 +778,7 @@ else:
</div>
</script>

<script type="text/javascript">
<script type="text/javascript" nonce="${request.csp_nonce}">
(function () {

function AceKeyboardShortcutsViewModel () {
Expand Down
18 changes: 9 additions & 9 deletions desktop/core/src/desktop/templates/config_ko_components.mako
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ else:
</div>
</script>

<script type="text/javascript">
<script nonce="${request.csp_nonce}" type="text/javascript">
(function () {
function MultiGroupAlternative(alt, params, initiallyChecked) {
var self = this;
Expand Down Expand Up @@ -215,7 +215,7 @@ else:
})();
</script>

<script type="text/javascript">
<script nonce="${request.csp_nonce}" type="text/javascript">
(function () {

function PropertySelectorViewModel(params) {
Expand Down Expand Up @@ -297,7 +297,7 @@ else:
})();
</script>

<script type="text/html" id="property">
<script nonce="${request.csp_nonce}" type="text/html" id="property">
<div class="config-property" data-bind="visibleOnHover: { selector: '.hover-actions' }">
<label class="config-label" data-bind="click: function(data, event){ $(event.target).siblings('.config-controls').find('.config-property-add-value a').click(); }">
<!-- ko text: label --><!-- /ko --><!-- ko if: typeof helpText !== 'undefined' --><div class="property-help" data-bind="tooltip: { title: helpText(), placement: 'bottom' }"><i class="fa fa-question-circle-o"></i></div><!-- /ko -->
Expand Down Expand Up @@ -365,7 +365,7 @@ else:
<input type="text" class="input-small" data-bind="numericTextInput: { value: value, precision: 0, allowEmpty: true }" /> <select class="input-mini" data-bind="options: units, value: selectedUnit"></select>
</script>

<script type="text/javascript">
<script nonce="${request.csp_nonce}" type="text/javascript">
(function () {
var JVM_MEM_PATTERN = /([0-9]+)([MG])$/;
var UNITS = {'MB': 'M', 'GB': 'G'};
Expand Down Expand Up @@ -427,7 +427,7 @@ else:
<div class="clearfix"></div>
</script>

<script type="text/javascript">
<script nonce="${request.csp_nonce}" type="text/javascript">
(function () {

function KeyValueListInputViewModel(params) {
Expand Down Expand Up @@ -530,7 +530,7 @@ else:
</div>
</script>

<script type="text/javascript">
<script nonce="${request.csp_nonce}" type="text/javascript">
(function () {

function NameValueListInputViewModel(params) {
Expand Down Expand Up @@ -595,7 +595,7 @@ else:
</div>
</script>

<script type="text/javascript">
<script nonce="${request.csp_nonce}" type="text/javascript">
(function () {

function FunctionListInputViewModel(params) {
Expand Down Expand Up @@ -649,7 +649,7 @@ else:
</div>
</script>

<script type="text/javascript">
<script nonce="${request.csp_nonce}" type="text/javascript">
(function () {

var identifyType = function (path) {
Expand Down Expand Up @@ -729,7 +729,7 @@ else:
</div>
</script>

<script type="text/javascript">
<script nonce="${request.csp_nonce}" type="text/javascript">
(function () {
function CsvListInputViewModel(params) {
this.valueObservable = params.value;
Expand Down
42 changes: 22 additions & 20 deletions desktop/core/src/desktop/templates/hue.mako
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@

% if conf.COLLECT_USAGE.get():
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=${conf.GTAG_ID.get()}"></script>
<script>
<script nonce="${request.csp_nonce}" async src="https://www.googletagmanager.com/gtag/js?id=${conf.GTAG_ID.get()}"></script>
<script nonce="${request.csp_nonce}" >
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
Expand Down Expand Up @@ -93,10 +93,10 @@
<%
global_constants_url = '/desktop/globalJsConstants.js?v=' + hue_version()
%>
<script src="${global_constants_url}"></script>
<script nonce="${request.csp_nonce}" src="${global_constants_url}"></script>

% if not conf.DEV.get():
<script src="${ static('desktop/js/hue.errorcatcher.js') }"></script>
<script nonce="${request.csp_nonce}" src="${ static('desktop/js/hue.errorcatcher.js') }"></script>
% endif
</head>

Expand All @@ -110,8 +110,8 @@
</ul>

<!-- UserVoice JavaScript SDK -->
<script>(function(){var uv=document.createElement('script');uv.type='text/javascript';uv.async=true;uv.src='//widget.uservoice.com/8YpsDfIl1Y2sNdONoLXhrg.js';var s=document.getElementsByTagName('script')[0];s.parentNode.insertBefore(uv,s)})()</script>
<script>
<script nonce="${request.csp_nonce}" >(function(){var uv=document.createElement('script');uv.type='text/javascript';uv.async=true;uv.src='//widget.uservoice.com/8YpsDfIl1Y2sNdONoLXhrg.js';var s=document.getElementsByTagName('script')[0];s.parentNode.insertBefore(uv,s)})()</script>
<script nonce="${request.csp_nonce}" >
UserVoice = window.UserVoice || [];
function showClassicWidget() {
UserVoice.push(['showLightbox', 'classic_widget', {
Expand Down Expand Up @@ -295,24 +295,26 @@ ${ hueIcons.symbols() }
</div>
${ commonshare() | n,unicode }

% for bundle in get_hue_bundles('hue'):
${ render_bundle(bundle) | n,unicode }
% for bundle in get_hue_bundles('login' if section == 'login' else 'hue', 'LOGIN' if section == 'login' else 'DEFAULT'):
<% text = render_bundle(bundle, config='LOGIN' if section == 'login' else 'DEFAULT').replace('<script ', '<script nonce="' + request.csp_nonce + '" ') %>
${ text | n}
% endfor

<script src="${ static('desktop/js/polyfills.js') }"></script>
<script src="${ static('desktop/ext/js/tether.js') }"></script>
<script src="${ static('desktop/ext/js/moment-with-locales.min.js') }"></script>
<script src="${ static('desktop/ext/js/moment-timezone-with-data.min.js') }"></script>
<script src="${ static('desktop/ext/js/tzdetect.js') }"></script>

<script src="${ static('desktop/ext/js/bootstrap-fileupload.js') }"></script>
<script src="${ static('desktop/js/bootstrap-tooltip.js') }"></script>
<script src="${ static('desktop/js/bootstrap-typeahead-touchscreen.js') }"></script>
<script src="${ static('desktop/ext/js/bootstrap-better-typeahead.min.js') }"></script>
<script nonce="${request.csp_nonce}" src="${ static('desktop/js/polyfills.js') }"></script>
<script nonce="${request.csp_nonce}" src="${ static('desktop/ext/js/tether.js') }"></script>
<script nonce="${request.csp_nonce}" src="${ static('desktop/ext/js/moment-with-locales.min.js') }"></script>
<script nonce="${request.csp_nonce}" src="${ static('desktop/ext/js/moment-timezone-with-data.min.js') }"></script>
<script nonce="${request.csp_nonce}" src="${ static('desktop/ext/js/tzdetect.js') }"></script>

<script src="${ static('desktop/js/share2.vm.js') }"></script>
<script nonce="${request.csp_nonce}" src="${ static('desktop/ext/js/bootstrap-fileupload.js') }"></script>
<script nonce="${request.csp_nonce}" src="${ static('desktop/js/bootstrap-tooltip.js') }"></script>
<script nonce="${request.csp_nonce}" src="${ static('desktop/js/bootstrap-typeahead-touchscreen.js') }"></script>
<script nonce="${request.csp_nonce}" src="${ static('desktop/ext/js/bootstrap-better-typeahead.min.js') }"></script>

<script>
<script nonce="${request.csp_nonce}" src="${ static('desktop/js/share2.vm.js') }"></script>

<script nonce="${request.csp_nonce}" >
var shareViewModel = initSharing("#documentShareModal");
</script>

Expand All @@ -334,7 +336,7 @@ ${ smart_unicode(login_modal(request).content) | n,unicode }

<iframe id="zoomDetectFrame" style="width: 250px; display: none" ></iframe>

${ commonHeaderFooterComponents.footer(messages) }
${ commonHeaderFooterComponents.footer(messages, csp_nonce) }

## This includes common knockout templates that are shared with the Job Browser page and the mini job browser panel
## available in the upper right corner throughout Hue
Expand Down
4 changes: 2 additions & 2 deletions desktop/core/src/desktop/templates/hue_ace_autocompleter.mako
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ else:
</script>


<script type="text/javascript">
<script type="text/javascript" nonce="${request.csp_nonce}">
(function () {

var aceUtil = ace.require('ace/autocomplete/util');
Expand Down Expand Up @@ -722,7 +722,7 @@ else:
<!-- /ko -->
</script>

<script type="text/javascript">
<script type="text/javascript" nonce="${request.csp_nonce}">
(function () {

var COMMENT_LOAD_DELAY = 1500;
Expand Down
4 changes: 2 additions & 2 deletions desktop/core/src/desktop/templates/login.mako
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ ${ commonheader(_("Welcome to Hue"), "login", user, request, "50px", True, True)
% endif
</div>

<script>
<script nonce="${ request.csp_nonce }">
$(document).ready(function () {
$("form").on("submit", function () {
window.setTimeout(function () {
Expand Down Expand Up @@ -195,4 +195,4 @@ ${ commonheader(_("Welcome to Hue"), "login", user, request, "50px", True, True)
});
</script>

${ commonfooter(None, messages) | n,unicode }
${ commonfooter(None, messages, False, request.csp_nonce) | n,unicode }
2 changes: 1 addition & 1 deletion desktop/core/src/desktop/templates/login_modal.mako
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
</div>
</div>

<script>
<script nonce="${request.csp_nonce}">
$(document).ready(function () {
$('.reload').on('click', function () {
location.reload();
Expand Down
Loading
Loading