blob: a8978fc60c0b2322dd39dbdc0b294ddedfc2b2a1 [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.bin import test
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):
version = 2
def load_baseline(self):
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.append(line)
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[
'component_extension_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.
Returns:
A list of dicts, each representing an extension. For more
information, see the pyauto documentation.
"""
complete_info = self.pyauto.GetExtensionsInfo()
logging.debug("GetExtensionsInfo:\n%s" %
self.pyauto.pformat(complete_info))
filtered_info = []
for rec in complete_info:
if not rec['id'] in self._ignored_extension_ids:
filtered_info.append(rec)
return filtered_info
def attempt_install(self, crx_file):
"""Try to install a crx, and log an error if it fails.
Args:
crx_file: A string containing the path to a .crx file.
"""
# This helps limit the degree to which future bugs like
# crbug.com/131480 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)
try:
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):
for crx_dir in crx_dirs:
if not os.path.exists(crx_dir):
continue
for file_name in os.listdir(crx_dir):
if not file_name.endswith('.crx'):
continue
crx_id = self.crx_id_from_filename(file_name)
if crx_id in self._ignored_extension_ids:
logging.debug('Ignoring %s' % file_name)
continue
self.attempt_install(os.path.join(crx_dir, file_name))
logging.debug('Done installing extensions')
def crx_id_from_filename(self, filename):
return filename.split('.crx')[0]
def install_and_compare(self):
test_fail = False
# Install all bundled extensions on the device.
main_crx_dir = '/opt/google/chrome/extensions'
board_specific_crx_dir = '/usr/share/google-chrome/extensions'
self.install_all([main_crx_dir, board_specific_crx_dir])
# * 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 +
self._component_extension_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,
actual)
# 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,
actual)
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):
self.load_baseline()
if self.pyauto.GetBrowserInfo()['properties']['is_official']:
self._component_extension_baseline.extend(self._official_components)
self.install_and_compare()