summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Frysinger <vapier@google.com>2021-04-29 23:15:31 -0400
committerMike Frysinger <vapier@google.com>2021-04-30 05:54:11 +0000
commitf69c7ee3187eded54e83d2524fea423706380766 (patch)
treee78c5ba603ef7e7975a2df87071a8afa1910dd5a
parentaabf79d3f0736af3bbe9f2e830df6165e06b6ef6 (diff)
downloadgit-repo-f69c7ee3187eded54e83d2524fea423706380766.tar.gz
manifest_xml: ban use of newlines in paths
There should be no valid use of these anywhere, so just ban them to make things easier for people. Bug: https://crbug.com/gerrit/14156 Bug: https://crbug.com/gerrit/14200 Change-Id: I8d2cf988c510c98194c43a329a2b9bf313a3f0a8 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/304662 Reviewed-by: Raman Tenneti <rtenneti@google.com> Tested-by: Mike Frysinger <vapier@google.com>
-rw-r--r--manifest_xml.py11
-rw-r--r--tests/test_manifest_xml.py16
2 files changed, 26 insertions, 1 deletions
diff --git a/manifest_xml.py b/manifest_xml.py
index 0c2b45e5..64b7fb4e 100644
--- a/manifest_xml.py
+++ b/manifest_xml.py
@@ -1199,6 +1199,8 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
1199 if '~' in path: 1199 if '~' in path:
1200 return '~ not allowed (due to 8.3 filenames on Windows filesystems)' 1200 return '~ not allowed (due to 8.3 filenames on Windows filesystems)'
1201 1201
1202 path_codepoints = set(path)
1203
1202 # Some filesystems (like Apple's HFS+) try to normalize Unicode codepoints 1204 # Some filesystems (like Apple's HFS+) try to normalize Unicode codepoints
1203 # which means there are alternative names for ".git". Reject paths with 1205 # which means there are alternative names for ".git". Reject paths with
1204 # these in it as there shouldn't be any reasonable need for them here. 1206 # these in it as there shouldn't be any reasonable need for them here.
@@ -1222,10 +1224,17 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
1222 u'\u206F', # NOMINAL DIGIT SHAPES 1224 u'\u206F', # NOMINAL DIGIT SHAPES
1223 u'\uFEFF', # ZERO WIDTH NO-BREAK SPACE 1225 u'\uFEFF', # ZERO WIDTH NO-BREAK SPACE
1224 } 1226 }
1225 if BAD_CODEPOINTS & set(path): 1227 if BAD_CODEPOINTS & path_codepoints:
1226 # This message is more expansive than reality, but should be fine. 1228 # This message is more expansive than reality, but should be fine.
1227 return 'Unicode combining characters not allowed' 1229 return 'Unicode combining characters not allowed'
1228 1230
1231 # Reject newlines as there shouldn't be any legitmate use for them, they'll
1232 # be confusing to users, and they can easily break tools that expect to be
1233 # able to iterate over newline delimited lists. This even applies to our
1234 # own code like .repo/project.list.
1235 if {'\r', '\n'} & path_codepoints:
1236 return 'Newlines not allowed'
1237
1229 # Assume paths might be used on case-insensitive filesystems. 1238 # Assume paths might be used on case-insensitive filesystems.
1230 path = path.lower() 1239 path = path.lower()
1231 1240
diff --git a/tests/test_manifest_xml.py b/tests/test_manifest_xml.py
index eda06968..e78d85c3 100644
--- a/tests/test_manifest_xml.py
+++ b/tests/test_manifest_xml.py
@@ -52,6 +52,9 @@ INVALID_FS_PATHS = (
52 'blah/foo~', 52 'blah/foo~',
53 # Block Unicode characters that get normalized out by filesystems. 53 # Block Unicode characters that get normalized out by filesystems.
54 u'foo\u200Cbar', 54 u'foo\u200Cbar',
55 # Block newlines.
56 'f\n/bar',
57 'f\r/bar',
55) 58)
56 59
57# Make sure platforms that use path separators (e.g. Windows) are also 60# Make sure platforms that use path separators (e.g. Windows) are also
@@ -91,6 +94,11 @@ class ManifestParseTestCase(unittest.TestCase):
91 fp.write(data) 94 fp.write(data)
92 return manifest_xml.XmlManifest(self.repodir, self.manifest_file) 95 return manifest_xml.XmlManifest(self.repodir, self.manifest_file)
93 96
97 @staticmethod
98 def encodeXmlAttr(attr):
99 """Encode |attr| using XML escape rules."""
100 return attr.replace('\r', '&#x000d;').replace('\n', '&#x000a;')
101
94 102
95class ManifestValidateFilePaths(unittest.TestCase): 103class ManifestValidateFilePaths(unittest.TestCase):
96 """Check _ValidateFilePaths helper. 104 """Check _ValidateFilePaths helper.
@@ -303,6 +311,7 @@ class IncludeElementTests(ManifestParseTestCase):
303 def test_allow_bad_name_from_user(self): 311 def test_allow_bad_name_from_user(self):
304 """Check handling of bad name attribute from the user's input.""" 312 """Check handling of bad name attribute from the user's input."""
305 def parse(name): 313 def parse(name):
314 name = self.encodeXmlAttr(name)
306 manifest = self.getXmlManifest(f""" 315 manifest = self.getXmlManifest(f"""
307<manifest> 316<manifest>
308 <remote name="default-remote" fetch="http://localhost" /> 317 <remote name="default-remote" fetch="http://localhost" />
@@ -327,6 +336,7 @@ class IncludeElementTests(ManifestParseTestCase):
327 def test_bad_name_checks(self): 336 def test_bad_name_checks(self):
328 """Check handling of bad name attribute.""" 337 """Check handling of bad name attribute."""
329 def parse(name): 338 def parse(name):
339 name = self.encodeXmlAttr(name)
330 # Setup target of the include. 340 # Setup target of the include.
331 with open(os.path.join(self.manifest_dir, 'target.xml'), 'w') as fp: 341 with open(os.path.join(self.manifest_dir, 'target.xml'), 'w') as fp:
332 fp.write(f'<manifest><include name="{name}"/></manifest>') 342 fp.write(f'<manifest><include name="{name}"/></manifest>')
@@ -408,6 +418,8 @@ class ProjectElementTests(ManifestParseTestCase):
408 def test_trailing_slash(self): 418 def test_trailing_slash(self):
409 """Check handling of trailing slashes in attributes.""" 419 """Check handling of trailing slashes in attributes."""
410 def parse(name, path): 420 def parse(name, path):
421 name = self.encodeXmlAttr(name)
422 path = self.encodeXmlAttr(path)
411 return self.getXmlManifest(f""" 423 return self.getXmlManifest(f"""
412<manifest> 424<manifest>
413 <remote name="default-remote" fetch="http://localhost" /> 425 <remote name="default-remote" fetch="http://localhost" />
@@ -437,6 +449,8 @@ class ProjectElementTests(ManifestParseTestCase):
437 def test_toplevel_path(self): 449 def test_toplevel_path(self):
438 """Check handling of path=. specially.""" 450 """Check handling of path=. specially."""
439 def parse(name, path): 451 def parse(name, path):
452 name = self.encodeXmlAttr(name)
453 path = self.encodeXmlAttr(path)
440 return self.getXmlManifest(f""" 454 return self.getXmlManifest(f"""
441<manifest> 455<manifest>
442 <remote name="default-remote" fetch="http://localhost" /> 456 <remote name="default-remote" fetch="http://localhost" />
@@ -453,6 +467,8 @@ class ProjectElementTests(ManifestParseTestCase):
453 def test_bad_path_name_checks(self): 467 def test_bad_path_name_checks(self):
454 """Check handling of bad path & name attributes.""" 468 """Check handling of bad path & name attributes."""
455 def parse(name, path): 469 def parse(name, path):
470 name = self.encodeXmlAttr(name)
471 path = self.encodeXmlAttr(path)
456 manifest = self.getXmlManifest(f""" 472 manifest = self.getXmlManifest(f"""
457<manifest> 473<manifest>
458 <remote name="default-remote" fetch="http://localhost" /> 474 <remote name="default-remote" fetch="http://localhost" />