109 lines
4.2 KiB
Python
109 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Focused refusal and preservation checks for the ISO FIPS derivative."""
|
|
|
|
import importlib.util
|
|
import io
|
|
import hashlib
|
|
from pathlib import Path
|
|
import sys
|
|
import tarfile
|
|
import tempfile
|
|
import unittest
|
|
|
|
SOURCE = Path(__file__).with_name("build-iso-fips-package.py")
|
|
spec = importlib.util.spec_from_file_location("iso_fips_package", SOURCE)
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
|
|
|
|
def tar(members):
|
|
buffer = io.BytesIO()
|
|
with tarfile.open(fileobj=buffer, mode="w:xz") as archive:
|
|
for name, content in members:
|
|
entry = tarfile.TarInfo("./" + name)
|
|
entry.mode = 0o644
|
|
entry.uid = entry.gid = 0
|
|
entry.size = len(content)
|
|
archive.addfile(entry, io.BytesIO(content))
|
|
return buffer.getvalue()
|
|
|
|
|
|
def fixture(*, version="0.4.1-1", nft=True, declared=True, md5sums=False):
|
|
controls = [
|
|
("control", f"Package: fips\nVersion: {version}\nArchitecture: amd64\n".encode()),
|
|
("conffiles", b"/etc/fips/hosts\n" + (b"/etc/fips/fips.nft\n" if declared else b"")),
|
|
("postinst", b"#!/bin/sh\nexit 0\n"),
|
|
]
|
|
if md5sums:
|
|
controls.append(("md5sums", b"deadbeef etc/fips/fips.nft\n"))
|
|
data = [("etc/fips/hosts", b"hosts\n")]
|
|
if nft:
|
|
data.append(("etc/fips/fips.nft", b"old nft\n"))
|
|
return module.write_ar([
|
|
("debian-binary", b"2.0\n"),
|
|
("control.tar.xz", tar(controls)),
|
|
("data.tar.xz", tar(data)),
|
|
])
|
|
|
|
|
|
class DerivativeTest(unittest.TestCase):
|
|
def test_only_declared_changes(self):
|
|
result, receipt = module.derive(fixture())
|
|
members = module.read_ar(result)
|
|
controls = dict((module.normalized(item.name), content)
|
|
for item, content in module.tar_items(members[1][1]))
|
|
data = dict((module.normalized(item.name), content)
|
|
for item, content in module.tar_items(members[2][1]))
|
|
self.assertIn(b"Version: 0.4.1-1+archy.iso1", controls["control"])
|
|
self.assertEqual(controls["conffiles"], b"/etc/fips/hosts\n")
|
|
self.assertEqual(controls["postinst"], b"#!/bin/sh\nexit 0\n")
|
|
self.assertEqual(data, {"etc/fips/hosts": b"hosts\n"})
|
|
self.assertEqual(receipt["unchanged_data_members"], 1)
|
|
|
|
def test_missing_file_refused(self):
|
|
with self.assertRaisesRegex(ValueError, "default fips.nft"):
|
|
module.derive(fixture(nft=False))
|
|
|
|
def test_missing_conffile_refused(self):
|
|
with self.assertRaisesRegex(ValueError, "conffile declaration"):
|
|
module.derive(fixture(declared=False))
|
|
|
|
def test_unreviewed_version_refused(self):
|
|
with self.assertRaisesRegex(ValueError, "package version"):
|
|
module.derive(fixture(version="0.5.3"))
|
|
|
|
def test_unreviewed_md5sums_refused(self):
|
|
with self.assertRaisesRegex(ValueError, "md5sums"):
|
|
module.derive(fixture(md5sums=True))
|
|
|
|
def test_reviewed_handoff_version(self):
|
|
_, receipt = module.derive(
|
|
fixture(version="0.5.3+archy.handoff1"),
|
|
"0.5.3+archy.handoff1", "0.5.3+archy.handoff1.iso1")
|
|
self.assertEqual(receipt["new_version"], "0.5.3+archy.handoff1.iso1")
|
|
|
|
def test_dangling_output_symlink_refused(self):
|
|
package = fixture()
|
|
digest = hashlib.sha256(package).hexdigest()
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
root = Path(directory)
|
|
source, output, receipt = root / "source.deb", root / "output.deb", root / "receipt.json"
|
|
source.write_bytes(package)
|
|
output.symlink_to(root / "missing")
|
|
old_argv = sys.argv
|
|
module.REVIEWED_INPUTS[digest] = ("0.4.1-1", "0.4.1-1+archy.iso1")
|
|
sys.argv = [str(SOURCE), "--input", str(source), "--expected-input-sha256", digest,
|
|
"--output", str(output), "--receipt", str(receipt)]
|
|
try:
|
|
with self.assertRaises(FileExistsError):
|
|
module.main()
|
|
finally:
|
|
sys.argv = old_argv
|
|
del module.REVIEWED_INPUTS[digest]
|
|
self.assertFalse((root / "missing").exists())
|
|
self.assertFalse(receipt.exists())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|