diff options
| author | Mike Frysinger <vapier@google.com> | 2021-04-29 23:15:31 -0400 |
|---|---|---|
| committer | Mike Frysinger <vapier@google.com> | 2021-04-30 05:54:11 +0000 |
| commit | f69c7ee3187eded54e83d2524fea423706380766 (patch) | |
| tree | e78c5ba603ef7e7975a2df87071a8afa1910dd5a | |
| parent | aabf79d3f0736af3bbe9f2e830df6165e06b6ef6 (diff) | |
| download | git-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.py | 11 | ||||
| -rw-r--r-- | tests/test_manifest_xml.py | 16 |
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', '
').replace('\n', '
') | ||
| 101 | |||
| 94 | 102 | ||
| 95 | class ManifestValidateFilePaths(unittest.TestCase): | 103 | class 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" /> |
