Desk Clarity

Everyday document & browser help

Pathlib Join Drops the Base Directory: Check Absolute and Rooted Components

By ON Lab · Sources checked November 9, 2026

Inspect each component’s path type and anchor. An absolute component can replace what came before it; joining paths is not a guarantee of containment.

Reproduce the boundary in a disposable example

Joining a child path unexpectedly produces a path outside the chosen base.

from pathlib import PurePosixPath, PureWindowsPath
base = PurePosixPath("/work/export")
assert base / "report.csv" == PurePosixPath("/work/export/report.csv")
assert base / "/report.csv" == PurePosixPath("/report.csv")
windows = PureWindowsPath("C:/work/export")
assert windows / "D:/report.csv" == PureWindowsPath("D:/report.csv")
assert windows / "/report.csv" == PureWindowsPath("C:/report.csv")
# Even relative text may contain parent traversal.
child = PurePosixPath("../outside.csv")
assert not child.is_absolute() and ".." in child.parts
print("POSIX and Windows lexical joins verified")

Read the result before changing application code

Pure path classes let this test compare path grammars without writing files or pretending the host is Windows. The rooted Windows case retains the drive while replacing the directory portion. A relative-path check also does not rule out parent traversal, as the last assertion demonstrates.

Choose the behavior your caller needs

For a trusted accidental leading separator, fix the producing field so it supplies the relative component it promises. Do not blindly strip separators from arbitrary input: that changes meaning and can hide an upstream schema error. If the task is restricting writes under a directory, separately define permitted names and perform a resolved-path containment review that accounts for symlinks and concurrent filesystem changes. The lexical join test alone is insufficient for that security contract.

Scope and limits

This article diagnoses path composition, not a complete sandbox implementation. It does not perform file writes or normalize a real network share.

Related: compare the adjacent data or execution boundary.

The embedded example was executed in an isolated local Python process on November 9, 2026. It is a reproducible boundary check, not a test of your full application.

Sources