Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(77)

Unified Diff: tools/isolate/run_test_from_archive.py

Issue 10880009: Enforces strict manifest content. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | tools/isolate/run_test_from_archive_test.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/isolate/run_test_from_archive.py
diff --git a/tools/isolate/run_test_from_archive.py b/tools/isolate/run_test_from_archive.py
index 7710a6ed7059e7391b29b50e260c338a79ac9031..023a0df779f889fef65138f320a92eedeabda4e1 100755
--- a/tools/isolate/run_test_from_archive.py
+++ b/tools/isolate/run_test_from_archive.py
@@ -26,6 +26,13 @@ import urllib
# Types of action accepted by recreate_tree().
HARDLINK, SYMLINK, COPY = range(1, 4)
+RE_IS_SHA1 = re.compile(r'^[a-fA-F0-9]{40}$')
+
+
+class ConfigError(ValueError):
+ """Generic failure to load a manifest."""
+ pass
+
class MappingError(OSError):
"""Failed to recreate the tree."""
@@ -172,6 +179,101 @@ def make_temp_dir(prefix, root_dir):
return tempfile.mkdtemp(prefix=prefix, dir=base_temp_dir)
+def assert_is_str(value):
+ if not isinstance(value, basestring):
+ raise ConfigError('Expected string, got %r' % value)
+
+
+def assert_is_int(value):
+ if not isinstance(value, int):
+ raise ConfigError('Expected int, got %r' % value)
+
+
+def assert_is_bool(value):
+ if value not in (None, True, False):
+ raise ConfigError('Expected bool, got %r' % value)
+
+
+def assert_is_sha1(value):
+ assert_is_str(value)
+ if not RE_IS_SHA1.match(value):
+ raise ConfigError('Expected sha-1, got %r' % value)
+
+
+def get_assert_is_list(check):
+ """Returns a function that asserts the value is a list."""
+ def is_list_item(value):
+ if not isinstance(value, list):
+ raise ConfigError('Expected list, got %r' % value)
+ any(check(item) for item in value)
+ return is_list_item
+
+
+def get_assert_is_dict(key_check, values_check):
+ """Returns a function that ensure the value is a dict.
+
+ It checks all the keys passes key_check and values_check is a dict of each
+ checks for each key.
+
+ If values_check contains a None entry, keys not in values_check will be
+ checked with this key. If None is not present in values_check, unknown keys
+ will be refused.
+ """
+ def is_dict_item(value):
+ if not isinstance(value, dict):
+ raise ConfigError('Expected dict, got %r' % value)
+
+ for key in value:
+ key_check(key)
+
+ # values_check is the set of valid keys.
+ unknown = set(value) - set(values_check)
+ if unknown and not None in values_check:
+ raise ConfigError(
+ 'Got unknown key(s) %s in %r; expected only %s' %
+ (', '.join(unknown), value, ', '.join(values_check)))
+ for k, v in value.iteritems():
+ if k in values_check:
+ values_check[k](v)
+ else:
+ values_check[None](v)
+ return is_dict_item
+
+
+def load_manifest(content):
+ """Verifies the manifest is valid and loads this object with the json data.
+ """
+ data = json.loads(content)
+
+ file_keys = {
+ 'link': assert_is_str,
+ 'mode': assert_is_int,
+ 'sha-1': assert_is_sha1,
+ 'timestamp': assert_is_int,
+ }
+
+ files_key = {
+ # Any file has to fit files_keys specification.
+ None: get_assert_is_dict(assert_is_str, file_keys),
+ }
+
+ keys = {
+ 'command': get_assert_is_list(assert_is_str),
+ # Could use assert_is_valid_filename instead of assert_is_str.
+ 'files': get_assert_is_dict(assert_is_str, files_key),
+ 'read_only': assert_is_bool,
+ 'relative_cwd': assert_is_str,
+ }
+
+ get_assert_is_dict(assert_is_str, keys)(data)
cmp 2012/08/28 17:38:29 this code requires a lot of state to be in-mind to
+ # Add a special verification.
+ for value in data.get('files', {}).itervalues():
+ if bool('sha-1' in value) and bool('link' in value):
+ raise ConfigError(
+ 'Did not expect both \'sha-1\' and \'link\', got: %r' % value)
+ return data
+
+
def fix_python_path(cmd):
"""Returns the fixed command line to call the right python executable."""
out = cmd[:]
@@ -393,7 +495,7 @@ def run_tha_test(manifest, cache_dir, remote, policies):
# A symlink.
os.symlink(properties['link'], outfile)
else:
- raise ValueError('Unexpected entry: %s' % properties)
+ raise ConfigError('Unexpected entry: %s' % properties)
if 'mode' in properties:
# It's not set on Windows.
os.chmod(outfile, properties['mode'])
@@ -482,7 +584,7 @@ def main():
# First calculate the reference to it.
options.manifest = '%s/%s' % (options.remote.rstrip('/'), options.hash)
try:
- manifest = json.load(open_remote(options.manifest))
+ manifest = load_manifest(open_remote(options.manifest).read())
except IOError as e:
parser.error(
'Failed to read manifest %s; remote:%s; hash:%s; %s' %
@@ -496,7 +598,7 @@ def main():
os.path.abspath(options.cache),
options.remote,
policies)
- except MappingError, e:
+ except (ConfigError, MappingError), e:
print >> sys.stderr, str(e)
return 1
« no previous file with comments | « no previous file | tools/isolate/run_test_from_archive_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698