Skip to content

Commit 3485007

Browse files
rashley-iqtZsailer
authored andcommitted
Merge pull request from GHSA-q874-g24w-4q9g
* added checks for hidden files and directories on FileManager Class * added checks for hidden files and directories in api handlers * updated error messages to not mention hidden files * cleaned up issues flagged by pre-commit
1 parent 8de001a commit 3485007

File tree

4 files changed

+479
-5
lines changed

4 files changed

+479
-5
lines changed

jupyter_server/services/contents/filemanager.py

+30-5
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,12 @@ def _base_model(self, path):
188188
os_path = self._get_os_path(path)
189189
info = os.lstat(os_path)
190190

191+
four_o_four = "file or directory does not exist: %r" % path
192+
193+
if is_hidden(os_path, self.root_dir) and not self.allow_hidden:
194+
self.log.info("Refusing to serve hidden file or directory %r, via 404 Error", os_path)
195+
raise web.HTTPError(404, four_o_four)
196+
191197
try:
192198
# size of file
193199
size = info.st_size
@@ -365,11 +371,16 @@ def get(self, path, content=True, type=None, format=None):
365371
of the file or directory as well.
366372
"""
367373
path = path.strip("/")
374+
os_path = self._get_os_path(path)
375+
four_o_four = "file or directory does not exist: %r" % path
368376

369377
if not self.exists(path):
370-
raise web.HTTPError(404, "No such file or directory: %s" % path)
378+
raise web.HTTPError(404, four_o_four)
379+
380+
if is_hidden(os_path, self.root_dir) and not self.allow_hidden:
381+
self.log.info("Refusing to serve hidden file or directory %r, via 404 Error", os_path)
382+
raise web.HTTPError(404, four_o_four)
371383

372-
os_path = self._get_os_path(path)
373384
if os.path.isdir(os_path):
374385
if type not in (None, "directory"):
375386
raise web.HTTPError(
@@ -389,7 +400,7 @@ def get(self, path, content=True, type=None, format=None):
389400
def _save_directory(self, os_path, model, path=""):
390401
"""create a directory"""
391402
if is_hidden(os_path, self.root_dir) and not self.allow_hidden:
392-
raise web.HTTPError(400, "Cannot create hidden directory %r" % os_path)
403+
raise web.HTTPError(400, "Cannot create directory %r" % os_path)
393404
if not os.path.exists(os_path):
394405
with self.perm_to_403():
395406
os.mkdir(os_path)
@@ -410,6 +421,10 @@ def save(self, model, path=""):
410421
raise web.HTTPError(400, "No file content provided")
411422

412423
os_path = self._get_os_path(path)
424+
425+
if is_hidden(os_path, self.root_dir) and not self.allow_hidden:
426+
raise web.HTTPError(400, f"Cannot create file or directory {os_path!r}")
427+
413428
self.log.debug("Saving %s", os_path)
414429

415430
validation_error: dict = {}
@@ -452,8 +467,13 @@ def delete_file(self, path):
452467
path = path.strip("/")
453468
os_path = self._get_os_path(path)
454469
rm = os.unlink
455-
if not os.path.exists(os_path):
456-
raise web.HTTPError(404, "File or directory does not exist: %s" % os_path)
470+
four_o_four = "file or directory does not exist: %r" % path
471+
472+
if not self.exists(path):
473+
raise web.HTTPError(404, four_o_four)
474+
475+
if is_hidden(os_path, self.root_dir) and not self.allow_hidden:
476+
raise web.HTTPError(400, f"Cannot delete file or directory {os_path!r}")
457477

458478
def _check_trash(os_path):
459479
if sys.platform in {"win32", "darwin"}:
@@ -518,6 +538,11 @@ def rename_file(self, old_path, new_path):
518538
new_os_path = self._get_os_path(new_path)
519539
old_os_path = self._get_os_path(old_path)
520540

541+
if (
542+
is_hidden(old_os_path, self.root_dir) or is_hidden(new_os_path, self.root_dir)
543+
) and not self.allow_hidden:
544+
raise web.HTTPError(400, f"Cannot rename file or directory {old_os_path!r}")
545+
521546
# Should we proceed with the move?
522547
if os.path.exists(new_os_path) and not samefile(old_os_path, new_os_path):
523548
raise web.HTTPError(409, "File already exists: %s" % new_path)

jupyter_server/services/contents/handlers.py

+38
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ async def get(self, path=""):
9595
of the files and directories it contains.
9696
"""
9797
path = path or ""
98+
cm = self.contents_manager
99+
98100
type = self.get_query_argument("type", default=None)
99101
if type not in {None, "directory", "file", "notebook"}:
100102
raise web.HTTPError(400, "Type %r is invalid" % type)
@@ -107,6 +109,9 @@ async def get(self, path=""):
107109
raise web.HTTPError(400, "Content %r is invalid" % content_str)
108110
content = int(content_str or "")
109111

112+
if await ensure_async(cm.is_hidden(path)) and not cm.allow_hidden:
113+
raise web.HTTPError(404, f"file or directory {path!r} does not exist")
114+
110115
model = await ensure_async(
111116
self.contents_manager.get(
112117
path=path,
@@ -126,6 +131,17 @@ async def patch(self, path=""):
126131
model = self.get_json_body()
127132
if model is None:
128133
raise web.HTTPError(400, "JSON body missing")
134+
135+
old_path = model.get("path")
136+
if (
137+
old_path
138+
and (
139+
await ensure_async(cm.is_hidden(path)) or await ensure_async(cm.is_hidden(old_path))
140+
)
141+
and not cm.allow_hidden
142+
):
143+
raise web.HTTPError(400, f"Cannot rename file or directory {path!r}")
144+
129145
model = await ensure_async(cm.update(model, path))
130146
validate_model(model, expect_content=False)
131147
self._finish_model(model)
@@ -191,6 +207,16 @@ async def post(self, path=""):
191207
raise web.HTTPError(400, "Cannot POST to files, use PUT instead.")
192208

193209
model = self.get_json_body()
210+
copy_from = model.get("copy_from")
211+
if (
212+
copy_from
213+
and (
214+
await ensure_async(cm.is_hidden(path))
215+
or await ensure_async(cm.is_hidden(copy_from))
216+
)
217+
and not cm.allow_hidden
218+
):
219+
raise web.HTTPError(400, f"Cannot copy file or directory {path!r}")
194220

195221
if model is not None:
196222
copy_from = model.get("copy_from")
@@ -217,9 +243,17 @@ async def put(self, path=""):
217243
create a new empty notebook.
218244
"""
219245
model = self.get_json_body()
246+
cm = self.contents_manager
247+
220248
if model:
221249
if model.get("copy_from"):
222250
raise web.HTTPError(400, "Cannot copy with PUT, only POST")
251+
if (
252+
(model.get("path") and await ensure_async(cm.is_hidden(model.get("path"))))
253+
or await ensure_async(cm.is_hidden(path))
254+
) and not cm.allow_hidden:
255+
raise web.HTTPError(400, f"Cannot create file or directory {path!r}")
256+
223257
exists = await ensure_async(self.contents_manager.file_exists(path))
224258
if exists:
225259
await self._save(model, path)
@@ -233,6 +267,10 @@ async def put(self, path=""):
233267
async def delete(self, path=""):
234268
"""delete a file in the given path"""
235269
cm = self.contents_manager
270+
271+
if await ensure_async(cm.is_hidden(path)) and not cm.allow_hidden:
272+
raise web.HTTPError(400, f"Cannot delete file or directory {path!r}")
273+
236274
self.log.warning("delete %s", path)
237275
await ensure_async(cm.delete(path))
238276
self.set_status(204)

0 commit comments

Comments
 (0)