phpwarden 2.1.0__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {phpwarden-2.1.0 → phpwarden-2.2.0}/PKG-INFO +114 -34
- {phpwarden-2.1.0 → phpwarden-2.2.0}/README.md +112 -32
- {phpwarden-2.1.0 → phpwarden-2.2.0}/pyproject.toml +2 -2
- phpwarden-2.2.0/src/phpwarden/__init__.py +9 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/src/phpwarden/cli.py +274 -28
- phpwarden-2.2.0/src/phpwarden/code.py +1813 -0
- phpwarden-2.2.0/src/phpwarden/code_rules.py +369 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/src/phpwarden/data/php-cve-feed.json +20335 -19726
- {phpwarden-2.1.0 → phpwarden-2.2.0}/src/phpwarden/feed.py +62 -9
- {phpwarden-2.1.0 → phpwarden-2.2.0}/src/phpwarden/guard.py +5 -4
- phpwarden-2.2.0/src/phpwarden/probe.py +902 -0
- phpwarden-2.2.0/src/phpwarden/rules.py +259 -0
- phpwarden-2.2.0/src/phpwarden/sarif.py +134 -0
- phpwarden-2.2.0/src/phpwarden/verdict.py +462 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/src/phpwarden.egg-info/PKG-INFO +114 -34
- {phpwarden-2.1.0 → phpwarden-2.2.0}/src/phpwarden.egg-info/SOURCES.txt +19 -0
- phpwarden-2.2.0/tests/test_account_deletion.py +138 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_advisories.py +123 -2
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_agency.py +71 -1
- phpwarden-2.2.0/tests/test_assess.py +152 -0
- phpwarden-2.2.0/tests/test_business_metrics.py +45 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_cli_edges.py +7 -0
- phpwarden-2.2.0/tests/test_code.py +418 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_e2e_readiness.py +2 -2
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_feed_license.py +2 -2
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_feed_subscription.py +1 -1
- phpwarden-2.2.0/tests/test_free_feed.py +108 -0
- phpwarden-2.2.0/tests/test_kev_alert.py +97 -0
- phpwarden-2.2.0/tests/test_kev_gaps.py +52 -0
- phpwarden-2.2.0/tests/test_livefeed.py +76 -0
- phpwarden-2.2.0/tests/test_probe.py +196 -0
- phpwarden-2.2.0/tests/test_probe_cli.py +99 -0
- phpwarden-2.2.0/tests/test_probe_dashboard.py +312 -0
- phpwarden-2.2.0/tests/test_sarif.py +80 -0
- phpwarden-2.2.0/tests/test_shield_plan.py +36 -0
- phpwarden-2.2.0/tests/test_waf_dryrun.py +76 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_wordpress_and_report.py +2 -1
- phpwarden-2.1.0/src/phpwarden/__init__.py +0 -8
- phpwarden-2.1.0/src/phpwarden/rules.py +0 -152
- {phpwarden-2.1.0 → phpwarden-2.2.0}/LICENSE +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/setup.cfg +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/src/phpwarden/deps.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/src/phpwarden/detect.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/src/phpwarden/engine.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/src/phpwarden/env.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/src/phpwarden/integrity.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/src/phpwarden/report.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/src/phpwarden/version.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/src/phpwarden/vulndb.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/src/phpwarden/wordpress.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/src/phpwarden.egg-info/dependency_links.txt +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/src/phpwarden.egg-info/entry_points.txt +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/src/phpwarden.egg-info/top_level.txt +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_accounts.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_assignment.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_audit.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_billing.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_connect_flow.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_dashboard_states.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_deps_and_rules.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_detection_validation.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_developer.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_discussion.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_email.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_engine.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_engine_feed.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_fleet.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_google_oauth.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_guard_live.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_integrity.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_mentions.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_reachability.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_saas.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_saas_billing.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_server.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_settings.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_shield.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_slack.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_sso.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_store_connections.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_team.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_triage.py +0 -0
- {phpwarden-2.1.0 → phpwarden-2.2.0}/tests/test_webhook_idempotency.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: phpwarden
|
|
3
|
-
Version: 2.
|
|
4
|
-
Summary: PHP vulnerability scanner for Composer, WordPress and the
|
|
3
|
+
Version: 2.2.0
|
|
4
|
+
Summary: PHP vulnerability scanner for the PHP engine, Composer, WordPress, your own code and the live site. Ranked by real-world exploitation (CISA KEV, then EPSS), not CVSS. Offline, zero dependencies.
|
|
5
5
|
Author: oroma-alfred
|
|
6
6
|
License-Expression: Apache-2.0
|
|
7
7
|
Project-URL: Homepage, https://phpwarden.io
|
|
@@ -23,8 +23,8 @@ Dynamic: license-file
|
|
|
23
23
|
# phpwarden
|
|
24
24
|
|
|
25
25
|
**PHP vulnerability scanner that ranks findings by real-world exploitation,
|
|
26
|
-
not CVSS.** Of
|
|
27
|
-
exploited (CISA KEV, as of 2026-11-
|
|
26
|
+
not CVSS.** Of about 19,500 PHP vulnerabilities in the feed, 54 are known to be
|
|
27
|
+
exploited (CISA KEV, as of 2026-11-14). phpwarden puts those first, then sorts
|
|
28
28
|
by EPSS probability, then CVSS — so a `composer.lock` with 30 findings tells
|
|
29
29
|
you which one to fix today.
|
|
30
30
|
|
|
@@ -32,6 +32,13 @@ It scans the PHP engine/FPM version, Composer dependencies and WordPress
|
|
|
32
32
|
installs against one bundled, offline advisory feed. Zero runtime dependencies
|
|
33
33
|
(Python 3.8+), Apache-2.0.
|
|
34
34
|
|
|
35
|
+
**Reproducible benchmark:** on four real PHP stacks, phpwarden found 295 of 295
|
|
36
|
+
known vulnerabilities, `composer audit` 286 and OSV.dev 262. Of 297 findings,
|
|
37
|
+
100 were rated critical or high and 4 were actually exploited or likely to be;
|
|
38
|
+
phpwarden ranked each of those first or second in its project. Method, numbers
|
|
39
|
+
and a script to rerun it:
|
|
40
|
+
[the benchmark write-up](https://phpwarden.io/blog/phpwarden-vs-composer-audit-osv-2026-11).
|
|
41
|
+
|
|
35
42
|
```bash
|
|
36
43
|
$ phpwarden deps composer.lock
|
|
37
44
|
18 vulnerable package(s) in composer.lock; 0 actively exploited (KEV). Ranked by exploitation risk, then reachability:
|
|
@@ -51,6 +58,10 @@ $ phpwarden deps composer.lock
|
|
|
51
58
|
| PHP engine / FPM CVE exposure | `scan` | image & network scanners |
|
|
52
59
|
| Composer dependency CVEs | `deps` | Snyk, `composer audit` |
|
|
53
60
|
| WordPress core, plugin and theme CVEs | `wordpress` | Patchstack, Wordfence |
|
|
61
|
+
| WordPress file integrity (wordpress.org checksums, injected files) | `integrity` | Wordfence CLI malware-scan (partly) |
|
|
62
|
+
| Your own PHP code (75 rules) | `code` | — (not a PHPStan/Psalm replacement) |
|
|
63
|
+
| Live site from outside: exposed files, debug endpoints | `probe` | — |
|
|
64
|
+
| One ranked verdict across all of the above | `assess` | — |
|
|
54
65
|
| Exploitation signatures in logs | `detect` | SIEM content |
|
|
55
66
|
| WAF blocking rules (PHP-specific) | `rules` | the gap a generic WAF leaves |
|
|
56
67
|
| Fleet exposure dashboard | `report` | the CNAPP's PHP view |
|
|
@@ -71,15 +82,18 @@ $ phpwarden deps composer.lock
|
|
|
71
82
|
configuration is reported as *possibly* exposed with the exact condition to
|
|
72
83
|
confirm — not flagged as a certain hit, and not silently dismissed.
|
|
73
84
|
- **Offline by default.** The feed ships inside the package. Nothing leaves the
|
|
74
|
-
machine unless you opt in to `--report-to` or `feed update`.
|
|
85
|
+
machine unless you opt in to `--report-to` or `feed update`. Two commands
|
|
86
|
+
need the network by nature: `integrity` fetches checksums from wordpress.org,
|
|
87
|
+
and `probe` talks only to the site you point it at.
|
|
75
88
|
|
|
76
89
|
## Install
|
|
77
90
|
|
|
78
91
|
```bash
|
|
79
92
|
composer require --dev phpwarden/phpwarden # PHP projects → vendor/bin/phpwarden
|
|
80
93
|
pipx install phpwarden # Python-tooling box
|
|
81
|
-
|
|
82
|
-
|
|
94
|
+
brew install oroma-alfred/tap/phpwarden # macOS / Linux with Homebrew
|
|
95
|
+
curl -LO https://phpwarden.io/phpwarden.pyz && python3 phpwarden.pyz scan # single file, no install
|
|
96
|
+
docker run --rm phpwarden/phpwarden scan --version 8.3.10 --sapi fpm --os linux # Docker Hub, amd64 + arm64
|
|
83
97
|
```
|
|
84
98
|
|
|
85
99
|
On modern macOS / Debian / Ubuntu a plain `pip install phpwarden` into the
|
|
@@ -90,8 +104,8 @@ system Python is blocked by [PEP 668](https://peps.python.org/pep-0668/); use
|
|
|
90
104
|
python3 -m venv ~/.venvs/phpwarden && ~/.venvs/phpwarden/bin/pip install phpwarden
|
|
91
105
|
```
|
|
92
106
|
|
|
93
|
-
Docker usage (mounted projects, CI, dashboard reporting) is
|
|
94
|
-
[
|
|
107
|
+
Docker usage (mounted projects, exit codes, CI, dashboard reporting) is on the
|
|
108
|
+
image's [Docker Hub page](https://hub.docker.com/r/phpwarden/phpwarden).
|
|
95
109
|
|
|
96
110
|
## Use
|
|
97
111
|
|
|
@@ -103,21 +117,80 @@ phpwarden wordpress /var/www/html # WordPress core, plugins, themes
|
|
|
103
117
|
phpwarden integrity /var/www/html # tampered or injected files (vs wordpress.org checksums)
|
|
104
118
|
phpwarden detect /var/log/nginx/access.log # exploit signatures in a log
|
|
105
119
|
phpwarden rules --format nginx --out php-block.conf # PHP-specific WAF rules (or modsecurity)
|
|
120
|
+
phpwarden rules --format nginx --dry-run # the same rules, log-only: nothing is blocked
|
|
121
|
+
phpwarden rules --replay /var/log/nginx/access.log # what the rules would have blocked in an existing log
|
|
122
|
+
phpwarden probe https://example.com # live check of a site you own: exposed files, debug endpoints, versions vs the feed
|
|
123
|
+
phpwarden code . # your own PHP: 75 high-signal rules, 74 on by default (injection, unserialize, WordPress handlers)
|
|
124
|
+
phpwarden assess . --url https://example.com --log access.log # one ranked verdict; --out / --baseline shows what is fixed and verified
|
|
106
125
|
phpwarden report web01.json web02.json --out fleet.html # aggregate a fleet view
|
|
107
126
|
phpwarden feed status # which feed is active, how many advisories
|
|
127
|
+
phpwarden feed update # pull a newer feed: free weekly snapshot, or the maintained channel with a key
|
|
108
128
|
```
|
|
109
129
|
|
|
130
|
+
`scan`, `deps`, `wordpress`, `probe` and `code` also take `--format sarif`
|
|
131
|
+
(SARIF 2.1.0, for GitHub code scanning and other CI dashboards).
|
|
132
|
+
|
|
133
|
+
`probe`, `code`, `assess`, `--format sarif`, `rules --dry-run` / `--replay` and
|
|
134
|
+
the no-key `feed update` are new in 2.2.0.
|
|
135
|
+
|
|
110
136
|
Every command takes `--json`. Exit codes are shared across subcommands and
|
|
111
|
-
built for gating: `0` clean, `1` exposed / vulnerable / signature hits
|
|
137
|
+
built for gating: `0` clean, `1` exposed / vulnerable / signature hits / tampered
|
|
138
|
+
files / high or critical `code` and `probe` findings (`probe` also on any
|
|
139
|
+
known-exploited finding) / anything to act on now or this week (`assess`),
|
|
112
140
|
`2` possibly exposed (`scan`), `3` PHP version unknown (`scan`), `4` input
|
|
113
141
|
error, `5` `--report-to` upload failed on an otherwise clean run, `64` usage
|
|
114
142
|
error. Full table: `phpwarden --help`.
|
|
115
143
|
|
|
144
|
+
|
|
145
|
+
## New in 2.2.0
|
|
146
|
+
|
|
147
|
+
- **`phpwarden probe URL`**: a live check of a running site using GET and HEAD
|
|
148
|
+
requests only. It looks for exposed files (`.env`, `.git`, backups), open debug
|
|
149
|
+
endpoints, and the WordPress, plugin and PHP versions the site reveals, and
|
|
150
|
+
matches those against the feed. Active checks run only after you prove the
|
|
151
|
+
site is yours (DNS TXT record, a file, or a meta tag; `--print-token` shows
|
|
152
|
+
how). Without that it reads the homepage and stops.
|
|
153
|
+
- **`phpwarden code PATH`**: 75 rules (74 on by default) for your own PHP code, following request
|
|
154
|
+
data to `eval`, shell commands, SQL, `unserialize`, includes and redirects.
|
|
155
|
+
It follows data through one level of same-file helper, not across files, and
|
|
156
|
+
does not replace PHPStan or Psalm. `phpwarden code --list-rules` lists every rule.
|
|
157
|
+
- **`phpwarden assess`**: one ranked list from every view of a site: installed
|
|
158
|
+
versions, your code, the live site from outside, and the access log, with the
|
|
159
|
+
reason for each item ("exploited, reachable, visible from outside, already
|
|
160
|
+
fetched"). `--out` then `--baseline` reports what is fixed and verified.
|
|
161
|
+
- **`--format sarif`** on `scan`, `deps`, `wordpress`, `probe` and `code`.
|
|
162
|
+
- **`rules --dry-run` and `rules --replay ACCESS_LOG`**: see what the WAF rules
|
|
163
|
+
would block before they block anything.
|
|
164
|
+
- **Free weekly feed updates**: `phpwarden feed update` without a key pulls a
|
|
165
|
+
weekly snapshot of the maintained feed. A vulnerability newly listed as known
|
|
166
|
+
exploited is released to the free channel straight away.
|
|
167
|
+
- **Coverage commitment**: in-scope CISA KEV entries are in the maintained feed
|
|
168
|
+
within 24 hours of listing, checked on every rebuild and published at
|
|
169
|
+
https://phpwarden.io/feeds/kev-status.json. Details: https://phpwarden.io/sla
|
|
170
|
+
- **Editors**: a VS Code extension in `integrations/vscode` (build and install
|
|
171
|
+
the `.vsix` from that folder; not on the Marketplace yet) and PhpStorm External Tool steps in
|
|
172
|
+
`integrations/phpstorm`.
|
|
173
|
+
|
|
174
|
+
## Replacing Wordfence CLI
|
|
175
|
+
|
|
176
|
+
Wordfence closed new licences for its CLI in October 2026; Free licences stopped working on 2026-10-14 and Premium
|
|
177
|
+
licences are not renewed. phpwarden runs the same way (on the server, no plugin in the site) and covers:
|
|
178
|
+
|
|
179
|
+
| Wordfence CLI | phpwarden | Notes |
|
|
180
|
+
|---|---|---|
|
|
181
|
+
| `wordfence vuln-scan /path` | `phpwarden wordpress /path` | Same job, ranked by real-world exploitation; JSON/SARIF and CI exit codes. Also scans Composer and the PHP engine. |
|
|
182
|
+
| `wordfence malware-scan /path` | `phpwarden integrity /path` | Compares core and wordpress.org plugin files with official checksums; flags injected files, PHP in uploads and common backdoor code. **Not a signature scanner**: premium plugins and themes cannot be verified and are listed as such. |
|
|
183
|
+
| `wordfence remediate` | – | Reinstall the flagged component with WP-CLI. |
|
|
184
|
+
| `wordfence db-scan` | – | Not covered. |
|
|
185
|
+
|
|
186
|
+
Full guide: https://phpwarden.io/compare/phpwarden-vs-wordfence-cli#replace
|
|
187
|
+
|
|
116
188
|
## Send results to the dashboard
|
|
117
189
|
|
|
118
190
|
Any `scan`, `deps`, `wordpress` or `detect` run can post its result to
|
|
119
|
-
[app.phpwarden.io](https://app.phpwarden.io) for history across hosts
|
|
120
|
-
|
|
191
|
+
[app.phpwarden.io](https://app.phpwarden.io) for history across hosts.
|
|
192
|
+
It marks findings that become known-exploited; email and webhook alerts are on
|
|
193
|
+
Pro and Team, Slack on Team:
|
|
121
194
|
|
|
122
195
|
```bash
|
|
123
196
|
phpwarden scan --report-to https://app.phpwarden.io/v1/reports --report-token <token>
|
|
@@ -162,7 +235,7 @@ see [phpwarden/laravel](https://packagist.org/packages/phpwarden/laravel).
|
|
|
162
235
|
```yaml
|
|
163
236
|
repos:
|
|
164
237
|
- repo: https://github.com/oroma-alfred/phpwarden
|
|
165
|
-
rev:
|
|
238
|
+
rev: v2.1.0 # pin to a tag with `pre-commit autoupdate`
|
|
166
239
|
hooks:
|
|
167
240
|
- id: phpwarden-deps
|
|
168
241
|
# args: ["--no-reachability"] # faster: skip the reachability pass
|
|
@@ -173,44 +246,51 @@ timer recipes are in [`docs/recipes.md`](docs/recipes.md).
|
|
|
173
246
|
|
|
174
247
|
## The feed
|
|
175
248
|
|
|
176
|
-
The bundled community feed (
|
|
249
|
+
The bundled community feed (2026.11.13.3 in this release) works forever, offline:
|
|
177
250
|
|
|
178
|
-
- **~21,
|
|
251
|
+
- **~21,300 advisories**: ~8,800 Composer (from [OSV.dev](https://osv.dev) and
|
|
179
252
|
[FriendsOfPHP](https://github.com/FriendsOfPHP/security-advisories)),
|
|
180
253
|
~12,300 WordPress (from [NVD](https://nvd.nist.gov): plugin, core and theme),
|
|
181
254
|
and 145 PHP engine/FPM CVEs, including hand-curated entries with per-branch
|
|
182
|
-
fixed versions, exposure conditions and detection signatures. 19,
|
|
183
|
-
EPSS score;
|
|
255
|
+
fixed versions, exposure conditions and detection signatures. About 19,700 carry an
|
|
256
|
+
EPSS score; 41 distinct vulnerabilities were in KEV when this feed was built.
|
|
184
257
|
- **License-independent by construction.** Every source is open or
|
|
185
|
-
public-domain (OSV, NVD, FriendsOfPHP, CISA KEV, FIRST EPSS). No commercial
|
|
258
|
+
public-domain (OSV, NVD, FriendsOfPHP, php-src release notes, CISA KEV, FIRST EPSS). No commercial
|
|
186
259
|
vendor's feed is used, so coverage cannot be revoked or priced away.
|
|
187
260
|
- Every build passes `feedbuilder/check_feed.py` (no open-ended range with a
|
|
188
261
|
known fix, no fixed version inside its own affected range, no duplicate
|
|
189
262
|
(package|slug, cve)). The feed is assembled by `feedbuilder/` from a curated
|
|
190
263
|
base plus bulk imports and enrichment; curated entries always win.
|
|
191
264
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
`
|
|
195
|
-
the
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
| CVE
|
|
206
|
-
|
|
265
|
+
Updating the feed is optional and has two channels:
|
|
266
|
+
|
|
267
|
+
- **Free weekly.** `phpwarden feed update` with no key downloads a weekly
|
|
268
|
+
snapshot of the maintained feed. A vulnerability newly listed as known
|
|
269
|
+
exploited is released to this channel straight away. If the server cannot be
|
|
270
|
+
reached, phpwarden keeps the feed it has and says so. Set
|
|
271
|
+
`PHPWARDEN_FREE_FEED_URL=off` to stop the no-key update from calling out.
|
|
272
|
+
- **Maintained.** A [phpwarden.io](https://phpwarden.io) subscription gives a
|
|
273
|
+
license key; `PHPWARDEN_LICENSE=... phpwarden feed update` then pulls the
|
|
274
|
+
channel that is rebuilt twice a day.
|
|
275
|
+
|
|
276
|
+
Examples of hand-curated engine CVEs:
|
|
277
|
+
|
|
278
|
+
| CVE | What | Scope | Log signature |
|
|
279
|
+
|-----|------|-------|---------------|
|
|
280
|
+
| CVE-2019-11043 | PHP-FPM env_path_info RCE | FPM + nginx, config-gated | yes |
|
|
281
|
+
| CVE-2023-3824 | PHAR stack overflow RCE | any SAPI, untrusted PHAR | no |
|
|
282
|
+
| CVE-2024-4577 | PHP-CGI argument injection | Windows + CGI | yes |
|
|
283
|
+
| CVE-2024-8926 | cgi.force_redirect bypass | Windows + CGI | no |
|
|
284
|
+
| CVE-2024-8927 | cgi.force_redirect bypass | CGI / FPM | no |
|
|
285
|
+
| CVE-2024-11236 | DB-quoter integer overflow | any SAPI | no |
|
|
286
|
+
| CVE-2026-109618 / 109619 | php2shell FPM memory RCE | any SAPI | yes |
|
|
207
287
|
|
|
208
288
|
## Continuous guard
|
|
209
289
|
|
|
210
290
|
`scan` and `detect` are point-in-time. For always-on containment of
|
|
211
291
|
in-progress php2shell-class attacks (OOM on a worker, then that worker reused),
|
|
212
292
|
run `phpwarden-guard`, which boots in dry-run and never acts until you promote
|
|
213
|
-
it after a burn-in. See
|
|
293
|
+
it after a burn-in. See [`VALIDATION.md`](VALIDATION.md) for what was validated.
|
|
214
294
|
|
|
215
295
|
## Honest limits
|
|
216
296
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# phpwarden
|
|
2
2
|
|
|
3
3
|
**PHP vulnerability scanner that ranks findings by real-world exploitation,
|
|
4
|
-
not CVSS.** Of
|
|
5
|
-
exploited (CISA KEV, as of 2026-11-
|
|
4
|
+
not CVSS.** Of about 19,500 PHP vulnerabilities in the feed, 54 are known to be
|
|
5
|
+
exploited (CISA KEV, as of 2026-11-14). phpwarden puts those first, then sorts
|
|
6
6
|
by EPSS probability, then CVSS — so a `composer.lock` with 30 findings tells
|
|
7
7
|
you which one to fix today.
|
|
8
8
|
|
|
@@ -10,6 +10,13 @@ It scans the PHP engine/FPM version, Composer dependencies and WordPress
|
|
|
10
10
|
installs against one bundled, offline advisory feed. Zero runtime dependencies
|
|
11
11
|
(Python 3.8+), Apache-2.0.
|
|
12
12
|
|
|
13
|
+
**Reproducible benchmark:** on four real PHP stacks, phpwarden found 295 of 295
|
|
14
|
+
known vulnerabilities, `composer audit` 286 and OSV.dev 262. Of 297 findings,
|
|
15
|
+
100 were rated critical or high and 4 were actually exploited or likely to be;
|
|
16
|
+
phpwarden ranked each of those first or second in its project. Method, numbers
|
|
17
|
+
and a script to rerun it:
|
|
18
|
+
[the benchmark write-up](https://phpwarden.io/blog/phpwarden-vs-composer-audit-osv-2026-11).
|
|
19
|
+
|
|
13
20
|
```bash
|
|
14
21
|
$ phpwarden deps composer.lock
|
|
15
22
|
18 vulnerable package(s) in composer.lock; 0 actively exploited (KEV). Ranked by exploitation risk, then reachability:
|
|
@@ -29,6 +36,10 @@ $ phpwarden deps composer.lock
|
|
|
29
36
|
| PHP engine / FPM CVE exposure | `scan` | image & network scanners |
|
|
30
37
|
| Composer dependency CVEs | `deps` | Snyk, `composer audit` |
|
|
31
38
|
| WordPress core, plugin and theme CVEs | `wordpress` | Patchstack, Wordfence |
|
|
39
|
+
| WordPress file integrity (wordpress.org checksums, injected files) | `integrity` | Wordfence CLI malware-scan (partly) |
|
|
40
|
+
| Your own PHP code (75 rules) | `code` | — (not a PHPStan/Psalm replacement) |
|
|
41
|
+
| Live site from outside: exposed files, debug endpoints | `probe` | — |
|
|
42
|
+
| One ranked verdict across all of the above | `assess` | — |
|
|
32
43
|
| Exploitation signatures in logs | `detect` | SIEM content |
|
|
33
44
|
| WAF blocking rules (PHP-specific) | `rules` | the gap a generic WAF leaves |
|
|
34
45
|
| Fleet exposure dashboard | `report` | the CNAPP's PHP view |
|
|
@@ -49,15 +60,18 @@ $ phpwarden deps composer.lock
|
|
|
49
60
|
configuration is reported as *possibly* exposed with the exact condition to
|
|
50
61
|
confirm — not flagged as a certain hit, and not silently dismissed.
|
|
51
62
|
- **Offline by default.** The feed ships inside the package. Nothing leaves the
|
|
52
|
-
machine unless you opt in to `--report-to` or `feed update`.
|
|
63
|
+
machine unless you opt in to `--report-to` or `feed update`. Two commands
|
|
64
|
+
need the network by nature: `integrity` fetches checksums from wordpress.org,
|
|
65
|
+
and `probe` talks only to the site you point it at.
|
|
53
66
|
|
|
54
67
|
## Install
|
|
55
68
|
|
|
56
69
|
```bash
|
|
57
70
|
composer require --dev phpwarden/phpwarden # PHP projects → vendor/bin/phpwarden
|
|
58
71
|
pipx install phpwarden # Python-tooling box
|
|
59
|
-
|
|
60
|
-
|
|
72
|
+
brew install oroma-alfred/tap/phpwarden # macOS / Linux with Homebrew
|
|
73
|
+
curl -LO https://phpwarden.io/phpwarden.pyz && python3 phpwarden.pyz scan # single file, no install
|
|
74
|
+
docker run --rm phpwarden/phpwarden scan --version 8.3.10 --sapi fpm --os linux # Docker Hub, amd64 + arm64
|
|
61
75
|
```
|
|
62
76
|
|
|
63
77
|
On modern macOS / Debian / Ubuntu a plain `pip install phpwarden` into the
|
|
@@ -68,8 +82,8 @@ system Python is blocked by [PEP 668](https://peps.python.org/pep-0668/); use
|
|
|
68
82
|
python3 -m venv ~/.venvs/phpwarden && ~/.venvs/phpwarden/bin/pip install phpwarden
|
|
69
83
|
```
|
|
70
84
|
|
|
71
|
-
Docker usage (mounted projects, CI, dashboard reporting) is
|
|
72
|
-
[
|
|
85
|
+
Docker usage (mounted projects, exit codes, CI, dashboard reporting) is on the
|
|
86
|
+
image's [Docker Hub page](https://hub.docker.com/r/phpwarden/phpwarden).
|
|
73
87
|
|
|
74
88
|
## Use
|
|
75
89
|
|
|
@@ -81,21 +95,80 @@ phpwarden wordpress /var/www/html # WordPress core, plugins, themes
|
|
|
81
95
|
phpwarden integrity /var/www/html # tampered or injected files (vs wordpress.org checksums)
|
|
82
96
|
phpwarden detect /var/log/nginx/access.log # exploit signatures in a log
|
|
83
97
|
phpwarden rules --format nginx --out php-block.conf # PHP-specific WAF rules (or modsecurity)
|
|
98
|
+
phpwarden rules --format nginx --dry-run # the same rules, log-only: nothing is blocked
|
|
99
|
+
phpwarden rules --replay /var/log/nginx/access.log # what the rules would have blocked in an existing log
|
|
100
|
+
phpwarden probe https://example.com # live check of a site you own: exposed files, debug endpoints, versions vs the feed
|
|
101
|
+
phpwarden code . # your own PHP: 75 high-signal rules, 74 on by default (injection, unserialize, WordPress handlers)
|
|
102
|
+
phpwarden assess . --url https://example.com --log access.log # one ranked verdict; --out / --baseline shows what is fixed and verified
|
|
84
103
|
phpwarden report web01.json web02.json --out fleet.html # aggregate a fleet view
|
|
85
104
|
phpwarden feed status # which feed is active, how many advisories
|
|
105
|
+
phpwarden feed update # pull a newer feed: free weekly snapshot, or the maintained channel with a key
|
|
86
106
|
```
|
|
87
107
|
|
|
108
|
+
`scan`, `deps`, `wordpress`, `probe` and `code` also take `--format sarif`
|
|
109
|
+
(SARIF 2.1.0, for GitHub code scanning and other CI dashboards).
|
|
110
|
+
|
|
111
|
+
`probe`, `code`, `assess`, `--format sarif`, `rules --dry-run` / `--replay` and
|
|
112
|
+
the no-key `feed update` are new in 2.2.0.
|
|
113
|
+
|
|
88
114
|
Every command takes `--json`. Exit codes are shared across subcommands and
|
|
89
|
-
built for gating: `0` clean, `1` exposed / vulnerable / signature hits
|
|
115
|
+
built for gating: `0` clean, `1` exposed / vulnerable / signature hits / tampered
|
|
116
|
+
files / high or critical `code` and `probe` findings (`probe` also on any
|
|
117
|
+
known-exploited finding) / anything to act on now or this week (`assess`),
|
|
90
118
|
`2` possibly exposed (`scan`), `3` PHP version unknown (`scan`), `4` input
|
|
91
119
|
error, `5` `--report-to` upload failed on an otherwise clean run, `64` usage
|
|
92
120
|
error. Full table: `phpwarden --help`.
|
|
93
121
|
|
|
122
|
+
|
|
123
|
+
## New in 2.2.0
|
|
124
|
+
|
|
125
|
+
- **`phpwarden probe URL`**: a live check of a running site using GET and HEAD
|
|
126
|
+
requests only. It looks for exposed files (`.env`, `.git`, backups), open debug
|
|
127
|
+
endpoints, and the WordPress, plugin and PHP versions the site reveals, and
|
|
128
|
+
matches those against the feed. Active checks run only after you prove the
|
|
129
|
+
site is yours (DNS TXT record, a file, or a meta tag; `--print-token` shows
|
|
130
|
+
how). Without that it reads the homepage and stops.
|
|
131
|
+
- **`phpwarden code PATH`**: 75 rules (74 on by default) for your own PHP code, following request
|
|
132
|
+
data to `eval`, shell commands, SQL, `unserialize`, includes and redirects.
|
|
133
|
+
It follows data through one level of same-file helper, not across files, and
|
|
134
|
+
does not replace PHPStan or Psalm. `phpwarden code --list-rules` lists every rule.
|
|
135
|
+
- **`phpwarden assess`**: one ranked list from every view of a site: installed
|
|
136
|
+
versions, your code, the live site from outside, and the access log, with the
|
|
137
|
+
reason for each item ("exploited, reachable, visible from outside, already
|
|
138
|
+
fetched"). `--out` then `--baseline` reports what is fixed and verified.
|
|
139
|
+
- **`--format sarif`** on `scan`, `deps`, `wordpress`, `probe` and `code`.
|
|
140
|
+
- **`rules --dry-run` and `rules --replay ACCESS_LOG`**: see what the WAF rules
|
|
141
|
+
would block before they block anything.
|
|
142
|
+
- **Free weekly feed updates**: `phpwarden feed update` without a key pulls a
|
|
143
|
+
weekly snapshot of the maintained feed. A vulnerability newly listed as known
|
|
144
|
+
exploited is released to the free channel straight away.
|
|
145
|
+
- **Coverage commitment**: in-scope CISA KEV entries are in the maintained feed
|
|
146
|
+
within 24 hours of listing, checked on every rebuild and published at
|
|
147
|
+
https://phpwarden.io/feeds/kev-status.json. Details: https://phpwarden.io/sla
|
|
148
|
+
- **Editors**: a VS Code extension in `integrations/vscode` (build and install
|
|
149
|
+
the `.vsix` from that folder; not on the Marketplace yet) and PhpStorm External Tool steps in
|
|
150
|
+
`integrations/phpstorm`.
|
|
151
|
+
|
|
152
|
+
## Replacing Wordfence CLI
|
|
153
|
+
|
|
154
|
+
Wordfence closed new licences for its CLI in October 2026; Free licences stopped working on 2026-10-14 and Premium
|
|
155
|
+
licences are not renewed. phpwarden runs the same way (on the server, no plugin in the site) and covers:
|
|
156
|
+
|
|
157
|
+
| Wordfence CLI | phpwarden | Notes |
|
|
158
|
+
|---|---|---|
|
|
159
|
+
| `wordfence vuln-scan /path` | `phpwarden wordpress /path` | Same job, ranked by real-world exploitation; JSON/SARIF and CI exit codes. Also scans Composer and the PHP engine. |
|
|
160
|
+
| `wordfence malware-scan /path` | `phpwarden integrity /path` | Compares core and wordpress.org plugin files with official checksums; flags injected files, PHP in uploads and common backdoor code. **Not a signature scanner**: premium plugins and themes cannot be verified and are listed as such. |
|
|
161
|
+
| `wordfence remediate` | – | Reinstall the flagged component with WP-CLI. |
|
|
162
|
+
| `wordfence db-scan` | – | Not covered. |
|
|
163
|
+
|
|
164
|
+
Full guide: https://phpwarden.io/compare/phpwarden-vs-wordfence-cli#replace
|
|
165
|
+
|
|
94
166
|
## Send results to the dashboard
|
|
95
167
|
|
|
96
168
|
Any `scan`, `deps`, `wordpress` or `detect` run can post its result to
|
|
97
|
-
[app.phpwarden.io](https://app.phpwarden.io) for history across hosts
|
|
98
|
-
|
|
169
|
+
[app.phpwarden.io](https://app.phpwarden.io) for history across hosts.
|
|
170
|
+
It marks findings that become known-exploited; email and webhook alerts are on
|
|
171
|
+
Pro and Team, Slack on Team:
|
|
99
172
|
|
|
100
173
|
```bash
|
|
101
174
|
phpwarden scan --report-to https://app.phpwarden.io/v1/reports --report-token <token>
|
|
@@ -140,7 +213,7 @@ see [phpwarden/laravel](https://packagist.org/packages/phpwarden/laravel).
|
|
|
140
213
|
```yaml
|
|
141
214
|
repos:
|
|
142
215
|
- repo: https://github.com/oroma-alfred/phpwarden
|
|
143
|
-
rev:
|
|
216
|
+
rev: v2.1.0 # pin to a tag with `pre-commit autoupdate`
|
|
144
217
|
hooks:
|
|
145
218
|
- id: phpwarden-deps
|
|
146
219
|
# args: ["--no-reachability"] # faster: skip the reachability pass
|
|
@@ -151,44 +224,51 @@ timer recipes are in [`docs/recipes.md`](docs/recipes.md).
|
|
|
151
224
|
|
|
152
225
|
## The feed
|
|
153
226
|
|
|
154
|
-
The bundled community feed (
|
|
227
|
+
The bundled community feed (2026.11.13.3 in this release) works forever, offline:
|
|
155
228
|
|
|
156
|
-
- **~21,
|
|
229
|
+
- **~21,300 advisories**: ~8,800 Composer (from [OSV.dev](https://osv.dev) and
|
|
157
230
|
[FriendsOfPHP](https://github.com/FriendsOfPHP/security-advisories)),
|
|
158
231
|
~12,300 WordPress (from [NVD](https://nvd.nist.gov): plugin, core and theme),
|
|
159
232
|
and 145 PHP engine/FPM CVEs, including hand-curated entries with per-branch
|
|
160
|
-
fixed versions, exposure conditions and detection signatures. 19,
|
|
161
|
-
EPSS score;
|
|
233
|
+
fixed versions, exposure conditions and detection signatures. About 19,700 carry an
|
|
234
|
+
EPSS score; 41 distinct vulnerabilities were in KEV when this feed was built.
|
|
162
235
|
- **License-independent by construction.** Every source is open or
|
|
163
|
-
public-domain (OSV, NVD, FriendsOfPHP, CISA KEV, FIRST EPSS). No commercial
|
|
236
|
+
public-domain (OSV, NVD, FriendsOfPHP, php-src release notes, CISA KEV, FIRST EPSS). No commercial
|
|
164
237
|
vendor's feed is used, so coverage cannot be revoked or priced away.
|
|
165
238
|
- Every build passes `feedbuilder/check_feed.py` (no open-ended range with a
|
|
166
239
|
known fix, no fixed version inside its own affected range, no duplicate
|
|
167
240
|
(package|slug, cve)). The feed is assembled by `feedbuilder/` from a curated
|
|
168
241
|
base plus bulk imports and enrichment; curated entries always win.
|
|
169
242
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
`
|
|
173
|
-
the
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
| CVE
|
|
184
|
-
|
|
243
|
+
Updating the feed is optional and has two channels:
|
|
244
|
+
|
|
245
|
+
- **Free weekly.** `phpwarden feed update` with no key downloads a weekly
|
|
246
|
+
snapshot of the maintained feed. A vulnerability newly listed as known
|
|
247
|
+
exploited is released to this channel straight away. If the server cannot be
|
|
248
|
+
reached, phpwarden keeps the feed it has and says so. Set
|
|
249
|
+
`PHPWARDEN_FREE_FEED_URL=off` to stop the no-key update from calling out.
|
|
250
|
+
- **Maintained.** A [phpwarden.io](https://phpwarden.io) subscription gives a
|
|
251
|
+
license key; `PHPWARDEN_LICENSE=... phpwarden feed update` then pulls the
|
|
252
|
+
channel that is rebuilt twice a day.
|
|
253
|
+
|
|
254
|
+
Examples of hand-curated engine CVEs:
|
|
255
|
+
|
|
256
|
+
| CVE | What | Scope | Log signature |
|
|
257
|
+
|-----|------|-------|---------------|
|
|
258
|
+
| CVE-2019-11043 | PHP-FPM env_path_info RCE | FPM + nginx, config-gated | yes |
|
|
259
|
+
| CVE-2023-3824 | PHAR stack overflow RCE | any SAPI, untrusted PHAR | no |
|
|
260
|
+
| CVE-2024-4577 | PHP-CGI argument injection | Windows + CGI | yes |
|
|
261
|
+
| CVE-2024-8926 | cgi.force_redirect bypass | Windows + CGI | no |
|
|
262
|
+
| CVE-2024-8927 | cgi.force_redirect bypass | CGI / FPM | no |
|
|
263
|
+
| CVE-2024-11236 | DB-quoter integer overflow | any SAPI | no |
|
|
264
|
+
| CVE-2026-109618 / 109619 | php2shell FPM memory RCE | any SAPI | yes |
|
|
185
265
|
|
|
186
266
|
## Continuous guard
|
|
187
267
|
|
|
188
268
|
`scan` and `detect` are point-in-time. For always-on containment of
|
|
189
269
|
in-progress php2shell-class attacks (OOM on a worker, then that worker reused),
|
|
190
270
|
run `phpwarden-guard`, which boots in dry-run and never acts until you promote
|
|
191
|
-
it after a burn-in. See
|
|
271
|
+
it after a burn-in. See [`VALIDATION.md`](VALIDATION.md) for what was validated.
|
|
192
272
|
|
|
193
273
|
## Honest limits
|
|
194
274
|
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "phpwarden"
|
|
7
|
-
version = "2.
|
|
8
|
-
description = "PHP vulnerability scanner for Composer, WordPress and the
|
|
7
|
+
version = "2.2.0"
|
|
8
|
+
description = "PHP vulnerability scanner for the PHP engine, Composer, WordPress, your own code and the live site. Ranked by real-world exploitation (CISA KEV, then EPSS), not CVSS. Offline, zero dependencies."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
11
11
|
license = "Apache-2.0"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""phpwarden: a PHP vulnerability scanner.
|
|
2
|
+
|
|
3
|
+
It checks the PHP engine, Composer dependencies, WordPress, your own code and
|
|
4
|
+
the live site against a feed of known vulnerabilities, ranks findings by
|
|
5
|
+
real-world exploitation (CISA KEV, then EPSS, then CVSS), and can watch PHP-FPM
|
|
6
|
+
for active exploitation. The engine and the bundled feed are open; free users
|
|
7
|
+
get weekly feed updates, and a subscription adds the feed rebuilt twice a day.
|
|
8
|
+
"""
|
|
9
|
+
__version__ = "2.2.0"
|