From fc60d33a94b1dbd2a40624888bf356ce6dd9578c Mon Sep 17 00:00:00 2001 From: wind Date: Sat, 28 Feb 2026 17:05:32 +0100 Subject: [PATCH] fix: remove -O from nmap command; OS detection requires root nmap hard-codes geteuid()==0 for -O (OS fingerprinting) regardless of Linux file capabilities. Running as non-root uid 1000 always triggers: 'TCP/IP fingerprinting (for OS scan) requires root privileges. QUITTING!' causing the entire nmap scan to abort, losing all service/version data. -O was documented as best-effort; os_guess will be empty string. All other scan data (ports, services, version banners) is unaffected. Test added: test_does_not_use_os_detection_flag asserts -O absent. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- backend/app/scanner/nmap_scan.py | 4 +++- backend/tests/test_scanner.py | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) 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=[]) == []