Cross-reference WordPress installs against vulnerability feeds. Built for managing large numbers of sites — dump inventory via WP-CLI, fetch CVEs from NVD, get a per-site vulnerability report.
# 1. Dump a site inventory (runs on the target, nothing to install)
ssh user@mysite.com 'bash -s' < wp-dump.sh > dumps/mysite.json
# 2. Cross-match against CVEs
./wp-vulns.sh --sites dumps/That's it. On first run, the full 5-year CVE history is fetched from NVD and cached locally. Subsequent runs refresh only the last 30 days and merge into the existing DB, so history is never lost.
| WordPress Version | Status | Plugins dumped | Notes |
|---|---|---|---|
| 5.9 | ✅ | 16 | Some plugins require WP 6.x+ and won't install |
| 6.0 | ✅ | 17 | Some plugins require WP 6.4+ and won't install |
| 6.4 | ✅ | 20 | |
| 6.6 | ✅ | 24 | Plugins requiring WP 6.7+ won't install |
| latest (6.9) | ✅ | 31 | Full plugin set installs cleanly |
| Tool | Minimum Version | Notes |
|---|---|---|
| bash | 4.0 | |
| curl | 7.x | |
| jq | 1.6 | |
| wp-cli | 2.0 | Target sites only |
| fzf | 0.30 | Optional, for interactive browsing |
- Collect — run
wp-dump.shon each site (SSH-pipeable, no installation required) - Fetch — pull recent WordPress/plugin/theme CVEs from NVD
- Match — cross-reference installed versions against CVE ranges, get a report
# Single site
ssh user@site1.com 'bash -s' < wp-dump.sh > dumps/site1.json
# Batch across many hosts (10 in parallel)
while read host; do
ssh -o StrictHostKeyChecking=no "$host" 'bash -s' < wp-dump.sh > "dumps/${host}.json" &
(( $(jobs -r | wc -l) >= 10 )) && wait -n
done < hosts.txt
wait
# Local WP install
./wp-dump.sh --path /var/www/html --site mysite.com > dumps/mysite.jsonDump format:
{
"site": "https://example.com",
"label": "example.com",
"wp_version": "6.4.2",
"dumped_at": "2026-04-29T10:00:00Z",
"plugins": [
{"name": "woocommerce", "version": "8.1.0", "status": "active"}
],
"themes": [
{"name": "storefront", "version": "4.4.2", "status": "active"}
]
}# Full report across all dumps (fetches CVEs if DB is missing or stale)
./wp-vulns.sh --sites dumps/
# Filter to a specific plugin across all sites
./wp-vulns.sh --sites dumps/ --grep woocommerce
# Interactive fzf browser — filter by site/plugin/CVE/severity as you type
./wp-vulns.sh --sites dumps/ --fzf
# Combine: fzf filtered to elementor hits only
./wp-vulns.sh --sites dumps/ --fzf --grep elementor
# Force re-fetch CVEs (default: reuses cached DB if < 30 days old)
./wp-vulns.sh --fetch --sites dumps/
# Change CVE lookback window
./wp-vulns.sh --days 90 --sites dumps/Sample output:
═══════════════════════════════════════════════════════
mysite.com WP 6.4.2 (20 plugins · 3 themes)
═══════════════════════════════════════════════════════
[CRITICAL/9.8] CVE-2024-1234 (woocommerce 8.1.0)
WooCommerce is vulnerable to SQL Injection via the 'order_by' parameter...
[HIGH/8.8] CVE-2024-5678 (elementor 3.18.0)
Elementor Page Builder is vulnerable to Stored XSS via the url parameter...
[MEDIUM/5.3] CVE-2024-9012 (wordpress 6.4.2)
WordPress core is vulnerable to path traversal in the Filesystem API...
Site total: 3 CVE(s) — CRITICAL:1 HIGH:1 MEDIUM:1
═══════════════════════════════════════════════════════
SUMMARY
═══════════════════════════════════════════════════════
mysite.com CRIT:1 HIGH:1 MED:1 TOTAL:3
staging.mysite CRIT:0 HIGH:2 MED:4 TOTAL:6
[*] Report saved to wp-report-20260430-120802.txt
# Simple slug list instead of dumps
./wp-vulns.sh --match plugins.txt # one slug per line, or slug:version
# WPScan enrichment
WPSCAN_TOKEN=yourtoken ./wp-vulns.sh --sites dumps/
# Custom DB and report paths
./wp-vulns.sh --db /tmp/vulns.json --output /tmp/report.txt --sites dumps/Vulnerabilities are matched in two layers:
-
CPE (structured) — when NVD has CPE data for a CVE, the plugin slug is matched against the CPE
productfield and the installed version is checked against the explicit version range (versionStartIncluding,versionEndExcluding, etc.) -
Description fallback — for CVEs without CPE data (common for recently published entries), a case-insensitive substring match is done against the description. No version filtering is applied; these are flagged conservatively.
WordPress core is matched only against CVEs where both CPE vendor and product are wordpress, avoiding false positives from plugin CVEs.
| Source | Auth | Notes |
|---|---|---|
| NVD | None | Primary source, free, bulk query |
| WPScan | Token (free tier) | WordPress-specific, more precise version ranges |
NVD has enrichment lag — newly published CVEs often lack CPE data for weeks. WPScan fills that gap for WordPress specifically.
A Podman Compose setup under test/ spins up five WordPress versions with a shared MariaDB, installs WP-CLI and a representative plugin set, then exports dumps ready for cross-matching.
cd test/
# Start all containers
podman compose up -d
# Install WP core + ~30 plugins in each container (actual count varies by WP version)
./setup.sh
# Export inventory from every running container → test/dumps/*.json
./dump-all.sh
# Run the cross-match against the test dumps
../wp-vulns.sh --sites dumps/Versions included: 5.9 · 6.0 · 6.4 · 6.6 · latest
Ports: 8059 8060 8064 8066 8080 — each instance is accessible via browser too.
Override the default plugin set:
PLUGINS="woocommerce akismet wpforms-lite" ./setup.shDump a specific container only:
./dump-all.sh wp64 wplatest| Run | Behaviour |
|---|---|
| First run (no DB) | Fetches full 5-year history from NVD, paginating through results |
| DB exists, < 30 days old | Uses cached DB as-is |
| DB exists, ≥ 30 days old | Fetches last 30 days, merges into existing DB (no history lost) |
--fetch |
Forces a refresh of the last --days window and merges |
--fetch --days 1825 |
Full re-fetch of 5-year history |
NVD paginates at 2000 results per request. The script handles this automatically with a short sleep between pages to stay within the rate limit.
wp-vulndb.json— cached CVE database (auto-refreshed when stale)wp-report-TIMESTAMP.txt— full report, saved alongside console output
- NVD enrichment lag — newly published CVEs often lack CPE data for days or weeks after disclosure. During that window, matches fall back to description substring search with no version filtering, so hits are conservative (may include patched versions).
- Description fallback is noisy — a CVE mentioning "woocommerce" in its description will match any site running WooCommerce, regardless of version. These are clearly marked in the report.
- No CVSS v4 yet — NVD is still rolling out CVSS v4 scores; the script uses CVSS v3.1 where available, falling back to v2.
- Slug matching is approximate — CPE
productfields are not always identical to WordPress.org slugs. Uncommon plugins may be missed or produce false positives. - Inactive plugins are included — the dump captures all installed plugins regardless of active status. A deactivated plugin is still a risk if it's on disk.