NETMapper is a network administration tool that holds switch credentials. This page covers what the software does to protect them, how to report a problem in it, and what it sends out about your installation.
Use NETMapper only on networks you own or are explicitly authorized to assess.
Security posture
- HttpOnly plus CSRF cookie auth. The access token rides in an HttpOnly
cookie, never in localStorage. A separate readable
csrf_tokencookie is validated server-side on every mutating route (double-submit), which blocks CSRF even in the presence of XSS. - bcrypt passwords with a Have I Been Pwned check. Passwords are hashed with bcrypt. On set or change, the hash prefix is checked against the Have I Been Pwned range API using k-anonymity, so no full hash leaves the server.
- Secrets encrypted at rest. Switch passwords, notification-channel
configs and AI provider keys are sealed with Fernet (AES-128-CBC plus
HMAC-SHA256) through an
EncryptedString/EncryptedJSONtype decorator backed byNETMAPPER_ENCRYPTION_KEY. Losing that key permanently destroys access to those secrets, so back it up out of band. - Per-endpoint authorization plus an audit log. Mutating endpoints are
gated by granular permission checks from a 24-permission catalog across 9
categories, with three built-in roles (admin, operator, viewer). Every
privileged action writes a persistent
AuditLogrow. - Rate-limited login.
POST /auth/loginandPOST /auth/login/jsonare rate-limited per remote IP (default5/minute), and a 429 carries aRetry-Afterheader. - Two-factor authentication. TOTP 2FA is available for user accounts.
- TLS.
docker-compose.https.ymlterminates TLS in nginx with automatic Let’s Encrypt certificates, or you can bring your own certificate. - Supply chain. Python dependencies are hash-locked with pip-compile
--generate-hashes; npm installs runnpm ci --ignore-scriptsagainst a committed lockfile with sha512 integrity. Docker base images and GitHub Actions are pinned by digest, Renovate enforces a seven-day release cooldown on new dependency versions, and CI runs Bandit, Trivy, CodeQL, pip-audit, npm audit and Gitleaks on every push.
Known gaps
Rate limiting beyond the login endpoints is not implemented yet; it is
tracked on the roadmap. NETMapper is pre-1.0, only main is supported, and
there are no backports to earlier tags. Deploy on a trusted, isolated
management network behind a firewall, ideally behind a zero-trust access
proxy.
Reporting a vulnerability
Do not open a public GitHub issue for a security report. A public issue exposes the details before a fix exists.
- Preferred: open a private GitHub Security Advisory.
- Alternative: the contact form, with the word “security” in the message so it is routed to the maintainers.
What to expect:
| Stage | Target |
|---|---|
| Initial acknowledgment | 72 hours |
| Triage and severity assessment | 7 days |
| Fix available or mitigation guidance | 90 days from acknowledgment |
| Public disclosure | When the fix ships, or at 90 days, whichever comes first |
You are credited in the CHANGELOG security entry unless you would rather stay anonymous. If a fix cannot land inside the window, a mitigation advisory is published on the original acknowledgment date and the open issue is tracked publicly.
In scope: the backend (FastAPI application, migrations, scanner modules,
auth, security and crypto modules, notification delivery), the frontend
(React app, API client, pages and components), the published Docker images,
and the docker-compose.yml and docker-compose.dev.yml deployment
configuration.
Out of scope: your own switch credentials and private keys, your network topology and scan results (that is your data), third-party dependencies (file those upstream, though findings are tracked and lockfiles updated), vulnerabilities that need physical access to the host, and social engineering of the maintainers.
Research only against instances you own and control, or a lab you set up for the purpose. Do not probe other people’s deployments or switches.
The full policy is in SECURITY.md.
Privacy
NETMapper is self-hosted: your inventory, topology, scan results and audit log never leave your deployment. Two mechanisms send anything outward at all, and both are described below.
Anonymous usage statistics, on by default
NETMapper Core sends one small anonymous ping per day. It exists so the project knows which versions, platforms and scanner kinds to test and support. It is on by default and the opt-out is one switch or one variable.
This is the complete payload; the backend enforces the list:
| Field | Meaning |
|---|---|
install_id |
Random UUID generated once per installation. Carries no user or network identity. |
version |
NETMapper version string. |
python_version |
Python version string. |
os |
Operating system family, for example Linux. |
switch_count_bucket |
Active switch count as a bucket (0, 1-4, 5-9, 10-19, 20-49, 50-99, 100-199, 200+), never the exact number. |
device_count_bucket |
Active device count, same buckets. |
scans_per_day_bucket |
Scans in the last 24 hours, same buckets. |
alerts_per_day_bucket |
Alerts in the last 24 hours, same buckets. |
scanner_kinds |
Scanner kinds in use, for example ["cisco_ios", "nokia_srl"]. |
ai_provider_kinds |
AI provider kinds configured, for example ["openai", "ollama"]. |
Nothing about your network: no IPs, MACs, hostnames, switch names, credentials or scan output. Never sent either: usernames, passwords, contact details, AI API keys, prompt contents, or audit log contents.
How to turn it off. Two ways, and either is enough:
-
In the UI, as an admin: open Settings, then Telemetry, and switch off “Anonymous usage statistics (this installation)”. The choice is stored in the database and takes effect immediately.
-
In the environment, which is the right choice for fleet or automated deployments:
NETMAPPER_TELEMETRY_ENABLED=falseRestart the backend afterwards. The environment variable always wins over the UI switch, and the UI then shows the switch as locked.
You can also keep the ping but send it to yourself, by pointing
NETMAPPER_TELEMETRY_URL at an endpoint you control. The payload is a JSON
POST with exactly the fields above.
The install UUID lives in the system_settings table under
telemetry_install_id. Deleting that row makes NETMapper generate a new UUID
on the next start, with no association between the old and new values.
Crash reporting, off by default
Sentry crash reporting stays off unless you set NETMAPPER_SENTRY_DSN to a
Sentry project you own. When it is set, unhandled exceptions from the backend
and the frontend are forwarded there, along with the user-agent string, the
NETMapper version and the URL path that failed.
Scrubbed before anything is sent: all HTTP request and response bodies, all
environment variables (including NETMAPPER_ENCRYPTION_KEY,
NETMAPPER_JWT_SECRET and any *_PASSWORD or *_TOKEN variable), switch SSH
credentials and switch IP addresses, AI provider API keys, and
notification-channel secrets such as SMTP passwords, webhook URLs and bot
tokens.
The Sentry project, its retention policy and its team access are entirely yours. The NETMapper maintainers hold no copy of it.
The AI assistant
The assistant is read-only and its tool use is gated to a fixed whitelist:
list_devices, list_switches, list_neighbors, list_alerts and
stats_summary. It cannot change anything. It talks to whichever
OpenAI-compatible provider you configure, including a local one such as
Ollama, so you decide whether any prompt leaves your network at all. Provider
keys are encrypted at rest.
The full privacy policy is in
PRIVACY.md. For
privacy questions, use the contact form with
the subject prefix [PRIVACY].