67 lines
3.0 KiB
Python
67 lines
3.0 KiB
Python
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
from rpcscope.catalog import CHECKS
|
|
from rpcscope.runner import ScanConfig, _msf_command, _redacted, scan
|
|
|
|
|
|
class RunnerTests(unittest.TestCase):
|
|
def test_nonfinite_and_invalid_api_limits_are_rejected_before_collection(self):
|
|
for attribute, value in (("timeout", float("nan")), ("timeout", float("inf")),
|
|
("jobs", True), ("discovery_workers", 1.5),
|
|
("connect_timeout", float("nan")), ("connect_timeout", True)):
|
|
with self.subTest(attribute=attribute, value=value), patch("rpcscope.runner.doctor") as inspect:
|
|
config = ScanConfig(["192.0.2.1"])
|
|
setattr(config, attribute, value)
|
|
with self.assertRaises(ValueError):
|
|
scan(config)
|
|
inspect.assert_not_called()
|
|
|
|
def test_redacts_embedded_password(self):
|
|
command = ["rpcdump", "user:[email protected]"]
|
|
self.assertEqual(
|
|
_redacted(command, "supersecret"),
|
|
["rpcdump", "user:<redacted>@10.10.10.25"],
|
|
)
|
|
|
|
def test_dry_run_does_not_require_installed_collectors(self):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
config = ScanConfig(
|
|
targets=["10.10.10.0/24"],
|
|
username="",
|
|
password="",
|
|
shares=True,
|
|
dry_run=True,
|
|
out_dir=Path(directory),
|
|
)
|
|
self.assertEqual(scan(config), {})
|
|
self.assertEqual(config.commands[0][0:3], ["nxc", "smb", "10.10.10.0/24"])
|
|
|
|
def test_msf_escaped_credentials_are_never_recorded(self):
|
|
config = ScanConfig(targets=["192.0.2.1"], username="auditor", password='a\\b"c')
|
|
command = _msf_command("msfconsole", "192.0.2.1", config)
|
|
redacted = _redacted(command, config.password)
|
|
self.assertEqual(redacted[-1], "<redacted Metasploit resource script>")
|
|
self.assertNotIn(config.password, " ".join(redacted))
|
|
|
|
def test_discovery_interruption_retains_partial_hosts_and_skips_checks(self):
|
|
with tempfile.TemporaryDirectory() as temporary:
|
|
config = ScanConfig(["192.0.2.0/24"], out_dir=Path(temporary) / "out",
|
|
checks=[CHECKS["ms17-010"]])
|
|
tools = {"nxc": "nxc", "enum4linux-ng": None, "rpcdump": None, "msfconsole": None}
|
|
def interrupted(command, timeout, log, password, cancel):
|
|
cancel.set()
|
|
return 130, "SMB 192.0.2.1 445 HOST [*] Windows 11 (name:HOST) (domain:LAB)"
|
|
with patch("rpcscope.runner.doctor", return_value=tools), patch("rpcscope.runner._run", side_effect=interrupted) as run:
|
|
hosts = scan(config)
|
|
self.assertTrue(config.cancelled)
|
|
self.assertIn("192.0.2.1", hosts)
|
|
self.assertEqual(hosts["192.0.2.1"].checks["ms17-010"], "skipped-cancelled")
|
|
self.assertEqual(run.call_count, 1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|