Given a path to a file or a directory, detect whether the path can traverse to outside the current working directory, or attempt to traverse to the "parent directory" of the root directory.
This challenge is about implementing a loose filter of paths that may perform a directory traversal attack.
(There is a stricter variant of the filter, implemented already in programs like GNU tar, that disallows all occurrences of .. as pathname components. I chose to use a loose filter for a coding challenge to be fun. 🙂)
Input
A string representing a Unix file path. For simplicity of this challenge, the path is assumed to never contain any control character ([0x00, 0x1F] ∪ 0x7F), newline, or space (0x20). The input string would also be non-empty. A forward slash (/, 0x2F) is used as a path separator.
The input path may be relative or absolute. An absolute path is indicated by the leading slash in the path string (for example, /usr/share/misc).
- If there are multiple consecutive slashes in the path, they are treated as if there is only one slash (footnote 1). Example:
a//b///c////d/////is treated the same asa/b/c/d/. - A directory name of
.(dot) refers to the same directory as the previous part of the file path. The.directory at the beginning of the file path refers to the working directory. The.doesn't increase any level of directories in the filesystem hierarchy. Example:./e/./f/././gis the same ase/f/g. - A directory name of
..(dot-dot) refers to the parent directory of the directory previously specified in the file path. It decreases one level of directories in the filesystem hierarchy. Example:h/i/../j/../k/l/../../mis the same ash/mprovided thath/i,h/jandh/k/lare all directories and not any other kind of file. POSIX has set an exception that the "parent directory" of the root directory "may refer to the root directory itself" (reference), however for this challenge you must report such path as an attack attempt (see below).
Output
A boolean value (for each input, if your program or function allows multiple path inputs):
True (or "attack") if the path would traverse to outside the working directory when resolved, or, in the case of an absolute path, would traverse to outside the root directory if there were no guard about the "parent directory of root directory" above. (In other words, assume the "chroot jail" is weak that it can break.)
False (or "no-attack") if the path would remain inside (i.e. never traverse out) the working directory for relative path, or inside the (jailed) root directory for an absolute path when resolved.
- You may output in other data types as appropriate for your language that can represent boolean states (e.g. zero and nonzero).
- You may swap the True and False meanings in the output of your submission. Please mention if you do swap them in your submission. (And perhaps give us a reason, such as whether it can save more bytes with the True and False meanings swapped.)
(Note: Just because the result is "false" or "no-attack" doesn't mean the path is always "safe" against other problems (e.g. spoofing).)
(There is no need to print out the resolved file path. That is another challenge.)
Additional requirements
- Except for standard input and output streams, there should be no access to the file system during program execution. (If you use
mkdir(2)orchdir(2)then your submission entry would be non-competing.) This challenge is about parsing of the path strings, not about actual traversal. - Assume all filenames specified in the input paths exist and are directories. (Note: In a stricter filtering scenario, a path like
a/../b/c/../..would be disallowed as that checks the presence ofa,bandb/cas directories and would look like an attack attempt.) - Support of non-ASCII character sets (such as UTF-8) is optional.
- Using built-in functions to resolve paths is allowed, but you must indicate so in your submission. I'm not sure if any built-in from a language would trivialize the problem, but I don't want to ban such submissions if they can provide informative examples for real-world uses.
- code-golf. Aim for code size as small as possible.
Test data
. -> False
... -> False (footnote 2)
..a -> False
./ -> False
.// -> False
a -> False
a/b -> False
a//b -> False
a/./b -> False
a/.. -> False
a/... -> False (footnote 2)
a/.../.. -> False (footnote 2)
a/..b -> False
a/../b/.. -> False
a/b/../.. -> False
.. -> True
../a/ -> True
./.. -> True
a/../.. -> True
a/../../b -> True
a/../../b/c -> True
a/../b/../.. -> True
a/./../.. -> True
a/b/../../.. -> True
/ -> False
// -> False (footnote 1)
/a/.. -> False
/.. -> True
/a/../.. -> True
C:/ -> False (footnote 3)
C:/.. -> False (footnote 3)
https:// -> False (footnote 3)
https://.. -> False (footnote 3)
C:/../.. -> True (footnote 3)
https://../.. -> True (footnote 3)
(Updated to add more test cases suggested by @doubleunary and @Themoonisacheese, thank you all.)
Footnotes
- POSIX reserves the paths with two leading slashes to be special and "may be interpreted in an implementation-defined manner" (reference). See also this question in Unix/Linux Stack Exchange. For this challenge we ignore such special interpretation.
- Unlike in DOS, a directory name of three dots (
...) is not special in Unix systems. - Unix filesystems allow colons in filenames, so these examples refer to ordinary directories, not DOS paths or URLs.

a/../../b/cand./..\$\endgroup\$a/b/../.. -> falseanda/b/../../.. ->true\$\endgroup\$