The Web console is a browser-based management interface for browsing scan projects, viewing live tasks, editing configuration, and managing plugins. It is implemented entirely with the Python standard library; the frontend is a single-file SPA that requires no build step or external CDN.

Starting

python main.py --web                # http://0.0.0.0:8050
python main.py --web --port 9000    # Custom port
  • Listens on 0.0.0.0:8050 by default
  • Long-running service; exit with Ctrl+C
  • After startup, access it via a browser (default http://localhost:8050)

Authentication

The console is unauthenticated by default. Set the ZSANS_WEB_PASSWORD environment variable to require a password — once set, every endpoint is blocked until authorized (page requests get a standalone login page, API requests get 401):

ZSANS_WEB_PASSWORD='your-secret' python main.py --web

Two authorization ways:

Client Method
Browser Log in on the login page → receives an HttpOnly + SameSite=Lax session cookie (valid for 24h; the server only stores its SHA-256 digest)
Third-party programs Pass the password directly as a token header — no login round-trip needed

Third-party API access example:

curl -H "Authorization: Bearer your-secret" http://127.0.0.1:8050/api/tasks
curl -H "X-API-Key: your-secret" http://127.0.0.1:8050/api/projects

Login attempts are rate-limited (more than 10 failures within 60 seconds returns 429). POST /api/auth/logout revokes the current session.

Binding to non-loopback addresses

The unauthenticated default assumes a trusted localhost/LAN. When exposing the console beyond localhost via --host, always set ZSANS_WEB_PASSWORD.

Feature Overview

Page Function
Projects Browse historical scan projects under output/, view asset graphs and charts
Compare Compare two scans, find assets present in only one of them
New Task Start a scan with script/url/ip seeds, customizable depth, strategy, and concurrency
Live Tasks Real-time streaming logs (SSE), stop tasks, rescan
Config View and edit breeding-config.yaml online (with YAML validation)
Plugins Enable/disable plugins, plugin built-in Web UI, JSON-Schema form configuration

Creating a Scan Task

The form supports three types of seeds (one per line):

  • Domain domain: example.com
  • URL url: https://example.com
  • IP ip: 93.184.216.34

Advanced options:

Option Range
max_depth 1–10
strategy priority_based / depth_first / breadth_first / time_based
concurrency 1–50

Server-side seed validation

Seeds submitted through the Web console are strictly validated before a scan starts: domains must be RFC-valid hostnames, URLs must have an http(s) scheme and a valid host, and IPs must be literal IPv4/IPv6. Invalid seeds are rejected with 400 and per-seed reasons — this keeps untrusted input away from external tool command lines (tools are always invoked with argument lists, never through a shell).

After submission, the task runs in a background daemon thread (zsans-web-<id>), and you can immediately view the live logs.

Live Tasks and SSE Logs

  • The task list is refreshed by polling /api/tasks every 3 seconds
  • Live logs prefer SSE (EventSource('/api/tasks/<id>/logs/stream'))
  • The first push sends the full log history, then increments every 0.5 seconds
  • When the task ends, event: done is sent, then the stream closes
  • Falls back to 1-second polling when EventSource is unsupported

Actions:

  • Stop task: calls engine.stop(), triggering checkpoint saving and report generation; the status shows stopped
  • Rescan: reuses the original task's seeds and configuration, generating a new task ID

Browsing Projects and Comparison

  • The project list recognizes directories matching the YYYYMMDD_HHMMSS format under output/
  • Project details read the JSON asset graph; while a scan is running, a live snapshot is provided (read directly from the running engine's asset graph)
  • Project comparison: select up to 10 projects, use the first as the base, compute set intersection/difference on node uids, output only_in_base, only_in_others, common, etc.

Editing Configuration

  • GET /api/config returns the configuration file's raw text; after editing online, POST /api/config overwrites the whole file
  • yaml.safe_load validation runs before saving; syntax errors return YAML syntax error: ...
  • POST /api/config/patch supports partial updates by dot path (a.b.c), preserving comments
  • Plugin enable/disable uses plain-text regex rewriting of the plugins.disabled list, without erasing user comments

Plugin Management

  • GET /api/plugins lists all plugins' names, versions, events, states, and Web UIs
  • POST /api/plugins/<name>/toggle enables/disables (no restart required)
  • A plugin can provide a webui (HTML entry); the console embeds it in a sandboxed iframe, loading sub-resources via the plugin-name path
  • A plugin can declare a schema (JSON-Schema); the console automatically renders a form, saving the configuration to output/plugin_config/<name>.yaml

API Overview

GET

Path Function
/ /index.html Frontend single page
/static/<name> Local frontend assets (Vue runtime)
/api/health Health probe + version + language
/api/i18n Frontend language pack
/api/projects Project list
/api/projects/<pid> Project asset graph JSON (or live snapshot)
/api/compare?ids=a,b Project comparison
/api/tasks Task list
/api/tasks/<tid>/logs/stream SSE live logs
/api/tasks/<tid>/logs?tail=N Task log tail
/api/tasks/<tid> Task details
/api/config Raw text of the configuration file
/api/config/json Parsed configuration dict
/api/config/schema Default configuration (form schema)
/api/plugins Plugin list
/api/plugins/<name>/webui[/sub] Plugin Web UI assets
/api/plugins/<name>/config Plugin configuration

POST

Path Function
/api/auth/login Log in ({password}) → sets session cookie; rate-limited
/api/auth/logout Revoke the current session
/api/scan/start Create a task ({seeds, config}) → {id: "task-N"}; seeds are server-validated
/api/projects/delete Delete projects ({ids})
/api/tasks/stop Stop a task ({id})
/api/tasks/rescan Rescan ({id}) → new task ID
/api/config Save the full configuration (raw text)
/api/config/patch Partial update by dot path
/api/plugins/<name>/toggle Enable/disable a plugin
/api/plugins/<name>/config Save plugin configuration

Security Notes

  • Authentication: when ZSANS_WEB_PASSWORD is set, all endpoints require authorization (see above); password comparison uses constant-time comparison and login is rate-limited
  • Request bodies are capped at 2 MB to prevent memory-exhaustion DoS
  • The SSE log stream no longer sends Access-Control-Allow-Origin: *; scan logs are only readable by the same-origin frontend
  • Project IDs must match the \d{8}_\d{6} timestamp format to prevent path traversal
  • Static assets only take the basename; .. is rejected
  • Plugin Web UI assets are validated against directory escape
  • Deleting a project does not delete the run_dir of a task currently running

Screenshots

web1

web2