test: offline suite + committed fixtures for every stage
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
"""Full deterministic pipeline on canned fixtures — zero network, no live scan.
|
||||
|
||||
This is the offline demo and the proof that recon->normalize->ground->report works
|
||||
without any model or external call.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from recon_triage.orchestrator import build_report_from_fixtures
|
||||
from recon_triage.report import markdown
|
||||
from recon_triage.schema import ReconReport
|
||||
from recon_triage.scope import Scope
|
||||
from recon_triage.triage.llm import run_triage
|
||||
from recon_triage.triage.ranking import deterministic_triage
|
||||
|
||||
FIXTURES = Path(__file__).parent / "fixtures"
|
||||
|
||||
|
||||
def _build(scope=None, search_fn=None) -> ReconReport:
|
||||
return build_report_from_fixtures(
|
||||
FIXTURES, run_id="test", scope=scope, search_fn=search_fn, enable_nuclei=True
|
||||
)
|
||||
|
||||
|
||||
def test_offline_run_schema_valid(fixture_search_fn):
|
||||
report = _build(search_fn=fixture_search_fn)
|
||||
# Re-validate to prove schema-validity.
|
||||
ReconReport.model_validate_json(report.model_dump_json())
|
||||
assert report.hosts
|
||||
assert report.nuclei_findings
|
||||
|
||||
|
||||
def test_offline_run_merges_hosts(fixture_search_fn):
|
||||
report = _build(search_fn=fixture_search_fn)
|
||||
by_name = {h.hostname: h for h in report.hosts}
|
||||
api = by_name["api.example.com"]
|
||||
# ports merged from naabu + nmap; service+http merged
|
||||
ports = {p.number for p in api.ports}
|
||||
assert {22, 443}.issubset(ports)
|
||||
svc443 = next(p.service for p in api.ports if p.number == 443)
|
||||
assert svc443.product == "Apache httpd"
|
||||
assert svc443.version == "2.4.49"
|
||||
assert svc443.http is not None
|
||||
assert svc443.http.status == 200
|
||||
|
||||
|
||||
def test_offline_run_grounds_exploitdb(fixture_search_fn):
|
||||
report = _build(search_fn=fixture_search_fn)
|
||||
api = next(h for h in report.hosts if h.hostname == "api.example.com")
|
||||
svc443 = next(p.service for p in api.ports if p.number == 443)
|
||||
assert {m.edb_id for m in svc443.exploitdb_candidates} == {"50383", "50406"}
|
||||
assert all(not m.verified for m in svc443.exploitdb_candidates)
|
||||
|
||||
|
||||
def test_offline_run_scope_drops_out_of_scope(fixture_search_fn):
|
||||
scope = Scope.load(FIXTURES / "scope.yaml")
|
||||
report = _build(scope=scope, search_fn=fixture_search_fn)
|
||||
names = {h.hostname for h in report.hosts}
|
||||
assert "external.notmine.com" not in names
|
||||
assert "api.example.com" in names
|
||||
|
||||
|
||||
def test_offline_run_markdown_renders(fixture_search_fn):
|
||||
report = _build(search_fn=fixture_search_fn)
|
||||
report.triage = deterministic_triage(report)
|
||||
md = markdown.render(report)
|
||||
assert "# recon-triage report" in md
|
||||
assert "Authorized use only" in md
|
||||
assert "EDB-50383" in md
|
||||
assert "Triage priorities" in md
|
||||
|
||||
|
||||
def test_deterministic_triage_without_llm(fixture_search_fn, monkeypatch):
|
||||
monkeypatch.delenv("LLM_BASE_URL", raising=False)
|
||||
report = _build(search_fn=fixture_search_fn)
|
||||
triage = run_triage(report)
|
||||
assert triage.generated_by == "deterministic"
|
||||
assert triage.prioritized_findings
|
||||
# High-severity nuclei finding should rank at the top.
|
||||
assert triage.prioritized_findings[0].severity == "high"
|
||||
|
||||
|
||||
def test_triage_refs_are_real(fixture_search_fn):
|
||||
report = _build(search_fn=fixture_search_fn)
|
||||
triage = deterministic_triage(report)
|
||||
from recon_triage.triage.ranking import collect_identifiers
|
||||
|
||||
valid = collect_identifiers(report)
|
||||
for item in triage.prioritized_findings:
|
||||
assert item.evidence_refs
|
||||
for ref in item.evidence_refs:
|
||||
assert ref in valid or any(v in ref for v in valid)
|
||||
Reference in New Issue
Block a user