def testCopy(self): sha1 = manifest.get_algorithm('sha1') sha1new = manifest.get_algorithm('sha1new') source = os.path.join(self.tmp, 'badname') os.mkdir(source) self.populate_sample(source) lines = list(sha1new.generate_manifest(source)) self.assertEqual(['F f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0 2 5 MyFile', 'S 570b0ce957ab43e774c82fca0ea3873fc452278b 19 a symlink', 'D /My Dir', 'F 0236ef92e1e37c57f0eb161e7e2f8b6a8face705 2 10 !a file!', 'X b4ab02f2c791596a980fd35f51f5d92ee0b4705c 2 10 !a file!.exe'], lines) digest = sha1.getID(manifest.add_manifest_file(source, sha1)) copy = tempfile.mktemp() os.mkdir(copy) try: # Source must be in the form alg=value try: cli.do_copy([source, copy]) assert 0 except BadDigest as ex: assert 'badname' in str(ex) source, badname = os.path.join(self.tmp, digest), source os.chmod(badname, 0o755) # can't rename RO directories on MacOS X os.rename(badname, source) os.chmod(source, 0o555) # Can't copy sha1 implementations (unsafe) try: cli.do_copy([source, copy]) except SafeException as ex: assert 'sha1' in str(ex) # Already have a .manifest try: manifest.add_manifest_file(source, sha1new) assert 0 except SafeException as ex: assert '.manifest' in str(ex) os.chmod(source, 0o700) os.unlink(os.path.join(source, '.manifest')) # Switch to sha1new digest = sha1new.getID(manifest.add_manifest_file(source, sha1new)) source, badname = os.path.join(self.tmp, digest), source os.chmod(badname, 0o755) os.rename(badname, source) os.chmod(source, 0o555) cli.do_copy([source, copy]) with open(os.path.join(copy, digest, 'MyFile'), 'rt') as stream: self.assertEqual('Hello', stream.read()) finally: support.ro_rmtree(copy)
def testCopy(self): sha1 = manifest.get_algorithm('sha1') sha1new = manifest.get_algorithm('sha1new') source = os.path.join(self.tmp, 'badname') os.mkdir(source) self.populate_sample(source) lines = list(sha1new.generate_manifest(source)) self.assertEquals([ 'F f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0 2 5 MyFile', 'S 570b0ce957ab43e774c82fca0ea3873fc452278b 19 a symlink', 'D /My Dir', 'F 0236ef92e1e37c57f0eb161e7e2f8b6a8face705 2 10 !a file!', 'X b4ab02f2c791596a980fd35f51f5d92ee0b4705c 2 10 !a file!.exe' ], lines) digest = sha1.getID(manifest.add_manifest_file(source, sha1)) copy = tempfile.mktemp() os.mkdir(copy) try: # Source must be in the form alg=value try: cli.do_copy([source, copy]) assert 0 except BadDigest as ex: assert 'badname' in str(ex) source, badname = os.path.join(self.tmp, digest), source os.rename(badname, source) # Can't copy sha1 implementations (unsafe) try: cli.do_copy([source, copy]) except SafeException as ex: assert 'sha1' in str(ex) # Already have a .manifest try: manifest.add_manifest_file(source, sha1new) assert 0 except SafeException as ex: assert '.manifest' in str(ex) os.chmod(source, 0o700) os.unlink(os.path.join(source, '.manifest')) # Switch to sha1new digest = sha1new.getID(manifest.add_manifest_file(source, sha1new)) source, badname = os.path.join(self.tmp, digest), source os.rename(badname, source) cli.do_copy([source, copy]) self.assertEquals( 'Hello', file(os.path.join(copy, digest, 'MyFile')).read()) finally: support.ro_rmtree(copy)
def get_digest(unpack_dir, alg_name): alg = manifest.get_algorithm(alg_name) digest = alg.new_digest() for line in alg.generate_manifest(unpack_dir): digest.update((line + '\n').encode('utf-8')) return alg.getID(digest)
def do_manifest(args): """manifest DIRECTORY [ALGORITHM]""" if len(args) < 1 or len(args) > 2: raise UsageError(_("Wrong number of arguments")) if len(args) == 2: alg = get_algorithm(args[1]) else: # If no algorithm was given, guess from the directory name name = os.path.basename(args[0]) try: alg, unused = manifest.splitID(name) except zerostore.BadDigest: alg = get_algorithm('sha1new') digest = alg.new_digest() for line in alg.generate_manifest(args[0]): print(line) digest.update((line + '\n').encode('utf-8')) print(alg.getID(digest)) sys.exit(0)
def do_manifest(args): """manifest DIRECTORY [ALGORITHM]""" if len(args) < 1 or len(args) > 2: raise UsageError(_("Wrong number of arguments")) if len(args) == 2: alg = get_algorithm(args[1]) else: # If no algorithm was given, guess from the directory name name = os.path.basename(args[0]) if '=' in name: alg = get_algorithm(name.split('=', 1)[0]) else: alg = get_algorithm('sha1new') digest = alg.new_digest() for line in alg.generate_manifest(args[0]): print line digest.update(line + '\n') print alg.getID(digest) sys.exit(0)
def assert_manifest(self, required): alg_name = required.split('=', 1)[0] manifest.fixup_permissions(self.tmpdir) sha1 = alg_name + '=' + manifest.add_manifest_file(self.tmpdir, manifest.get_algorithm(alg_name)).hexdigest() self.assertEqual(sha1, required) # Check permissions are sensible for root, dirs, files in os.walk(self.tmpdir): for f in files + dirs: full = os.path.join(root, f) if os.path.islink(full): continue full_mode = os.stat(full).st_mode self.assertEqual(0o444, full_mode & 0o666) # Must be r-?r-?r-?
def testCopy(self): sha1 = manifest.get_algorithm("sha1") sha1new = manifest.get_algorithm("sha1new") source = os.path.join(self.tmp, "badname") os.mkdir(source) self.populate_sample(source) lines = list(sha1new.generate_manifest(source)) self.assertEquals( [ "F f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0 2 5 MyFile", "S 570b0ce957ab43e774c82fca0ea3873fc452278b 19 a symlink", "D /My Dir", "F 0236ef92e1e37c57f0eb161e7e2f8b6a8face705 2 10 !a file!", "X b4ab02f2c791596a980fd35f51f5d92ee0b4705c 2 10 !a file!.exe", ], lines, ) digest = sha1.getID(manifest.add_manifest_file(source, sha1)) copy = tempfile.mktemp() os.mkdir(copy) try: # Source must be in the form alg=value try: cli.do_copy([source, copy]) assert 0 except BadDigest, ex: assert "badname" in str(ex) source, badname = os.path.join(self.tmp, digest), source os.rename(badname, source) # Can't copy sha1 implementations (unsafe) try: cli.do_copy([source, copy]) except SafeException, ex: assert "sha1" in str(ex)
def assert_manifest(self, required): alg_name = required.split('=', 1)[0] manifest.fixup_permissions(self.tmpdir) sha1 = alg_name + '=' + manifest.add_manifest_file( self.tmpdir, manifest.get_algorithm(alg_name)).hexdigest() self.assertEqual(sha1, required) # Check permissions are sensible for root, dirs, files in os.walk(self.tmpdir): for f in files + dirs: full = os.path.join(root, f) if os.path.islink(full): continue full_mode = os.stat(full).st_mode self.assertEqual(0o444, full_mode & 0o666) # Must be r-?r-?r-?
def testVerify(self): path = os.path.join(self.tmp, "MyLink") os.symlink("Hello", path) mfile = os.path.join(self.tmp, ".manifest") for alg_name in ["sha1", "sha256", "sha1new"]: try: alg = manifest.get_algorithm(alg_name) added_digest = alg.getID(manifest.add_manifest_file(self.tmp, alg)) digest = alg.new_digest() digest.update("Hello") self.assertEquals("S %s 5 MyLink\n" % digest.hexdigest(), file(mfile, "rb").read()) manifest.verify(self.tmp, added_digest) os.chmod(self.tmp, 0700) os.unlink(mfile) except BadDigest, ex: raise Exception("%s: %s\n%s" % (alg_name, ex, ex.detail))
def testVerify(self): path = os.path.join(self.tmp, 'MyLink') os.symlink('Hello', path) mfile = os.path.join(self.tmp, '.manifest') for alg_name in ['sha1', 'sha256', 'sha1new']: try: alg = manifest.get_algorithm(alg_name) added_digest = alg.getID(manifest.add_manifest_file(self.tmp, alg)) digest = alg.new_digest() digest.update('Hello') self.assertEquals("S %s 5 MyLink\n" % digest.hexdigest(), file(mfile, 'rb').read()) manifest.verify(self.tmp, added_digest) os.chmod(self.tmp, 0o700) os.unlink(mfile) except BadDigest as ex: raise Exception("%s: %s\n%s" % (alg_name, ex, ex.detail))
def testImplementationGenerateMissingId(self): old_out = sys.stdout try: sys.stdout = StringIO() self.child = server.handle_requests(('HelloWorld.tgz')) from zeroinstall.zerostore import manifest alg = manifest.get_algorithm('sha1') assert alg from zeroinstall.injector.reader import load_feed feed = load_feed(os.path.abspath('ImplementationNoId.xml'), True, False, False, alg, self.config) expected_id = 'sha1=3ce644dc725f1d21cfcf02562c76f375944b266a' assert feed.implementations[expected_id] assert feed.implementations[expected_id].id == expected_id finally: sys.stdout = old_out
def testVerify(self): path = os.path.join(self.tmp, 'MyLink') os.symlink('Hello', path) mfile = os.path.join(self.tmp, '.manifest') for alg_name in ['sha1', 'sha256', 'sha1new']: try: alg = manifest.get_algorithm(alg_name) added_digest = alg.getID( manifest.add_manifest_file(self.tmp, alg)) digest = alg.new_digest() digest.update('Hello') self.assertEquals("S %s 5 MyLink\n" % digest.hexdigest(), file(mfile, 'rb').read()) manifest.verify(self.tmp, added_digest) os.chmod(self.tmp, 0o700) os.unlink(mfile) except BadDigest as ex: raise Exception("%s: %s\n%s" % (alg_name, ex, ex.detail))
def add_digest(impl, alg_name): alg = manifest.get_algorithm(alg_name) # Scan through the existing digests # - If we've already got the one we need, return # - Otherwise, find a cached implementation we can use existing_path = None for a, value in digests(impl): if a in ('sha1', 'sha1new', 'sha256'): digest = '%s=%s' % (a, value) else: digest = '%s_%s' % (a, value) if a == alg_name: return False # Already signed with this algorithm if not existing_path: try: existing_path = stores.lookup(digest) if existing_path: existing_digest = digest except NotStored: pass # OK if existing_path is None: print("No implementations of %s cached; can't calculate new digest" % get_version(impl)) return False info("Verifying %s", existing_path) manifest.verify(existing_path, existing_digest) print("Adding new digest to version %s" % get_version(impl)) new_digest = alg.new_digest() for line in alg.generate_manifest(existing_path): new_digest.update((line + '\n').encode()) for md in xmltools.children(impl, 'manifest-digest'): break else: md = xmltools.create_element(impl, 'manifest-digest') _, digest_value = manifest.splitID(alg.getID(new_digest)) md.setAttribute(alg_name, digest_value) return True
def check_manifest_and_rename(self, required_digest, unpack_dir, dry_run = False): implementation = self.impl sha1new = get_digest(unpack_dir, 'sha1new') if not implementation.getAttribute('id'): implementation.setAttribute('id', sha1new) digests = [sha1new] def add_digest(alg_name): digest_id = get_digest(unpack_dir, alg_name) digests.append(digest_id) name, value = zerostore.parse_algorithm_digest_pair(digest_id) elem.setAttribute(alg_name, value) have_manifest_elem = False for elem in implementation.getElementsByTagNameNS(namespaces.XMLNS_IFACE, 'manifest-digest'): have_manifest_elem = True have_digests = False for attr_name, value in elem.attributes.items(): if value: continue add_digest(attr_name) have_digests = True if not have_digests: add_digest('sha256new') if not have_manifest_elem: print("WARNING: no <manifest-digest> element found") best_rating = -1 best_digest = None for digest_id in digests: alg_name, value = zerostore.parse_algorithm_digest_pair(digest_id) alg = manifest.get_algorithm(alg_name) if alg.rating > best_rating: best_rating = alg.rating best_digest = digest_id # Cache if necessary (saves downloading it again later) stores = self.real_stores if stores.lookup_maybe(digests) is None: stores.add_dir_to_cache(best_digest, unpack_dir)
def add_digest(impl, alg_name): alg = manifest.get_algorithm(alg_name) # Scan through the existing digests # - If we've already got the one we need, return # - Otherwise, find a cached implementation we can use existing_path = None for a, value in digests(impl): digest = '%s=%s' % (a, value) if a == alg_name: return False # Already signed with this algorithm if not existing_path: try: existing_path = stores.lookup(digest) if existing_path: existing_digest = digest except NotStored: pass # OK if existing_path is None: print "No implementations of %s cached; can't calculate new digest" % get_version(impl) return False info("Verifying %s", existing_path) manifest.verify(existing_path, existing_digest) print "Adding new digest to version %s" % get_version(impl) new_digest = alg.new_digest() for line in alg.generate_manifest(existing_path): new_digest.update(line + '\n') for md in xmltools.children(impl, 'manifest-digest'): break else: md = xmltools.create_element(impl, 'manifest-digest') md.setAttribute(alg_name, new_digest.hexdigest()) return True
def testUnknownAlgorithm(self): try: manifest.get_algorithm('unknown') assert False except BadDigest: pass
def create_archive_element(self, url, mime_type, root, extract, size, start_offset): alg = manifest.get_algorithm('sha1new') digest = alg.new_digest() for line in alg.generate_manifest(root): digest.update(line + '\n') id = alg.getID(digest) # Add it to the cache if missing # Helps with setting 'main' attribute later try: main.stores.lookup(id) except NotStored: main.stores.add_dir_to_cache(id, root) # Do we already have an implementation with this digest? impl_element = self.feed_editor.find_implementation(id) if impl_element is None: # No. Create a new implementation. Guess the details... leaf = url.split('/')[-1] version = None for m in re.finditer(version_regexp, leaf): match = m.group()[1:] if version is None or len(version) < len(match): version = match existing_versions = self.feed_editor.list_versions() older_versions = [] if existing_versions and version: parsed_version = try_parse_version(version) if parsed_version: older_versions = [(v, elem) for v, elem in existing_versions if v < parsed_version] impl_element = self.feed_editor.doc.createElementNS(XMLNS_INTERFACE, 'implementation') if older_versions: # Try to add it just after the previous version's element in the XML document insert_after(impl_element, max(older_versions)[1]) elif existing_versions: # Else add it before the first insert_before(impl_element, min(existing_versions)[1]) else: # Put it in the root insert_element(impl_element, self.feed_editor.doc.documentElement) impl_element.setAttribute('id', id) impl_element.setAttribute('released', time.strftime('%Y-%m-%d')) if version: impl_element.setAttribute('version', version) created_impl = True else: created_impl = False archive_element = create_element(impl_element, 'archive') archive_element.setAttribute('size', str(size)) archive_element.setAttribute('href', url) if extract: archive_element.setAttribute('extract', extract) if mime_type: archive_element.setAttribute('type', mime_type) if start_offset: archive_element.setAttribute('start-offset', str(start_offset)) self.feed_editor.update_version_model() if created_impl: self.feed_editor.edit_properties(element = impl_element)