We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Everyday Standard Library Tools step 45 of 55
Keep a Joined Path Inside Its Root Folder
Write safe_join(root, *parts), which joins path parts onto a root folder
and rejects any result outside that folder. A web app can use it to stop a
request for ../../etc/passwd from reading files outside its upload folder.
Work only with the text of the paths. Do not touch the disk or follow
symlinks.
safe_join(PurePosixPath("/srv/data"), "a", "..", "archive.tar.gz")
# PurePosixPath('/srv/data/archive.tar.gz')
safe_join(PurePosixPath("/srv/data"), "/etc/passwd")
# raises ValueError
What to write
Fix safe_join(root, *parts) in the starter:
- Join the parts with
root.joinpath(*parts). - Pass the joined path's string to
posixpath.normpath, and build aPurePosixPathcalledcandidatefrom the result. - If
candidateis not insideroot, raiseValueError. Check withcandidate.is_relative_to(root). Otherwise returncandidate.
The starter already does steps 1 and 2, but it checks with startswith,
which treats /srv/data2 as inside /srv/data.
Then write solve(root, parts):
- Clean up the root first with
base = PurePosixPath(posixpath.normpath(root)), and passbasetosafe_join. - On success, return
{"path": str(joined), "suffix": joined.suffix, "parent": str(joined.parent)}. - On
ValueError, return exactly{"path": "ValueError", "suffix": "", "parent": ""}.
For root "/srv/data" and parts ["a", "..", "archive.tar.gz"], solve
returns path "/srv/data/archive.tar.gz", suffix ".gz" and parent
"/srv/data".
Edge cases
- An absolute part replaces the path before it. Do not reject a part for
being absolute. Check only the final path. Under
/srv/data, accept/srv/data/file.txtand reject/etc/passwd. - Accept a path that goes up and comes back into the root, such as
a/../b. - With no parts, return the root itself.
is_relative_tocounts a path as relative to itself. - Accept empty parts and
.parts. /srv/data2is outside/srv/data.- A relative root follows the same rules. Do not turn it into an absolute path.
- Use the
suffixthat your Python version gives. From Python 3.14, a name that ends in a single dot has the suffix".".
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Python reference solution
Sign in to track your attempts toward unlocking the reference solution.