Skip to content

Commit 0be7294

Browse files
committed
Revert "core: add CSP header for Colab output frames (#2390)"
Summary: This commit broke Jupyter notebook integration: a content security policy that whitelists Colab also blocks access by any Jupyter notebooks. I don’t see a simple fix for this other than permitting all frame ancestors: Jupyter origins are arbitrary, and we can’t use `'self'` because Jupyter and TensorBoard are not in fact served from the same origin. This reverts commit ff05d1a. Test Plan: Build the Pip package, then install it into a virtualenv with Jupyter. From a Jupyter notebook, run `%tensorboard --logdir logs`. Before this commit, that rendered an empty cell and printed a console error, > Refused to display 'http://localhost:6006/' in a frame because an > ancestor violates the following Content Security Policy directive: > "frame-ancestors https://*.googleusercontent.com > https://*.google.com". As of this commit, this renders a working TensorBoard. wchargin-branch: revert-colab-csp
1 parent 2f76de1 commit 0be7294

File tree

2 files changed

+2
-67
lines changed

2 files changed

+2
-67
lines changed

tensorboard/backend/application.py

+2-35
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
import atexit
2626
import collections
27-
import functools
2827
import json
2928
import os
3029
import re
@@ -37,7 +36,6 @@
3736
import six
3837
from six.moves.urllib import parse as urlparse # pylint: disable=wrong-import-order
3938

40-
import werkzeug
4139
from werkzeug import wrappers
4240

4341
from tensorboard.backend import http_util
@@ -326,33 +324,6 @@ def _serve_plugins_listing(self, request):
326324
response[plugin.plugin_name] = plugin_metadata
327325
return http_util.Respond(request, response, 'application/json')
328326

329-
def _headers_with_colab_csp(self, headers):
330-
"""Add a Content-Security-Policy facilitating Colab output frames.
331-
332-
This is intended for use with the `google.colab.kernel.proxyPort`
333-
JavaScript function available from within a Colab output frame.
334-
335-
If the headers already include an explicit CSP, they are returned
336-
unchanged.
337-
338-
Args:
339-
headers: A list of WSGI headers (key-value tuples of `str`s).
340-
341-
Returns:
342-
A new list of WSGI headers; the original is unchanged.
343-
"""
344-
# use a Werkzeug `Headers` object for proper case-insensitivity
345-
headers = werkzeug.Headers(headers)
346-
csp_key = 'Content-Security-Policy'
347-
if csp_key not in headers:
348-
allowed_ancestors = ' '.join([
349-
'https://*.googleusercontent.com',
350-
'https://*.google.com',
351-
])
352-
csp = 'frame-ancestors %s' % allowed_ancestors
353-
headers[csp_key] = csp
354-
return headers.to_wsgi_list()
355-
356327
def __call__(self, environ, start_response): # pylint: disable=invalid-name
357328
"""Central entry point for the TensorBoard application.
358329
@@ -373,17 +344,13 @@ class are WSGI applications.
373344
parsed_url = urlparse.urlparse(request.path)
374345
clean_path = _clean_path(parsed_url.path, self._path_prefix)
375346

376-
@functools.wraps(start_response)
377-
def new_start_response(status, headers):
378-
return start_response(status, self._headers_with_colab_csp(headers))
379-
380347
# pylint: disable=too-many-function-args
381348
if clean_path in self.data_applications:
382-
return self.data_applications[clean_path](environ, new_start_response)
349+
return self.data_applications[clean_path](environ, start_response)
383350
else:
384351
logger.warn('path %s not found, sending 404', clean_path)
385352
return http_util.Respond(request, 'Not found', 'text/plain', code=404)(
386-
environ, new_start_response)
353+
environ, start_response)
387354
# pylint: enable=too-many-function-args
388355

389356

tensorboard/backend/application_test.py

-32
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
except ImportError:
3737
import mock # pylint: disable=g-import-not-at-top,unused-import
3838

39-
import werkzeug
4039
from werkzeug import test as werkzeug_test
4140
from werkzeug import wrappers
4241

@@ -149,23 +148,13 @@ def setUp(self):
149148
is_active_value=True,
150149
routes_mapping={
151150
'/esmodule': lambda req: None,
152-
'/no_csp': functools.partial(self._serve, False),
153-
'/csp': functools.partial(self._serve, True),
154151
},
155152
es_module_path_value='/esmodule'
156153
),
157154
]
158155
app = application.TensorBoardWSGI(plugins)
159156
self.server = werkzeug_test.Client(app, wrappers.BaseResponse)
160157

161-
@wrappers.Request.application
162-
def _serve(self, include_csp, request):
163-
assert isinstance(include_csp, bool), include_csp
164-
response = wrappers.Response('hello\n')
165-
if include_csp:
166-
response.headers['CONTENT-sEcUrItY-POLICY'] = "frame-ancestors 'none'"
167-
return response
168-
169158
def _get_json(self, path):
170159
response = self.server.get(path)
171160
self.assertEqual(200, response.status_code)
@@ -217,27 +206,6 @@ def testPluginsListing(self):
217206
}
218207
)
219208

220-
def testColabCsp_whenNoCspPresent(self):
221-
response = self.server.get('/data/plugin/baz/no_csp')
222-
self.assertEqual(
223-
response.headers.get('Content-Security-Policy'),
224-
'frame-ancestors https://*.googleusercontent.com https://*.google.com',
225-
)
226-
227-
def testColabCsp_whenExistingCspPresent(self):
228-
response = self.server.get('/data/plugin/baz/csp')
229-
self.assertEqual(
230-
response.headers.get('Content-Security-Policy'),
231-
"frame-ancestors 'none'",
232-
)
233-
234-
def testColabCsp_on404(self):
235-
response = self.server.get('/asdf')
236-
self.assertEqual(404, response.status_code)
237-
self.assertEqual(
238-
response.headers.get('Content-Security-Policy'),
239-
'frame-ancestors https://*.googleusercontent.com https://*.google.com',
240-
)
241209

242210
class ApplicationBaseUrlTest(tb_test.TestCase):
243211
path_prefix = '/test'

0 commit comments

Comments
 (0)