diff --git a/borg/helpers.py b/borg/helpers.py index 7c4a0fa91..2d1ec5100 100644 --- a/borg/helpers.py +++ b/borg/helpers.py @@ -823,11 +823,17 @@ class Location: """ # path must not contain :: (it ends at :: or string end), but may contain single colons. - # to avoid ambiguities with other regexes, it must also not start with ":". + # to avoid ambiguities with other regexes, it must also not start with ":" nor with "//" nor with "ssh://". path_re = r""" - (?!:) # not starting with ":" + (?!(:|//|ssh://)) # not starting with ":" or // or ssh:// (?P([^:]|(:(?!:)))+) # any chars, but no "::" """ + # abs_path must not contain :: (it ends at :: or string end), but may contain single colons. + # it must start with a / and that slash is part of the path. + abs_path_re = r""" + (?P(/([^:]|(:(?!:)))+)) # start with /, then any chars, but no "::" + """ + # optional ::archive_name at the end, archive name must not contain "/". # borg mount's FUSE filesystem creates one level of directories from # the archive names and of course "/" is not valid in a directory name. @@ -842,7 +848,7 @@ class Location: (?Pssh):// # ssh:// """ + optional_user_re + r""" # user@ (optional) (?P[^:/]+)(?::(?P\d+))? # host or host:port - """ + path_re + optional_archive_re, re.VERBOSE) # path or path::archive + """ + abs_path_re + optional_archive_re, re.VERBOSE) # path or path::archive file_re = re.compile(r""" (?Pfile):// # file:// diff --git a/borg/testsuite/helpers.py b/borg/testsuite/helpers.py index 3f9c70969..dcf1859ec 100644 --- a/borg/testsuite/helpers.py +++ b/borg/testsuite/helpers.py @@ -134,6 +134,11 @@ class TestLocationWithoutEnv: location_time2 = Location('/some/path::archive{now:%s}') assert location_time1.archive != location_time2.archive + def test_bad_syntax(self): + with pytest.raises(ValueError): + # this is invalid due to the 2nd colon, correct: 'ssh://user@host/path' + Location('ssh://user@host:/path') + class TestLocationWithEnv: def test_ssh(self, monkeypatch):