Commit 4bb678e9 authored by Iustin Pop's avatar Iustin Pop
Browse files

utils: Add a PathJoin function

This will replace os.path.join since it is not safe for directory
traversal issues.
Signed-off-by: default avatarIustin Pop <>
Reviewed-by: default avatarMichael Hanselmann <>
parent 714ea7ca
......@@ -1936,6 +1936,36 @@ def IsNormAbsPath(path):
return os.path.normpath(path) == path and os.path.isabs(path)
def PathJoin(*args):
"""Safe-join a list of path components.
- the first argument must be an absolute path
- no component in the path must have backtracking (e.g. /../),
since we check for normalization at the end
@param args: the path components to be joined
@raise ValueError: for invalid paths
# ensure we're having at least one path passed in
assert args
# ensure the first component is an absolute and normalized path name
root = args[0]
if not IsNormAbsPath(root):
raise ValueError("Invalid parameter to PathJoin: '%s'" % str(args[0]))
result = os.path.join(*args)
# ensure that the whole path is normalized
if not IsNormAbsPath(result):
raise ValueError("Invalid parameters to PathJoin: '%s'" % str(args))
# check that we're still under the original prefix
prefix = os.path.commonprefix([root, result])
if prefix != root:
raise ValueError("Error: path joining resulted in different prefix"
" (%s != %s)" % (prefix, root))
return result
def TailFile(fname, lines=20):
"""Return the last lines from a file.
......@@ -47,7 +47,7 @@ from ganeti.utils import IsProcessAlive, RunCmd, \
ShellQuote, ShellQuoteArgs, TcpPing, ListVisibleFiles, \
SetEtcHostsEntry, RemoveEtcHostsEntry, FirstFree, OwnIpAddress, \
TailFile, ForceDictType, SafeEncode, IsNormAbsPath, FormatTime, \
UnescapeAndSplit, RunParts
UnescapeAndSplit, RunParts, PathJoin
from ganeti.errors import LockError, UnitParseError, GenericError, \
......@@ -1260,5 +1260,22 @@ class TestUnescapeAndSplit(unittest.TestCase):
self.failUnlessEqual(UnescapeAndSplit(sep.join(a), sep=sep), b)
class TestPathJoin(unittest.TestCase):
"""Testing case for PathJoin"""
def testBasicItems(self):
mlist = ["/a", "b", "c"]
self.failUnlessEqual(PathJoin(*mlist), "/".join(mlist))
def testNonAbsPrefix(self):
self.failUnlessRaises(ValueError, PathJoin, "a", "b")
def testBackTrack(self):
self.failUnlessRaises(ValueError, PathJoin, "/a", "b/../c")
def testMultiAbs(self):
self.failUnlessRaises(ValueError, PathJoin, "/a", "/b")
if __name__ == '__main__':
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment