Skip to content

Commit

Permalink
Merge pull request from GHSA-q874-g24w-4q9g
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
rashley-iqt authored Jun 7, 2022
1 parent 37b78dd commit 877da10
Show file tree
Hide file tree
Showing 4 changed files with 479 additions and 5 deletions.
35 changes: 30 additions & 5 deletions jupyter_server/services/contents/filemanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,12 @@ def _base_model(self, path):
os_path = self._get_os_path(path)
info = os.lstat(os_path)

four_o_four = "file or directory does not exist: %r" % path

if is_hidden(os_path, self.root_dir) and not self.allow_hidden:
self.log.info("Refusing to serve hidden file or directory %r, via 404 Error", os_path)
raise web.HTTPError(404, four_o_four)

try:
# size of file
size = info.st_size
Expand Down Expand Up @@ -365,11 +371,16 @@ def get(self, path, content=True, type=None, format=None):
of the file or directory as well.
"""
path = path.strip("/")
os_path = self._get_os_path(path)
four_o_four = "file or directory does not exist: %r" % path

if not self.exists(path):
raise web.HTTPError(404, "No such file or directory: %s" % path)
raise web.HTTPError(404, four_o_four)

if is_hidden(os_path, self.root_dir) and not self.allow_hidden:
self.log.info("Refusing to serve hidden file or directory %r, via 404 Error", os_path)
raise web.HTTPError(404, four_o_four)

os_path = self._get_os_path(path)
if os.path.isdir(os_path):
if type not in (None, "directory"):
raise web.HTTPError(
Expand All @@ -389,7 +400,7 @@ def get(self, path, content=True, type=None, format=None):
def _save_directory(self, os_path, model, path=""):
"""create a directory"""
if is_hidden(os_path, self.root_dir) and not self.allow_hidden:
raise web.HTTPError(400, "Cannot create hidden directory %r" % os_path)
raise web.HTTPError(400, "Cannot create directory %r" % os_path)
if not os.path.exists(os_path):
with self.perm_to_403():
os.mkdir(os_path)
Expand All @@ -410,6 +421,10 @@ def save(self, model, path=""):
raise web.HTTPError(400, "No file content provided")

os_path = self._get_os_path(path)

if is_hidden(os_path, self.root_dir) and not self.allow_hidden:
raise web.HTTPError(400, f"Cannot create file or directory {os_path!r}")

self.log.debug("Saving %s", os_path)

validation_error: dict = {}
Expand Down Expand Up @@ -452,8 +467,13 @@ def delete_file(self, path):
path = path.strip("/")
os_path = self._get_os_path(path)
rm = os.unlink
if not os.path.exists(os_path):
raise web.HTTPError(404, "File or directory does not exist: %s" % os_path)
four_o_four = "file or directory does not exist: %r" % path

if not self.exists(path):
raise web.HTTPError(404, four_o_four)

if is_hidden(os_path, self.root_dir) and not self.allow_hidden:
raise web.HTTPError(400, f"Cannot delete file or directory {os_path!r}")

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

if (
is_hidden(old_os_path, self.root_dir) or is_hidden(new_os_path, self.root_dir)
) and not self.allow_hidden:
raise web.HTTPError(400, f"Cannot rename file or directory {old_os_path!r}")

# Should we proceed with the move?
if os.path.exists(new_os_path) and not samefile(old_os_path, new_os_path):
raise web.HTTPError(409, "File already exists: %s" % new_path)
Expand Down
38 changes: 38 additions & 0 deletions jupyter_server/services/contents/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ async def get(self, path=""):
of the files and directories it contains.
"""
path = path or ""
cm = self.contents_manager

type = self.get_query_argument("type", default=None)
if type not in {None, "directory", "file", "notebook"}:
raise web.HTTPError(400, "Type %r is invalid" % type)
Expand All @@ -107,6 +109,9 @@ async def get(self, path=""):
raise web.HTTPError(400, "Content %r is invalid" % content_str)
content = int(content_str or "")

if await ensure_async(cm.is_hidden(path)) and not cm.allow_hidden:
raise web.HTTPError(404, f"file or directory {path!r} does not exist")

model = await ensure_async(
self.contents_manager.get(
path=path,
Expand All @@ -126,6 +131,17 @@ async def patch(self, path=""):
model = self.get_json_body()
if model is None:
raise web.HTTPError(400, "JSON body missing")

old_path = model.get("path")
if (
old_path
and (
await ensure_async(cm.is_hidden(path)) or await ensure_async(cm.is_hidden(old_path))
)
and not cm.allow_hidden
):
raise web.HTTPError(400, f"Cannot rename file or directory {path!r}")

model = await ensure_async(cm.update(model, path))
validate_model(model, expect_content=False)
self._finish_model(model)
Expand Down Expand Up @@ -191,6 +207,16 @@ async def post(self, path=""):
raise web.HTTPError(400, "Cannot POST to files, use PUT instead.")

model = self.get_json_body()
copy_from = model.get("copy_from")
if (
copy_from
and (
await ensure_async(cm.is_hidden(path))
or await ensure_async(cm.is_hidden(copy_from))
)
and not cm.allow_hidden
):
raise web.HTTPError(400, f"Cannot copy file or directory {path!r}")

if model is not None:
copy_from = model.get("copy_from")
Expand All @@ -217,9 +243,17 @@ async def put(self, path=""):
create a new empty notebook.
"""
model = self.get_json_body()
cm = self.contents_manager

if model:
if model.get("copy_from"):
raise web.HTTPError(400, "Cannot copy with PUT, only POST")
if (
(model.get("path") and await ensure_async(cm.is_hidden(model.get("path"))))
or await ensure_async(cm.is_hidden(path))
) and not cm.allow_hidden:
raise web.HTTPError(400, f"Cannot create file or directory {path!r}")

exists = await ensure_async(self.contents_manager.file_exists(path))
if exists:
await self._save(model, path)
Expand All @@ -233,6 +267,10 @@ async def put(self, path=""):
async def delete(self, path=""):
"""delete a file in the given path"""
cm = self.contents_manager

if await ensure_async(cm.is_hidden(path)) and not cm.allow_hidden:
raise web.HTTPError(400, f"Cannot delete file or directory {path!r}")

self.log.warning("delete %s", path)
await ensure_async(cm.delete(path))
self.set_status(204)
Expand Down
Loading

0 comments on commit 877da10

Please sign in to comment.