Skip to content

Commit f69eb96

Browse files
committed
added hidden checks on FileContentsManager and accompanying tests
1 parent 2a76184 commit f69eb96

File tree

2 files changed

+172
-10
lines changed

2 files changed

+172
-10
lines changed

notebook/services/contents/filemanager.py

+29-10
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,12 @@ def _base_model(self, path):
242242
os_path = self._get_os_path(path)
243243
info = os.lstat(os_path)
244244

245+
four_o_four = "file or directory does not exist: %r" % path
246+
247+
if is_hidden(os_path, self.root_dir) and not self.allow_hidden:
248+
self.log.info("Refusing to serve hidden file or file in hidden directory %r, via 404 Error", os_path)
249+
raise web.HTTPError(404, four_o_four)
250+
245251
try:
246252
# size of file
247253
size = info.st_size
@@ -364,12 +370,6 @@ def _file_model(self, path, content=True, format=None):
364370

365371
os_path = self._get_os_path(path)
366372

367-
four_o_four = "file does not exist: %r" % path
368-
369-
if is_hidden(os_path, self.root_dir) and not self.allow_hidden:
370-
self.log.info("Refusing to serve hidden file or file in hidden directory %r, via 404 Error", os_path)
371-
raise web.HTTPError(404, four_o_four)
372-
373373
model['mimetype'] = mimetypes.guess_type(os_path)[0]
374374

375375
if content:
@@ -430,11 +430,17 @@ def get(self, path, content=True, type=None, format=None):
430430
of the file or directory as well.
431431
"""
432432
path = path.strip('/')
433+
os_path = self._get_os_path(path)
434+
four_o_four = "file or directory does not exist: %r" % path
433435

434436
if not self.exists(path):
435-
raise web.HTTPError(404, f'No such file or directory: {path}')
437+
raise web.HTTPError(404, four_o_four)
436438

437-
os_path = self._get_os_path(path)
439+
if is_hidden(os_path, self.root_dir) and not self.allow_hidden:
440+
self.log.info("Refusing to serve hidden file or file in hidden directory %r, via 404 Error", os_path)
441+
raise web.HTTPError(404, four_o_four)
442+
443+
438444
if os.path.isdir(os_path):
439445
if type not in (None, 'directory'):
440446
raise web.HTTPError(400,
@@ -472,6 +478,10 @@ def save(self, model, path=''):
472478
raise web.HTTPError(400, 'No file content provided')
473479

474480
os_path = self._get_os_path(path)
481+
482+
if is_hidden(os_path, self.root_dir) and not self.allow_hidden:
483+
raise web.HTTPError(400, f'Cannot create hidden file or directory {os_path!r}')
484+
475485
self.log.debug("Saving %s", os_path)
476486

477487
self.run_pre_save_hook(model=model, path=path)
@@ -515,8 +525,14 @@ def delete_file(self, path):
515525
path = path.strip('/')
516526
os_path = self._get_os_path(path)
517527
rm = os.unlink
518-
if not os.path.exists(os_path):
519-
raise web.HTTPError(404, f'File or directory does not exist: {os_path}')
528+
529+
four_o_four = "file or directory does not exist: %r" % path
530+
531+
if not self.exists(path):
532+
raise web.HTTPError(404, four_o_four)
533+
534+
if is_hidden(os_path, self.root_dir) and not self.allow_hidden:
535+
raise web.HTTPError(400, f'Cannot delete hidden file or directory {os_path!r}')
520536

521537
def is_non_empty_dir(os_path):
522538
if os.path.isdir(os_path):
@@ -559,6 +575,9 @@ def rename_file(self, old_path, new_path):
559575
if new_path == old_path:
560576
return
561577

578+
if (is_hidden(old_path, self.root_dir) or is_hidden(new_path, self.root_dir)) and not self.allow_hidden:
579+
raise web.HTTPError(400, f'Cannot rename hidden file or directory {os_path!r}')
580+
562581
# Perform path validation prior to converting to os-specific value since this
563582
# is still relative to root_dir.
564583
self._validate_path(new_path)

notebook/services/contents/tests/test_manager.py

+143
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,149 @@ def test_403(self):
181181
else:
182182
self.fail("Should have raised HTTPError(403)")
183183

184+
@skipIf(sys.platform.startswith('win'), "Can't test hidden files on Windows")
185+
def test_400(self):
186+
#Test Delete behavior
187+
#Test delete of file in hidden directory
188+
with self.assertRaises(HTTPError) as excinfo:
189+
with TemporaryDirectory() as td:
190+
cm = FileContentsManager(root_dir=td)
191+
hidden_dir = '.hidden'
192+
file_in_hidden_path = os.path.join(hidden_dir,'visible.txt')
193+
_make_dir(cm, hidden_dir)
194+
model = cm.new(path=file_in_hidden_path)
195+
os_path = cm._get_os_path(model['path'])
196+
197+
try:
198+
result = cm.delete_file(os_path)
199+
except HTTPError as e:
200+
self.assertEqual(e.status_code, 400)
201+
else:
202+
self.fail("Should have raised HTTPError(400)")
203+
#Test delete hidden file in visible directory
204+
with self.assertRaises(HTTPError) as excinfo:
205+
with TemporaryDirectory() as td:
206+
cm = FileContentsManager(root_dir=td)
207+
hidden_dir = 'visible'
208+
file_in_hidden_path = os.path.join(hidden_dir,'.hidden.txt')
209+
_make_dir(cm, hidden_dir)
210+
model = cm.new(path=file_in_hidden_path)
211+
os_path = cm._get_os_path(model['path'])
212+
213+
try:
214+
result = cm.delete_file(os_path)
215+
except HTTPError as e:
216+
self.assertEqual(e.status_code, 400)
217+
else:
218+
self.fail("Should have raised HTTPError(400)")
219+
220+
#Test Save behavior
221+
#Test save of file in hidden directory
222+
with self.assertRaises(HTTPError) as excinfo:
223+
with TemporaryDirectory() as td:
224+
cm = FileContentsManager(root_dir=td)
225+
hidden_dir = '.hidden'
226+
file_in_hidden_path = os.path.join(hidden_dir,'visible.txt')
227+
_make_dir(cm, hidden_dir)
228+
model = cm.new(path=file_in_hidden_path)
229+
os_path = cm._get_os_path(model['path'])
230+
231+
try:
232+
result = cm.save(model,path=os_path)
233+
except HTTPError as e:
234+
self.assertEqual(e.status_code, 400)
235+
else:
236+
self.fail("Should have raised HTTPError(400)")
237+
#Test save hidden file in visible directory
238+
with self.assertRaises(HTTPError) as excinfo:
239+
with TemporaryDirectory() as td:
240+
cm = FileContentsManager(root_dir=td)
241+
hidden_dir = 'visible'
242+
file_in_hidden_path = os.path.join(hidden_dir,'.hidden.txt')
243+
_make_dir(cm, hidden_dir)
244+
model = cm.new(path=file_in_hidden_path)
245+
os_path = cm._get_os_path(model['path'])
246+
247+
try:
248+
result = cm.save(model,path=os_path)
249+
except HTTPError as e:
250+
self.assertEqual(e.status_code, 400)
251+
else:
252+
self.fail("Should have raised HTTPError(400)")
253+
254+
#Test rename behavior
255+
#Test rename with source file in hidden directory
256+
with self.assertRaises(HTTPError) as excinfo:
257+
with TemporaryDirectory() as td:
258+
cm = FileContentsManager(root_dir=td)
259+
hidden_dir = '.hidden'
260+
file_in_hidden_path = os.path.join(hidden_dir,'visible.txt')
261+
_make_dir(cm, hidden_dir)
262+
model = cm.new(path=file_in_hidden_path)
263+
old_path = cm._get_os_path(model['path'])
264+
new_path = "new.txt"
265+
266+
try:
267+
result = cm.rename_file(old_path, new_path)
268+
except HTTPError as e:
269+
self.assertEqual(e.status_code, 400)
270+
else:
271+
self.fail("Should have raised HTTPError(400)")
272+
273+
#Test rename of dest file in hidden directory
274+
with self.assertRaises(HTTPError) as excinfo:
275+
with TemporaryDirectory() as td:
276+
cm = FileContentsManager(root_dir=td)
277+
hidden_dir = '.hidden'
278+
file_in_hidden_path = os.path.join(hidden_dir,'visible.txt')
279+
_make_dir(cm, hidden_dir)
280+
model = cm.new(path=file_in_hidden_path)
281+
new_path = cm._get_os_path(model['path'])
282+
old_path = "old.txt"
283+
284+
try:
285+
result = cm.rename_file(old_path, new_path)
286+
except HTTPError as e:
287+
self.assertEqual(e.status_code, 400)
288+
else:
289+
self.fail("Should have raised HTTPError(400)")
290+
291+
#Test rename with hidden source file in visible directory
292+
with self.assertRaises(HTTPError) as excinfo:
293+
with TemporaryDirectory() as td:
294+
cm = FileContentsManager(root_dir=td)
295+
hidden_dir = 'visible'
296+
file_in_hidden_path = os.path.join(hidden_dir,'.hidden.txt')
297+
_make_dir(cm, hidden_dir)
298+
model = cm.new(path=file_in_hidden_path)
299+
old_path = cm._get_os_path(model['path'])
300+
new_path = "new.txt"
301+
302+
try:
303+
result = cm.rename_file(old_path, new_path)
304+
except HTTPError as e:
305+
self.assertEqual(e.status_code, 400)
306+
else:
307+
self.fail("Should have raised HTTPError(400)")
308+
309+
#Test rename with hidden dest file in visible directory
310+
with self.assertRaises(HTTPError) as excinfo:
311+
with TemporaryDirectory() as td:
312+
cm = FileContentsManager(root_dir=td)
313+
hidden_dir = 'visible'
314+
file_in_hidden_path = os.path.join(hidden_dir,'.hidden.txt')
315+
_make_dir(cm, hidden_dir)
316+
model = cm.new(path=file_in_hidden_path)
317+
new_path = cm._get_os_path(model['path'])
318+
old_path = "old.txt"
319+
320+
try:
321+
result = cm.rename_file(old_path, new_path)
322+
except HTTPError as e:
323+
self.assertEqual(e.status_code, 400)
324+
else:
325+
self.fail("Should have raised HTTPError(400)")
326+
184327
@skipIf(sys.platform.startswith('win'), "Can't test hidden files on Windows")
185328
def test_404(self):
186329
#Test visible file in hidden folder

0 commit comments

Comments
 (0)