Bug 1374557 - Part 2 - Use the new preference to whitelist paths for reading that are needed by tests; r?haik,gcp draft
authorAlex Gaynor <agaynor@mozilla.com>
Wed, 21 Jun 2017 10:24:16 -0400
changeset 602744 97b7462a993da82ca1e5dc0048422a6915b4a175
parent 602743 8464eb0c00a44a3a635a955e7bf69a12d7c64384
child 635692 698ad0cd8257bc791cb57248e27f6f2ab64005ec
push id66530
push userbmo:agaynor@mozilla.com
push dateFri, 30 Jun 2017 18:02:22 +0000
reviewershaik, gcp
bugs1374557
milestone56.0a1
Bug 1374557 - Part 2 - Use the new preference to whitelist paths for reading that are needed by tests; r?haik,gcp MozReview-Commit-ID: 4IaTrHPHZaC
layout/tools/reftest/mach_commands.py
layout/tools/reftest/reftestcommandline.py
layout/tools/reftest/runreftest.py
testing/mochitest/mochitest_options.py
testing/mochitest/runtests.py
testing/mozbase/mozprofile/mozprofile/profile.py
testing/mozharness/configs/unittests/mac_unittest.py
testing/mozharness/scripts/desktop_unittest.py
--- a/layout/tools/reftest/mach_commands.py
+++ b/layout/tools/reftest/mach_commands.py
@@ -73,16 +73,17 @@ class ReftestRunner(MozbuildObject):
             "reftest": (self.topsrcdir, "layout", "reftests", "reftest.list"),
             "crashtest": (self.topsrcdir, "testing", "crashtest", "crashtests.list"),
             "jstestbrowser": (self.topobjdir, "dist", "test-stage", "jsreftest", "tests",
                               "jstests.list")
         }
 
         args.extraProfileFiles.append(os.path.join(self.topobjdir, "dist", "plugins"))
         args.symbolsPath = os.path.join(self.topobjdir, "crashreporter-symbols")
+        args.workPath = self.topsrcdir
 
         if not args.tests:
             args.tests = [os.path.join(*default_manifest[args.suite])]
 
         if args.suite == "jstestbrowser":
             args.extraProfileFiles.append(os.path.join(self.topobjdir, "dist",
                                                        "test-stage", "jsreftest",
                                                        "tests", "user.js"))
--- a/layout/tools/reftest/reftestcommandline.py
+++ b/layout/tools/reftest/reftestcommandline.py
@@ -236,16 +236,21 @@ class ReftestArgumentsParser(argparse.Ar
                           help="The maximum number of attempts to try and recover from a "
                                "crash before aborting the test run [default 4].")
 
         self.add_argument("tests",
                           metavar="TEST_PATH",
                           nargs="*",
                           help="Path to test file, manifest file, or directory containing tests")
 
+        self.add_argument("--work-path",
+                          action="store",
+                          dest="workPath",
+                          help="Path to the base dir of all test files.")
+
         mozlog.commandline.add_logging_group(self)
 
     def get_ip(self):
         import moznetwork
         if os.name != "nt":
             return moznetwork.get_ip()
         else:
             self.error(
--- a/layout/tools/reftest/runreftest.py
+++ b/layout/tools/reftest/runreftest.py
@@ -296,16 +296,28 @@ class RefTest(object):
             prefs['browser.tabs.remote.autostart'] = True
             prefs['extensions.e10sBlocksEnabling'] = False
 
         # Bug 1262954: For winXP + e10s disable acceleration
         if platform.system() in ("Windows", "Microsoft") and \
            '5.1' in platform.version() and options.e10s:
             prefs['layers.acceleration.disabled'] = True
 
+        sandbox_whitelist_paths = [SCRIPT_DIRECTORY]
+        try:
+            if options.workPath:
+                sandbox_whitelist_paths.append(options.workPath)
+        except AttributeError:
+            pass
+        if platform.system() == "Linux":
+            # Trailing slashes are needed to indicate directories on Linux
+            for idx, path in enumerate(sandbox_whitelist_paths):
+                if not path.endswith("/"):
+                    sandbox_whitelist_paths[idx] = path + "/"
+
         # Bug 1300355: Disable canvas cache for win7 as it uses
         # too much memory and causes OOMs.
         if platform.system() in ("Windows", "Microsoft") and \
            '6.1' in platform.version():
             prefs['reftest.nocache'] = True
 
         if options.marionette:
             port = options.marionette.split(':')[1]
@@ -340,17 +352,18 @@ class RefTest(object):
                 addons.append(os.path.join(distExtDir, f))
 
         # Install custom extensions.
         for f in options.extensionsToInstall:
             addons.append(self.getFullPath(f))
 
         kwargs = {'addons': addons,
                   'preferences': prefs,
-                  'locations': locations}
+                  'locations': locations,
+                  'whitelistpaths': sandbox_whitelist_paths}
         if profile_to_clone:
             profile = mozprofile.Profile.clone(profile_to_clone, **kwargs)
         else:
             profile = mozprofile.Profile(**kwargs)
 
         if os.path.join(here, 'chrome') not in options.extraProfileFiles:
             options.extraProfileFiles.append(os.path.join(here, 'chrome'))
 
--- a/testing/mochitest/mochitest_options.py
+++ b/testing/mochitest/mochitest_options.py
@@ -595,16 +595,22 @@ class MochitestArguments(ArgumentContain
           "help": "Port for websocket/process bridge. Default 8191.",
           }],
         [["--failure-pattern-file"],
          {"default": None,
           "dest": "failure_pattern_file",
           "help": "File describes all failure patterns of the tests.",
           "suppress": True,
           }],
+        [["--work-path"],
+         {"default": None,
+          "dest": "workPath",
+          "help": "Path to the base dir of all test files.",
+          "suppress": True,
+          }],
     ]
 
     defaults = {
         # Bug 1065098 - The geckomediaplugin process fails to produce a leak
         # log for some reason.
         'ignoreMissingLeaks': ["geckomediaplugin"],
         'extensionsToExclude': ['specialpowers'],
         # Set server information on the args object
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -1771,16 +1771,33 @@ toolbar#nav-bar {
 
         prefs.update(self.extraPrefs(options.extraPrefs))
 
         # Bug 1262954: For windows XP + e10s disable acceleration
         if platform.system() in ("Windows", "Microsoft") and \
            '5.1' in platform.version() and options.e10s:
             prefs['layers.acceleration.disabled'] = True
 
+        sandbox_whitelist_paths = [SCRIPT_DIR]
+        try:
+            if options.workPath:
+                sandbox_whitelist_paths.append(options.workPath)
+        except AttributeError:
+            pass
+        try:
+            if options.topsrcdir:
+                sandbox_whitelist_paths.append(options.topsrcdir)
+        except AttributeError:
+            pass
+        if platform.system() == "Linux":
+            # Trailing slashes are needed to indicate directories on Linux
+            for idx, path in enumerate(sandbox_whitelist_paths):
+                if not path.endswith("/"):
+                    sandbox_whitelist_paths[idx] = path + "/"
+
         # interpolate preferences
         interpolation = {
             "server": "%s:%s" %
             (options.webServer, options.httpPort)}
 
         prefs = json.loads(json.dumps(prefs) % interpolation)
         for pref in prefs:
             prefs[pref] = Preferences.cast(prefs[pref])
@@ -1804,17 +1821,18 @@ toolbar#nav-bar {
             prefs['media.audio_loopback_dev'] = self.mediaDevices['audio']
             prefs['media.video_loopback_dev'] = self.mediaDevices['video']
 
         # create a profile
         self.profile = Profile(profile=options.profilePath,
                                addons=extensions,
                                locations=self.locations,
                                preferences=prefs,
-                               proxy=proxy
+                               proxy=proxy,
+                               whitelistpaths=sandbox_whitelist_paths
                                )
 
         # Fix options.profilePath for legacy consumers.
         options.profilePath = self.profile.profile
 
         manifest = self.addChromeToProfile(options)
         self.copyExtraFilesToProfile(options)
 
--- a/testing/mozbase/mozprofile/mozprofile/profile.py
+++ b/testing/mozbase/mozprofile/mozprofile/profile.py
@@ -1,13 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import os
+import platform
 import time
 import tempfile
 import uuid
 
 from addons import AddonManager
 import mozfile
 from permissions import Permissions
 from prefs import Preferences
@@ -39,25 +40,28 @@ class Profile(object):
 
       with Profile() as profile:
           # do things with the profile
           pass
       # profile.cleanup() has been called here
     """
 
     def __init__(self, profile=None, addons=None, addon_manifests=None,
-                 preferences=None, locations=None, proxy=None, restore=True):
+                 preferences=None, locations=None, proxy=None, restore=True,
+                 whitelistpaths=None):
         """
         :param profile: Path to the profile
         :param addons: String of one or list of addons to install
         :param addon_manifests: Manifest for addons (see http://bit.ly/17jQ7i6)
         :param preferences: Dictionary or class of preferences
         :param locations: ServerLocations object
         :param proxy: Setup a proxy
         :param restore: Flag for removing all custom settings during cleanup
+        :param whitelistpaths: List of paths to pass to Firefox to allow read
+            access to from the content process sandbox.
         """
         self._addons = addons
         self._addon_manifests = addon_manifests
         self._locations = locations
         self._proxy = proxy
 
         # Prepare additional preferences
         if preferences:
@@ -65,16 +69,17 @@ class Profile(object):
                 # unordered
                 preferences = preferences.items()
 
             # sanity check
             assert not [i for i in preferences if len(i) != 2]
         else:
             preferences = []
         self._preferences = preferences
+        self._whitelistpaths = whitelistpaths
 
         # Handle profile creation
         self.create_new = not profile
         if profile:
             # Ensure we have a full path to the profile
             self.profile = os.path.abspath(os.path.expanduser(profile))
         else:
             self.profile = tempfile.mkdtemp(suffix='.mozrunner')
@@ -101,16 +106,35 @@ class Profile(object):
         # If sub-classes want to set default preferences
         if hasattr(self.__class__, 'preferences'):
             self.set_preferences(self.__class__.preferences)
         # Set additional preferences
         self.set_preferences(self._preferences)
 
         self.permissions = Permissions(self.profile, self._locations)
         prefs_js, user_js = self.permissions.network_prefs(self._proxy)
+
+        if self._whitelistpaths:
+            # On macOS we don't want to support a generalized read whitelist,
+            # and the macOS sandbox policy language doesn't have support for
+            # lists, so we handle these specially.
+            if platform.system() == "Darwin":
+                assert len(self._whitelistpaths) <= 2
+                if len(self._whitelistpaths) == 2:
+                    prefs_js.append((
+                        "security.sandbox.content.mac.testing_read_path2",
+                        self._whitelistpaths[1]
+                    ))
+                prefs_js.append((
+                    "security.sandbox.content.mac.testing_read_path1",
+                    self._whitelistpaths[0]
+                ))
+            else:
+                prefs_js.append(("security.sandbox.content.read_path_whitelist",
+                                 ",".join(self._whitelistpaths)))
         self.set_preferences(prefs_js, 'prefs.js')
         self.set_preferences(user_js)
 
         # handle add-on installation
         self.addon_manager = AddonManager(self.profile, restore=self.restore)
         self.addon_manager.install_addons(self._addons, self._addon_manifests)
 
     def __enter__(self):
--- a/testing/mozharness/configs/unittests/mac_unittest.py
+++ b/testing/mozharness/configs/unittests/mac_unittest.py
@@ -82,16 +82,17 @@ config = {
                 "--extra-profile-file=tests/bin/plugins",
                 "--symbols-path=%(symbols_path)s",
                 "--certificate-path=tests/certs",
                 "--quiet",
                 "--log-raw=%(raw_log_file)s",
                 "--log-errorsummary=%(error_summary_file)s",
                 "--screenshot-on-fail",
                 "--cleanup-crashes",
+                "--work-path=%(abs_work_dir)s",
             ],
             "run_filename": "runtests.py",
             "testsdir": "mochitest"
         },
         "mozbase": {
             "options": [
                 "-b",
                 "%(binary_path)s"
@@ -113,16 +114,17 @@ config = {
             "options": [
                 "--appname=%(binary_path)s",
                 "--utility-path=tests/bin",
                 "--extra-profile-file=tests/bin/plugins",
                 "--symbols-path=%(symbols_path)s",
                 "--log-raw=%(raw_log_file)s",
                 "--log-errorsummary=%(error_summary_file)s",
                 "--cleanup-crashes",
+                "--work-path=%(abs_work_dir)s",
             ],
             "run_filename": "runreftest.py",
             "testsdir": "reftest"
         },
         "xpcshell": {
             "options": [
                 "--symbols-path=%(symbols_path)s",
                 "--test-plugin-path=%(test_plugin_path)s",
--- a/testing/mozharness/scripts/desktop_unittest.py
+++ b/testing/mozharness/scripts/desktop_unittest.py
@@ -365,16 +365,17 @@ class DesktopUnittest(TestingMixin, Merc
                 'binary_path': self.binary_path,
                 'symbols_path': self._query_symbols_url(),
                 'abs_app_dir': abs_app_dir,
                 'abs_res_dir': abs_res_dir,
                 'raw_log_file': raw_log_file,
                 'error_summary_file': error_summary_file,
                 'gtest_dir': os.path.join(dirs['abs_test_install_dir'],
                                           'gtest'),
+                'abs_work_dir': dirs['abs_work_dir'],
             }
 
             # TestingMixin._download_and_extract_symbols() will set
             # self.symbols_path when downloading/extracting.
             if self.symbols_path:
                 str_format_values['symbols_path'] = self.symbols_path
 
             if suite_category in SUITE_DEFAULT_E10S and not c['e10s']: