mirror of
https://github.com/borgbackup/borg.git
synced 2024-12-25 01:06:50 +00:00
Make sure all paths included in an archive are relative and local
This commit is contained in:
parent
b6c8392c03
commit
af059fbdfc
6 changed files with 41 additions and 7 deletions
|
@ -11,7 +11,7 @@
|
|||
from . import xattr
|
||||
from .chunker import chunkify
|
||||
from .helpers import uid2user, user2uid, gid2group, group2gid, \
|
||||
Statistics, decode_dict, st_mtime_ns
|
||||
Statistics, decode_dict, st_mtime_ns, make_path_safe
|
||||
|
||||
ITEMS_BUFFER = 1024 * 1024
|
||||
CHUNK_MIN = 1024
|
||||
|
@ -223,7 +223,8 @@ def add(id):
|
|||
|
||||
def extract_item(self, item, restore_attrs=True, peek=None):
|
||||
dest = self.cwd
|
||||
assert item[b'path'][:1] not in ('/', '\\', ':')
|
||||
if item[b'path'].startswith('/') or item[b'path'].startswith('..'):
|
||||
raise Exception('Path should be relative and local')
|
||||
path = os.path.join(dest, item[b'path'])
|
||||
# Attempt to remove existing files, ignore errors on failure
|
||||
try:
|
||||
|
@ -355,23 +356,23 @@ def stat_attrs(self, st, path):
|
|||
return item
|
||||
|
||||
def process_item(self, path, st):
|
||||
item = {b'path': path.lstrip('/\\:')}
|
||||
item = {b'path': make_path_safe(path)}
|
||||
item.update(self.stat_attrs(st, path))
|
||||
self.add_item(item)
|
||||
|
||||
def process_dev(self, path, st):
|
||||
item = {b'path': path.lstrip('/\\:'), b'rdev': st.st_rdev}
|
||||
item = {b'path': make_path_safe(path), b'rdev': st.st_rdev}
|
||||
item.update(self.stat_attrs(st, path))
|
||||
self.add_item(item)
|
||||
|
||||
def process_symlink(self, path, st):
|
||||
source = os.readlink(path)
|
||||
item = {b'path': path.lstrip('/\\:'), b'source': source}
|
||||
item = {b'path': make_path_safe(path), b'source': source}
|
||||
item.update(self.stat_attrs(st, path))
|
||||
self.add_item(item)
|
||||
|
||||
def process_file(self, path, st, cache):
|
||||
safe_path = path.lstrip('/\\:')
|
||||
safe_path = make_path_safe(path)
|
||||
# Is it a hard link?
|
||||
if st.st_nlink > 1:
|
||||
source = self.hard_links.get((st.st_ino, st.st_dev))
|
||||
|
|
|
@ -91,6 +91,7 @@ def do_create(self, args):
|
|||
except IOError:
|
||||
pass
|
||||
for path in args.paths:
|
||||
path = os.path.normpath(path)
|
||||
if args.dontcross:
|
||||
try:
|
||||
restrict_dev = os.lstat(path).st_dev
|
||||
|
|
|
@ -362,6 +362,15 @@ def remove_surrogates(s, errors='replace'):
|
|||
return s.encode('utf-8', errors).decode('utf-8')
|
||||
|
||||
|
||||
_safe_re = re.compile('^((..)?/+)+')
|
||||
|
||||
|
||||
def make_path_safe(path):
|
||||
"""Make path safe by making it relative and local
|
||||
"""
|
||||
return _safe_re.sub('', path) or '.'
|
||||
|
||||
|
||||
def daemonize():
|
||||
"""Detach process from controlling terminal and run in background
|
||||
"""
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
class AtticTestCase(unittest.TestCase):
|
||||
"""
|
||||
"""
|
||||
assert_in = unittest.TestCase.assertIn
|
||||
assert_not_in = unittest.TestCase.assertNotIn
|
||||
assert_equal = unittest.TestCase.assertEqual
|
||||
assert_not_equal = unittest.TestCase.assertNotEqual
|
||||
assert_raises = unittest.TestCase.assertRaises
|
||||
|
|
|
@ -157,6 +157,15 @@ def test_extract_include_exclude(self):
|
|||
self.attic('extract', '--exclude=input/file2', self.repository_location + '::test')
|
||||
self.assert_equal(sorted(os.listdir('output/input')), ['file1', 'file3'])
|
||||
|
||||
def test_path_normalization(self):
|
||||
self.attic('init', self.repository_location)
|
||||
self.create_regual_file('dir1/dir2/file', size=1024 * 80)
|
||||
with changedir('input/dir1/dir2'):
|
||||
self.attic('create', self.repository_location + '::test', '../../../input/dir1/../dir1/dir2/..')
|
||||
output = self.attic('list', self.repository_location + '::test')
|
||||
self.assert_not_in('..', output)
|
||||
self.assert_in(' input/dir1/dir2/file', output)
|
||||
|
||||
def test_overwrite(self):
|
||||
self.create_regual_file('file1', size=1024 * 80)
|
||||
self.create_regual_file('dir2/file2', size=1024 * 80)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from datetime import datetime
|
||||
from attic.helpers import Location, format_timedelta, IncludePattern, ExcludePattern
|
||||
from attic.helpers import Location, format_timedelta, IncludePattern, ExcludePattern, make_path_safe
|
||||
from attic.testsuite import AtticTestCase
|
||||
|
||||
|
||||
|
@ -54,3 +54,15 @@ def test(self):
|
|||
self.assert_equal(ExcludePattern('/tmp').match('/tmp'), True)
|
||||
self.assert_equal(ExcludePattern('/tmp').match('/tmp/foo'), True)
|
||||
self.assert_equal(ExcludePattern('/tmp').match('/tmofoo'), False)
|
||||
|
||||
|
||||
class MakePathSafeTestCase(AtticTestCase):
|
||||
|
||||
def test(self):
|
||||
self.assert_equal(make_path_safe('/foo/bar'), 'foo/bar')
|
||||
self.assert_equal(make_path_safe('/foo/bar'), 'foo/bar')
|
||||
self.assert_equal(make_path_safe('../foo/bar'), 'foo/bar')
|
||||
self.assert_equal(make_path_safe('../../foo/bar'), 'foo/bar')
|
||||
self.assert_equal(make_path_safe('/'), '.')
|
||||
self.assert_equal(make_path_safe('/'), '.')
|
||||
|
||||
|
|
Loading…
Reference in a new issue