Skip to content
Permalink
Browse files Browse the repository at this point in the history
Simplify path handling, use safe_join
The current implementation of sanitized_join did not handle
".." properly. The problem is, that .absolute() does not do
what .resolve() does, but .resolve() does not work on non
existant paths.

Anyway, flask has a function exactly for this: safe_join.

So let's use that one.

While at it, simplified the whole path handling a bit.
  • Loading branch information
ChristianTacke committed Oct 5, 2020
1 parent f0fc744 commit 8205654
Showing 1 changed file with 15 additions and 34 deletions.
49 changes: 15 additions & 34 deletions xhu.py
Expand Up @@ -29,6 +29,7 @@
import typing

import flask
import werkzeug.exceptions

app = flask.Flask("xmpp-http-upload")
app.config.from_envvar("XMPP_HTTP_UPLOAD_CONFIG")
Expand All @@ -39,16 +40,11 @@
CORS(app)


def sanitized_join(path: str, root: pathlib.Path) -> pathlib.Path:
result = (root / path).absolute()
if not str(result).startswith(str(root) + "/"):
raise ValueError("resulting path is outside root")
return result


def get_paths(base_path: pathlib.Path):
data_file = pathlib.Path(str(base_path) + ".data")
metadata_file = pathlib.Path(str(base_path) + ".meta")
def get_paths(root: str, sub_path: str) \
-> typing.Tuple[pathlib.Path, pathlib.Path]:
base_path = flask.safe_join(root, sub_path)
data_file = pathlib.Path(base_path + ".data")
metadata_file = pathlib.Path(base_path + ".meta")

return data_file, metadata_file

Expand All @@ -58,15 +54,10 @@ def load_metadata(metadata_file):
return json.load(f)


def get_info(path: str, root: pathlib.Path) -> typing.Tuple[
def get_info(path: str) -> typing.Tuple[
pathlib.Path,
dict]:
dest_path = sanitized_join(
path,
pathlib.Path(app.config["DATA_ROOT"]),
)

data_file, metadata_file = get_paths(dest_path)
data_file, metadata_file = get_paths(app.config["DATA_ROOT"], path)

return data_file, load_metadata(metadata_file)

Expand Down Expand Up @@ -104,11 +95,8 @@ def stream_file(src, dest, nbytes):
@app.route("/<path:path>", methods=["PUT"])
def put_file(path):
try:
dest_path = sanitized_join(
path,
pathlib.Path(app.config["DATA_ROOT"]),
)
except ValueError:
data_file, metadata_file = get_paths(app.config["DATA_ROOT"], path)
except werkzeug.exceptions.NotFound:
return flask.Response(
"Not Found",
404,
Expand All @@ -134,8 +122,7 @@ def put_file(path):
"application/octet-stream",
)

dest_path.parent.mkdir(parents=True, exist_ok=True, mode=0o770)
data_file, metadata_file = get_paths(dest_path)
data_file.parent.mkdir(parents=True, exist_ok=True, mode=0o770)

try:
with write_file(data_file) as fout:
Expand Down Expand Up @@ -189,13 +176,10 @@ def generate_headers(response_headers, metadata_headers):
@app.route("/<path:path>", methods=["HEAD"])
def head_file(path):
try:
data_file, metadata = get_info(
path,
pathlib.Path(app.config["DATA_ROOT"])
)
data_file, metadata = get_info(path)

stat = data_file.stat()
except (OSError, ValueError):
except (OSError, werkzeug.exceptions.NotFound):
return flask.Response(
"Not Found",
404,
Expand All @@ -214,11 +198,8 @@ def head_file(path):
@app.route("/<path:path>", methods=["GET"])
def get_file(path):
try:
data_file, metadata = get_info(
path,
pathlib.Path(app.config["DATA_ROOT"])
)
except (OSError, ValueError):
data_file, metadata = get_info(path)
except (OSError, werkzeug.exceptions.NotFound):
return flask.Response(
"Not Found",
404,
Expand Down

0 comments on commit 8205654

Please sign in to comment.