This is an automated email from the ASF dual-hosted git repository.
brondsem pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git
The following commit(s) were added to refs/heads/master by this push:
new 45275ae41 [#8479] modified exisinting logic on settings and added
support for script-src
45275ae41 is described below
commit 45275ae41bacf0143702ec736a2afde188e3918f
Author: Guillermo Cruz <[email protected]>
AuthorDate: Thu Nov 17 11:09:51 2022 -0600
[#8479] modified exisinting logic on settings and added support for
script-src
---
Allura/allura/lib/custom_middleware.py | 47 ++++++++++++++++++-----------
Allura/allura/tests/functional/test_root.py | 47 ++++++++++++++++++-----------
Allura/development.ini | 17 +++++++++--
3 files changed, 74 insertions(+), 37 deletions(-)
diff --git a/Allura/allura/lib/custom_middleware.py
b/Allura/allura/lib/custom_middleware.py
index 1968ad31c..4ea99c6d3 100644
--- a/Allura/allura/lib/custom_middleware.py
+++ b/Allura/allura/lib/custom_middleware.py
@@ -468,43 +468,56 @@ class ContentSecurityPolicyMiddleware:
def __call__(self, environ, start_response):
req = Request(environ)
resp = req.get_response(self.app)
- rules = resp.headers.getall('Content-Security-Policy')
- report_rules =
resp.headers.getall('Content-Security-Policy-Report-Only')
- report_mode = asbool(self.config.get('csp.report_mode',False))
- report_enforce_mode =
asbool(self.config.get('csp.report_enforce_mode',False))
+ rules = set(resp.headers.getall('Content-Security-Policy'))
+ report_rules =
set(resp.headers.getall('Content-Security-Policy-Report-Only'))
report_uri = self.config.get('csp.report_uri', None)
report_uri_enforce = self.config.get('csp.report_uri_enforce', None)
if rules:
resp.headers.pop('Content-Security-Policy')
+
if report_rules:
resp.headers.pop('Content-Security-Policy-Report-Only')
- if report_mode and report_uri:
- report_rules.append(f'report-uri {report_uri}')
if self.config['base_url'].startswith('https'):
- rules.append('upgrade-insecure-requests')
+ rules.add('upgrade-insecure-requests')
- if report_enforce_mode and report_uri_enforce:
- rules.append(f'report-uri {report_uri_enforce}')
if self.config.get('csp.frame_sources'):
- if report_mode:
- report_rules.append(f"frame-src
{self.config['csp.frame_sources']}")
+ if asbool(self.config.get('csp.frame_sources_enforce',False)):
+ rules.add(f"frame-src {self.config['csp.frame_sources']}")
else:
- rules.append(f"frame-src {self.config['csp.frame_sources']}")
+ report_rules.add(f"frame-src
{self.config['csp.frame_sources']}")
if self.config.get('csp.form_action_urls'):
- if report_mode:
- report_rules.append(f"form-action
{self.config['csp.form_action_urls']}")
+ if asbool(self.config.get('csp.form_actions_enforce',False)):
+ rules.add(f"form-action {self.config['csp.form_action_urls']}")
+ else:
+ report_rules.add(f"form-action
{self.config['csp.form_action_urls']}")
+
+ if self.config.get('csp.script_src'):
+ script_srcs = self.config['csp.script_src']
+ """
+ Sometimes you might have the need to build custom values from
inside a controller and pass it
+ to the middleware. In this case we pass a custom list of domains
from google that can't be built
+ directly in here.
+ """
+ if environ.get('csp_script_domains',''):
+ script_srcs = f"{script_srcs} {'
'.join(environ['csp_script_domains'])}"
+
+ if asbool(self.config.get('csp.script_src_enforce',False)):
+ rules.add(f"script-src {script_srcs}
{self.config.get('csp.script_src.extras','')}")
else:
- rules.append(f"form-action
{self.config['csp.form_action_urls']}")
+ report_rules.add(f"script-src {script_srcs}
{self.config.get('csp.script_src.extras','')}")
+
- rules.append("object-src 'none'")
- rules.append("frame-ancestors 'self'")
+ rules.add("object-src 'none'")
+ rules.add("frame-ancestors 'self'")
if rules:
+ rules.add(f'report-uri {report_uri_enforce}')
resp.headers.add('Content-Security-Policy', '; '.join(rules))
if report_rules:
+ report_rules.add(f'report-uri {report_uri}')
resp.headers.add('Content-Security-Policy-Report-Only', ';
'.join(report_rules))
return resp(environ, start_response)
diff --git a/Allura/allura/tests/functional/test_root.py
b/Allura/allura/tests/functional/test_root.py
index 6b5f56ca3..dc5dace07 100644
--- a/Allura/allura/tests/functional/test_root.py
+++ b/Allura/allura/tests/functional/test_root.py
@@ -186,33 +186,46 @@ class TestRootController(TestController):
r = self.app.get('/error/document')
r.mustcontain("We're sorry but we weren't able to process")
+ @mock.patch.dict(tg.config, {'csp.frame_sources_enforce': True, \
+ 'csp.report_uri_enforce':
'https://example.com/r/d/csp/enforce', \
+ 'csp.form_actions_enforce': True,
+ 'csp.script_src_enforce': True})
def test_headers(self):
resp = self.app.get('/p')
- assert resp.headers.getall('Content-Security-Policy')[0] == ';
'.join(["frame-src 'self' www.youtube-nocookie.com",
- "form-action
'self'",
- "object-src
'none'",
-
"frame-ancestors 'self'"])
+ expected_headers = "form-action 'self'; frame-src 'self'
www.youtube-nocookie.com; object-src 'none';"
+ expected_headers += "frame-ancestors 'self'; report-uri
https://example.com/r/d/csp/enforce; script-src 'self;"
+ csp_headers = resp.headers.getall('Content-Security-Policy')[0]
+ assert all([h.strip() in csp_headers for h in
expected_headers.split(';')])
+ @mock.patch.dict(tg.config, {'csp.frame_sources_enforce': True,
+ 'csp.report_uri_enforce':
'https://example.com/r/d/csp/enforce'})
def test_headers_config(self):
resp = self.app.get('/p')
- assert "frame-src 'self' www.youtube-nocookie.com;" in
resp.headers.getall('Content-Security-Policy')[0]
+ assert "frame-src 'self' www.youtube-nocookie.com" in
resp.headers.getall('Content-Security-Policy')[0]
- @mock.patch.dict(tg.config, {'csp.report_mode': True, 'csp.report_uri':
'https://example.com/r/d/csp/reportOnly'})
+ @mock.patch.dict(tg.config, {'csp.report_uri':
'https://example.com/r/d/csp/reportOnly'})
def test_headers_report(self):
resp = self.app.get('/p/wiki/Home/')
- assert resp.headers.getall('Content-Security-Policy-Report-Only')[0]
== '; '.join([
- "report-uri https://example.com/r/d/csp/reportOnly",
- "frame-src 'self' www.youtube-nocookie.com",
- "form-action 'self'",
- ])
-
- @mock.patch.dict(tg.config, {'csp.report_uri_enforce':
'https://example.com/r/d/csp/enforce', 'csp.report_enforce_mode': True})
- def test_headers_report_enforce(self):
- resp = self.app.get('/p/wiki/Home/')
- assert "report-uri https://example.com/r/d/csp/enforce; frame-src
'self' www.youtube-nocookie.com;" \
- in resp.headers.getall('Content-Security-Policy')[0]
+ expected_headers = "report-uri https://example.com/r/d/csp/reportOnly;"
+ expected_headers += "frame-src 'self' www.youtube-nocookie.com;
script-src 'self' ;"
+ expected_headers += "form-action 'self'"
+
+ csp_headers =
resp.headers.getall('Content-Security-Policy-Report-Only')[0]
+ assert all([h.strip() in csp_headers for h in
expected_headers.split(';')])
+ @mock.patch.dict(tg.config, {'csp.report_uri_enforce':
'https://example.com/r/d/csp/enforce', 'csp.frame_sources_enforce': True})
+ def test_headers_frame_sources_enforce(self):
+ resp = self.app.get('/p/wiki/Home/')
+ expected_headers = "report-uri https://example.com/r/d/csp/enforce;"
+ expected_headers += "frame-src 'self' www.youtube-nocookie.com;"
+ expected_headers += "object-src 'none'"
+ expected_report_headers = "script-src 'self' ; form-action 'self';
report-uri None"
+ csp_headers = resp.headers.getall('Content-Security-Policy')[0]
+ csp_report_headers =
resp.headers.getall('Content-Security-Policy-Report-Only')[0]
+ assert all([h.strip() in csp_headers for h in
expected_headers.split(';')])
+ assert all([h.strip() in csp_report_headers for h in
expected_report_headers.split(';')])
+
class TestRootWithSSLPattern(TestController):
def setup_method(self, method):
with td.patch_middleware_config({'force_ssl.pattern': '^/auth'}):
diff --git a/Allura/development.ini b/Allura/development.ini
index 3ded52b9b..171240692 100644
--- a/Allura/development.ini
+++ b/Allura/development.ini
@@ -658,20 +658,31 @@ userstats.count_lines_of_code = true
; Minutes to cache saved search "bins" numbers. 0 will disable entirely, so
caches are permanent
;forgetracker.bin_cache_expire = 60
+;
; CSP Headers
-; enable report mode
-; csp.report_mode = false
-; csp.report_enforce_mode = false
+;
+
; csp.report_uri = https://example.com/r/d/csp/reportOnly
; csp.report_uri_enforce = https://example.com/r/d/csp/enforce
+; to enable enforce mode on frame-src
+;csp.frame_sources_enforce = true
+
; frame-src list of valid sources for loading frames
csp.frame_sources = 'self' www.youtube-nocookie.com
+; to enable enforce mode on form-action
+; csp.form_actions_enforce = true
+
; form-action valid sources that can be used as an HTML <form> action
csp.form_action_urls = 'self'
+; to enable enforce mode on script-src
+; csp.script_src_enforce = true
+
+csp.script_src = 'self'
+
;
; Settings for comment reactions
;