69 lines
2.3 KiB
Python
69 lines
2.3 KiB
Python
"""Exploit-DB matcher: parses real searchsploit JSON, never fabricates, fails soft."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
from recon_triage.grounding.exploitdb import ground_report
|
|
from recon_triage.schema import Host, Port, ReconReport, Service
|
|
from recon_triage.tools import searchsploit
|
|
|
|
FIXTURES = Path(__file__).parent / "fixtures"
|
|
|
|
|
|
def _index() -> dict:
|
|
return json.loads((FIXTURES / "searchsploit" / "index.json").read_text())
|
|
|
|
|
|
def test_normalize_parses_results():
|
|
doc = _index()["Apache httpd 2.4.49"]
|
|
matches = searchsploit.normalize(json.dumps(doc), query="Apache httpd 2.4.49")
|
|
assert {m.edb_id for m in matches} == {"50383", "50406"}
|
|
assert all(m.verified is False for m in matches)
|
|
assert all(m.source == "exploit-db" for m in matches)
|
|
assert matches[0].url.endswith("/50383")
|
|
|
|
|
|
def test_normalize_empty_results():
|
|
doc = _index()["nginx 1.18.0"]
|
|
assert searchsploit.normalize(json.dumps(doc)) == []
|
|
|
|
|
|
def test_normalize_handles_garbage():
|
|
assert searchsploit.normalize("") == []
|
|
assert searchsploit.normalize("not json") == []
|
|
|
|
|
|
def test_ground_report_attaches_only_returned(fixture_search_fn):
|
|
report = ReconReport(
|
|
run_id="t",
|
|
hosts=[
|
|
Host(
|
|
hostname="api.example.com",
|
|
ports=[
|
|
Port(number=443, service=Service(product="Apache httpd", version="2.4.49")),
|
|
Port(number=22, service=Service(product="OpenSSH", version="8.2p1 Ubuntu 4ubuntu0.5")),
|
|
],
|
|
)
|
|
],
|
|
)
|
|
attached = ground_report(report, fixture_search_fn)
|
|
assert attached == 2 # only the Apache service yields 2 candidates
|
|
apache = report.hosts[0].ports[0].service
|
|
ssh = report.hosts[0].ports[1].service
|
|
assert {m.edb_id for m in apache.exploitdb_candidates} == {"50383", "50406"}
|
|
assert ssh.exploitdb_candidates == [] # nothing returned -> empty, not fabricated
|
|
|
|
|
|
def test_ground_report_failsoft_on_search_error():
|
|
def boom(term: str):
|
|
raise RuntimeError("searchsploit blew up")
|
|
|
|
report = ReconReport(
|
|
run_id="t",
|
|
hosts=[Host(hostname="h", ports=[Port(number=443, service=Service(product="X", version="1"))])],
|
|
)
|
|
attached = ground_report(report, boom) # must not raise
|
|
assert attached == 0
|