fix(mcp): fix nuclei_scan timeouts — smart template defaults, bypass proxy

Root cause: nuclei loaded all 2252 templates (5249 requests) through
Caido proxy, exceeding 600s timeout on most targets.

Fixes:
- Default to focused tags (exposure,misconfig,cve,takeover,default-login,token)
  instead of all templates — reduces to ~500-800 requests
- Add -env-vars=false to bypass system proxy for direct scanning
- Add -no-httpx to skip probe (target already known live)
- Replace -silent with -stats for progress visibility
- Parse and return last stats line in scan_progress field

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ms6RB 2026-03-26 01:46:26 +02:00
parent 0e4e26037f
commit 86780fa89c
2 changed files with 23 additions and 2 deletions

View file

@ -170,11 +170,18 @@ def build_nuclei_command(
f"-rate-limit {rate_limit}",
"-jsonl",
f"-o {output_file}",
"-silent",
"-stats", # show progress stats on stderr
"-stats-interval 10", # every 10 seconds
"-no-httpx", # skip httpx probe (target already known live)
"-env-vars=false", # bypass system proxy for direct scanning
]
if templates:
for t in templates:
parts.append(f"-t {t}")
else:
# Default: use focused template tags instead of loading all 2000+
# These cover the highest-value checks without the full scan overhead
parts.append("-tags exposure,misconfig,cve,takeover,default-login,token")
return " ".join(parts)

View file

@ -150,9 +150,21 @@ def register_recon_tools(mcp: FastMCP, sandbox: SandboxManager) -> None:
sev = _normalize_severity(f["severity"])
severity_breakdown[sev] = severity_breakdown.get(sev, 0) + 1
# Extract last stats line from stderr for progress info
last_stats: dict[str, Any] = {}
if nuclei_stderr:
for line in reversed(nuclei_stderr.splitlines()):
line = line.strip()
if line.startswith("{") and "requests" in line:
try:
last_stats = json.loads(line)
except json.JSONDecodeError:
pass
break
result_data: dict[str, Any] = {
"target": target,
"templates_used": templates or ["all"],
"templates_used": templates or ["exposure,misconfig,cve,takeover,default-login,token (default tags)"],
"total_findings": len(findings),
"auto_filed": filed,
"skipped_duplicates": skipped,
@ -163,6 +175,8 @@ def register_recon_tools(mcp: FastMCP, sandbox: SandboxManager) -> None:
for f in findings
],
}
if last_stats:
result_data["scan_progress"] = last_stats
if nuclei_stderr:
result_data["nuclei_stderr"] = nuclei_stderr[:1000]
return json.dumps(result_data)