Speedy port scanner with service fingerprinting for Node.js
A fast, lightweight port scanner with service fingerprinting, built in Node.js. Designed for network administrators and security learners to audit their own systems.
Only scan systems you own or have explicit written permission to test. Unauthorized port scanning may violate the Computer Fraud and Abuse Act (US), the Computer Misuse Act (UK), and equivalent laws in your jurisdiction. The authors assume no liability for misuse.
- β‘ Concurrent scanning with configurable connection limits
- π TCP and UDP scanning (
--protocol tcp|udp) - π― Port range support (e.g.,
1-1024,80,443,8080) - π Service fingerprinting β identifies common services via banner grabbing
- β±οΈ Configurable timeouts for slow or filtered hosts
- π‘οΈ Rate limiting to reduce network noise (
--rate) - π Clean tabular output with optional JSON or HTML export
- πͺΆ Zero heavy dependencies β uses Node's built-in
netanddgrammodules
The quickest way to run NodeJS-PortScanner is the pre-built image on the GitHub Container Registry β no Node.js install or clone required. Any flags after the image name are passed straight to the scanner:
docker run --rm ghcr.io/zuedev/nodejs-portscanner --host example.com --ports 1-1024
docker run --rm ghcr.io/zuedev/nodejs-portscanner --helpBy default the container scans from its own isolated network namespace. On Linux, share the host network to reach the host's own interfaces or localhost:
docker run --rm --network host ghcr.io/zuedev/nodejs-portscanner --host 127.0.0.1 --ports 1-1024πͺ Windows & macOS (Docker Desktop):
--network hostshares the network of Docker's internal Linux VM β not your machine β so127.0.0.1won't reach services running on your host. Drop--network hostand targethost.docker.internalinstead, which Docker Desktop routes to the host:docker run --rm ghcr.io/zuedev/nodejs-portscanner --host host.docker.internal --ports 1-1024If a service is bound only to
127.0.0.1on the host (rather than0.0.0.0), rebind it to all interfaces so the container can reach it.
Mount a writable directory and write the report into it:
docker run --rm -v "${PWD}:/data" ghcr.io/zuedev/nodejs-portscanner -h example.com -p 1-1024 -o /data/results.json
# On Linux, add --user "$(id -u):$(id -g)" so the file is owned by you.From a clone of the repo:
docker build -t nodejs-portscanner .
docker run --rm nodejs-portscanner --host example.com --ports 1-1024git clone https://github.com/zuedev/nodejs-portscanner.git
cd nodejs-portscanner
npm installOr install globally:
npm install -g .π‘ Using Docker? Replace
node scanner.jswithdocker run --rm ghcr.io/zuedev/nodejs-portscannerin any example below.
node scanner.js --host 192.168.1.1 --ports 1-1024node scanner.js --host example.com --ports 22,80,443,8080node scanner.js --host 192.168.1.1 --protocol udp --ports 53,123,161UDP is connectionless, so ports that never reply are reported as open|filtered
(open or firewalled) rather than open. Tailored probes are sent to common
services (DNS, NTP) to encourage a response.
node scanner.js --host 10.0.0.5 --ports 1-65535 --concurrency 200 --timeout 1500node scanner.js --host 192.168.1.1 --ports 1-1024 --rate 50Caps how many new probes are started each second across all workers, reducing network noise and load on the target. Unlimited by default.
node scanner.js --host 192.168.1.1 --ports 1-1024 --output results.jsonnode scanner.js --host 192.168.1.1 --ports 1-1024 --output report.htmlThe format is chosen from the file extension: .html (or .htm) writes a
self-contained, styled HTML report you can open in any browser; any other
extension writes JSON. Banner text is HTML-escaped, so reports are safe to open
even after scanning untrusted hosts.
| Flag | Alias | Description | Default |
|---|---|---|---|
--host |
-h |
Target hostname or IP address (required) | β |
--ports |
-p |
Port range or comma-separated list | 1-1024 |
--protocol |
-P |
Transport protocol (tcp or udp) |
tcp |
--concurrency |
-c |
Max simultaneous connections | 100 |
--timeout |
-t |
Connection timeout in milliseconds | 2000 |
--rate |
-r |
Max new probes started per second | unlimited |
--output |
-o |
Export results to a file (.html β HTML report, otherwise JSON) |
none |
--help |
β | Show help menu | β |
Scanning 192.168.1.1 (ports 1-1024)...
PORT STATE SERVICE
22/tcp open SSH (OpenSSH 8.9)
80/tcp open HTTP (nginx 1.18.0)
443/tcp open HTTPS
3306/tcp open MySQL
Scan complete: 4 open ports found in 3.2s
NodeJS-PortScanner uses Node's built-in net.Socket to attempt TCP connections against target ports. For each open port, it:
- Establishes a connection within the configured timeout.
- Attempts banner grabbing by reading initial server response data.
- Matches banners against a fingerprint database to identify the service.
Concurrency is managed via a connection pool to avoid overwhelming the target or the host's file descriptor limits.
UDP scans (--protocol udp) use Node's dgram module instead. Since UDP has no handshake, each port is classified by the target's response:
- a datagram reply β
open - an ICMP port-unreachable error β
closed - silence until the timeout β
open|filtered(open or firewalled)
- Concurrent TCP scanning with a connection pool
- Service fingerprinting via banner grabbing
- Port ranges and lists (
1-1024,80,443,8080) - JSON export
- Test suite backed by a local mock server
- UDP scanning support
- Configurable fingerprint database (JSON-based)
- CIDR range scanning (e.g.,
192.168.1.0/24) - Rate limiting to reduce network noise
- HTML report generation
npm testTests use a local mock server to validate scanning logic without external network calls.
Contributions are welcome! Please open an issue first to discuss major changes. Make sure tests pass before submitting a PR.
This project is licensed under the Unlicense β see the LICENSE file for details.
Built by zuedev as part of a hands-on security learning portfolio.