Files
bug-bounty-harness/recon-triage/src/recon_triage/tools/nuclei.py
T

60 lines
1.9 KiB
Python

"""nuclei — template-based exposure/misconfig/CVE checks. Output: ``-jsonl``.
Gated behind ``--enable-nuclei`` (off by default in V0): it needs network for
template fetch and meaningful runtime. Templates cache to a runtime volume.
"""
from __future__ import annotations
from ..schema import NucleiFinding, Severity
from .base import parse_jsonl, run_tool
BINARY = "nuclei"
_SEVERITIES = {s.value for s in Severity}
def _severity(value) -> Severity:
v = str(value or "").lower().strip()
return Severity(v) if v in _SEVERITIES else Severity.UNKNOWN
def normalize(raw: str) -> list[NucleiFinding]:
"""Map nuclei -jsonl lines into NucleiFinding records."""
out: list[NucleiFinding] = []
for rec in parse_jsonl(raw):
info = rec.get("info") or {}
ref = info.get("reference") or []
if isinstance(ref, str):
ref = [ref]
template_id = rec.get("template-id") or rec.get("templateID") or rec.get("template_id")
if not template_id:
continue
out.append(
NucleiFinding(
template_id=str(template_id),
name=info.get("name"),
severity=_severity(info.get("severity")),
matched_at=rec.get("matched-at") or rec.get("matched_at"),
host=rec.get("host"),
reference=[str(r) for r in ref if r],
)
)
return out
def run(
targets: list[str],
*,
timeout: float = 600.0,
rate_limit: int | None = None,
templates_dir: str | None = None,
) -> tuple[list[NucleiFinding], object]:
cmd = [BINARY, "-jsonl", "-silent", "-disable-update-check"]
if templates_dir:
cmd += ["-update-template-dir", templates_dir, "-templates", templates_dir]
if rate_limit:
cmd += ["-rate-limit", str(rate_limit)]
res = run_tool(cmd, timeout=timeout, input_text="\n".join(targets) + "\n")
return normalize(res.stdout), res