diff --git a/backend/app/scanner/nmap_scan.py b/backend/app/scanner/nmap_scan.py index 81dcb10..59e220b 100644 --- a/backend/app/scanner/nmap_scan.py +++ b/backend/app/scanner/nmap_scan.py @@ -64,7 +64,9 @@ def run_nmap_scan( cmd = [ "nmap", "-sV", # service/version detection - "-O", # OS detection (best-effort) + # -O (OS detection) intentionally omitted: nmap hard-codes geteuid()==0 + # for OS fingerprinting regardless of file capabilities, so it always + # quits when running as non-root uid 1000. os_guess will be empty. "--top-ports", "1000", "-e", diff --git a/backend/tests/test_scanner.py b/backend/tests/test_scanner.py index 427b860..6fe0a23 100644 --- a/backend/tests/test_scanner.py +++ b/backend/tests/test_scanner.py @@ -270,6 +270,24 @@ def test_os_highest_accuracy_wins(self): class TestRunNmapScan: """run_nmap_scan() mocks subprocess.run; XML writing verified via fixture.""" + @pytest.mark.unit + def test_does_not_use_os_detection_flag(self): + """-O must not appear in the nmap command: it hard-codes geteuid()==0 and + always quits when running as non-root uid 1000, regardless of file caps.""" + fixture_xml = _fixture("nmap_two_hosts.xml") + + def fake_run(cmd, **kwargs): + idx = cmd.index("-oX") + Path(cmd[idx + 1]).write_text(fixture_xml) + return MagicMock(returncode=0, stdout="", stderr="") + + with patch("app.scanner.nmap_scan.subprocess.run", side_effect=fake_run) as mock_run: + run_nmap_scan(hosts=["192.168.1.1"], interface="eth0") + + cmd = mock_run.call_args[0][0] + assert "-O" not in cmd + assert "-sV" in cmd + @pytest.mark.unit def test_returns_empty_list_for_empty_hosts(self): assert run_nmap_scan(hosts=[]) == []