blob: 362316a547c290a1e822c8fe17d592cee9c66033 [file] [log] [blame]
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import json
import logging
import os
from autotest_lib.client.common_lib import error, site_utils
from autotest_lib.client.cros import cros_ui_test
class security_BundledExtensions(cros_ui_test.UITest):
"""Verify security properties of bundled (on-disk) Extensions."""
version = 2
def load_baseline(self):
Loads the set of expected security properties.
@return Dictionary of expected security properties
bfile = open(os.path.join(self.bindir, 'baseline'))
with open(os.path.join(self.bindir, 'baseline')) as bfile:
baseline = []
for line in bfile:
if not line.startswith('#'):
baseline = json.loads(''.join(baseline))
self._ignored_extension_ids = baseline['ignored_extension_ids']
self._bundled_crx_baseline = baseline['bundled_crx_baseline']
self._component_extension_baseline = baseline[
self._official_components = baseline['official_components']
def get_extensions_info(self):
Wraps the pyauto method GetExtensionsInfo().
Filters out extensions that are on the to-be-ignored list.
@return list of dicts, each representing an extension
complete_info = self.pyauto.GetExtensionsInfo()
filtered_info = []
for rec in complete_info:
if not rec['id'] in self._ignored_extension_ids:
return filtered_info
def attempt_install(self, crx_file):
"""Try to install a crx, and log an error if it fails.
@param crx_file string containing the path to a .crx file
# This helps limit the degree to which future bugs like
# interfere with testing. The test will still
# fail, but at least the test will complete (and notice any
# problems in any *other* extensions).
import pyauto_errors
logging.debug('Installing %s', crx_file)
self.pyauto.InstallExtension(crx_file, from_webstore=True)
except pyauto_errors.JSONInterfaceError:
logging.error('Installation failed for %s', crx_file)
def install_all(self, crx_dirs):
Install all crx's located in crx_dirs
@param crx_dirs a list of strings, each a path to a directory
for crx_dir in crx_dirs:
if not os.path.exists(crx_dir):
for file_name in os.listdir(crx_dir):
if not file_name.endswith('.crx'):
crx_id = self.crx_id_from_filename(file_name)
if crx_id in self._ignored_extension_ids:
logging.debug('Ignoring %s', file_name)
self.attempt_install(os.path.join(crx_dir, file_name))
logging.debug('Done installing extensions')
def crx_id_from_filename(self, filename):
Obtain the id of a crx, given its filename.
@param filename the name of the crx file
@return the crx id
return filename.split('.crx')[0]
def install_and_compare(self):
"""Install all extensions and compare to the expected set."""
test_fail = False
# Install all bundled extensions on the device.
# Per these all now live here:
# * Find the set of expected IDs.
# * Find the set of observed IDs.
# * Do set comparison to find the unexpected, and the expected/missing.
combined_baseline = (self._bundled_crx_baseline +
# Filter out any baseline entries that don't apply to this board.
# If there is no 'boards' limiter on a given record, the record applies.
# If there IS a 'boards' limiter, check that it applies.
board = site_utils.get_current_board()
combined_baseline = [x for x in combined_baseline
if ((not 'boards' in x) or
('boards' in x and board in x['boards']))]
observed_extensions = self.get_extensions_info()
observed_ids = set([x['id'] for x in observed_extensions])
expected_ids = set([x['id'] for x in combined_baseline])
missing_ids = expected_ids - observed_ids
missing_names = ['%s (%s)' % (x['name'], x['id'])
for x in combined_baseline if x['id'] in missing_ids]
unexpected_ids = observed_ids - expected_ids
unexpected_names = ['%s (%s)' % (x['name'], x['id'])
for x in observed_extensions if
x['id'] in unexpected_ids]
good_ids = expected_ids.intersection(observed_ids)
if missing_names:
logging.error('Missing: %s', '; '.join(missing_names))
test_fail = True
if unexpected_names:
logging.error('Unexpected: %s', '; '.join(unexpected_names))
test_fail = True
# For those IDs in both the expected-and-observed, ie, "good":
# Compare sets of expected-vs-actual API permissions, report diffs.
# Do same for host permissions.
for good_id in good_ids:
baseline = [x for x in combined_baseline if x['id'] == good_id][0]
actual = [x for x in observed_extensions if x['id'] == good_id][0]
# Check the API permissions.
baseline_apis = set(baseline['api_permissions'])
actual_apis = set(actual['api_permissions'])
missing_apis = baseline_apis - actual_apis
unexpected_apis = actual_apis - baseline_apis
if missing_apis or unexpected_apis:
test_fail = True
self._report_attribute_diffs(missing_apis, unexpected_apis,
# Check the host permissions.
baseline_hosts = set(baseline['effective_host_permissions'])
actual_hosts = set(actual['effective_host_permissions'])
missing_hosts = baseline_hosts - actual_hosts
unexpected_hosts = actual_hosts - baseline_hosts
if missing_hosts or unexpected_hosts:
test_fail = True
self._report_attribute_diffs(missing_hosts, unexpected_hosts,
if test_fail:
raise error.TestFail('Bundled extensions mismatch, see error log.')
def _report_attribute_diffs(self, missing, unexpected, rec):
logging.error('Problem with %s (%s):', rec['name'], rec['id'])
if missing:
logging.error('It no longer uses: %s', '; '.join(missing))
if unexpected:
logging.error('It unexpectedly uses: %s', '; '.join(unexpected))
def run_once(self, mode=None):
if self.pyauto.GetBrowserInfo()['properties']['is_official']: