Relative path sanitization

I am trying to write a function that resolves a path (provided by the user) relative to a directory, ensuring that the result is inside the directory. The one below seems to work, but I am counting on community eyeballs to tell me if I left any holes in it, or that I overcomplicated it :wink: Does not need to be super-secure (just to prevent users shooting themselves in the foot), but portability would be nice. I tested on Linux. OS X and windows tests would be welcome.

"""
    ensure_proper_subpath(dir, subpath)

Return the normalized `dir/subpath`, ensuring that it is inside `dir`, otherwise
raising an error. *None of the paths are required to exist, the function operates
on paths, not the filesystem.*
"""
function ensure_proper_subpath(dir, subpath)
    norm_dir = normpath(dir)
    norm_subpath = normpath(joinpath(norm_dir, subpath))
    @assert startswith(norm_subpath, norm_dir) "$(subpath) not in $(dir)."
    norm_subpath
end

ensure_proper_subpath("/tmp", "foo")            # "/tmp/foo"
ensure_proper_subpath("/tmp/dir/", "foo")       # "/tmp/dir/foo"
ensure_proper_subpath("/tmp/dir/", "../foo")    # ERROR
ensure_proper_subpath("/tmp/", "dir/../foo")    # "/tmp/foo"
ensure_proper_subpath("/tmp/", "dir/../../foo") # ERROR

You may want realpath, not normpath, since otherwise you can be fooled by symbolic links or case changes (on case-insensitive filesystems)

realpath requires that the path actually exists, however.

Note also that you should not use @assert to check for incorrect user input — you should throw a different exception. @assert should only be used to check for bugs (i.e. to assert things that should always be true if your code is functioning as intended).

4 Likes