"""Headless CLI for IoT PreTester 4.6 Demo. Exit codes: 0 no high/critical findings, 2 at least one, 1 runtime error. """ from __future__ import annotations import argparse import logging import sys from . import __version__ from .engine import AnalysisEngine from .findings import Severity from .reporting import render, FORMATS from . import logutil def build_parser() -> argparse.ArgumentParser: p = argparse.ArgumentParser( prog="analyzer", description="Analyze the security of a PCAP or PCAPNG network capture.", ) p.add_argument("capture", help="Path to a .pcap or .pcapng file") p.add_argument("-f", "--format", choices=FORMATS, default="md", help="Output format (default: md)") p.add_argument("-o", "--output", default=None, help="Output file (default: standard output)") p.add_argument("--packet-limit", type=int, default=None, help="Maximum packets to read from a large capture") p.add_argument("--device-ip", default=None, help="IP address of the device under test (focuses and attributes findings)") p.add_argument("-v", "--verbose", action="store_true", help="Debug logging on stderr") p.add_argument("--log-file", default=None, help="Write the analysis log to this file") p.add_argument( "--version", action="version", version=f"IoT PreTester 4.6 Demo analyzer {__version__}", ) return p def main(argv=None) -> int: args = build_parser().parse_args(argv) logutil.configure_console("DEBUG" if args.verbose else "WARNING") try: with logutil.run_log(args.log_file): result = AnalysisEngine().analyze_pcap(args.capture, packet_limit=args.packet_limit, device_ip=args.device_ip) except FileNotFoundError as exc: print(f"Error: {exc}", file=sys.stderr) return 1 except RuntimeError as exc: print(f"Error: {exc}", file=sys.stderr) return 1 except Exception as exc: logging.getLogger("analyzer.cli").exception("Unexpected failure") print(f"Error: {exc!r}", file=sys.stderr) return 1 report = render(result, args.format) if args.output: with open(args.output, "w", encoding="utf-8") as fh: fh.write(report) print(f"Report written: {args.output} " f"({result.meta.get('finding_count', 0)} Findings)", file=sys.stderr) else: print(report) worst = max((f.severity.rank for f in result.findings), default=0) return 2 if worst >= Severity.HIGH.rank else 0 if __name__ == "__main__": # pragma: no cover raise SystemExit(main())