terrasync.py: more thorough checking of .dirindex contents
- only accept ASCII-encoded .dirindex files (this is guaranteed to work fine "everywhere"); - reject .dirindex files with a 'path' entry that contains a backslash or starts with a slash; - reject .dirindex files with a 'path' entry that contains a '..' component; - reject .dirindex files with an 'f', 'd' or 't' entry whose name field contains a slash or a backslash; - reject .dirindex files with an 'f', 'd' or 't' entry whose name field is '..'; - add comment lines (starting with '#') in the sample good .dirindex file used by unit tests.
This commit is contained in:
parent
4049acd84e
commit
692ab6835f
16 changed files with 86 additions and 3 deletions
|
@ -38,11 +38,29 @@ class DirIndex:
|
||||||
# attribute. This is useful for troubleshooting.
|
# attribute. This is useful for troubleshooting.
|
||||||
self._rawContents = None
|
self._rawContents = None
|
||||||
|
|
||||||
with open(dirIndexFile, "r", encoding="utf-8") as f:
|
with open(dirIndexFile, "r", encoding="ascii") as f:
|
||||||
self.readFrom(f)
|
self.readFrom(f)
|
||||||
|
|
||||||
self._sanityCheck()
|
self._sanityCheck()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def checkForBackslashOrLeadingSlash(cls, line, path):
|
||||||
|
if '\\' in path or path.startswith('/'):
|
||||||
|
raise InvalidDirIndexFile(
|
||||||
|
r"invalid '\' or leading '/' in path field from line {!r}"
|
||||||
|
.format(line))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def checkForSlashBackslashOrDoubleColon(cls, line, name):
|
||||||
|
if '/' in name or '\\' in name:
|
||||||
|
raise InvalidDirIndexFile(
|
||||||
|
r"invalid '\' or '/' in name field from line {!r}"
|
||||||
|
.format(line))
|
||||||
|
|
||||||
|
if name == "..":
|
||||||
|
raise InvalidDirIndexFile(
|
||||||
|
r"invalid name field equal to '..' in line {!r}".format(line))
|
||||||
|
|
||||||
def readFrom(self, readable):
|
def readFrom(self, readable):
|
||||||
self._rawContents = readable.read()
|
self._rawContents = readable.read()
|
||||||
|
|
||||||
|
@ -57,14 +75,23 @@ class DirIndex:
|
||||||
elif tokens[0] == "version":
|
elif tokens[0] == "version":
|
||||||
self.version = int(tokens[1])
|
self.version = int(tokens[1])
|
||||||
elif tokens[0] == "path":
|
elif tokens[0] == "path":
|
||||||
|
self.checkForBackslashOrLeadingSlash(line, tokens[1])
|
||||||
# This is relative to the repository root
|
# This is relative to the repository root
|
||||||
self.path = VirtualPath(tokens[1])
|
self.path = VirtualPath(tokens[1])
|
||||||
|
|
||||||
|
if ".." in self.path.parts:
|
||||||
|
raise InvalidDirIndexFile(
|
||||||
|
"'..' component found in 'path' entry {!r}"
|
||||||
|
.format(self.path))
|
||||||
elif tokens[0] == "d":
|
elif tokens[0] == "d":
|
||||||
|
self.checkForSlashBackslashOrDoubleColon(line, tokens[1])
|
||||||
self.directories.append({'name': tokens[1], 'hash': tokens[2]})
|
self.directories.append({'name': tokens[1], 'hash': tokens[2]})
|
||||||
elif tokens[0] == "f":
|
elif tokens[0] == "f":
|
||||||
|
self.checkForSlashBackslashOrDoubleColon(line, tokens[1])
|
||||||
self.files.append({'name': tokens[1],
|
self.files.append({'name': tokens[1],
|
||||||
'hash': tokens[2], 'size': int(tokens[3])})
|
'hash': tokens[2], 'size': int(tokens[3])})
|
||||||
elif tokens[0] == "t":
|
elif tokens[0] == "t":
|
||||||
|
self.checkForSlashBackslashOrDoubleColon(line, tokens[1])
|
||||||
self.tarballs.append({'name': tokens[1], 'hash': tokens[2],
|
self.tarballs.append({'name': tokens[1], 'hash': tokens[2],
|
||||||
'size': int(tokens[3])})
|
'size': int(tokens[3])})
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
version:1
|
||||||
|
path:some/path
|
||||||
|
d:some\illegal directory name with a backslash:378b3dd58ce3058f2992b70aa5ecf8947a4d7f9e
|
|
@ -0,0 +1,3 @@
|
||||||
|
version:1
|
||||||
|
path:some/path
|
||||||
|
f:some\illegal file name with a backslash:4cbf3d1746a1249bff7809e4b079dd80cfce594c:123
|
|
@ -0,0 +1,3 @@
|
||||||
|
version:1
|
||||||
|
path:some/path
|
||||||
|
t:some\illegal tarball name with a backslash.tgz:b63a067d82824f158d6bde66f9e76654274277fe:1234567
|
|
@ -0,0 +1,3 @@
|
||||||
|
version:1
|
||||||
|
path:some/path
|
||||||
|
d:..:378b3dd58ce3058f2992b70aa5ecf8947a4d7f9e
|
|
@ -0,0 +1,2 @@
|
||||||
|
version:1
|
||||||
|
path:some/path/with/a/../component
|
|
@ -0,0 +1,2 @@
|
||||||
|
version:1
|
||||||
|
path:some/path/non-ASCII chars like é, ê, €, Œ, Ÿ, etc./foo/bar
|
|
@ -0,0 +1,3 @@
|
||||||
|
version:1
|
||||||
|
path:some/path
|
||||||
|
f:..:4cbf3d1746a1249bff7809e4b079dd80cfce594c:123
|
|
@ -0,0 +1,2 @@
|
||||||
|
version:1
|
||||||
|
path:some/path/that/contains \ a/backslash
|
|
@ -0,0 +1,2 @@
|
||||||
|
version:1
|
||||||
|
path:/some/path/that/starts/with/a/slash
|
|
@ -0,0 +1,3 @@
|
||||||
|
version:1
|
||||||
|
path:some/path
|
||||||
|
d:some/illegal directory name with a slash:378b3dd58ce3058f2992b70aa5ecf8947a4d7f9e
|
|
@ -0,0 +1,3 @@
|
||||||
|
version:1
|
||||||
|
path:some/path
|
||||||
|
f:some/illegal file name with a slash:4cbf3d1746a1249bff7809e4b079dd80cfce594c:123
|
|
@ -0,0 +1,3 @@
|
||||||
|
version:1
|
||||||
|
path:some/path
|
||||||
|
t:some/illegal tarball name with a slash.tgz:b63a067d82824f158d6bde66f9e76654274277fe:1234567
|
|
@ -0,0 +1,3 @@
|
||||||
|
version:1
|
||||||
|
path:some/path
|
||||||
|
t:..:b63a067d82824f158d6bde66f9e76654274277fe:1234567
|
|
@ -1,9 +1,11 @@
|
||||||
|
# Comment line
|
||||||
version:1
|
version:1
|
||||||
path:some/path
|
path:some/path
|
||||||
time:20200926-10:38Z
|
time:20200926-10:38Z
|
||||||
d:Airports:8a93b5d8a2b04d2fb8de4ef58ad02f9e8819d314
|
d:Airports:8a93b5d8a2b04d2fb8de4ef58ad02f9e8819d314
|
||||||
d:Models:bee221c9d2621dc9b69cd9e0ad7dd0605f6ea928
|
d:Models:bee221c9d2621dc9b69cd9e0ad7dd0605f6ea928
|
||||||
d:Objects:10ae32c986470fa55b56b8eefbc6ed565cce0642
|
d:Objects:10ae32c986470fa55b56b8eefbc6ed565cce0642
|
||||||
|
# Other comment line
|
||||||
d:Terrain:e934024dc0f959f9a433e47c646d256630052c2e
|
d:Terrain:e934024dc0f959f9a433e47c646d256630052c2e
|
||||||
d:Buildings:19060725efc2a301fa6844991e2922d42d8de5e2
|
d:Buildings:19060725efc2a301fa6844991e2922d42d8de5e2
|
||||||
d:Pylons:378b3dd58ce3058f2992b70aa5ecf8947a4d7f9e
|
d:Pylons:378b3dd58ce3058f2992b70aa5ecf8947a4d7f9e
|
|
@ -28,6 +28,7 @@
|
||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
from terrasync.dirindex import DirIndex
|
from terrasync.dirindex import DirIndex
|
||||||
|
from terrasync.exceptions import InvalidDirIndexFile
|
||||||
from terrasync.virtual_path import VirtualPath
|
from terrasync.virtual_path import VirtualPath
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,10 +67,28 @@ tarballs_in_sample_dirindex_1 = [
|
||||||
class TestDirIndex(unittest.TestCase):
|
class TestDirIndex(unittest.TestCase):
|
||||||
"""Unit tests for the DirIndex class."""
|
"""Unit tests for the DirIndex class."""
|
||||||
|
|
||||||
def test_readFrom(self):
|
def test_constructor(self):
|
||||||
d = DirIndex(testData("sample_dirindex_1"))
|
d = DirIndex(testData("good", "sample_dirindex_1"))
|
||||||
self.assertEqual(d.version, 1)
|
self.assertEqual(d.version, 1)
|
||||||
self.assertEqual(d.path, VirtualPath("some/path"))
|
self.assertEqual(d.path, VirtualPath("some/path"))
|
||||||
self.assertEqual(d.directories, directories_in_sample_dirindex_1)
|
self.assertEqual(d.directories, directories_in_sample_dirindex_1)
|
||||||
self.assertEqual(d.files, files_in_sample_dirindex_1)
|
self.assertEqual(d.files, files_in_sample_dirindex_1)
|
||||||
self.assertEqual(d.tarballs, tarballs_in_sample_dirindex_1)
|
self.assertEqual(d.tarballs, tarballs_in_sample_dirindex_1)
|
||||||
|
|
||||||
|
for stem in ("path_starts_with_slash",
|
||||||
|
"path_contains_a_backslash",
|
||||||
|
"dotdot_in_path",
|
||||||
|
"slash_in_directory_name",
|
||||||
|
"slash_in_file_name",
|
||||||
|
"slash_in_tarball_name",
|
||||||
|
"backslash_in_directory_name",
|
||||||
|
"backslash_in_file_name",
|
||||||
|
"backslash_in_tarball_name",
|
||||||
|
"directory_name_is_double_colon",
|
||||||
|
"file_name_is_double_colon",
|
||||||
|
"tarball_name_is_double_colon"):
|
||||||
|
with self.assertRaises(InvalidDirIndexFile):
|
||||||
|
DirIndex(testData("bad", "bad_dirindex_" + stem))
|
||||||
|
|
||||||
|
with self.assertRaises(UnicodeDecodeError):
|
||||||
|
d = DirIndex(testData("bad", "bad_dirindex_encoding"))
|
||||||
|
|
Loading…
Reference in a new issue