Discover what's on your network. Map how it's connected. Find what's vulnerable.
PyNetworkIntel scans a network target with nmap, optionally pulls
configuration from reachable devices over SSH, checks discovered services
against known vulnerability rules and the NVD CVE database, and derives a
subnet/gateway topology graph from the results - in plain Python, with no
hidden network calls beyond nmap/SSH/NVD/(optional) Anthropic.
This tool actively probes network hosts. Only scan networks and devices you own or have explicit written permission to test. Unauthorized network scanning may be illegal in your jurisdiction. Both the CLI and the underlying commands require you to confirm authorization before a scan runs (see Security below) - this isn't a formality, it's there so the tool never scans a target silently just because it was handed one.
pip install pynetworkintel
# nmap must also be installed and on PATH (brew install nmap / apt install nmap / etc.)
pynetworkintel scan 192.168.1.0/24
# -> prompts to confirm you're authorized to scan this target, then runsfrom pynetworkintel import Scanner
scanner = Scanner() # no SSH by default - see "SSH config grabbing" below
result = scanner.scan("192.168.1.0/24")
for device in result.devices:
print(f"{device.ip} ({device.hostname or 'unknown'})")
for service in device.services:
print(f" - {service.name} {service.version or ''} on port {service.port}")
print(result.topology().to_dict()) # subnet groupings + inferred gateway edges- A one-off network inventory —
pynetworkintel scan <subnet>for "what's actually on this network right now," with a real topology graph, not a flat list. - Recurring drift/change detection — repeated scans against the same SQLite history to catch new/removed devices between runs.
- Pairing discovery with vulnerability triage —
analyzecross-checks discovered services against config-based rules and live NVD CVE lookups in one pass. - Not yet a good fit for: enterprise multitenancy/HA/auth/REST API, or a conversational architecture advisor — both are explicitly excluded from the package (see Module status), not partially built.
- Device discovery -
nmap -sVbased host/port/service discovery (pynetworkintel.discovery) - Topology mapping - a real graph (nodes/edges), not just a flat device
list: devices are grouped into inferred subnets, with gateway-style edges
derived from IP addressing (
pynetworkintel.topology) - Vulnerability detection - config-based rule checks (weak SSH settings,
telnet exposure, etc.) plus live NVD CVE lookups by service/version
(
pynetworkintel.analysis) - Optional SSH config grabbing - pulls
sshd_config, firewall rules, etc. from devices you have credentials for, concurrently across devices - Change tracking - detects new/removed devices across scans, backed by
a local SQLite/SQLAlchemy history (
pynetworkintel.changes,pynetworkintel.db) - Reports & alerts - Markdown/JSON reports, chat-webhook/email/generic
webhook alert channels (
pynetworkintel.reporting,pynetworkintel.alerts) - Optional AI summary - plain-English findings summary via the
Anthropic API, only when
--summarize/summarize=Trueis used
pip install pynetworkintelRequires the nmap binary to be installed separately and on PATH
(the Python package wraps it via subprocess; without it, discovery
returns no results but the rest of the API still works, e.g. for testing).
Optional extras for the lighter-tested cloud/kubernetes asset-discovery modules (see Module status):
pip install "pynetworkintel[cloud]" # boto3, azure-mgmt-*, google-cloud-*
pip install "pynetworkintel[kubernetes]" # kubernetes python client# Scan a subnet (prompts for scan authorization the first time)
pynetworkintel scan 192.168.1.0/24
# Scan and grab SSH config from reachable devices (explicit --ssh-user required;
# there is no default user, on purpose - see Security below)
pynetworkintel scan 192.168.1.0/24 --ssh-user admin --ssh-key ~/.ssh/id_rsa
# Non-interactive / CI use: skip the confirmation prompt explicitly
pynetworkintel scan 192.168.1.0/24 --i-am-authorized
# Full pipeline: scan + vulnerability analysis + AI summary
pynetworkintel analyze 192.168.1.0/24 --summarize
# JSON output (includes devices, findings, and the topology graph)
pynetworkintel scan 192.168.1.0/24 --output json
# Save results to a file
pynetworkintel analyze 192.168.1.0/24 --output-file results.jsonSSH passwords are never accepted as a CLI flag (they'd end up in shell
history and process listings). Use --ssh-key or set
PYNETWORKINTEL_SSH_PASSWORD in the environment instead.
from pynetworkintel import Pipeline
pipeline = Pipeline(
ssh_username="admin", # optional; omit to skip SSH config grabbing
ssh_key_path="~/.ssh/id_rsa",
)
result = pipeline.run("192.168.1.0/24", summarize=False)
print(result["summary"]) # counts: devices, findings by severity, subnets
print(result["topology"]) # {"nodes": [...], "edges": [...], "subnets": [...]}
for finding in result["findings"]:
print(finding["severity"], finding["title"])PyNetworkIntel is a dual-use network scanning tool, and the security posture of the tool itself is treated as a first-class concern, not an afterthought:
- Scan authorization is required.
scan/analyzewill not run without--i-am-authorized,PYNETWORKINTEL_I_AM_AUTHORIZED=1, or an interactive confirmation. - No default SSH user. You must pass
--ssh-userexplicitly; the tool will not assumeroot(or anything else) on your behalf. - No
--ssh-passwordflag. Use--ssh-keyor thePYNETWORKINTEL_SSH_PASSWORDenvironment variable. - SSH passwords are never written to disk, including in the persisted
config file (
~/.pynetworkintel/config.yaml, which is chmod'd0600). - SSH host key verification uses
paramiko.WarningPolicy(logs loudly on an unknown/changed host key rather than silently trusting it). Seepynetworkintel/discovery/ssh_config.pyfor the documented tradeoff - first-contact scanning of arbitrary devices is fundamentally in tension with strict known-hosts verification. - Scan targets are validated before reaching the
nmapcommand line, rejecting anything that isn't a plausible IP/CIDR/hostname (in particular anything starting with-, which would otherwise be parsed as an nmap flag). - Dependency versions are pinned with upper bounds in
pyproject.toml.
See SECURITY.md for how to report a vulnerability.
Being direct about what's tested and what's real-but-lighter-tested vs. not shipped at all:
| Module | Status | Notes |
|---|---|---|
models, topology, topology_export, analysis/cve, analysis/rules, db, config |
Core, well-tested | 83-100% line coverage (measured with pytest --cov); this is where the primary, meaningful test coverage lives |
discovery/scanner |
Core, tested | 64% line coverage; XML parsing, target validation, and per-record error isolation are covered, some subprocess edge cases aren't |
core, discovery/ssh_config, analysis/llm |
Core, partially tested | 34-48% line coverage - the security-relevant paths (auth gate, SSH defaults, no-password-flag) are well covered by tests/test_security.py, but general code paths in these files have real gaps |
cli, dashboard, scheduler, alerts, changes, reporting |
Core, thin test coverage | 10-52% line coverage measured directly - previous versions of this README called these "tested" alongside the 90-100%-covered modules above; that conflated very different actual coverage levels. reporting.py in particular is 10% covered. |
cloud (major cloud provider asset discovery) |
Shipped, lighter-tested | Real code, lazy-imports SDKs (boto3, azure-mgmt-, google-cloud-) via the cloud extra; 12-34% line coverage |
kubernetes (cluster/RBAC/pod security) |
Shipped, lighter-tested | Real code, lazy-imports the kubernetes client via the kubernetes extra; 11-25% line coverage |
iot (MQTT/CoAP/Modbus/S7comm discovery) |
Shipped, lighter-tested | Real socket-based probing; 19-41% line coverage |
ml (statistical baselines/anomaly detection) |
Shipped, lighter-tested | Real statistics-based logic, not a trained model - "ML" in the name overstates it; 38-45% line coverage |
devops (CI/CD & IaC static scanning) |
Shipped, lighter-tested | Real regex-based Terraform/GitHub Actions scanning, self-contained; 18-44% line coverage |
enterprise (multitenancy, HA, auth, REST API) |
Not shipped | Excluded from the package entirely - enterprise-platform scope, unrelated to network discovery, untested |
architect (conversational architecture advisor) |
Not shipped | Excluded from the package entirely - unrelated to network discovery |
_mcp_connector.py / _mcp_tools.py (NetworkIntelligence, exported from pynetworkintel.__init__) |
Shipped but fake - do not use | Every method returns hardcoded fake data (e.g. a hardcoded "src_ip": "192.168.1.100"), not real analysis. Untested, undocumented until this pass, references a dab binary and statguardian package this project does not depend on. See ROADMAP_HONEST.md. |
- Python 3.10+
nmapinstalled and onPATHfor live scanning- SSH access (key or password) to a device, only if you want config grabbing
- An Anthropic API key, only if you use
--summarize
- Dashboard - the live terminal stats viewer (
--dashboard) - CLAUDE.md - accurate architecture description for engineers (human or AI) working on this codebase
- ROADMAP_HONEST.md - current honest status: what's fixed, what's broken, what's a fake stub, what's genuinely pending, and technical debt with file:line specifics
- CHANGELOG.md, CONTRIBUTING.md, SECURITY.md
docs/ARCHITECTURE.md, docs/PRODUCT_VISION.md, and docs/ROADMAP.md are
stale/placeholder documents from earlier planning and are not kept in sync
with the implementation (each now has an in-file banner saying so); refer
to CLAUDE.md and ROADMAP_HONEST.md
instead. docs/VERSION_MANAGEMENT.md was corrected in this pass and is
accurate.
- Fake stub shipped in the public API:
pynetworkintel.NetworkIntelligence(_mcp_connector.py/_mcp_tools.py) returns hardcoded fake data, not real analysis - see Module status andROADMAP_HONEST.md. - Lighter-tested modules:
cloud,kubernetes,iot,ml, anddevopsare real, shipped code but have less test coverage than the core scan -> analyze -> topology path; see Module status. - Stale planning docs:
docs/ARCHITECTURE.md,docs/PRODUCT_VISION.md, anddocs/ROADMAP.mdpredate the current implementation and are not kept in sync - each now carries an in-file banner saying so; use CLAUDE.md andROADMAP_HONEST.mdfor accurate descriptions instead. - Misleading git tag: the local
v2.0.0tag points at a commit older and less complete thanv1.4.0(an out-of-order/mislabeled tag from earlier history) - don't trust tag names as a version indicator; seeROADMAP_HONEST.md. paramiko3.5.1 has an open advisory (PYSEC-2026-2858, SHA-1 allowed inrsakey.py) with no fixed release available yet as of this pass; seeROADMAP_HONEST.mdfor tracking.- No committed performance benchmarks. There is no benchmark script or results file in this repo, so no throughput/latency numbers are claimed anywhere in this README.
- No open GitHub issues and no
TODO/FIXMEmarkers inpynetworkintel/as of this pass (though there is real lint debt - 23 bareexcept:clauses and ~48 files notblack-formatted; seeROADMAP_HONEST.md). pynetworkintel/__init__.py's__version__had drifted to1.3.1whilepyproject.tomlmoved on to1.4.0- fixed in this pass, andscripts/check-version-sync.shnow checks both files so it won't silently recur.
This project is licensed under the Apache License 2.0.