def test_audit_multiple_frameworks(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={"Click-Version": "0.4"}, manifest={ "name": "test-package", "version": "1.0", "framework": "ubuntu-sdk-14.04-basic, ubuntu-sdk-14.04-webapps", }, control_scripts={"preinst": static_preinst}) installer = ClickInstaller(self.db) self._setup_frameworks(preloads, frameworks=["dummy"]) self.assertRaisesRegex( ClickInstallerAuditError, 'Frameworks "ubuntu-sdk-14.04-basic", ' '"ubuntu-sdk-14.04-webapps" not present on system.*', installer.audit, path) self._setup_frameworks( preloads, frameworks=["dummy", "ubuntu-sdk-14.04-basic"]) self.assertRaisesRegex( ClickInstallerAuditError, 'Framework "ubuntu-sdk-14.04-webapps" not present on ' 'system.*', installer.audit, path) self._setup_frameworks(preloads, frameworks=[ "dummy", "ubuntu-sdk-14.04-basic", "ubuntu-sdk-14.04-webapps", ]) self.assertEqual(("test-package", "1.0"), installer.audit(path))
def test_audit_matching_md5sums(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() data_path = os.path.join(self.temp_dir, "foo") with mkfile(data_path) as data: print("test", file=data) with open(data_path, "rb") as data: data_md5sum = hashlib.md5(data.read()).hexdigest() path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, manifest={ "name": "test-package", "version": "1.0", "framework": "ubuntu-sdk-13.10", }, control_scripts={ "preinst": static_preinst, "md5sums": "%s foo" % data_md5sum, }, data_files={"foo": data_path}) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) with mock_quiet_subprocess_call(): installer = ClickInstaller(self.db) self.assertEqual(("test-package", "1.0"), installer.audit(path, slow=True))
def test_multiple_architectures(self, mock_dpkg_architecture, mock_package_install_hooks): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() mock_dpkg_architecture.return_value = "armhf" path = self.make_fake_package( control_fields={ "Package": "test-package", "Version": "1.1", "Architecture": "multi", "Maintainer": "Foo Bar <*****@*****.**>", "Description": "test", "Click-Version": "0.2", }, manifest={ "name": "test-package", "version": "1.1", "framework": "ubuntu-sdk-13.10", "architecture": ["armhf", "i386"], }, control_scripts={"preinst": static_preinst}) root = os.path.join(self.temp_dir, "root") db = Click.DB() db.add(root) installer = ClickInstaller(db) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) with mock_quiet_subprocess_call(): installer.install(path) self.assertTrue( os.path.exists(os.path.join(root, "test-package", "current")))
def test_upgrade(self, mock_package_install_hooks): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() os.environ["TEST_QUIET"] = "1" path = self.make_fake_package( control_fields={ "Package": "test-package", "Version": "1.1", "Architecture": "all", "Maintainer": "Foo Bar <*****@*****.**>", "Description": "test", "Click-Version": "0.2", }, manifest={ "name": "test-package", "version": "1.1", "framework": "ubuntu-sdk-13.10", }, control_scripts={"preinst": static_preinst}, data_files={"foo": None}) root = os.path.join(self.temp_dir, "root") package_dir = os.path.join(root, "test-package") inst_dir = os.path.join(package_dir, "current") os.makedirs(os.path.join(package_dir, "1.0")) os.symlink("1.0", inst_dir) db = Click.DB() db.add(root) installer = ClickInstaller(db) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) with mock_quiet_subprocess_call(): installer.install(path) self.assertCountEqual([".click", "test-package"], os.listdir(root)) self.assertCountEqual(["1.1", "current"], os.listdir(package_dir)) self.assertTrue(os.path.islink(inst_dir)) self.assertEqual("1.1", os.readlink(inst_dir)) self.assertCountEqual([".click", "foo"], os.listdir(inst_dir)) status_path = os.path.join(inst_dir, ".click", "status") with open(status_path) as status_file: # .readlines() avoids the need for a python-apt backport to # Ubuntu 12.04 LTS. status = list(Deb822.iter_paragraphs(status_file.readlines())) self.assertEqual(1, len(status)) self.assertEqual( { "Package": "test-package", "Status": "install ok installed", "Version": "1.1", "Architecture": "all", "Maintainer": "Foo Bar <*****@*****.**>", "Description": "test", "Click-Version": "0.2", }, status[0]) mock_package_install_hooks.assert_called_once_with(db, "test-package", "1.0", "1.1", user_name=None)
def run(argv): parser = OptionParser( dedent("""\ %prog install [options] PACKAGE-FILE This is a low-level tool; to install a package as an ordinary user you should generally use "pkcon install-local PACKAGE-FILE" instead.""")) parser.add_option("--root", metavar="PATH", help="install packages underneath PATH") parser.add_option("--force-missing-framework", action="store_true", default=False, help="install despite missing system framework") parser.add_option("--user", metavar="USER", help="register package for USER") parser.add_option("--all-users", default=False, action="store_true", help="register package for all users") parser.add_option("--allow-unauthenticated", default=False, action="store_true", help="allow installing packages with no signatures") parser.add_option("--verbose", default=False, action="store_true", help="be more verbose on install") options, args = parser.parse_args(argv) if len(args) < 1: parser.error("need package file name") db = Click.DB() db.read(db_dir=None) if options.root is not None: db.add(options.root) package_path = args[0] installer = ClickInstaller( db=db, force_missing_framework=options.force_missing_framework, allow_unauthenticated=options.allow_unauthenticated) try: installer.install(package_path, user=options.user, all_users=options.all_users, quiet=not options.verbose) except ClickInstallerError as e: print("Cannot install %s: %s" % (package_path, e), file=sys.stderr) return 1 return 0
def test_audit_passes_correct_package(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, manifest={ "name": "test-package", "version": "1.0", "framework": "ubuntu-sdk-13.10", }, control_scripts={"preinst": static_preinst}) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) installer = ClickInstaller(self.db) self.assertEqual(("test-package", "1.0"), installer.audit(path))
def test_audit_name_bad_character(self): path = self.make_fake_package(control_fields={"Click-Version": "0.2"}, manifest={"name": "../evil"}) self.assertRaisesRegex( ClickInstallerAuditError, 'Invalid character "/" in "name" entry: ../evil', ClickInstaller(self.db).audit, path)
def test_world_readable(self, mock_package_install_hooks): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() owner_only_file = os.path.join(self.temp_dir, "owner-only-file") touch(owner_only_file) os.chmod(owner_only_file, stat.S_IRUSR | stat.S_IWUSR) owner_only_dir = os.path.join(self.temp_dir, "owner-only-dir") os.mkdir(owner_only_dir, stat.S_IRWXU) path = self.make_fake_package( control_fields={ "Package": "test-package", "Version": "1.1", "Architecture": "all", "Maintainer": "Foo Bar <*****@*****.**>", "Description": "test", "Click-Version": "0.2", }, manifest={ "name": "test-package", "version": "1.1", "framework": "ubuntu-sdk-13.10", }, control_scripts={"preinst": static_preinst}, data_files={ "world-readable-file": owner_only_file, "world-readable-dir": owner_only_dir, }) root = os.path.join(self.temp_dir, "root") db = Click.DB() db.add(root) installer = ClickInstaller(db) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) with mock_quiet_subprocess_call(): installer.install(path) inst_dir = os.path.join(root, "test-package", "current") self.assertEqual( stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH, self._get_mode(os.path.join(inst_dir, "world-readable-file"))) self.assertEqual( stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH, self._get_mode(os.path.join(inst_dir, "world-readable-dir")))
def test_audit_requires_manifest(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, control_scripts={"preinst": static_preinst}) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) self.assertRaisesRegex(ClickInstallerAuditError, "Package has no manifest", ClickInstaller(self.db).audit, path)
def test_audit_no_framework(self): path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, manifest={ "name": "test-package", "version": "1.0" }, control_scripts={"preinst": static_preinst}) self.assertRaisesRegex(ClickInstallerAuditError, 'No "framework" entry in manifest', ClickInstaller(self.db).audit, path)
def run(argv): parser = OptionParser("%prog verify [options] PACKAGE-FILE") parser.add_option("--force-missing-framework", action="store_true", default=False, help="ignore missing system framework") parser.add_option("--allow-unauthenticated", action="store_true", default=False, help="allow installing packages with no sigantures") options, args = parser.parse_args(argv) if len(args) < 1: parser.error("need package file name") package_path = args[0] installer = ClickInstaller( db=None, force_missing_framework=options.force_missing_framework, allow_unauthenticated=options.allow_unauthenticated) installer.audit(package_path, slow=True) return 0
def test_audit_invalid_manifest_json(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, control_scripts={ "manifest": "{", "preinst": static_preinst }) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) self.assertRaises(ValueError, ClickInstaller(self.db).audit, path)
def test_audit_missing_framework_force(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, manifest={ "name": "test-package", "version": "1.0", "framework": "missing", }) self._setup_frameworks(preloads, frameworks=["present"]) ClickInstaller(self.db, True).audit(path)
def test_audit_forbids_depends(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package(control_fields={ "Click-Version": "0.2", "Depends": "libc6", }) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) self.assertRaisesRegex( ClickInstallerAuditError, "Depends field is forbidden in Click packages", ClickInstaller(self.db).audit, path)
def test_audit_no_signature(self): if not Click.find_on_path("debsig-verify"): self.skipTest("this test needs debsig-verify") path = self.make_fake_package(control_fields={"Click-Version": "0.4"}, manifest={ "name": "test-package", "version": "1.0", "framework": "", }) self.debsig_patcher.stop() self.assertRaisesRegex(ClickInstallerAuditError, "Signature verification error", ClickInstaller(self.db).audit, path) self.debsig_patcher.start()
def test_reinstall_preinstalled(self): # Attempting to reinstall a preinstalled version shouldn't actually # reinstall it in an overlay database (which would cause # irreconcilable confusion about the correct target for system hook # symlinks), but should instead simply update the user registration. path = self.make_fake_package( control_fields={ "Package": "test-package", "Version": "1.1", "Architecture": "all", "Maintainer": "Foo Bar <*****@*****.**>", "Description": "test", "Click-Version": "0.4", }, manifest={ "name": "test-package", "version": "1.1", "framework": "ubuntu-sdk-13.10", }, control_scripts={"preinst": static_preinst}) underlay = os.path.join(self.temp_dir, "underlay") overlay = os.path.join(self.temp_dir, "overlay") db = Click.DB() db.add(underlay) installer = ClickInstaller(db, True) with mock_quiet_subprocess_call(): installer.install(path, all_users=True) underlay_unpacked = os.path.join(underlay, "test-package", "1.1") self.assertTrue(os.path.exists(underlay_unpacked)) all_link = os.path.join(underlay, ".click", "users", "@all", "test-package") self.assertTrue(os.path.islink(all_link)) self.assertEqual(underlay_unpacked, os.readlink(all_link)) db.add(overlay) registry = Click.User.for_user(db, "test-user") registry.remove("test-package") user_link = os.path.join(overlay, ".click", "users", "test-user", "test-package") self.assertTrue(os.path.islink(user_link)) self.assertEqual("@hidden", os.readlink(user_link)) installer = ClickInstaller(db, True) with mock_quiet_subprocess_call(): installer.install(path, user="******") overlay_unpacked = os.path.join(overlay, "test-package", "1.1") self.assertFalse(os.path.exists(overlay_unpacked)) self.assertEqual("1.1", registry.get_version("test-package"))
def test_audit_forbids_maintscript(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, control_scripts={ "preinst": "#! /bin/sh\n", "postinst": "#! /bin/sh\n", }) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) self.assertRaisesRegex( ClickInstallerAuditError, r"Maintainer scripts are forbidden in Click packages " r"\(found: postinst preinst\)", ClickInstaller(self.db).audit, path)
def test_audit_missing_framework(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, manifest={ "name": "test-package", "version": "1.0", "framework": "missing", }, control_scripts={"preinst": static_preinst}) self._setup_frameworks(preloads, frameworks=["present"]) self.assertRaisesRegex( ClickInstallerAuditError, 'Framework "missing" not present on system.*', ClickInstaller(self.db).audit, path)
def test_audit_missing_dot_slash(self): # Manually construct a package with data paths that do not start # with "./", which could be used to bypass path filtering. with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, manifest={ "name": "test-package", "version": "1.0", "framework": "ubuntu-sdk-13.10", }, control_scripts={"preinst": static_preinst}, data_files={".click/tmp.ci/manifest": None}) # Repack without the leading "./". data_dir = os.path.join(self.temp_dir, "fake-package") data_tar_path = os.path.join(self.temp_dir, "data.tar.gz") control_tar_path = os.path.join(self.temp_dir, "control.tar.gz") package_path = '%s.click' % data_dir with closing( tarfile.TarFile.open( name=data_tar_path, mode="w:gz", format=tarfile.GNU_FORMAT)) as data_tar: data_tar.add(os.path.join(data_dir, ".click"), arcname=".click") with ArFile(name=package_path, mode="w") as package: package.add_magic() package.add_data("debian-binary", b"2.0\n") package.add_data("_click-binary", ("%s\n" % spec_version).encode("UTF-8")) package.add_file("control.tar.gz", control_tar_path) package.add_file("data.tar.gz", data_tar_path) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) with mock_quiet_subprocess_call(): installer = ClickInstaller(self.db) self.assertRaisesRegex( ClickInstallerAuditError, 'File name ".click" in package does not start with "./"', installer.audit, path)
def test_no_write_permission(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, manifest={ "name": "test-package", "version": "1.0", "framework": "ubuntu-sdk-13.10", }, control_scripts={"preinst": static_preinst}) write_mask = ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) installer = ClickInstaller(self.db) temp_dir_mode = os.stat(self.temp_dir).st_mode try: os.chmod(self.temp_dir, temp_dir_mode & write_mask) self.assertRaises(ClickInstallerPermissionDenied, installer.install, path) finally: os.chmod(self.temp_dir, temp_dir_mode)
def test_sandbox(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() original_call = subprocess.check_output def call_side_effect(*args, **kwargs): return original_call( ["touch", os.path.join(self.temp_dir, "sentinel")], **kwargs) path = self.make_fake_package( control_fields={ "Package": "test-package", "Version": "1.0", "Architecture": "all", "Maintainer": "Foo Bar <*****@*****.**>", "Description": "test", "Click-Version": "0.2", }, manifest={ "name": "test-package", "version": "1.0", "framework": "ubuntu-sdk-13.10", }, control_scripts={"preinst": static_preinst}, data_files={"foo": None}) root = os.path.join(self.temp_dir, "root") db = Click.DB() db.add(root) installer = ClickInstaller(db) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) with mock.patch("subprocess.check_output") as mock_call: mock_call.side_effect = call_side_effect self.assertRaises(subprocess.CalledProcessError, installer.install, path) self.assertFalse( os.path.exists(os.path.join(self.temp_dir, "sentinel")))
def test_audit_broken_md5sums(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, manifest={ "name": "test-package", "version": "1.0", "framework": "ubuntu-sdk-13.10", }, control_scripts={ "preinst": static_preinst, "md5sums": "%s foo" % ("0" * 32), }, data_files={"foo": None}) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) with mock_quiet_subprocess_call(): installer = ClickInstaller(self.db) self.assertRaises(subprocess.CalledProcessError, installer.audit, path, slow=True)
class TestClickInstaller(TestCase): def setUp(self): super(TestClickInstaller, self).setUp() self.use_temp_dir() self.db = Click.DB() self.db.add(self.temp_dir) # mock signature checks during the tests self.debsig_patcher = mock.patch("click_package.install.DebsigVerify") self.debsig_patcher.start() def tearDown(self): self.debsig_patcher.stop() def make_fake_package(self, control_fields=None, manifest=None, control_scripts=None, data_files=None): """Build a fake package with given contents.""" control_fields = {} if control_fields is None else control_fields control_scripts = {} if control_scripts is None else control_scripts data_files = {} if data_files is None else data_files data_dir = os.path.join(self.temp_dir, "fake-package") control_dir = os.path.join(self.temp_dir, "DEBIAN") with mkfile(os.path.join(control_dir, "control")) as control: for key, value in control_fields.items(): print('%s: %s' % (key.title(), value), file=control) print(file=control) if manifest is not None: with mkfile(os.path.join(control_dir, "manifest")) as f: json.dump(manifest, f) print(file=f) for name, contents in control_scripts.items(): with mkfile(os.path.join(control_dir, name)) as script: script.write(contents) Click.ensuredir(data_dir) for name, path in data_files.items(): Click.ensuredir(os.path.dirname(os.path.join(data_dir, name))) if path is None: touch(os.path.join(data_dir, name)) elif os.path.isdir(path): shutil.copytree(path, os.path.join(data_dir, name)) else: shutil.copy2(path, os.path.join(data_dir, name)) package_path = '%s.click' % data_dir ClickBuilder()._pack(self.temp_dir, control_dir, data_dir, package_path) return package_path def test_audit_no_click_version(self): path = self.make_fake_package() self.assertRaisesRegex(ClickInstallerAuditError, "No Click-Version field", ClickInstaller(self.db).audit, path) def test_audit_bad_click_version(self): path = self.make_fake_package(control_fields={"Click-Version": "|"}) self.assertRaises(ValueError, ClickInstaller(self.db).audit, path) def test_audit_new_click_version(self): path = self.make_fake_package(control_fields={"Click-Version": "999"}) self.assertRaisesRegex( ClickInstallerAuditError, "Click-Version: 999 newer than maximum supported version .*", ClickInstaller(self.db).audit, path) def test_audit_forbids_depends(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package(control_fields={ "Click-Version": "0.2", "Depends": "libc6", }) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) self.assertRaisesRegex( ClickInstallerAuditError, "Depends field is forbidden in Click packages", ClickInstaller(self.db).audit, path) def test_audit_forbids_maintscript(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, control_scripts={ "preinst": "#! /bin/sh\n", "postinst": "#! /bin/sh\n", }) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) self.assertRaisesRegex( ClickInstallerAuditError, r"Maintainer scripts are forbidden in Click packages " r"\(found: postinst preinst\)", ClickInstaller(self.db).audit, path) def test_audit_requires_manifest(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, control_scripts={"preinst": static_preinst}) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) self.assertRaisesRegex(ClickInstallerAuditError, "Package has no manifest", ClickInstaller(self.db).audit, path) def test_audit_invalid_manifest_json(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, control_scripts={ "manifest": "{", "preinst": static_preinst }) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) self.assertRaises(ValueError, ClickInstaller(self.db).audit, path) def test_audit_no_name(self): path = self.make_fake_package(control_fields={"Click-Version": "0.2"}, manifest={}) self.assertRaisesRegex(ClickInstallerAuditError, 'No "name" entry in manifest', ClickInstaller(self.db).audit, path) def test_audit_name_bad_character(self): path = self.make_fake_package(control_fields={"Click-Version": "0.2"}, manifest={"name": "../evil"}) self.assertRaisesRegex( ClickInstallerAuditError, 'Invalid character "/" in "name" entry: ../evil', ClickInstaller(self.db).audit, path) def test_audit_no_version(self): path = self.make_fake_package(control_fields={"Click-Version": "0.2"}, manifest={"name": "test-package"}) self.assertRaisesRegex(ClickInstallerAuditError, 'No "version" entry in manifest', ClickInstaller(self.db).audit, path) def test_audit_no_framework(self): path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, manifest={ "name": "test-package", "version": "1.0" }, control_scripts={"preinst": static_preinst}) self.assertRaisesRegex(ClickInstallerAuditError, 'No "framework" entry in manifest', ClickInstaller(self.db).audit, path) def test_audit_missing_framework(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, manifest={ "name": "test-package", "version": "1.0", "framework": "missing", }, control_scripts={"preinst": static_preinst}) self._setup_frameworks(preloads, frameworks=["present"]) self.assertRaisesRegex( ClickInstallerAuditError, 'Framework "missing" not present on system.*', ClickInstaller(self.db).audit, path) # FIXME: we really want a unit test with a valid signature too def test_audit_no_signature(self): if not Click.find_on_path("debsig-verify"): self.skipTest("this test needs debsig-verify") path = self.make_fake_package(control_fields={"Click-Version": "0.4"}, manifest={ "name": "test-package", "version": "1.0", "framework": "", }) self.debsig_patcher.stop() self.assertRaisesRegex(ClickInstallerAuditError, "Signature verification error", ClickInstaller(self.db).audit, path) self.debsig_patcher.start() @disable_logging def test_audit_missing_framework_force(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, manifest={ "name": "test-package", "version": "1.0", "framework": "missing", }) self._setup_frameworks(preloads, frameworks=["present"]) ClickInstaller(self.db, True).audit(path) def test_audit_passes_correct_package(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, manifest={ "name": "test-package", "version": "1.0", "framework": "ubuntu-sdk-13.10", }, control_scripts={"preinst": static_preinst}) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) installer = ClickInstaller(self.db) self.assertEqual(("test-package", "1.0"), installer.audit(path)) def test_audit_multiple_frameworks(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={"Click-Version": "0.4"}, manifest={ "name": "test-package", "version": "1.0", "framework": "ubuntu-sdk-14.04-basic, ubuntu-sdk-14.04-webapps", }, control_scripts={"preinst": static_preinst}) installer = ClickInstaller(self.db) self._setup_frameworks(preloads, frameworks=["dummy"]) self.assertRaisesRegex( ClickInstallerAuditError, 'Frameworks "ubuntu-sdk-14.04-basic", ' '"ubuntu-sdk-14.04-webapps" not present on system.*', installer.audit, path) self._setup_frameworks( preloads, frameworks=["dummy", "ubuntu-sdk-14.04-basic"]) self.assertRaisesRegex( ClickInstallerAuditError, 'Framework "ubuntu-sdk-14.04-webapps" not present on ' 'system.*', installer.audit, path) self._setup_frameworks(preloads, frameworks=[ "dummy", "ubuntu-sdk-14.04-basic", "ubuntu-sdk-14.04-webapps", ]) self.assertEqual(("test-package", "1.0"), installer.audit(path)) def test_audit_missing_dot_slash(self): # Manually construct a package with data paths that do not start # with "./", which could be used to bypass path filtering. with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, manifest={ "name": "test-package", "version": "1.0", "framework": "ubuntu-sdk-13.10", }, control_scripts={"preinst": static_preinst}, data_files={".click/tmp.ci/manifest": None}) # Repack without the leading "./". data_dir = os.path.join(self.temp_dir, "fake-package") data_tar_path = os.path.join(self.temp_dir, "data.tar.gz") control_tar_path = os.path.join(self.temp_dir, "control.tar.gz") package_path = '%s.click' % data_dir with closing( tarfile.TarFile.open( name=data_tar_path, mode="w:gz", format=tarfile.GNU_FORMAT)) as data_tar: data_tar.add(os.path.join(data_dir, ".click"), arcname=".click") with ArFile(name=package_path, mode="w") as package: package.add_magic() package.add_data("debian-binary", b"2.0\n") package.add_data("_click-binary", ("%s\n" % spec_version).encode("UTF-8")) package.add_file("control.tar.gz", control_tar_path) package.add_file("data.tar.gz", data_tar_path) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) with mock_quiet_subprocess_call(): installer = ClickInstaller(self.db) self.assertRaisesRegex( ClickInstallerAuditError, 'File name ".click" in package does not start with "./"', installer.audit, path) def test_audit_broken_md5sums(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, manifest={ "name": "test-package", "version": "1.0", "framework": "ubuntu-sdk-13.10", }, control_scripts={ "preinst": static_preinst, "md5sums": "%s foo" % ("0" * 32), }, data_files={"foo": None}) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) with mock_quiet_subprocess_call(): installer = ClickInstaller(self.db) self.assertRaises(subprocess.CalledProcessError, installer.audit, path, slow=True) def test_audit_matching_md5sums(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() data_path = os.path.join(self.temp_dir, "foo") with mkfile(data_path) as data: print("test", file=data) with open(data_path, "rb") as data: data_md5sum = hashlib.md5(data.read()).hexdigest() path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, manifest={ "name": "test-package", "version": "1.0", "framework": "ubuntu-sdk-13.10", }, control_scripts={ "preinst": static_preinst, "md5sums": "%s foo" % data_md5sum, }, data_files={"foo": data_path}) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) with mock_quiet_subprocess_call(): installer = ClickInstaller(self.db) self.assertEqual(("test-package", "1.0"), installer.audit(path, slow=True)) def test_no_write_permission(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={"Click-Version": "0.2"}, manifest={ "name": "test-package", "version": "1.0", "framework": "ubuntu-sdk-13.10", }, control_scripts={"preinst": static_preinst}) write_mask = ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) installer = ClickInstaller(self.db) temp_dir_mode = os.stat(self.temp_dir).st_mode try: os.chmod(self.temp_dir, temp_dir_mode & write_mask) self.assertRaises(ClickInstallerPermissionDenied, installer.install, path) finally: os.chmod(self.temp_dir, temp_dir_mode) @skipUnless(os.path.exists(ClickInstaller(None)._preload_path()), "preload bits not built; installing packages will fail") @mock.patch("gi.repository.Click.package_install_hooks") def test_install(self, mock_package_install_hooks): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() path = self.make_fake_package( control_fields={ "Package": "test-package", "Version": "1.0", "Architecture": "all", "Maintainer": "Foo Bar <*****@*****.**>", "Description": "test", "Click-Version": "0.2", }, manifest={ "name": "test-package", "version": "1.0", "framework": "ubuntu-sdk-13.10", }, control_scripts={"preinst": static_preinst}, data_files={"foo": None}) root = os.path.join(self.temp_dir, "root") db = Click.DB() db.add(root) installer = ClickInstaller(db) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) with mock_quiet_subprocess_call(): installer.install(path) self.assertCountEqual([".click", "test-package"], os.listdir(root)) package_dir = os.path.join(root, "test-package") self.assertCountEqual(["1.0", "current"], os.listdir(package_dir)) inst_dir = os.path.join(package_dir, "current") self.assertTrue(os.path.islink(inst_dir)) self.assertEqual("1.0", os.readlink(inst_dir)) self.assertCountEqual([".click", "foo"], os.listdir(inst_dir)) status_path = os.path.join(inst_dir, ".click", "status") with open(status_path) as status_file: # .readlines() avoids the need for a python-apt backport to # Ubuntu 12.04 LTS. status = list(Deb822.iter_paragraphs(status_file.readlines())) self.assertEqual(1, len(status)) self.assertEqual( { "Package": "test-package", "Status": "install ok installed", "Version": "1.0", "Architecture": "all", "Maintainer": "Foo Bar <*****@*****.**>", "Description": "test", "Click-Version": "0.2", }, status[0]) mock_package_install_hooks.assert_called_once_with(db, "test-package", None, "1.0", user_name=None) @skipUnless(os.path.exists(ClickInstaller(None)._preload_path()), "preload bits not built; installing packages will fail") def test_sandbox(self): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() original_call = subprocess.check_output def call_side_effect(*args, **kwargs): return original_call( ["touch", os.path.join(self.temp_dir, "sentinel")], **kwargs) path = self.make_fake_package( control_fields={ "Package": "test-package", "Version": "1.0", "Architecture": "all", "Maintainer": "Foo Bar <*****@*****.**>", "Description": "test", "Click-Version": "0.2", }, manifest={ "name": "test-package", "version": "1.0", "framework": "ubuntu-sdk-13.10", }, control_scripts={"preinst": static_preinst}, data_files={"foo": None}) root = os.path.join(self.temp_dir, "root") db = Click.DB() db.add(root) installer = ClickInstaller(db) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) with mock.patch("subprocess.check_output") as mock_call: mock_call.side_effect = call_side_effect self.assertRaises(subprocess.CalledProcessError, installer.install, path) self.assertFalse( os.path.exists(os.path.join(self.temp_dir, "sentinel"))) @skipUnless(os.path.exists(ClickInstaller(None)._preload_path()), "preload bits not built; installing packages will fail") @mock.patch("gi.repository.Click.package_install_hooks") def test_upgrade(self, mock_package_install_hooks): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() os.environ["TEST_QUIET"] = "1" path = self.make_fake_package( control_fields={ "Package": "test-package", "Version": "1.1", "Architecture": "all", "Maintainer": "Foo Bar <*****@*****.**>", "Description": "test", "Click-Version": "0.2", }, manifest={ "name": "test-package", "version": "1.1", "framework": "ubuntu-sdk-13.10", }, control_scripts={"preinst": static_preinst}, data_files={"foo": None}) root = os.path.join(self.temp_dir, "root") package_dir = os.path.join(root, "test-package") inst_dir = os.path.join(package_dir, "current") os.makedirs(os.path.join(package_dir, "1.0")) os.symlink("1.0", inst_dir) db = Click.DB() db.add(root) installer = ClickInstaller(db) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) with mock_quiet_subprocess_call(): installer.install(path) self.assertCountEqual([".click", "test-package"], os.listdir(root)) self.assertCountEqual(["1.1", "current"], os.listdir(package_dir)) self.assertTrue(os.path.islink(inst_dir)) self.assertEqual("1.1", os.readlink(inst_dir)) self.assertCountEqual([".click", "foo"], os.listdir(inst_dir)) status_path = os.path.join(inst_dir, ".click", "status") with open(status_path) as status_file: # .readlines() avoids the need for a python-apt backport to # Ubuntu 12.04 LTS. status = list(Deb822.iter_paragraphs(status_file.readlines())) self.assertEqual(1, len(status)) self.assertEqual( { "Package": "test-package", "Status": "install ok installed", "Version": "1.1", "Architecture": "all", "Maintainer": "Foo Bar <*****@*****.**>", "Description": "test", "Click-Version": "0.2", }, status[0]) mock_package_install_hooks.assert_called_once_with(db, "test-package", "1.0", "1.1", user_name=None) def _get_mode(self, path): return stat.S_IMODE(os.stat(path).st_mode) @skipUnless(os.path.exists(ClickInstaller(None)._preload_path()), "preload bits not built; installing packages will fail") @mock.patch("gi.repository.Click.package_install_hooks") def test_world_readable(self, mock_package_install_hooks): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() owner_only_file = os.path.join(self.temp_dir, "owner-only-file") touch(owner_only_file) os.chmod(owner_only_file, stat.S_IRUSR | stat.S_IWUSR) owner_only_dir = os.path.join(self.temp_dir, "owner-only-dir") os.mkdir(owner_only_dir, stat.S_IRWXU) path = self.make_fake_package( control_fields={ "Package": "test-package", "Version": "1.1", "Architecture": "all", "Maintainer": "Foo Bar <*****@*****.**>", "Description": "test", "Click-Version": "0.2", }, manifest={ "name": "test-package", "version": "1.1", "framework": "ubuntu-sdk-13.10", }, control_scripts={"preinst": static_preinst}, data_files={ "world-readable-file": owner_only_file, "world-readable-dir": owner_only_dir, }) root = os.path.join(self.temp_dir, "root") db = Click.DB() db.add(root) installer = ClickInstaller(db) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) with mock_quiet_subprocess_call(): installer.install(path) inst_dir = os.path.join(root, "test-package", "current") self.assertEqual( stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH, self._get_mode(os.path.join(inst_dir, "world-readable-file"))) self.assertEqual( stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH, self._get_mode(os.path.join(inst_dir, "world-readable-dir"))) @skipUnless(os.path.exists(ClickInstaller(None)._preload_path()), "preload bits not built; installing packages will fail") @mock.patch("gi.repository.Click.package_install_hooks") @mock.patch("click_package.install.ClickInstaller._dpkg_architecture") def test_single_architecture(self, mock_dpkg_architecture, mock_package_install_hooks): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() mock_dpkg_architecture.return_value = "armhf" path = self.make_fake_package( control_fields={ "Package": "test-package", "Version": "1.1", "Architecture": "armhf", "Maintainer": "Foo Bar <*****@*****.**>", "Description": "test", "Click-Version": "0.2", }, manifest={ "name": "test-package", "version": "1.1", "framework": "ubuntu-sdk-13.10", "architecture": "armhf", }, control_scripts={"preinst": static_preinst}) root = os.path.join(self.temp_dir, "root") db = Click.DB() db.add(root) installer = ClickInstaller(db) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) with mock_quiet_subprocess_call(): installer.install(path) self.assertTrue( os.path.exists(os.path.join(root, "test-package", "current"))) @skipUnless(os.path.exists(ClickInstaller(None)._preload_path()), "preload bits not built; installing packages will fail") @mock.patch("gi.repository.Click.package_install_hooks") @mock.patch("click_package.install.ClickInstaller._dpkg_architecture") def test_multiple_architectures(self, mock_dpkg_architecture, mock_package_install_hooks): with self.run_in_subprocess("click_get_frameworks_dir") as (enter, preloads): enter() mock_dpkg_architecture.return_value = "armhf" path = self.make_fake_package( control_fields={ "Package": "test-package", "Version": "1.1", "Architecture": "multi", "Maintainer": "Foo Bar <*****@*****.**>", "Description": "test", "Click-Version": "0.2", }, manifest={ "name": "test-package", "version": "1.1", "framework": "ubuntu-sdk-13.10", "architecture": ["armhf", "i386"], }, control_scripts={"preinst": static_preinst}) root = os.path.join(self.temp_dir, "root") db = Click.DB() db.add(root) installer = ClickInstaller(db) self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) with mock_quiet_subprocess_call(): installer.install(path) self.assertTrue( os.path.exists(os.path.join(root, "test-package", "current"))) @disable_logging def test_reinstall_preinstalled(self): # Attempting to reinstall a preinstalled version shouldn't actually # reinstall it in an overlay database (which would cause # irreconcilable confusion about the correct target for system hook # symlinks), but should instead simply update the user registration. path = self.make_fake_package( control_fields={ "Package": "test-package", "Version": "1.1", "Architecture": "all", "Maintainer": "Foo Bar <*****@*****.**>", "Description": "test", "Click-Version": "0.4", }, manifest={ "name": "test-package", "version": "1.1", "framework": "ubuntu-sdk-13.10", }, control_scripts={"preinst": static_preinst}) underlay = os.path.join(self.temp_dir, "underlay") overlay = os.path.join(self.temp_dir, "overlay") db = Click.DB() db.add(underlay) installer = ClickInstaller(db, True) with mock_quiet_subprocess_call(): installer.install(path, all_users=True) underlay_unpacked = os.path.join(underlay, "test-package", "1.1") self.assertTrue(os.path.exists(underlay_unpacked)) all_link = os.path.join(underlay, ".click", "users", "@all", "test-package") self.assertTrue(os.path.islink(all_link)) self.assertEqual(underlay_unpacked, os.readlink(all_link)) db.add(overlay) registry = Click.User.for_user(db, "test-user") registry.remove("test-package") user_link = os.path.join(overlay, ".click", "users", "test-user", "test-package") self.assertTrue(os.path.islink(user_link)) self.assertEqual("@hidden", os.readlink(user_link)) installer = ClickInstaller(db, True) with mock_quiet_subprocess_call(): installer.install(path, user="******") overlay_unpacked = os.path.join(overlay, "test-package", "1.1") self.assertFalse(os.path.exists(overlay_unpacked)) self.assertEqual("1.1", registry.get_version("test-package"))
def test_audit_bad_click_version(self): path = self.make_fake_package(control_fields={"Click-Version": "|"}) self.assertRaises(ValueError, ClickInstaller(self.db).audit, path)
def test_audit_new_click_version(self): path = self.make_fake_package(control_fields={"Click-Version": "999"}) self.assertRaisesRegex( ClickInstallerAuditError, "Click-Version: 999 newer than maximum supported version .*", ClickInstaller(self.db).audit, path)
def test_audit_no_version(self): path = self.make_fake_package(control_fields={"Click-Version": "0.2"}, manifest={"name": "test-package"}) self.assertRaisesRegex(ClickInstallerAuditError, 'No "version" entry in manifest', ClickInstaller(self.db).audit, path)
def test_audit_no_click_version(self): path = self.make_fake_package() self.assertRaisesRegex(ClickInstallerAuditError, "No Click-Version field", ClickInstaller(self.db).audit, path)