Show More
@@ -96,8 +96,16 b' class FileManagerMixin(object):' | |||||
96 | ------- |
|
96 | ------- | |
97 | path : string |
|
97 | path : string | |
98 | Native, absolute OS path to for a file. |
|
98 | Native, absolute OS path to for a file. | |
|
99 | ||||
|
100 | Raises | |||
|
101 | ------ | |||
|
102 | 404: if path is outside root | |||
99 | """ |
|
103 | """ | |
100 |
r |
|
104 | root = os.path.abspath(self.root_dir) | |
|
105 | os_path = to_os_path(path, root) | |||
|
106 | if not (os.path.abspath(os_path) + os.path.sep).startswith(root): | |||
|
107 | raise HTTPError(404, "%s is outside root contents directory" % path) | |||
|
108 | return os_path | |||
101 |
|
109 | |||
102 | def _read_notebook(self, os_path, as_version=4): |
|
110 | def _read_notebook(self, os_path, as_version=4): | |
103 | """Read a notebook from an os path.""" |
|
111 | """Read a notebook from an os path.""" |
@@ -373,10 +373,11 b' class FileContentsManager(FileManagerMixin, ContentsManager):' | |||||
373 | if 'content' not in model and model['type'] != 'directory': |
|
373 | if 'content' not in model and model['type'] != 'directory': | |
374 | raise web.HTTPError(400, u'No file content provided') |
|
374 | raise web.HTTPError(400, u'No file content provided') | |
375 |
|
375 | |||
376 | self.run_pre_save_hook(model=model, path=path) |
|
|||
377 |
|
||||
378 | os_path = self._get_os_path(path) |
|
376 | os_path = self._get_os_path(path) | |
379 | self.log.debug("Saving %s", os_path) |
|
377 | self.log.debug("Saving %s", os_path) | |
|
378 | ||||
|
379 | self.run_pre_save_hook(model=model, path=path) | |||
|
380 | ||||
380 | try: |
|
381 | try: | |
381 | if model['type'] == 'notebook': |
|
382 | if model['type'] == 'notebook': | |
382 | nb = nbformat.from_dict(model['content']) |
|
383 | nb = nbformat.from_dict(model['content']) |
@@ -5,6 +5,7 b' from __future__ import print_function' | |||||
5 | import os |
|
5 | import os | |
6 | import sys |
|
6 | import sys | |
7 | import time |
|
7 | import time | |
|
8 | from contextlib import contextmanager | |||
8 |
|
9 | |||
9 | from nose import SkipTest |
|
10 | from nose import SkipTest | |
10 | from tornado.web import HTTPError |
|
11 | from tornado.web import HTTPError | |
@@ -164,7 +165,17 b' class TestContentsManager(TestCase):' | |||||
164 |
|
165 | |||
165 | def tearDown(self): |
|
166 | def tearDown(self): | |
166 | self._temp_dir.cleanup() |
|
167 | self._temp_dir.cleanup() | |
167 |
|
168 | |||
|
169 | @contextmanager | |||
|
170 | def assertRaisesHTTPError(self, status, msg=None): | |||
|
171 | msg = msg or "Should have raised HTTPError(%i)" % status | |||
|
172 | try: | |||
|
173 | yield | |||
|
174 | except HTTPError as e: | |||
|
175 | self.assertEqual(e.status_code, status) | |||
|
176 | else: | |||
|
177 | self.fail(msg) | |||
|
178 | ||||
168 | def make_dir(self, api_path): |
|
179 | def make_dir(self, api_path): | |
169 | """make a subdirectory at api_path |
|
180 | """make a subdirectory at api_path | |
170 |
|
181 | |||
@@ -461,4 +472,29 b' class TestContentsManager(TestCase):' | |||||
461 | cm.mark_trusted_cells(nb, path) |
|
472 | cm.mark_trusted_cells(nb, path) | |
462 | cm.check_and_sign(nb, path) |
|
473 | cm.check_and_sign(nb, path) | |
463 | assert cm.notary.check_signature(nb) |
|
474 | assert cm.notary.check_signature(nb) | |
|
475 | ||||
|
476 | def test_escape_root(self): | |||
|
477 | cm = self.contents_manager | |||
|
478 | # make foo, bar next to root | |||
|
479 | with open(os.path.join(cm.root_dir, '..', 'foo'), 'w') as f: | |||
|
480 | f.write('foo') | |||
|
481 | with open(os.path.join(cm.root_dir, '..', 'bar'), 'w') as f: | |||
|
482 | f.write('bar') | |||
|
483 | ||||
|
484 | with self.assertRaisesHTTPError(404): | |||
|
485 | cm.get('..') | |||
|
486 | with self.assertRaisesHTTPError(404): | |||
|
487 | cm.get('foo/../../../bar') | |||
|
488 | with self.assertRaisesHTTPError(404): | |||
|
489 | cm.delete('../foo') | |||
|
490 | with self.assertRaisesHTTPError(404): | |||
|
491 | cm.rename('../foo', '../bar') | |||
|
492 | with self.assertRaisesHTTPError(404): | |||
|
493 | cm.save(model={ | |||
|
494 | 'type': 'file', | |||
|
495 | 'content': u'', | |||
|
496 | 'format': 'text', | |||
|
497 | }, path='../foo') | |||
|
498 | ||||
|
499 | ||||
464 |
|
500 |
General Comments 0
You need to be logged in to leave comments.
Login now