"""Tests for topazextract, including the path-traversal regression test.""" from io import BytesIO import dedrm_test_utils as U tz = U.load("topazextract", package="dedrm") def test_book_read_encoded_number(): assert tz.bookReadEncodedNumber(BytesIO(bytes([0x05]))) == 5 assert tz.bookReadEncodedNumber(BytesIO(bytes([0x81, 0x00]))) == 128 def test_book_read_string(): assert tz.bookReadString(BytesIO(bytes([0x03]) + b"abc")) == b"abc" def _bare_book(outdir): """A TopazBook instance with just the attributes extractFiles() needs.""" book = tz.TopazBook.__new__(tz.TopazBook) book.outdir = str(outdir) return book def test_extractfiles_strips_path_traversal(tmp_path): """A malicious Topaz header tag must not write outside the output dir. Regression test for the arbitrary-file-write fix: the record name is read verbatim from the (untrusted) book file, so ``../../evil`` must be reduced to a basename inside ``outdir`` rather than escaping it. """ outdir = tmp_path / "out" outdir.mkdir() book = _bare_book(outdir) evil_tag = b"../../evil" book.bookHeaderRecords = {evil_tag: [[0, 5, 0]]} # Attacker-controlled payload bytes, no key required. book.getBookPayloadRecord = lambda name, index: b"PWNED" book.extractFiles() # Primary oracle (independent of traversal depth): with the fix, basename() # strips every "../" so the record always lands INSIDE outdir. The pre-fix # code did os.path.join(outdir, "../../evil0000.dat"), writing OUTSIDE outdir, # so this assertion fails without the fix no matter how deep tmp_path nests. assert (outdir / "evil0000.dat").read_bytes() == b"PWNED" # outdir contains exactly that one sanitised file, nothing unexpected. assert sorted(p.name for p in outdir.iterdir()) == ["evil0000.dat"] # Defense in depth: nothing escaped to the dirs the "../../" payload targets. assert not (tmp_path / "evil0000.dat").exists() assert not (tmp_path.parent / "evil0000.dat").exists() def test_extractfiles_writes_normal_record(tmp_path): outdir = tmp_path / "out" outdir.mkdir() book = _bare_book(outdir) book.bookHeaderRecords = {b"spam": [[0, 4, 0]]} book.getBookPayloadRecord = lambda name, index: b"eggs" book.extractFiles() assert (outdir / "spam0000.dat").read_bytes() == b"eggs"