๐Ÿ” NodeJS-PortScanner

A fast, lightweight TCP and UDP port scanner with service fingerprinting, built for network administrators and security learners to audit their own systems.

Features

Installation

๐Ÿณ Run with Docker (recommended)

The quickest way to run NodeJS-PortScanner โ€” no Node.js install or clone required. Pull the pre-built image from the GitHub Container Registry; 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 --help

On Linux, scanning the host's own interfaces or localhost needs the host network โ€” otherwise the scan runs from the container's isolated namespace:

$ docker run --rm --network host ghcr.io/zuedev/nodejs-portscanner --host 127.0.0.1 --ports 1-1024

๐ŸชŸ On Windows & macOS (Docker Desktop), --network host shares the network of Docker's internal Linux VM โ€” not your machine โ€” so 127.0.0.1 won't reach services running on your host. Drop --network host and target host.docker.internal instead, which Docker Desktop routes to the host:

$ docker run --rm ghcr.io/zuedev/nodejs-portscanner --host host.docker.internal --ports 1-1024

If a service is bound only to 127.0.0.1 on the host (rather than 0.0.0.0), rebind it to all interfaces so the container can reach it.

Save a JSON or HTML report by mounting a writable directory and writing into it โ€” a .html extension produces an HTML report, any other writes JSON:

$ 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.

Prefer to build the image yourself? From a clone of the repo:

$ docker build -t nodejs-portscanner .
$ docker run --rm nodejs-portscanner --host example.com --ports 1-1024

From source

Requires Node.js ≥ 25. No build step.

$ git clone https://github.com/zuedev/NodeJS-PortScanner.git
$ cd NodeJS-PortScanner
$ npm install

Or install globally to expose a nodejs-portscanner command on your PATH:

$ npm install -g .
$ nodejs-portscanner --host 192.168.1.1 --ports 1-1024

Quick start

  1. Scan a host you own:
    $ node scanner.js --host 192.168.1.1 --ports 1-1024
  2. Read the table โ€” open ports are listed with their detected service.
  3. Save a report:
    $ node scanner.js --host 192.168.1.1 --ports 1-1024 --output results.json

Use cases

Practical, lawful ways to put NodeJS-PortScanner to work on infrastructure you control.

๐Ÿ–ฅ๏ธ Audit your own server

Confirm exactly which ports your VPS or home server exposes to the network, and spot anything you didn't intend to leave open.

$ node scanner.js -h my-server.lan -p 1-65535 -c 200

๐Ÿ  Inventory your home network

Discover what services are running on devices across your LAN โ€” printers, NAS boxes, IoT gadgets โ€” and fingerprint them.

$ node scanner.js -h 192.168.1.10 -p 1-1024

๐Ÿงฑ Verify firewall rules

After changing firewall or security-group rules, prove that the ports you closed are actually filtered and the ones you need stay reachable.

$ node scanner.js -h 10.0.0.5 -p 22,80,443,3306 -t 1500

๐Ÿš€ Pre-deployment smoke check

Before going live, confirm a fresh box only answers on the services you expect (for example, just 80 and 443).

$ node scanner.js -h staging.internal -p 1-1024 -o pre-deploy.json

๐Ÿค– CI/CD service checks

Import the API into a test to assert that a freshly started service is listening on its expected port before the pipeline continues.

$ node --test    # uses scanPorts() under the hood

๐ŸŽ“ Learn how scanning works

The code is small and readable โ€” a hands-on way to understand TCP connect scans, banner grabbing, and concurrency pools.

$ node scanner.js -h 127.0.0.1 -p 1-1024

Usage & examples

Common command-line recipes. Every flag has a short alias.

Basic scan

Scan the default range (1-1024).

$ node scanner.js --host 192.168.1.1 --ports 1-1024

Scan specific ports

Pass a comma-separated list.

$ node scanner.js --host example.com --ports 22,80,443,8080

Tune concurrency and timeout

Scan every port with 200 simultaneous connections and a 1.5s timeout.

$ node scanner.js --host 10.0.0.5 --ports 1-65535 --concurrency 200 --timeout 1500

Scan UDP ports

Switch to UDP with --protocol. Tailored probes are sent to common services (DNS, NTP); ports that never reply are reported as open|filtered.

$ node scanner.js --host 192.168.1.1 --protocol udp --ports 53,123,161

Limit the scan rate

Cap how many new probes start each second across all workers to keep the scan quiet and gentle on the target.

$ node scanner.js --host 192.168.1.1 --ports 1-1024 --rate 50

Export results to JSON

Great for diffing scans or feeding other tools.

$ node scanner.js --host 192.168.1.1 --ports 1-1024 --output results.json

Export an HTML report

A .html (or .htm) extension writes a self-contained, styled 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.

$ node scanner.js --host 192.168.1.1 --ports 1-1024 --output report.html

Options

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 the help menu โ€”

Example output

Scanning 192.168.1.1 (TCP 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

Programmatic API

NodeJS-PortScanner ships as an ES module, so you can import its building blocks directly โ€” ideal for tests and automation.

Scan ports from code

import { scanPorts } from "nodejs-portscanner";

const results = await scanPorts("127.0.0.1", [22, 80, 443], {
  protocol: "tcp",
  concurrency: 50,
  timeout: 1500,
  rate: 100,
});

for (const r of results) {
  if (r.open) console.log(`${r.port}/tcp open โ€” ${r.service}`);
}

Parse a port spec

import { parsePorts } from "nodejs-portscanner";

parsePorts("22,80,1000-1002"); // โ†’ [22, 80, 1000, 1001, 1002]

Fingerprint a banner

import { fingerprint } from "nodejs-portscanner";

fingerprint(22, "SSH-2.0-OpenSSH_8.9"); // โ†’ "SSH (OpenSSH 8.9)"
fingerprint(443);                       // โ†’ "HTTPS" (well-known fallback)
Export What it does
scanPorts(host, ports, opts) Scan many ports concurrently; resolves to sorted results.
scanPort(host, port, opts) Scan a single port and grab its banner.
scanUdpPort(host, port, opts) Probe a single UDP port and classify it as open, closed, or open|filtered.
createRateLimiter(rate) Build a limiter that spaces probe starts to a per-second rate.
parsePorts(spec) Expand a port spec into a sorted, de-duplicated list.
fingerprint(port, banner) Identify a service from its banner or well-known port.
buildJsonReport(params) Produce the JSON structure used by --output.
buildHtmlReport(params) Render a self-contained HTML report from a scan's results.
formatTable(results) Render the aligned, human-readable results table.

How it works

NodeJS-PortScanner uses Node's built-in net.Socket to attempt TCP connections against target ports. For each open port it:

  1. Establishes a connection within the configured timeout.
  2. Attempts banner grabbing by reading the initial server response.
  3. Matches the banner against a fingerprint database to identify the service.

Concurrency is managed via a bounded connection pool, so you never exceed the configured number of simultaneous connections or overwhelm the host's file-descriptor limits. An optional --rate limit spaces out how many new probes start each second to keep network noise down.

UDP scans (--protocol udp) use Node's dgram module instead. Because UDP is connectionless, each port is classified by the response: a datagram reply means open, an ICMP port-unreachable means closed, and silence until the timeout is reported as open|filtered (open or firewalled).

Testing

Tests run against a local mock server โ€” no external network calls.

$ npm test