mirror of
https://github.com/SteamFramelyHomebrew/framely.git
synced 2026-10-17 23:01:23 +02:00
91 lines
4.3 KiB
Python
91 lines
4.3 KiB
Python
import hashlib
|
|
import importlib.util
|
|
import io
|
|
import json
|
|
import contextlib
|
|
import pathlib
|
|
import tarfile
|
|
import tempfile
|
|
import unittest
|
|
|
|
spec = importlib.util.spec_from_file_location('bootstrap', pathlib.Path(__file__).parents[1] / 'tools/bootstrap.py')
|
|
bootstrap = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(bootstrap)
|
|
|
|
|
|
class BootstrapTests(unittest.TestCase):
|
|
def test_external_checksums_match_exact_name_and_reject_tampering(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
root = pathlib.Path(tmp)
|
|
archive = root / 'framely-1-linux-arm64.tar.gz'
|
|
sums = root / 'SHA256SUMS'
|
|
archive.write_bytes(b'payload')
|
|
entry = hashlib.sha256(b'payload').hexdigest() + ' ' + archive.name + '\n'
|
|
sums.write_text(entry)
|
|
bootstrap.verify_archive(archive, sums)
|
|
archive.write_bytes(b'changed')
|
|
with self.assertRaises(ValueError):
|
|
bootstrap.verify_archive(archive, sums)
|
|
sums.write_text(entry * 2)
|
|
with self.assertRaises(ValueError):
|
|
bootstrap.verify_archive(archive, sums)
|
|
sums.write_text(entry.replace(archive.name, './' + archive.name))
|
|
with self.assertRaises(ValueError):
|
|
bootstrap.verify_archive(archive, sums)
|
|
|
|
def archive(self, root, entries):
|
|
archive = root / 'release.tar.gz'
|
|
with tarfile.open(archive, 'w:gz') as out:
|
|
for name, contents in entries:
|
|
member = tarfile.TarInfo(name)
|
|
if contents is None:
|
|
member.type = tarfile.SYMTYPE
|
|
member.linkname = '/etc/shadow'
|
|
out.addfile(member)
|
|
else:
|
|
member.size = len(contents)
|
|
out.addfile(member, io.BytesIO(contents))
|
|
return archive
|
|
|
|
def test_safe_extraction_checks_structure_before_writing(self):
|
|
entries = [('framely-1/' + name, b'1' if name == 'VERSION' else b'test') for name in ['VERSION', 'SHA256SUMS', 'install.sh', 'uninstall.sh', 'bin/framely']]
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
root = pathlib.Path(tmp)
|
|
base = bootstrap.extract_archive(self.archive(root, entries), root / 'out')
|
|
self.assertEqual(base.name, 'framely-1')
|
|
for extra in [('framely-1/../../escape', b'bad'), ('framely-1/link', None), ('framely-1/VERSION', b'1'), ('framely-2/file', b'bad')]:
|
|
with self.subTest(extra=extra), tempfile.TemporaryDirectory() as tmp:
|
|
root = pathlib.Path(tmp)
|
|
with self.assertRaises(ValueError):
|
|
bootstrap.extract_archive(self.archive(root, entries + [extra]), root / 'out')
|
|
self.assertFalse((root / 'out').exists())
|
|
|
|
def test_redirect_cannot_downgrade_https(self):
|
|
with self.assertRaises(ValueError):
|
|
bootstrap.HTTPSRedirect().redirect_request(None, None, 302, '', {}, 'http://example.org/package')
|
|
|
|
def test_extraction_progress_reports_real_bytes_and_never_completes_invalid_release(self):
|
|
entries = [('framely-1/' + name, b'1' if name == 'VERSION' else b'test') for name in ['VERSION', 'SHA256SUMS', 'install.sh', 'uninstall.sh', 'bin/framely']]
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
root = pathlib.Path(tmp)
|
|
reports = []
|
|
bootstrap.extract_archive(self.archive(root, entries), root / 'out', lambda *value: reports.append(value))
|
|
total = sum(len(contents) for _, contents in entries)
|
|
self.assertEqual(reports[0], ('extract', 0, total))
|
|
self.assertEqual(reports[-1], ('extract', total, total))
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
root = pathlib.Path(tmp)
|
|
reports = []
|
|
with self.assertRaises(ValueError):
|
|
bootstrap.extract_archive(self.archive(root, [('framely-1/VERSION', b'wrong')]), root / 'out', lambda *value: reports.append(value))
|
|
self.assertFalse(any(done == total and total > 0 for _, done, total in reports))
|
|
output = io.StringIO()
|
|
with contextlib.redirect_stdout(output):
|
|
bootstrap.emit_progress('configure')
|
|
payload = json.loads(output.getvalue().removeprefix('FRAMELY_PROGRESS '))
|
|
self.assertEqual(payload, {'step': 'configure'})
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|