Join one or more path components to the base path component intelligently. Return a normalized, absolute version of the final path. Raise SuspiciousFileOperation if the final path isn't located inside of the base path component.
(base, *paths)
| 63 | |
| 64 | |
| 65 | def safe_join(base, *paths): |
| 66 | """ |
| 67 | Join one or more path components to the base path component intelligently. |
| 68 | Return a normalized, absolute version of the final path. |
| 69 | |
| 70 | Raise SuspiciousFileOperation if the final path isn't located inside of the |
| 71 | base path component. |
| 72 | """ |
| 73 | final_path = abspath(join(base, *paths)) |
| 74 | base_path = abspath(base) |
| 75 | # Ensure final_path starts with base_path (using normcase to ensure we |
| 76 | # don't false-negative on case insensitive operating systems like Windows), |
| 77 | # further, one of the following conditions must be true: |
| 78 | # a) The next character is the path separator (to prevent conditions like |
| 79 | # safe_join("/dir", "/../d")) |
| 80 | # b) The final path must be the same as the base path. |
| 81 | # c) The base path must be the most root path (meaning either "/" or |
| 82 | # "C:\\") |
| 83 | if ( |
| 84 | not normcase(final_path).startswith(normcase(base_path + sep)) |
| 85 | and normcase(final_path) != normcase(base_path) |
| 86 | and dirname(normcase(base_path)) != normcase(base_path) |
| 87 | ): |
| 88 | raise SuspiciousFileOperation( |
| 89 | "The joined path ({}) is located outside of the base path " |
| 90 | "component ({})".format(final_path, base_path) |
| 91 | ) |
| 92 | return final_path |
| 93 | |
| 94 | |
| 95 | def symlinks_supported(): |
searching dependent graphs…