CD CyberDuty / CLI Reference
← Back to site
CLI Reference

CyberDuty Scanner

Complete command reference for local Docker-based pentesting with OWASP ZAP and Nuclei. Scans run entirely on your machine; pushing findings to a hosted portal is optional. Nuclei downloads its templates on first run.

v0.1.0 Requires Docker · Node.js ≥ 20

Installation

Install from the public repository. Clone, install dependencies, build the dashboard UI, and link the cyber command globally.

shell
# Clone the repository
git clone https://github.com/pv-cloud/cyberduty-scanner.git
cd cyberduty-scanner

# Install dependencies and build the dashboard UI
npm install
npm run build

# Link the CLI globally (exposes the `cyber` command)
npm link -w @cyber/cli

# Verify
cyber --version
📋
Prerequisites: Docker must be running and the target application must already be running inside a container before you invoke cyber pentest. Requires Node.js ≥ 20.
Don't skip npm run build — it compiles the local dashboard UI. Without it, cyber dashboard serves a blank page (the CLI will warn you).

Quick start

From zero to a local security report. Pick one engine per scan with --engine (default zap); run it twice to use both.

shell
# 1. Start your app (if it isn't running already)
docker compose up -d

# 2. Run a ZAP scan — use the container name and the port inside the container
cyber pentest my-app --port 3000

# 3. (optional) Run a Nuclei template scan too — a separate scan entry
cyber pentest my-app --port 3000 --engine nuclei

# 4. Open the findings dashboard
cyber dashboard
→ Cyber dashboard running at http://localhost:8787

Each run is stored as its own scan. The dashboard lists every scan and shows findings grouped by severity with evidence, affected URLs, and a Claude-ready remediation brief.

cyber pentest

The primary command. Resolves the target container, starts the selected scan engines, groups findings, and publishes a report to the local dashboard.

usage
cyber pentest <container-ref> [options]

The positional <container-ref> (or -c, --container) selects the target container. These are the core scan flags; authentication flags are covered under Authenticated scans.

Flag Description
-c, --container <ref> Target container Container name, short ID, or full ID — alternative to the positional argument.
-p, --port <n> Target port HTTP port the app listens on inside the container's Docker network. default: 3000
-s, --scheme <s> URL scheme http or https. default: http
--path <path> Target path Starting URL path for the scan. default: /
-e, --engine <e> Scan engine zap or nuclei. One engine per run — run twice to use both; each is stored as a separate scan. default: zap
-m, --mode <m> Scan depth For ZAP: baseline (passive + spider) or full (active). For Nuclei it maps to severities (baseline = medium,high,critical; full = all). default: baseline
--severity <list> Nuclei severities Nuclei only. Comma-separated from info,low,medium,high,critical; overrides the --mode mapping.
--network <name> Docker network Use a specific Docker network instead of auto-detecting the container's.

Example — targeted run

shell
# Full ZAP active scan on a specific network
cyber pentest api-server \
  --port 8080 \
  --engine zap \
  --mode full \
  --network app-net

# Nuclei, critical + high templates only
cyber pentest api-server \
  --port 8080 \
  --engine nuclei \
  --severity critical,high
Active scan warning: ZAP --mode full performs active probing that may modify application state. Use only against containers running locally for testing purposes, and only on systems you own or have explicit authorization to test.

cyber scans

List every scan stored locally, newest first — one line per scan with its ID, status, mode, engine, target, and severity counts. Use the ID with cyber export or cyber push.

shell
cyber scans

m6rQCt7sBIcs completed baseline juice-shop  http://juice-shop:3000/  high=0 medium=10
W9GGoiwKJ-V5 completed baseline parento-portal-nginx-1  http://parento-portal-nginx-1:80/  high=0 medium=0

cyber export

Export a scan's findings as a Claude-ready remediation brief in Markdown — distinct issues grouped from raw findings, with affected endpoints, evidence, and a fix task. Hand the file to a Claude Code session in the target app's repo to drive the fixes.

usage
cyber export [scanId] [options]
Flag Description
[scanId] Scan to export Positional. Omit to export the most recent completed scan.
-m, --min-risk <risk> Minimum severity One of High, Medium, Low, Informational, Unknown. default: High
-o, --out <file> Output file File path, or - for stdout. default: <report-dir>/remediation.md
--format <fmt> Output format Currently markdown. default: markdown
example
# Latest completed scan, High findings, to the default file
cyber export

# A specific scan, include Medium+, write to a file
cyber export m6rQCt7sBIcs --min-risk Medium -o remediation.md

# Pipe straight to stdout
cyber export m6rQCt7sBIcs -o -

cyber dashboard

Starts the local dashboard API + UI and opens it in your browser. It reads the local scan database, so every scan you've run (ZAP and Nuclei) appears here.

shell
cyber dashboard

Cyber dashboard running at http://localhost:8787
Flag Description
-p, --port <port> Dashboard/API port default: 8787
--no-open Don't open the browser Start the server without launching a browser tab.

Each scan's detail view shows findings by severity, request/response evidence, affected URLs, and a copy-ready "Remediation brief (for Claude)".

Blank dashboard? The UI must be built. Run npm run build in the repo, then restart cyber dashboard. If the UI isn't built, the command prints a warning and won't auto-open a blank page.

cyber push & login

Optional. Upload a scan's findings to a hosted CyberDuty portal for team-wide triage and AI-assisted fixes. Scanning is fully local; this is the only step that sends data off your machine.

cyber login / logout

cyber login opens a browser OAuth flow and stores a CLI token in ~/.cyber-scanner/credentials (chmod 600). cyber logout clears it.

Flag Description
--url <url> Portal base URL Overrides $CYBER_CLOUD_URL or the saved URL. (login & push)
--org <slug> Destination org push only. Required if you belong to more than one organization.
--all Push every scan push only. Upload all completed scans instead of a single one.
shell
# Sign in (stores a CLI token)
cyber login --url https://portal.example.com

# Push the latest completed scan (or a specific scanId)
cyber push
cyber push m6rQCt7sBIcs --org acme

# CI / headless: use env vars instead of `cyber login`
CYBER_CLOUD_URL=https://portal.example.com \
CYBER_CLOUD_TOKEN=cyk_… \
  cyber push --all

OWASP ZAP

CyberDuty runs OWASP ZAP as a Docker container internally. You don't need to install ZAP separately. Two scan modes are available:

Baseline (passive)

Spiders the application and runs passive checks only. No requests are modified. Safe to run against any environment. Covers: missing security headers, insecure cookies, information disclosure, and common misconfigurations.

shell
cyber pentest my-app --port 3000 --engine zap --mode baseline

Full (active)

Actively probes the application for vulnerabilities including SQL injection, XSS, path traversal, and authentication weaknesses. Sends crafted requests that may alter data. Use only against a dedicated test instance.

shell
cyber pentest my-app --port 3000 --engine zap --mode full

Nuclei

Nuclei runs template-based checks against the target. Templates cover a wide range of vulnerability classes:

CVEs exposures misconfigurations default credentials tech detection network

Severity levels

critical high medium low info

Select severities with --severity (overrides the --mode mapping, where baseline = medium,high,critical and full = all).

shell
# Nuclei, default severities (medium,high,critical from --mode baseline)
cyber pentest my-app --port 3000 --engine nuclei

# Nuclei, critical + high only
cyber pentest my-app --port 3000 \
  --engine nuclei \
  --severity critical,high
📦
Templates: Nuclei downloads its template set on the first scan and caches it at ~/.cyber-scanner/nuclei-templates, so subsequent runs are fast. The first run needs internet access. Nuclei critical findings are recorded as High (the model has no separate Critical tier).

Authenticated scans

Both engines can scan behind a login using the same flags. ZAP drives its built-in form-login engine (form auth + cookie session + anti-CSRF, forced-user mode); Nuclei performs a headless form login in a helper container and replays the session cookie on every request. Framework presets fill in the login path and field names — override any of them individually.

Flag Description
--framework <preset> Auth preset laravel · django · rails · express — pre-fills login path, username/password field names, and CSRF token.
--auth-user <username> Username / email for the login form.
--auth-pass <password> Password for the login form.
--login-url <url> Login POST URL. Defaults to the target + the preset's login path.
--user-field / --pass-field <name> Form field names for username and password (override the preset).
--csrf-field <name> Anti-CSRF token field name.
--logged-in-regex / --logged-out-regex <regex> Session indicators — response regexes marking an authenticated / logged-out session.
--auth-config <file> JSON auth file — supply any of the above as JSON instead of flags (see Configuration).
--rewrite-host <url> ZAP only. Rewrite a base URL to the scan target in responses (e.g. an app APP_URL unreachable from the scan network). Repeatable; ignored by Nuclei.

Each preset fills in that framework's login path, username/password field names, and CSRF token — so you usually only need --framework, --auth-user, and --auth-pass. The same flags work with --engine nuclei.

Laravel

laravel
# Preset fills: email / password fields · _token CSRF · /login
cyber pentest app-nginx --port 80 --network app-net --mode full \
  --framework laravel --auth-user admin@example.com --auth-pass secret \
  --rewrite-host http://localhost:8001

Django

django
# Preset fills: username / password fields · csrfmiddlewaretoken · /accounts/login/
cyber pentest web --port 8000 --network app-net --mode full \
  --framework django --auth-user admin --auth-pass secret

Rails

rails
# Preset fills: user[email] / user[password] fields · authenticity_token · /users/sign_in (Devise)
cyber pentest rails-app --port 3000 --network app-net --mode full \
  --framework rails --auth-user admin@example.com --auth-pass secret

Express

express
# Preset fills: username / password fields · _csrf · /login   (shown with the Nuclei engine)
cyber pentest node-app --port 3000 \
  --engine nuclei --severity high,critical \
  --framework express --auth-user admin --auth-pass secret
🔒
Credentials are used only for the scan and are never written to the saved report. The authenticated Nuclei login runs in a throwaway node:20-alpine container on the target's Docker network (the first authenticated scan pulls that image).

Container references

The <container-ref> argument accepts three forms. CyberDuty resolves whichever you provide.

Form Example Notes
Container name my-app The NAME column from docker ps. Easiest to use in scripts.
Short ID 8c91a4d72fb1 First 12 characters of the full container ID.
Full ID 8c91a4d72fb1a… Full 64-character container ID from docker inspect.
shell
# Find your container name
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"

NAMES             IMAGE             STATUS
my-app            node:20-alpine    Up 4 minutes
postgres-db       postgres:15       Up 4 minutes

# Pentest by name
cyber pentest my-app --port 3000

Output & storage

Everything is stored under ~/.cyber-scanner on your machine.

Output Description
Console summary Real-time engine output and a severity summary on completion, with the scan ID.
Local dashboard Interactive report at http://localhost:8787 via cyber dashboard. Lists all scans and shows findings + the remediation brief.
Remediation brief Claude-ready Markdown via cyber export — grouped issues with a fix task per issue.
~/.cyber-scanner/cyber.db SQLite database holding scan metadata and findings for every run.
~/.cyber-scanner/reports/<scanId>/ Per-scan raw reports: zap-report.html + zap-report.json (ZAP) or nuclei-report.jsonl (Nuclei), plus remediation.md.

Configuration

CyberDuty is configured with CLI flags plus a few environment variables and an optional auth file — there is no project config file. Flags always take precedence.

Environment variables

Variable Description
CYBER_CLOUD_URL Default hosted-portal base URL for cyber login / cyber push (overridden by --url).
CYBER_CLOUD_TOKEN CLI token for headless cyber push (CI), instead of running cyber login.

Saved login state lives in ~/.cyber-scanner/credentials (portal URL + token, chmod 600).

Auth config file (--auth-config)

Supply authentication settings as JSON instead of flags. Any field can also be set with its matching --auth-* flag.

auth.json
{
  "username": "admin@example.com",
  "password": "secret",
  "loginUrl": "http://app-nginx:80/login",
  "userField": "email",
  "passField": "password",
  "csrfTokens": ["_token"],
  "loggedInRegex": "Log ?out"
}
shell
cyber pentest app-nginx --port 80 --auth-config ./auth.json

CI integration

Run CyberDuty as a step in your CI pipeline to surface security issues before they merge or deploy. cyber pentest exits non-zero if the scan fails to complete; it does not currently gate on finding severity, so export the report as an artifact for review.

GitHub Actions

.github/workflows/security.yml
name: Security scan

on: [push, pull_request]

jobs:
  pentest:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Start application
        run: docker compose up -d

      - name: Install CyberDuty
        run: |
          git clone https://github.com/pv-cloud/cyberduty-scanner.git
          cd cyberduty-scanner
          npm ci && npm run build
          npm link -w @cyber/cli

      - name: Run pentest
        run: cyber pentest my-app --port 3000

      - name: Export remediation brief
        if: always()
        run: cyber export --min-risk Medium -o ./remediation.md

      - name: Upload scan artifact
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: cyberduty-report
          path: remediation.md

GitLab CI

.gitlab-ci.yml
security-scan:
  stage: test
  services:
    - docker:dind
  script:
    - docker compose up -d
    - git clone https://github.com/pv-cloud/cyberduty-scanner.git && cd cyberduty-scanner
    - npm ci && npm run build && npm link -w @cyber/cli
    - cyber pentest my-app --port 3000
    - cyber export --min-risk Medium -o remediation.md
  artifacts:
    paths:
      - remediation.md
    when: always

Troubleshooting

Container not found

error
Error: container "my-app" not found

Verify the container is running: docker ps. The name must match exactly. Check that Docker is running and you have permission to access the Docker socket.

Dashboard is blank

The dashboard UI must be built. Run npm run build in the repo, then restart cyber dashboard. If the UI isn't built, the command prints a warning and won't open a browser tab.

Dashboard port in use

Start it on another port: cyber dashboard -p 8899, or stop the process occupying port 8787.

Nuclei: "no templates provided"

Nuclei downloads its templates on the first scan into ~/.cyber-scanner/nuclei-templates. Ensure the machine has internet access for that first run; later runs reuse the cache.

ZAP scan fails to start

ZAP runs in its own Docker container. Ensure Docker has enough memory allocated (at least 2 GB). On macOS, check Docker Desktop resource settings.

💬
Need help? Open an issue on the CyberDuty repository with the failing command, the scanner output, and your Docker version.