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,8 +69,29 @@ class BaseHandler(RequestHandler):
|
|||||||
def finish(self, *args, **kwargs):
|
def finish(self, *args, **kwargs):
|
||||||
"""Roll back any uncommitted transactions from the handler."""
|
"""Roll back any uncommitted transactions from the handler."""
|
||||||
self.db.rollback()
|
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):
|
def set_default_headers(self):
|
||||||
"""
|
"""
|
||||||
Set any headers passed as tornado_settings['headers'].
|
Set any headers passed as tornado_settings['headers'].
|
||||||
@@ -78,7 +99,8 @@ class BaseHandler(RequestHandler):
|
|||||||
By default sets Content-Security-Policy of frame-ancestors 'self'.
|
By default sets Content-Security-Policy of frame-ancestors 'self'.
|
||||||
"""
|
"""
|
||||||
headers = self.settings.get('headers', {})
|
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():
|
for header_name, header_content in headers.items():
|
||||||
self.set_header(header_name, header_content)
|
self.set_header(header_name, header_content)
|
||||||
|
|
||||||
@@ -395,6 +417,7 @@ class PrefixRedirectHandler(BaseHandler):
|
|||||||
self.hub.server.base_url, path,
|
self.hub.server.base_url, path,
|
||||||
), permanent=False)
|
), permanent=False)
|
||||||
|
|
||||||
|
|
||||||
class UserSpawnHandler(BaseHandler):
|
class UserSpawnHandler(BaseHandler):
|
||||||
"""Requests to /user/name handled by the Hub
|
"""Requests to /user/name handled by the Hub
|
||||||
should result in spawning the single-user server and
|
should result in spawning the single-user server and
|
||||||
@@ -432,6 +455,15 @@ class UserSpawnHandler(BaseHandler):
|
|||||||
{'next': self.request.uri,
|
{'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 = [
|
default_handlers = [
|
||||||
(r'/user/([^/]+)/?.*', UserSpawnHandler),
|
(r'/user/([^/]+)/?.*', UserSpawnHandler),
|
||||||
|
(r'/security/csp-report', CSPReportHandler),
|
||||||
]
|
]
|
||||||
|
@@ -66,7 +66,9 @@ def api_request(app, *api_path, **kwargs):
|
|||||||
method = kwargs.pop('method', 'get')
|
method = kwargs.pop('method', 'get')
|
||||||
f = getattr(requests, method)
|
f = getattr(requests, method)
|
||||||
resp = f(url, **kwargs)
|
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
|
return resp
|
||||||
|
|
||||||
def test_auth_api(app):
|
def test_auth_api(app):
|
||||||
|
Reference in New Issue
Block a user