wpsecscan v2.9.1
☰
/ docs

Documentation

Quick reference. For deeper docs, see the /docs folder in the repo.

Install

Three ways: pip, the Windows .exe from the GitHub release (wpsecscan.exe and wpsecscan-gui.exe, about 22 MB each), or source. See the download page.

# pip (Python 3.10+)
pip install wpsecscan

# from source (all optional extras)
pip install -e ".[all]"

# Docker: build locally, there is no published image
docker build -t wpsecscan . && docker run --rm wpsecscan https://example.com

First scan

The target is a positional argument; there is no scan verb.

# Walk every check with a synthetic site
wpsecscan --demo

# Pull the latest CVE feed
wpsecscan db update

# Scan your site (passive checks only by default)
wpsecscan https://your-site.com

# Save the report
wpsecscan https://your-site.com --out ./reports --format html

# Several report formats at once
wpsecscan https://your-site.com --out ./reports --format json,html,sarif

The CVE feed is aggregated nightly from up to 8 public sources (NVD, MITRE, GHSA, OSV, Wordfence, WPVulnerability, Patchstack, CIRCL); per-source counts are published in the feed header.

Aggressive mode

31 checks send active payloads (SQLi probes, XSS reflections, SSRF, etc.). They're off by default. Opt in with --aggressive and confirm you are allowed to test the target with --owned (see consent below).

wpsecscan https://your-site.com --aggressive --owned

# Also write read-only proof artifacts for confirmed findings
wpsecscan https://your-site.com --aggressive --owned --prove

--prove runs one of 5 proof helpers for each confirmed aggressive finding. Each collects minimal read-only evidence (see docs/proof.md) and writes it under the scan's output directory as proof-CHECK_ID.txt. It works on a single target and requires --aggressive.

Aggressive checks run only against targets you have marked as owned: pass --owned, set WPSECSCAN_OWNED_TARGETS, or record the host with wpsecscan owned add. Otherwise the scan runs passive and records why.

wpsecscan owned add your-site.com
wpsecscan owned list
wpsecscan owned remove your-site.com

Report formats

9 values for --format (repeat the flag or comma-separate them):

  • json · machine-readable
  • html · interactive report (--single-page-html for a print-friendly layout)
  • csv · spreadsheet-friendly
  • md · Markdown report
  • xlsx · Excel workbook
  • sarif · for code-scanning UIs
  • burp · Burp Suite scope XML
  • docx · Word-compatible (.rtf fallback)
  • exec-pdf · one-page executive summary PDF

14 more formats are produced from a saved JSON report with wpsecscan emit <format> report.json:

attestation-letter · cab · ce-plus · cef · e8 · fedramp · gdpr-dpia · hipaa-map · intoto · leef · risk-csv · risk-json · spdx-sbom · stakeholder-bundle

exec-pdf uses the optional [pdf] extra (reportlab) when installed and falls back to an HTML print-to-PDF page otherwise.

Subcommand families

Three umbrella subcommands work from saved scan reports: emit (14 export formats), push (16 destinations) and ai (7 LLM helpers). In total the CLI has 66 subcommands; run wpsecscan --help for the list.

# export formats (SBOM, in-toto, CEF, LEEF, HIPAA, FedRAMP, CE+, E8, ...)
wpsecscan emit spdx-sbom report.json --out sbom.json
wpsecscan emit cef report.json --out splunk-events.cef
wpsecscan emit hipaa-map report.json
wpsecscan emit --help        # list all formats

# push destinations (provider settings come from environment variables)
wpsecscan push gitlab-ci report.json
wpsecscan push sentry report.json
wpsecscan push defectdojo report.json
wpsecscan push chat report.json   # Mattermost / RocketChat / Telegram
wpsecscan push --help             # env vars per provider

# LLM helpers (OpenAI, Anthropic or Ollama)
wpsecscan ai injection-check response.txt
wpsecscan ai control-map report.json --framework hipaa
wpsecscan ai waf-rule finding.json --target-waf cloudflare
wpsecscan ai drift https://example.com 42

push providers: gitlab-ci · circleci · azure-devops · buildkite · shortcut · plane · wiz · chat · hosting · automation · osv-enrich · exploitdb-xref · nuclei · sentry · datadog · defectdojo

Utilities

wpsecscan init                          # interactive first-run wizard
wpsecscan doctor                        # audit optional deps + tokens
wpsecscan check disable wpgraphql       # toggle a check off
wpsecscan check enable wpgraphql        # turn it back on
wpsecscan check list --json             # machine-readable inventory
wpsecscan benchmark https://example.com # per-check timing table
wpsecscan export-config                 # show effective merged config
wpsecscan compare-pypi-version          # is your install behind PyPI?

Daemon mode

Runs scans on cron-style schedules from a YAML config (needs the [yaml] extra). A target with aggressive: true only runs aggressive checks when it also has owned: true (or consent resolves through WPSECSCAN_OWNED_TARGETS or wpsecscan owned add); otherwise it is downgraded to passive.

wpsecscan --daemon daemon.yml
# daemon.yml
targets:
  - url: https://example.com
    schedule: "0 3 * * *"        # cron: daily at 3am
    aggressive: false
    fail_on: high
  - url: https://shop.example.com
    schedule: "0 */6 * * *"      # every 6 hours
    aggressive: true
    owned: true                  # you own / are authorised to test this host
    fail_on: critical
out_dir: /var/log/wpsecscan
webhook_url: https://hooks.slack.com/...

API

A small HTTP API server can trigger scans and fetch past reports. It refuses to start without a bearer token.

export WPSECSCAN_API_TOKEN=change-me
wpsecscan --api-server 127.0.0.1:8765

POST /scan accepts "owned": true in the JSON body as ownership consent for "aggressive": true. The response carries consent: granted, downgraded (aggressive was requested but no consent resolved, so the scan runs passive) or n/a (not aggressive). When WPSECSCAN_API_ALLOWED_TARGETS is set (comma-separated hosts) and excludes the target host, the server returns 403 before a scan starts. Details: docs/api-reference.md.

Scheduled scans

Add sites to your list, install the scheduled job (Task Scheduler on Windows), and optionally configure a digest of new findings.

wpsecscan sites add https://example.com --weekly
wpsecscan sites list
wpsecscan schedule install --time 03:00
wpsecscan schedule status
wpsecscan schedule pause
wpsecscan schedule resume
wpsecscan schedule uninstall

wpsecscan digest configure --to [email protected] --smtp smtp.example.com:587
wpsecscan digest test
wpsecscan digest send

Companion plugin

For server-side checks (MFA audit, DB triggers, wp-cron jobs, webhook inventory), install the companion plugin. It is source-only: the plugin folder is wp-plugin/ in the repo. Generate a shared secret in the plugin settings, then store it with the site entry or pass it for a single scan:

wpsecscan sites add https://example.com --companion-token sk_xxx
wpsecscan https://example.com --companion-token sk_xxx

AI options (opt-in)

AI-assisted triage toggles behind an opt-in panel. All off by default. They need an LLM key (OpenAI / Anthropic) or a local Ollama URL. The separate wpsecscan ai subcommand has 7 helpers (see subcommand families).

export OPENAI_API_KEY=sk-...
wpsecscan ai-options list
wpsecscan ai-options set severity_auto_tuner true
wpsecscan ai-options set exec_brief_audience cto

# Or via GUI: Tools → Advanced AI options...

Usage analytics (opt-in, local-first)

Off by default. Local-only by default (events never leave your machine unless you also set an upload URL). Per-event field allowlist prevents PII leaks.

wpsecscan analytics enable
wpsecscan analytics show 50      # inspect last 50 events
wpsecscan analytics export out.jsonl
wpsecscan analytics forget       # delete everything locally

Full doc: docs/analytics.md.

Multi-site use

Self-hostable, one codebase. For more than one site, the scanner provides:

  • a sites registry (wpsecscan sites add / list);
  • batch scanning from a file of URLs;
  • the --daemon scheduler;
  • a token-protected --api-server, with an optional target allowlist (WPSECSCAN_API_ALLOWED_TARGETS);
  • an HMAC-chained audit log, which records credential-vault and marketplace actions only.

There is no separate enterprise mode: no single sign-on, role-based access control or two-person approval is wired into the scanner. Details: docs/api-reference.md.

Integrations

• Burp Suite
• OWASP ZAP
• Nuclei templates
• JIRA · Linear
• Splunk · Datadog
• Grafana dashboard
• Prometheus exporter
• ServiceNow
• Slack · Teams · Discord webhooks
• OpenAPI 3.1 + Postman
• Webhook v2 (HMAC+nonce)
• Ansible role · HashiCorp Sentinel

The Python, JavaScript and Go SDKs are source-only, in sdks/; no packages are published. The Ansible role is in ansible/. The Kubernetes operator is a design scaffold (k8s/), not released.

Per-check reference

The per-check reference pages live in the repository for now, in docs/checks/ on GitHub.

Windows Defender false-positive

Defender may flag wpsecscan.exe on first run. It's a known false-positive because the binary contains string-pattern signatures for webshells (so we can detect them in your site). The .exe is Sigstore-signed; verify the signature to confirm authenticity.

Workaround: add an exclusion via Tools → Add Windows Defender exclusion... in the GUI, or run the PowerShell helper:

Add-MpPreference -ExclusionPath "C:\Tools\wpsecscan.exe"

Writing a check

Drop a .py module in ~/.wpsecscan/checks/ or contribute one upstream. The scaffolder generates a skeleton:

python scripts/new-check.py --id my_check
python scripts/lint-checks.py           # enforce hygiene

Full tutorial: docs/writing-a-check.md.