diff options
-rw-r--r-- | manifest_xml.py | 7 | ||||
-rw-r--r-- | project.py | 12 | ||||
-rw-r--r-- | tests/test_manifest_xml.py | 30 |
3 files changed, 45 insertions, 4 deletions
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 | |||
1039 | if not path: | 1039 | if not path: |
1040 | path = name | 1040 | path = name |
1041 | else: | 1041 | else: |
1042 | msg = self._CheckLocalPath(path, dir_ok=True) | 1042 | # NB: The "." project is handled specially in Project.Sync_LocalHalf. |
1043 | msg = self._CheckLocalPath(path, dir_ok=True, cwd_dot_ok=True) | ||
1043 | if msg: | 1044 | if msg: |
1044 | raise ManifestInvalidPathError( | 1045 | raise ManifestInvalidPathError( |
1045 | '<project> invalid "path": %s: %s' % (path, msg)) | 1046 | '<project> invalid "path": %s: %s' % (path, msg)) |
@@ -1227,7 +1228,9 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
1227 | # our constructed logic here. Especially since manifest authors only use | 1228 | # our constructed logic here. Especially since manifest authors only use |
1228 | # / in their paths. | 1229 | # / in their paths. |
1229 | resep = re.compile(r'[/%s]' % re.escape(os.path.sep)) | 1230 | resep = re.compile(r'[/%s]' % re.escape(os.path.sep)) |
1230 | parts = resep.split(path) | 1231 | # Strip off trailing slashes as those only produce '' elements, and we use |
1232 | # parts to look for individual bad components. | ||
1233 | parts = resep.split(path.rstrip('/')) | ||
1231 | 1234 | ||
1232 | # Some people use src="." to create stable links to projects. Lets allow | 1235 | # Some people use src="." to create stable links to projects. Lets allow |
1233 | # that but reject all other uses of "." to keep things simple. | 1236 | # that but reject all other uses of "." to keep things simple. |
@@ -1227,6 +1227,18 @@ class Project(object): | |||
1227 | self.CleanPublishedCache(all_refs) | 1227 | self.CleanPublishedCache(all_refs) |
1228 | revid = self.GetRevisionId(all_refs) | 1228 | revid = self.GetRevisionId(all_refs) |
1229 | 1229 | ||
1230 | # Special case the root of the repo client checkout. Make sure it doesn't | ||
1231 | # contain files being checked out to dirs we don't allow. | ||
1232 | if self.relpath == '.': | ||
1233 | PROTECTED_PATHS = {'.repo'} | ||
1234 | paths = set(self.work_git.ls_tree('-z', '--name-only', '--', revid).split('\0')) | ||
1235 | bad_paths = paths & PROTECTED_PATHS | ||
1236 | if bad_paths: | ||
1237 | syncbuf.fail(self, | ||
1238 | 'Refusing to checkout project that writes to protected ' | ||
1239 | 'paths: %s' % (', '.join(bad_paths),)) | ||
1240 | return | ||
1241 | |||
1230 | def _doff(): | 1242 | def _doff(): |
1231 | self._FastForward(revid) | 1243 | self._FastForward(revid) |
1232 | self._CopyAndLinkFiles() | 1244 | 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 = ( | |||
32 | '..', | 32 | '..', |
33 | '../', | 33 | '../', |
34 | './', | 34 | './', |
35 | './/', | ||
35 | 'foo/', | 36 | 'foo/', |
36 | './foo', | 37 | './foo', |
37 | '../foo', | 38 | '../foo', |
@@ -427,6 +428,28 @@ class ProjectElementTests(ManifestParseTestCase): | |||
427 | self.assertEqual(manifest.projects[0].objdir, | 428 | self.assertEqual(manifest.projects[0].objdir, |
428 | os.path.join(self.tempdir, '.repo/project-objects/a/path.git')) | 429 | os.path.join(self.tempdir, '.repo/project-objects/a/path.git')) |
429 | 430 | ||
431 | manifest = parse('a/path', 'foo//////') | ||
432 | self.assertEqual(manifest.projects[0].gitdir, | ||
433 | os.path.join(self.tempdir, '.repo/projects/foo.git')) | ||
434 | self.assertEqual(manifest.projects[0].objdir, | ||
435 | os.path.join(self.tempdir, '.repo/project-objects/a/path.git')) | ||
436 | |||
437 | def test_toplevel_path(self): | ||
438 | """Check handling of path=. specially.""" | ||
439 | def parse(name, path): | ||
440 | return self.getXmlManifest(f""" | ||
441 | <manifest> | ||
442 | <remote name="default-remote" fetch="http://localhost" /> | ||
443 | <default remote="default-remote" revision="refs/heads/main" /> | ||
444 | <project name="{name}" path="{path}" /> | ||
445 | </manifest> | ||
446 | """) | ||
447 | |||
448 | for path in ('.', './', './/', './//'): | ||
449 | manifest = parse('server/path', path) | ||
450 | self.assertEqual(manifest.projects[0].gitdir, | ||
451 | os.path.join(self.tempdir, '.repo/projects/..git')) | ||
452 | |||
430 | def test_bad_path_name_checks(self): | 453 | def test_bad_path_name_checks(self): |
431 | """Check handling of bad path & name attributes.""" | 454 | """Check handling of bad path & name attributes.""" |
432 | def parse(name, path): | 455 | def parse(name, path): |
@@ -454,8 +477,11 @@ class ProjectElementTests(ManifestParseTestCase): | |||
454 | 477 | ||
455 | with self.assertRaises(error.ManifestInvalidPathError): | 478 | with self.assertRaises(error.ManifestInvalidPathError): |
456 | parse(path, 'ok') | 479 | parse(path, 'ok') |
457 | with self.assertRaises(error.ManifestInvalidPathError): | 480 | |
458 | parse('ok', path) | 481 | # We have a dedicated test for path=".". |
482 | if path not in {'.'}: | ||
483 | with self.assertRaises(error.ManifestInvalidPathError): | ||
484 | parse('ok', path) | ||
459 | 485 | ||
460 | 486 | ||
461 | class SuperProjectElementTests(ManifestParseTestCase): | 487 | class SuperProjectElementTests(ManifestParseTestCase): |