import unittest from rpcscope.models import HostEvidence, RpcInterface, Share class ModelTests(unittest.TestCase): def test_share_merge_retains_richer_permissions_and_deduplicates(self): host = HostEvidence("10.10.10.25", shares=[Share("Data", ["READ"])]) other = HostEvidence("10.10.10.25", shares=[Share("data", ["WRITE"]), Share("Public", ["READ"]), Share("PUBLIC", ["WRITE"])]) host.merge(other) self.assertEqual(len(host.shares), 2) self.assertEqual(host.shares[0].access, ["READ", "WRITE"]) self.assertEqual(host.shares[1].access, ["READ", "WRITE"]) self.assertEqual(other.shares[0].access, ["WRITE"]) def test_rpc_merge_retains_bindings_and_metadata(self): host = HostEvidence("10.10.10.25", rpc_interfaces=[ RpcInterface("12345678-1234-ABCD-EF00-0123456789AB", "1.0", endpoint="ncalrpc:[spooler]") ]) other = HostEvidence("10.10.10.25", rpc_interfaces=[ RpcInterface("12345678-1234-abcd-ef00-0123456789ab", "1.0", endpoint="ncacn_ip_tcp:10.10.10.25[49665]", provider="spoolsv.exe"), RpcInterface("12345678-1234-abcd-ef00-0123456789ab", "1.0", endpoint="ncacn_ip_tcp:10.10.10.25[49665]"), ]) host.merge(other) self.assertEqual(len(host.rpc_interfaces), 1) self.assertEqual(len(host.rpc_interfaces[0].bindings), 2) self.assertEqual(host.rpc_interfaces[0].provider, "spoolsv.exe") def test_conflicting_boolean_stays_unknown_across_later_merges(self): host = HostEvidence("10.10.10.25", signing=False) host.merge(HostEvidence("10.10.10.25", signing=True)) host.merge(HostEvidence("10.10.10.25", signing=False)) self.assertIsNone(host.signing) self.assertEqual(len(host.notes), 1) copied = HostEvidence("10.10.10.25", signing=True) copied.merge(host) self.assertIsNone(copied.signing) def test_confirmed_positive_survives_weaker_results(self): host = HostEvidence("10.10.10.25", checks={"ms17-010": "vulnerable"}, check_details={"ms17-010": "positive evidence"}) host.merge(HostEvidence("10.10.10.25", checks={"ms17-010": "inconclusive"}, check_details={"ms17-010": "timeout"})) self.assertEqual(host.checks["ms17-010"], "vulnerable") self.assertEqual(host.check_details["ms17-010"], "positive evidence") host.merge(HostEvidence("10.10.10.25", checks={"ms17-010": "not-vulnerable"})) self.assertEqual(host.checks["ms17-010"], "vulnerable") self.assertIn("Conflicting", host.notes[0]) def test_suspected_survives_negative_and_collection_updates(self): host = HostEvidence("10.10.10.25", checks={"check": "suspected"}, collection={"rpcdump": "pending"}) host.merge(HostEvidence("10.10.10.25", checks={"check": "not-vulnerable"}, collection={"rpcdump": "success"})) self.assertEqual(host.checks["check"], "suspected") self.assertEqual(host.collection["rpcdump"], "success") self.assertEqual(host.conflicting_checks(), {"check"}) def test_capability_conflict_is_retained_in_both_merge_orders(self): for first, second in (("exposed", "not-exposed"), ("not-exposed", "exposed")): host = HostEvidence("192.0.2.1", checks={"smbghost": first}) host.merge(HostEvidence("192.0.2.1", checks={"smbghost": second})) self.assertEqual(host.conflicting_checks(), {"smbghost"}) if __name__ == "__main__": unittest.main()