mirror of
https://github.com/usestrix/strix.git
synced 2026-08-22 11:02:08 +02:00
Every scan now writes a complete log file at ``{run_dir}/strix.log``
captured from the moment ``run_dir`` is resolved through teardown.
Stdlib ``logging`` only — no parallel framework.
New ``strix/telemetry/logging.py``:
* ``setup_scan_logging(run_dir, debug=)`` attaches a ``FileHandler``
(DEBUG, all ``strix.*``) plus a ``StreamHandler`` (ERROR by
default; DEBUG via ``STRIX_DEBUG=1``).
* ``ContextVar``-backed ``scan_id`` and ``agent_id`` injected by a
``Filter`` so every line is auto-tagged across asyncio tasks
without callers passing them explicitly.
* Third-party noise (``httpx``, ``litellm``, ``openai``,
``anthropic``, ``urllib3``, ``httpcore``) capped at WARNING.
* Returns a teardown handle for ``finally`` cleanup.
Wiring:
* ``orchestration/scan.py`` calls ``setup_scan_logging`` once per
scan after ``run_dir`` resolves; sets scan_id; tears down in
``finally``. Adds INFO logs for sandbox bring-up + scan
start/end.
* ``orchestration/hooks.py`` sets/clears ``agent_id`` ContextVar in
``on_agent_start`` / ``on_agent_end`` and emits INFO for agent
lifecycle, DEBUG for every tool start/end and LLM call.
* ``interface/main.py`` drops the ``setLevel(ERROR)`` silencer.
Coverage expanded across ~20 files (orchestration, agents, runtime,
llm, tools, interface, config, skills) with INFO for lifecycle and
DEBUG for verbose detail. Per the system instructions in
``logger.warning(f"…{e}")`` were converted to module logger calls.
156 lines
6.0 KiB
Python
156 lines
6.0 KiB
Python
"""``finish_scan`` — root-agent termination + executive report persistence."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import json
|
|
import logging
|
|
from typing import Any
|
|
|
|
from agents import RunContextWrapper, function_tool
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def _do_finish(
|
|
*,
|
|
parent_id: str | None,
|
|
executive_summary: str,
|
|
methodology: str,
|
|
technical_analysis: str,
|
|
recommendations: str,
|
|
) -> dict[str, Any]:
|
|
if parent_id is not None:
|
|
return {
|
|
"success": False,
|
|
"error": "finish_scan_wrong_agent",
|
|
"message": "This tool can only be used by the root/main agent",
|
|
"suggestion": "If you are a subagent, use agent_finish instead",
|
|
}
|
|
|
|
errors: list[str] = []
|
|
if not executive_summary.strip():
|
|
errors.append("Executive summary cannot be empty")
|
|
if not methodology.strip():
|
|
errors.append("Methodology cannot be empty")
|
|
if not technical_analysis.strip():
|
|
errors.append("Technical analysis cannot be empty")
|
|
if not recommendations.strip():
|
|
errors.append("Recommendations cannot be empty")
|
|
if errors:
|
|
return {"success": False, "message": "Validation failed", "errors": errors}
|
|
|
|
try:
|
|
from strix.telemetry.tracer import get_global_tracer
|
|
|
|
tracer = get_global_tracer()
|
|
if tracer is None:
|
|
logger.warning("No global tracer; scan results not persisted")
|
|
return {
|
|
"success": True,
|
|
"scan_completed": True,
|
|
"message": "Scan completed (not persisted)",
|
|
"warning": "Results could not be persisted - tracer unavailable",
|
|
}
|
|
tracer.update_scan_final_fields(
|
|
executive_summary=executive_summary.strip(),
|
|
methodology=methodology.strip(),
|
|
technical_analysis=technical_analysis.strip(),
|
|
recommendations=recommendations.strip(),
|
|
)
|
|
vuln_count = len(tracer.vulnerability_reports)
|
|
except (ImportError, AttributeError) as e:
|
|
logger.exception("finish_scan persistence failed")
|
|
return {"success": False, "message": f"Failed to complete scan: {e!s}"}
|
|
else:
|
|
logger.info(
|
|
"finish_scan: completed scan with %d vulnerability report(s)",
|
|
vuln_count,
|
|
)
|
|
return {
|
|
"success": True,
|
|
"scan_completed": True,
|
|
"message": "Scan completed successfully",
|
|
"vulnerabilities_found": vuln_count,
|
|
}
|
|
|
|
|
|
@function_tool(timeout=60)
|
|
async def finish_scan(
|
|
ctx: RunContextWrapper,
|
|
executive_summary: str,
|
|
methodology: str,
|
|
technical_analysis: str,
|
|
recommendations: str,
|
|
) -> str:
|
|
"""Finalize the scan — persist the customer-facing report.
|
|
|
|
**Root-agent only.** Subagents must call ``agent_finish`` from the
|
|
multi-agent graph tools instead. Calling this finalizes everything:
|
|
|
|
1. Verifies you are the root agent.
|
|
2. Writes the four narrative sections to the scan record.
|
|
3. Marks the scan completed and stops execution.
|
|
|
|
**Pre-flight checklist (mandatory — do not skip):**
|
|
|
|
1. **Call ``view_agent_graph`` first.** Inspect every entry in the
|
|
summary. If ANY agent is in ``running`` / ``waiting`` /
|
|
``llm_failed`` state, you MUST NOT call ``finish_scan`` yet —
|
|
wrap them up first via ``send_message_to_agent`` (ask them to
|
|
finish), ``wait_for_message`` (block until their report
|
|
arrives), or ``stop_agent`` (graceful cancel). Only ``completed``
|
|
/ ``crashed`` / ``stopped`` agents are safe to leave behind.
|
|
Calling ``finish_scan`` while children are alive orphans their
|
|
work and produces an incomplete report.
|
|
2. All vulnerabilities you found are filed via
|
|
``create_vulnerability_report`` (un-reported findings are not
|
|
tracked and not credited).
|
|
3. Don't double-report — one report per distinct vulnerability.
|
|
|
|
**Calling this multiple times overwrites the previous report.**
|
|
Make the single call comprehensive.
|
|
|
|
**Customer-facing report rules** (this output is rendered into the
|
|
final PDF the client sees):
|
|
|
|
- Never mention internal infrastructure: no local/absolute paths
|
|
(``/workspace/...``), no agent names, no sandbox/orchestrator/
|
|
tooling references, no system prompts, no model-internal errors.
|
|
- Tone: formal, third-person, objective, concise. This is a
|
|
consultant deliverable, not an engineering log.
|
|
- Each section has a specific role:
|
|
|
|
- ``executive_summary`` — for non-technical leadership. Risk
|
|
posture, business impact (data exposure / compliance /
|
|
reputation), notable criticals, overarching remediation
|
|
theme.
|
|
- ``methodology`` — frameworks followed (OWASP WSTG, PTES,
|
|
OSSTMM, NIST), engagement type (black/gray/white box), scope
|
|
and constraints, categories of testing performed. **No**
|
|
internal execution detail.
|
|
- ``technical_analysis`` — consolidated findings overview with
|
|
severity model and systemic root causes. Reference individual
|
|
vuln reports for repro steps; don't duplicate raw evidence.
|
|
- ``recommendations`` — prioritized actions grouped by urgency
|
|
(Immediate / Short-term / Medium-term), each with concrete
|
|
remediation steps. End with retest/validation guidance.
|
|
|
|
Args:
|
|
executive_summary: Business-level summary for leadership.
|
|
methodology: Frameworks, scope, and approach.
|
|
technical_analysis: Consolidated findings + systemic themes.
|
|
recommendations: Prioritized, actionable remediation.
|
|
"""
|
|
inner = ctx.context if isinstance(ctx.context, dict) else {}
|
|
result = await asyncio.to_thread(
|
|
_do_finish,
|
|
parent_id=inner.get("parent_id"),
|
|
executive_summary=executive_summary,
|
|
methodology=methodology,
|
|
technical_analysis=technical_analysis,
|
|
recommendations=recommendations,
|
|
)
|
|
return json.dumps(result, ensure_ascii=False, default=str)
|