From a29424ea6d6f5a38ef9c25141c9f095161dbd3ff Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Thu, 25 Feb 2021 21:53:49 -0500 Subject: manifest: validate project name & path and include name attributes These attribute values are used to construct local filesystem paths, so apply the existing filesystem checks to them. Bug: https://crbug.com/gerrit/14156 Change-Id: Ibcceecd60fa74f0eb97cd9ed1a9792e139534ed4 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/298443 Reviewed-by: Michael Mortensen Tested-by: Mike Frysinger --- tests/test_manifest_xml.py | 230 +++++++++++++++++++++++++++++++-------------- 1 file changed, 159 insertions(+), 71 deletions(-) (limited to 'tests/test_manifest_xml.py') diff --git a/tests/test_manifest_xml.py b/tests/test_manifest_xml.py index 4b545140..f69e9cf8 100644 --- a/tests/test_manifest_xml.py +++ b/tests/test_manifest_xml.py @@ -24,6 +24,40 @@ import error import manifest_xml +# Invalid paths that we don't want in the filesystem. +INVALID_FS_PATHS = ( + '', + '.', + '..', + '../', + './', + 'foo/', + './foo', + '../foo', + 'foo/./bar', + 'foo/../../bar', + '/foo', + './../foo', + '.git/foo', + # Check case folding. + '.GIT/foo', + 'blah/.git/foo', + '.repo/foo', + '.repoconfig', + # Block ~ due to 8.3 filenames on Windows filesystems. + '~', + 'foo~', + 'blah/foo~', + # Block Unicode characters that get normalized out by filesystems. + u'foo\u200Cbar', +) + +# Make sure platforms that use path separators (e.g. Windows) are also +# rejected properly. +if os.path.sep != '/': + INVALID_FS_PATHS += tuple(x.replace('/', os.path.sep) for x in INVALID_FS_PATHS) + + class ManifestParseTestCase(unittest.TestCase): """TestCase for parsing manifests.""" @@ -86,38 +120,7 @@ class ManifestValidateFilePaths(unittest.TestCase): def test_bad_paths(self): """Make sure bad paths (src & dest) are rejected.""" - PATHS = ( - '', - '.', - '..', - '../', - './', - 'foo/', - './foo', - '../foo', - 'foo/./bar', - 'foo/../../bar', - '/foo', - './../foo', - '.git/foo', - # Check case folding. - '.GIT/foo', - 'blah/.git/foo', - '.repo/foo', - '.repoconfig', - # Block ~ due to 8.3 filenames on Windows filesystems. - '~', - 'foo~', - 'blah/foo~', - # Block Unicode characters that get normalized out by filesystems. - u'foo\u200Cbar', - ) - # Make sure platforms that use path separators (e.g. Windows) are also - # rejected properly. - if os.path.sep != '/': - PATHS += tuple(x.replace('/', os.path.sep) for x in PATHS) - - for path in PATHS: + for path in INVALID_FS_PATHS: self.assertRaises( error.ManifestInvalidPathError, self.check_both, path, 'a') self.assertRaises( @@ -248,7 +251,82 @@ class XmlManifestTests(ManifestParseTestCase): '' + '') - def test_project_group(self): + +class IncludeElementTests(ManifestParseTestCase): + """Tests for .""" + + def test_group_levels(self): + root_m = os.path.join(self.manifest_dir, 'root.xml') + with open(root_m, 'w') as fp: + fp.write(""" + + + + + + + +""") + with open(os.path.join(self.manifest_dir, 'level1.xml'), 'w') as fp: + fp.write(""" + + + + +""") + with open(os.path.join(self.manifest_dir, 'level2.xml'), 'w') as fp: + fp.write(""" + + + +""") + include_m = manifest_xml.XmlManifest(self.repodir, root_m) + for proj in include_m.projects: + if proj.name == 'root-name1': + # Check include group not set on root level proj. + self.assertNotIn('level1-group', proj.groups) + if proj.name == 'root-name2': + # Check root proj group not removed. + self.assertIn('r2g1', proj.groups) + if proj.name == 'level1-name1': + # Check level1 proj has inherited group level 1. + self.assertIn('level1-group', proj.groups) + if proj.name == 'level2-name1': + # Check level2 proj has inherited group levels 1 and 2. + self.assertIn('level1-group', proj.groups) + self.assertIn('level2-group', proj.groups) + # Check level2 proj group not removed. + self.assertIn('l2g1', proj.groups) + + def test_bad_name_checks(self): + """Check handling of bad name attribute.""" + def parse(name): + manifest = self.getXmlManifest(f""" + + + + + +""") + # Force the manifest to be parsed. + manifest.ToXml() + + # Handle empty name explicitly because a different codepath rejects it. + with self.assertRaises(error.ManifestParseError): + parse('') + + for path in INVALID_FS_PATHS: + if not path: + continue + + with self.assertRaises(error.ManifestInvalidPathError): + parse(path) + + +class ProjectElementTests(ManifestParseTestCase): + """Tests for .""" + + def test_group(self): """Check project group settings.""" manifest = self.getXmlManifest(""" @@ -272,7 +350,7 @@ class XmlManifestTests(ManifestParseTestCase): result['extras'], ['g1', 'g2', 'g1', 'name:extras', 'all', 'path:path']) - def test_project_set_revision_id(self): + def test_set_revision_id(self): """Check setting of project's revisionId.""" manifest = self.getXmlManifest(""" @@ -292,51 +370,61 @@ class XmlManifestTests(ManifestParseTestCase): '' + '') - def test_include_levels(self): - root_m = os.path.join(self.manifest_dir, 'root.xml') - with open(root_m, 'w') as fp: - fp.write(""" - - - - - - - -""") - with open(os.path.join(self.manifest_dir, 'level1.xml'), 'w') as fp: - fp.write(""" + def test_trailing_slash(self): + """Check handling of trailing slashes in attributes.""" + def parse(name, path): + return self.getXmlManifest(f""" - - + + + """) - with open(os.path.join(self.manifest_dir, 'level2.xml'), 'w') as fp: - fp.write(""" + + 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')) + + 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_bad_path_name_checks(self): + """Check handling of bad path & name attributes.""" + def parse(name, path): + manifest = self.getXmlManifest(f""" - + + + """) - include_m = manifest_xml.XmlManifest(self.repodir, root_m) - for proj in include_m.projects: - if proj.name == 'root-name1': - # Check include group not set on root level proj. - self.assertNotIn('level1-group', proj.groups) - if proj.name == 'root-name2': - # Check root proj group not removed. - self.assertIn('r2g1', proj.groups) - if proj.name == 'level1-name1': - # Check level1 proj has inherited group level 1. - self.assertIn('level1-group', proj.groups) - if proj.name == 'level2-name1': - # Check level2 proj has inherited group levels 1 and 2. - self.assertIn('level1-group', proj.groups) - self.assertIn('level2-group', proj.groups) - # Check level2 proj group not removed. - self.assertIn('l2g1', proj.groups) + # Force the manifest to be parsed. + manifest.ToXml() + + # Verify the parser is valid by default to avoid buggy tests below. + parse('ok', 'ok') + + # Handle empty name explicitly because a different codepath rejects it. + # Empty path is OK because it defaults to the name field. + with self.assertRaises(error.ManifestParseError): + parse('', 'ok') + + for path in INVALID_FS_PATHS: + if not path or path.endswith('/'): + continue + + with self.assertRaises(error.ManifestInvalidPathError): + parse(path, 'ok') + with self.assertRaises(error.ManifestInvalidPathError): + parse('ok', path) -class SuperProjectTests(ManifestParseTestCase): +class SuperProjectElementTests(ManifestParseTestCase): """Tests for .""" def test_superproject(self): -- cgit v1.2.3-54-g00ecf