mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-16 06:22:59 +00:00
add CSP report handler
This commit is contained in:
@@ -69,7 +69,28 @@ class BaseHandler(RequestHandler):
|
||||
def finish(self, *args, **kwargs):
|
||||
"""Roll back any uncommitted transactions from the handler."""
|
||||
self.db.rollback()
|
||||
super(BaseHandler, self).finish(*args, **kwargs)
|
||||
super().finish(*args, **kwargs)
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Security policies
|
||||
#---------------------------------------------------------------
|
||||
|
||||
@property
|
||||
def csp_report_uri(self):
|
||||
return self.settings.get('csp_report_uri',
|
||||
url_path_join(self.hub.server.base_url, 'security/csp-report')
|
||||
)
|
||||
|
||||
@property
|
||||
def content_security_policy(self):
|
||||
"""The default Content-Security-Policy header
|
||||
|
||||
Can be overridden by defining Content-Security-Policy in settings['headers']
|
||||
"""
|
||||
return '; '.join([
|
||||
"frame-ancestors 'self'",
|
||||
"report-uri " + self.csp_report_uri,
|
||||
])
|
||||
|
||||
def set_default_headers(self):
|
||||
"""
|
||||
@@ -78,7 +99,8 @@ class BaseHandler(RequestHandler):
|
||||
By default sets Content-Security-Policy of frame-ancestors 'self'.
|
||||
"""
|
||||
headers = self.settings.get('headers', {})
|
||||
headers.setdefault('Content-Security-Policy', "frame-ancestors 'self'")
|
||||
headers.setdefault("Content-Security-Policy", self.content_security_policy)
|
||||
|
||||
for header_name, header_content in headers.items():
|
||||
self.set_header(header_name, header_content)
|
||||
|
||||
@@ -395,6 +417,7 @@ class PrefixRedirectHandler(BaseHandler):
|
||||
self.hub.server.base_url, path,
|
||||
), permanent=False)
|
||||
|
||||
|
||||
class UserSpawnHandler(BaseHandler):
|
||||
"""Requests to /user/name handled by the Hub
|
||||
should result in spawning the single-user server and
|
||||
@@ -432,6 +455,15 @@ class UserSpawnHandler(BaseHandler):
|
||||
{'next': self.request.uri,
|
||||
}))
|
||||
|
||||
class CSPReportHandler(BaseHandler):
|
||||
'''Accepts a content security policy violation report'''
|
||||
@web.authenticated
|
||||
def post(self):
|
||||
'''Log a content security policy violation report'''
|
||||
self.log.warn("Content security violation: %s",
|
||||
self.request.body.decode('utf8', 'replace'))
|
||||
|
||||
default_handlers = [
|
||||
(r'/user/([^/]+)/?.*', UserSpawnHandler),
|
||||
(r'/security/csp-report', CSPReportHandler),
|
||||
]
|
||||
|
@@ -66,7 +66,9 @@ def api_request(app, *api_path, **kwargs):
|
||||
method = kwargs.pop('method', 'get')
|
||||
f = getattr(requests, method)
|
||||
resp = f(url, **kwargs)
|
||||
assert resp.headers['Content-Security-Policy'] == "frame-ancestors 'self'"
|
||||
assert "frame-ancestors 'self'" in resp.headers['Content-Security-Policy']
|
||||
assert ujoin(app.hub.server.base_url, "security/csp-report") in resp.headers['Content-Security-Policy']
|
||||
assert 'http' not in resp.headers['Content-Security-Policy']
|
||||
return resp
|
||||
|
||||
def test_auth_api(app):
|
||||
|
Reference in New Issue
Block a user