diff --git a/manifest_xml.py b/manifest_xml.py index 6d8fca1d..d67ba72d 100644 --- a/manifest_xml.py +++ b/manifest_xml.py @@ -1039,7 +1039,8 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md if not path: path = name else: - msg = self._CheckLocalPath(path, dir_ok=True) + # NB: The "." project is handled specially in Project.Sync_LocalHalf. + msg = self._CheckLocalPath(path, dir_ok=True, cwd_dot_ok=True) if msg: raise ManifestInvalidPathError( ' invalid "path": %s: %s' % (path, msg)) @@ -1227,7 +1228,9 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md # our constructed logic here. Especially since manifest authors only use # / in their paths. resep = re.compile(r'[/%s]' % re.escape(os.path.sep)) - parts = resep.split(path) + # Strip off trailing slashes as those only produce '' elements, and we use + # parts to look for individual bad components. + parts = resep.split(path.rstrip('/')) # Some people use src="." to create stable links to projects. Lets allow # that but reject all other uses of "." to keep things simple. diff --git a/project.py b/project.py index bc385f20..2567c57d 100644 --- a/project.py +++ b/project.py @@ -1227,6 +1227,18 @@ class Project(object): self.CleanPublishedCache(all_refs) revid = self.GetRevisionId(all_refs) + # Special case the root of the repo client checkout. Make sure it doesn't + # contain files being checked out to dirs we don't allow. + if self.relpath == '.': + PROTECTED_PATHS = {'.repo'} + paths = set(self.work_git.ls_tree('-z', '--name-only', '--', revid).split('\0')) + bad_paths = paths & PROTECTED_PATHS + if bad_paths: + syncbuf.fail(self, + 'Refusing to checkout project that writes to protected ' + 'paths: %s' % (', '.join(bad_paths),)) + return + def _doff(): self._FastForward(revid) self._CopyAndLinkFiles() diff --git a/tests/test_manifest_xml.py b/tests/test_manifest_xml.py index 9060ef3d..eda06968 100644 --- a/tests/test_manifest_xml.py +++ b/tests/test_manifest_xml.py @@ -32,6 +32,7 @@ INVALID_FS_PATHS = ( '..', '../', './', + './/', 'foo/', './foo', '../foo', @@ -427,6 +428,28 @@ class ProjectElementTests(ManifestParseTestCase): self.assertEqual(manifest.projects[0].objdir, os.path.join(self.tempdir, '.repo/project-objects/a/path.git')) + manifest = parse('a/path', 'foo//////') + self.assertEqual(manifest.projects[0].gitdir, + os.path.join(self.tempdir, '.repo/projects/foo.git')) + self.assertEqual(manifest.projects[0].objdir, + os.path.join(self.tempdir, '.repo/project-objects/a/path.git')) + + def test_toplevel_path(self): + """Check handling of path=. specially.""" + def parse(name, path): + return self.getXmlManifest(f""" + + + + + +""") + + for path in ('.', './', './/', './//'): + manifest = parse('server/path', path) + self.assertEqual(manifest.projects[0].gitdir, + os.path.join(self.tempdir, '.repo/projects/..git')) + def test_bad_path_name_checks(self): """Check handling of bad path & name attributes.""" def parse(name, path): @@ -454,8 +477,11 @@ class ProjectElementTests(ManifestParseTestCase): with self.assertRaises(error.ManifestInvalidPathError): parse(path, 'ok') - with self.assertRaises(error.ManifestInvalidPathError): - parse('ok', path) + + # We have a dedicated test for path=".". + if path not in {'.'}: + with self.assertRaises(error.ManifestInvalidPathError): + parse('ok', path) class SuperProjectElementTests(ManifestParseTestCase):