Skip to content

Python Orchestrator

The netvuln Python package is a native, typed CLI that wraps the same scan pipeline as the Bash tooling. It is a drop-in replacement for vulnscan_recon.sh and also handles upload, push, diff, status queries, and daemon control. It shells out to the existing Bash modules for the actual scanning work, then adds Python-native nmap XML parsing, a native scoring engine, checkpoint/resume, progress events, and a license soft gate. Type checking is enforced with strict mypy, and the package ships a py.typed marker.

Installation

bash
# From the repo root (editable install puts the `netvuln` console script on PATH)
pip install -e .

# Or run without installing
python3 -m netvuln --version

The console script and the module form are equivalent: netvuln recon … and python3 -m netvuln recon ….

Subcommands

recon, scan, upload, push, diff, status, and daemon. The parser lives in netvuln/cli.py; each subcommand dispatches into netvuln/dispatch.py.

recon: multi-phase pipeline

bash
python3 -m netvuln recon -t 192.168.1.0/24 --full -R report.html
python3 -m netvuln recon -t 10.0.0.0/24 --full --exec-summary --email ciso@client.com --diff
FlagPurpose
-t/--target | -T/--target-fileTarget CIDR/host/domain, or a file of targets (mutually exclusive, one required)
--full | -p/--phases | -P/--profilePhase selection (mutually exclusive): all phases, an explicit comma list, or a named profile
-R/--report FILEHTML report output path
-c/--config FILEEngagement config (client metadata, portal credentials, branding)
--timingnmap timing template (default -T4)
--portsnmap port spec (default -F)
--no-cve-lookupSkip CVE description fetching
--exec-summaryAlso generate the one-page executive summary
--email ADDREmail the report
--diffRun the cross-session diff after the scan
--compare DIRBenchmark against a prior session directory
--resumeResume from the last checkpoint
--dry-runPrint the plan without running

scan: quick nmap modes

bash
python3 -m netvuln scan -m quick -t 192.168.1.1

-m/--mode is one of quick, full, vuln, custom (default quick); --extra-opts passes raw nmap options for custom.

upload / push: send sessions to the portal

bash
python3 -m netvuln upload -c ~/netvuln-tool/configs/acme_corp.conf --latest
python3 -m netvuln push   -c ~/netvuln-tool/configs/acme_corp.conf -d ~/…/sessions/20260718_120000 -R report.html -e exec_summary.html

upload requires -c/--config and selects a session with --latest, -i/--session-id, or -s/--session-dir (--dry-run previews). push takes -d/--session-dir or -j/--session-json (one required), plus -c/--config or explicit -u/--api-url + -k/--api-key, and optional -R/--report-file and -e/--exec-summary. push returns a shareable report link.

diff: compare sessions

bash
python3 -m netvuln diff --latest
python3 -m netvuln diff -c current.json -p previous.json --json

--latest diffs the two most recent sessions; otherwise pass -c/--current and -p/--previous. --json and --quiet control output. As with the Bash scan_diff.sh, new critical/high findings drive a non-zero exit for CI use.

status: local and remote

bash
python3 -m netvuln status                     # local sessions + daemon status
python3 -m netvuln status --remote -c acme_corp.conf --jwt "$NV_JWT"

--remote queries the portal session list over JWT bearer auth (from --jwt, NV_JWT, or CONSULTATION_JWT); -c/--config supplies CONSULTATION_API_URL.

daemon: control the scheduler

bash
python3 -m netvuln daemon status
python3 -m netvuln daemon start

The action (start, stop, restart, reload, status) and any trailing args are forwarded to netvuln_daemon_ctl.sh. See Daemon Mode.

Pipeline Runner

netvuln/pipeline.py defines the Pipeline class and netvuln/runner.py the BashRunner that executes it. The runner can invoke a standalone script (run_script) or source a library and call a single function (run_module), tee-ing stdout/stderr through reader threads and enforcing a timeout by killing the whole process group.

Pipeline.run() proceeds through the same phase model as the Bash orchestrator (discovery, enumeration, email_security, vuln, report):

  1. Check the license (soft gate), initialize the session JSON, and set up the checkpoint.
  2. Install a SIGINT handler that preserves partial results.
  3. For each phase: fire pre-report where applicable, run the phase modules (some in parallel via a thread pool), record phase status/timing in the checkpoint, and fire the matching post-phase hook (post-discovery, post-enumeration, post-assessment, post-report).
  4. Enrich the session from nmap XML, run native scoring, stamp the end time, save, auto-submit if configured, and fire post-pipeline.

Progress is surfaced through PipelineEvent callbacks (pipeline_start, phase_start, phase_end, pipeline_end), so a caller can render live progress. Checkpoints let --resume skip already-completed phases.

nmap XML Parsing

netvuln/parsers/nmap_xml.py parses nmap -oX output into typed Host and Port models (netvuln/models/):

  • parse_host_discovery() reads nmap -sn -oX output, returning hosts with status up (IPv4, hostname preferring type="user" over PTR, and MAC when present).
  • parse_port_scan() reads nmap -sV -oX output, returning hosts that have at least one open port, with service name and a composed version string (product version extrainfo) and an OS guess from the first osmatch.

Parsed hosts are merged into the session dict keyed by IP (ports keyed by (port, protocol), XML values winning), which is safer and faster than re-parsing text output. Malformed or missing XML returns an empty list rather than raising.

Native Scoring

netvuln/scoring.py is a pure-Python port of classify_severity.sh, risk_score.sh, and aggregate_results.sh, operating directly on the session dict:

  • classify_severity(): three-tier matching: title patterns (critical/high/medium/low, case-insensitive), then port-based risk for enumeration findings, then service-name risk; default info.
  • calculate_risk_score(): severity weights (critical 25, high 15, medium 5, low 1, info 0) plus bonuses for cleartext ports (FTP/Telnet, +10 each) and management ports (RDP, VNC, databases, +8 each), clamped to 0-100 and mapped to grades A-F.
  • calculate_operational_score(): subtracts the weight of accepted, non-expired exceptions to produce the operational score/grade and an exception_impact block.
  • aggregate_results(): dedups by (host, port, title), re-numbers IDs, sorts by severity, rebuilds the summary, then runs both scoring functions.

The scoring model matches the Bash implementation exactly, so a session scored in Python is consistent with one scored by the shell tooling. See Scoring Methodology.

License Soft Gate

netvuln/license.py mirrors lib/license.sh (key resolution, parsing, tier matrix) and lib/license_validate.sh (online validation with a 24h cache and 72h grace). It resolves keys of the form nvt_{pro|biz|msp}_{64 hex} with the precedence NV_LICENSE_KEY > NV_API_KEY > CONSULTATION_API_KEY > ~/.netvuln/license.key, gates features by tier (compliance/benchmark/remediation/webhooks need business; c2/agent_commands need msp), and warns rather than blocking. The plaintext key stays in memory only; the cache stores a sha256 fingerprint prefix at mode 600. The portal remains authoritative (a rejected key returns 403). See License Provisioning.

Type Checking and Tests

bash
# Strict static typing
python3 -m mypy netvuln/

# Python test suite
python3 -m pytest tests/python/

Strict mypy is configured in pyproject.toml. The pytest suite in tests/python/ covers the CLI subcommands, pipeline runner, scoring engine, upload/diff modules, remote session listing, plugin hooks, license gate, and progress events.

Apache-2.0 licensed (appliance subtree proprietary)