88 lines
3.6 KiB
Python
88 lines
3.6 KiB
Python
import importlib.util
|
|
import json
|
|
import pathlib
|
|
import tempfile
|
|
import unittest
|
|
import zipfile
|
|
|
|
ROOT = pathlib.Path(__file__).resolve().parents[1]
|
|
|
|
def load(name, path):
|
|
spec = importlib.util.spec_from_file_location(name, ROOT / path)
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
ZIP = load("zip_plugin", "plugins/zip_extract.py")
|
|
GHIDRA = load("ghidra_plugin", "plugins/ghidra/analyze.py")
|
|
PROBE = load("spike_probe", "plugins/spike_probe.py")
|
|
|
|
class PluginTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.temp = tempfile.TemporaryDirectory()
|
|
self.addCleanup(self.temp.cleanup)
|
|
self.root = pathlib.Path(self.temp.name)
|
|
self.input = self.root / "input"
|
|
self.output = self.root / "output"
|
|
self.input.mkdir()
|
|
self.output.mkdir()
|
|
self.request = {"protocol": 1, "input_dir": str(self.input), "output_dir": str(self.output), "workspace_bytes": 1024, "settings": {}}
|
|
|
|
def make_zip(self, name, content):
|
|
with zipfile.ZipFile(self.input / "test.zip", "w") as archive:
|
|
archive.writestr(name, content)
|
|
|
|
def test_exact_entries_and_opaque_supporting_files(self):
|
|
self.make_zip("config.txt", b"exact\x00bytes")
|
|
(self.input / "notes.txt").write_text("readme")
|
|
result = ZIP.run(self.request)
|
|
self.assertEqual(result["coverage"], "partial")
|
|
self.assertEqual((self.output / result["files"][0]).read_bytes(), b"exact\x00bytes")
|
|
|
|
def test_traversal_rejected(self):
|
|
self.make_zip("../../escaped", "content")
|
|
with self.assertRaises(ValueError):
|
|
ZIP.run(self.request)
|
|
self.assertFalse((self.root / "escaped").exists())
|
|
|
|
def test_expansion_budget(self):
|
|
self.make_zip("oversized", b"x" * 1024)
|
|
with self.assertRaisesRegex(ValueError, "allowance"):
|
|
ZIP.run(self.request)
|
|
|
|
def test_ghidra_missing_tool_is_explicit_failure(self):
|
|
self.request["settings"] = {"ghidra_home": str(self.root / "missing"), "expected_version": "fixture"}
|
|
with self.assertRaises(FileNotFoundError):
|
|
GHIDRA.run(self.request)
|
|
|
|
def test_ghidra_rejects_unpinned_version(self):
|
|
home = self.root / "ghidra"
|
|
(home / "Ghidra").mkdir(parents=True)
|
|
(home / "Ghidra" / "application.properties").write_text("application.version=fixture-v2\n")
|
|
self.request["settings"] = {"ghidra_home": str(home), "expected_version": "fixture-v1"}
|
|
with self.assertRaisesRegex(ValueError, "version"):
|
|
GHIDRA.run(self.request)
|
|
|
|
def test_probe_distinguishes_wrappers_without_guessing_generation(self):
|
|
for header, kind in [(b"SPKS", "spk"), (b"hsqs", "squashfs"), (b"\x1f\x8b", "gzip")]:
|
|
result = PROBE.classify(header)
|
|
self.assertEqual(result["format"], kind)
|
|
self.assertEqual(result["generation"], "unknown")
|
|
self.assertIsNone(result["required_secret"])
|
|
|
|
def test_luks_probe_reports_a_reference_not_a_key(self):
|
|
header = bytearray(4096)
|
|
header[:8] = b"LUKS\xba\xbe\x00\x02"
|
|
header[168:204] = b"5b22533c-7ee6-4e7c-8e9b-abb2392be418"
|
|
result = PROBE.classify(header)
|
|
self.assertEqual(result["required_secret"], "stern.spike3.luks")
|
|
self.assertEqual(result["uuid"], "5b22533c-7ee6-4e7c-8e9b-abb2392be418")
|
|
|
|
def test_probe_reads_first_split_header_inside_zip(self):
|
|
self.make_zip("game.spk.002.000", b"hsqs" + bytes(100))
|
|
report = PROBE.probe(self.input / "test.zip")
|
|
self.assertEqual(report["entries"][0]["format"], "squashfs")
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|