phpwarden 2.1.1__tar.gz → 2.2.1__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.1 → phpwarden-2.2.1}/PKG-INFO +87 -30
- {phpwarden-2.1.1 → phpwarden-2.2.1}/README.md +85 -28
- {phpwarden-2.1.1 → phpwarden-2.2.1}/pyproject.toml +2 -2
- phpwarden-2.2.1/src/phpwarden/__init__.py +9 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/cli.py +274 -28
- phpwarden-2.2.1/src/phpwarden/code.py +2263 -0
- phpwarden-2.2.1/src/phpwarden/code_rules.py +369 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/data/php-cve-feed.json +360 -37
- {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/feed.py +62 -9
- {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/guard.py +5 -4
- phpwarden-2.2.1/src/phpwarden/probe.py +902 -0
- phpwarden-2.2.1/src/phpwarden/rules.py +259 -0
- phpwarden-2.2.1/src/phpwarden/sarif.py +134 -0
- phpwarden-2.2.1/src/phpwarden/verdict.py +462 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden.egg-info/PKG-INFO +87 -30
- {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden.egg-info/SOURCES.txt +17 -0
- phpwarden-2.2.1/tests/test_account_deletion.py +138 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_agency.py +8 -0
- phpwarden-2.2.1/tests/test_assess.py +152 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_cli_edges.py +7 -0
- phpwarden-2.2.1/tests/test_code.py +490 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_e2e_readiness.py +2 -2
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_feed_license.py +2 -2
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_feed_subscription.py +1 -1
- phpwarden-2.2.1/tests/test_free_feed.py +108 -0
- phpwarden-2.2.1/tests/test_kev_alert.py +97 -0
- phpwarden-2.2.1/tests/test_kev_gaps.py +52 -0
- phpwarden-2.2.1/tests/test_probe.py +196 -0
- phpwarden-2.2.1/tests/test_probe_cli.py +99 -0
- phpwarden-2.2.1/tests/test_probe_dashboard.py +312 -0
- phpwarden-2.2.1/tests/test_report_proof.py +53 -0
- phpwarden-2.2.1/tests/test_sarif.py +80 -0
- phpwarden-2.2.1/tests/test_shield_plan.py +36 -0
- phpwarden-2.2.1/tests/test_waf_dryrun.py +76 -0
- phpwarden-2.1.1/src/phpwarden/__init__.py +0 -8
- phpwarden-2.1.1/src/phpwarden/rules.py +0 -152
- phpwarden-2.1.1/tests/test_kev_gaps.py +0 -23
- {phpwarden-2.1.1 → phpwarden-2.2.1}/LICENSE +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/setup.cfg +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/deps.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/detect.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/engine.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/env.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/integrity.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/report.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/version.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/vulndb.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/wordpress.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden.egg-info/dependency_links.txt +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden.egg-info/entry_points.txt +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden.egg-info/top_level.txt +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_accounts.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_advisories.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_assignment.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_audit.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_billing.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_business_metrics.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_connect_flow.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_dashboard_states.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_deps_and_rules.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_detection_validation.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_developer.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_discussion.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_email.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_engine.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_engine_feed.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_fleet.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_google_oauth.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_guard_live.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_integrity.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_livefeed.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_mentions.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_reachability.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_saas.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_saas_billing.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_server.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_settings.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_shield.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_slack.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_sso.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_store_connections.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_team.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_triage.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_webhook_idempotency.py +0 -0
- {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_wordpress_and_report.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.1
|
|
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 about 19,500 PHP vulnerabilities in the feed,
|
|
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
|
|
|
@@ -59,6 +59,9 @@ $ phpwarden deps composer.lock
|
|
|
59
59
|
| Composer dependency CVEs | `deps` | Snyk, `composer audit` |
|
|
60
60
|
| WordPress core, plugin and theme CVEs | `wordpress` | Patchstack, Wordfence |
|
|
61
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` | — |
|
|
62
65
|
| Exploitation signatures in logs | `detect` | SIEM content |
|
|
63
66
|
| WAF blocking rules (PHP-specific) | `rules` | the gap a generic WAF leaves |
|
|
64
67
|
| Fleet exposure dashboard | `report` | the CNAPP's PHP view |
|
|
@@ -79,7 +82,9 @@ $ phpwarden deps composer.lock
|
|
|
79
82
|
configuration is reported as *possibly* exposed with the exact condition to
|
|
80
83
|
confirm — not flagged as a certain hit, and not silently dismissed.
|
|
81
84
|
- **Offline by default.** The feed ships inside the package. Nothing leaves the
|
|
82
|
-
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.
|
|
83
88
|
|
|
84
89
|
## Install
|
|
85
90
|
|
|
@@ -87,7 +92,7 @@ $ phpwarden deps composer.lock
|
|
|
87
92
|
composer require --dev phpwarden/phpwarden # PHP projects → vendor/bin/phpwarden
|
|
88
93
|
pipx install phpwarden # Python-tooling box
|
|
89
94
|
brew install oroma-alfred/tap/phpwarden # macOS / Linux with Homebrew
|
|
90
|
-
python3 phpwarden.pyz scan
|
|
95
|
+
curl -LO https://phpwarden.io/phpwarden.pyz && python3 phpwarden.pyz scan # single file, no install
|
|
91
96
|
docker run --rm phpwarden/phpwarden scan --version 8.3.10 --sapi fpm --os linux # Docker Hub, amd64 + arm64
|
|
92
97
|
```
|
|
93
98
|
|
|
@@ -112,17 +117,61 @@ phpwarden wordpress /var/www/html # WordPress core, plugins, themes
|
|
|
112
117
|
phpwarden integrity /var/www/html # tampered or injected files (vs wordpress.org checksums)
|
|
113
118
|
phpwarden detect /var/log/nginx/access.log # exploit signatures in a log
|
|
114
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
|
|
115
125
|
phpwarden report web01.json web02.json --out fleet.html # aggregate a fleet view
|
|
116
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
|
|
117
128
|
```
|
|
118
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
|
+
|
|
119
136
|
Every command takes `--json`. Exit codes are shared across subcommands and
|
|
120
|
-
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`),
|
|
121
140
|
`2` possibly exposed (`scan`), `3` PHP version unknown (`scan`), `4` input
|
|
122
141
|
error, `5` `--report-to` upload failed on an otherwise clean run, `64` usage
|
|
123
142
|
error. Full table: `phpwarden --help`.
|
|
124
143
|
|
|
125
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 across files (helper functions and classes elsewhere in the
|
|
156
|
+
project, fixed includes, class properties); it is not a type checker like
|
|
157
|
+
PHPStan or Psalm. `phpwarden code --list-rules` lists every rule.
|
|
158
|
+
- **`phpwarden assess`**: one ranked list from every view of a site: installed
|
|
159
|
+
versions, your code, the live site from outside, and the access log, with the
|
|
160
|
+
reason for each item ("exploited, reachable, visible from outside, already
|
|
161
|
+
fetched"). `--out` then `--baseline` reports what is fixed and verified.
|
|
162
|
+
- **`--format sarif`** on `scan`, `deps`, `wordpress`, `probe` and `code`.
|
|
163
|
+
- **`rules --dry-run` and `rules --replay ACCESS_LOG`**: see what the WAF rules
|
|
164
|
+
would block before they block anything.
|
|
165
|
+
- **Free weekly feed updates**: `phpwarden feed update` without a key pulls a
|
|
166
|
+
weekly snapshot of the maintained feed. A vulnerability newly listed as known
|
|
167
|
+
exploited is released to the free channel straight away.
|
|
168
|
+
- **Coverage commitment**: in-scope CISA KEV entries are in the maintained feed
|
|
169
|
+
within 24 hours of listing, checked on every rebuild and published at
|
|
170
|
+
https://phpwarden.io/feeds/kev-status.json. Details: https://phpwarden.io/sla
|
|
171
|
+
- **Editors**: a VS Code extension in `integrations/vscode` (build and install
|
|
172
|
+
the `.vsix` from that folder; not on the Marketplace yet) and PhpStorm External Tool steps in
|
|
173
|
+
`integrations/phpstorm`.
|
|
174
|
+
|
|
126
175
|
## Replacing Wordfence CLI
|
|
127
176
|
|
|
128
177
|
Wordfence closed new licences for its CLI in October 2026; Free licences stopped working on 2026-10-14 and Premium
|
|
@@ -140,8 +189,9 @@ Full guide: https://phpwarden.io/compare/phpwarden-vs-wordfence-cli#replace
|
|
|
140
189
|
## Send results to the dashboard
|
|
141
190
|
|
|
142
191
|
Any `scan`, `deps`, `wordpress` or `detect` run can post its result to
|
|
143
|
-
[app.phpwarden.io](https://app.phpwarden.io) for history across hosts
|
|
144
|
-
|
|
192
|
+
[app.phpwarden.io](https://app.phpwarden.io) for history across hosts.
|
|
193
|
+
It marks findings that become known-exploited; email and webhook alerts are on
|
|
194
|
+
Pro and Team, Slack on Team:
|
|
145
195
|
|
|
146
196
|
```bash
|
|
147
197
|
phpwarden scan --report-to https://app.phpwarden.io/v1/reports --report-token <token>
|
|
@@ -186,7 +236,7 @@ see [phpwarden/laravel](https://packagist.org/packages/phpwarden/laravel).
|
|
|
186
236
|
```yaml
|
|
187
237
|
repos:
|
|
188
238
|
- repo: https://github.com/oroma-alfred/phpwarden
|
|
189
|
-
rev:
|
|
239
|
+
rev: v2.1.0 # pin to a tag with `pre-commit autoupdate`
|
|
190
240
|
hooks:
|
|
191
241
|
- id: phpwarden-deps
|
|
192
242
|
# args: ["--no-reachability"] # faster: skip the reachability pass
|
|
@@ -197,44 +247,51 @@ timer recipes are in [`docs/recipes.md`](docs/recipes.md).
|
|
|
197
247
|
|
|
198
248
|
## The feed
|
|
199
249
|
|
|
200
|
-
The bundled community feed (
|
|
250
|
+
The bundled community feed (2026.11.13.3 in this release) works forever, offline:
|
|
201
251
|
|
|
202
252
|
- **~21,300 advisories**: ~8,800 Composer (from [OSV.dev](https://osv.dev) and
|
|
203
253
|
[FriendsOfPHP](https://github.com/FriendsOfPHP/security-advisories)),
|
|
204
254
|
~12,300 WordPress (from [NVD](https://nvd.nist.gov): plugin, core and theme),
|
|
205
255
|
and 145 PHP engine/FPM CVEs, including hand-curated entries with per-branch
|
|
206
|
-
fixed versions, exposure conditions and detection signatures. 19,
|
|
207
|
-
EPSS score;
|
|
256
|
+
fixed versions, exposure conditions and detection signatures. About 19,700 carry an
|
|
257
|
+
EPSS score; 41 distinct vulnerabilities were in KEV when this feed was built.
|
|
208
258
|
- **License-independent by construction.** Every source is open or
|
|
209
|
-
public-domain (OSV, NVD, FriendsOfPHP, CISA KEV, FIRST EPSS). No commercial
|
|
259
|
+
public-domain (OSV, NVD, FriendsOfPHP, php-src release notes, CISA KEV, FIRST EPSS). No commercial
|
|
210
260
|
vendor's feed is used, so coverage cannot be revoked or priced away.
|
|
211
261
|
- Every build passes `feedbuilder/check_feed.py` (no open-ended range with a
|
|
212
262
|
known fix, no fixed version inside its own affected range, no duplicate
|
|
213
263
|
(package|slug, cve)). The feed is assembled by `feedbuilder/` from a curated
|
|
214
264
|
base plus bulk imports and enrichment; curated entries always win.
|
|
215
265
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
`
|
|
219
|
-
the
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
| CVE
|
|
230
|
-
|
|
266
|
+
Updating the feed is optional and has two channels:
|
|
267
|
+
|
|
268
|
+
- **Free weekly.** `phpwarden feed update` with no key downloads a weekly
|
|
269
|
+
snapshot of the maintained feed. A vulnerability newly listed as known
|
|
270
|
+
exploited is released to this channel straight away. If the server cannot be
|
|
271
|
+
reached, phpwarden keeps the feed it has and says so. Set
|
|
272
|
+
`PHPWARDEN_FREE_FEED_URL=off` to stop the no-key update from calling out.
|
|
273
|
+
- **Maintained.** A [phpwarden.io](https://phpwarden.io) subscription gives a
|
|
274
|
+
license key; `PHPWARDEN_LICENSE=... phpwarden feed update` then pulls the
|
|
275
|
+
channel that is rebuilt twice a day.
|
|
276
|
+
|
|
277
|
+
Examples of hand-curated engine CVEs:
|
|
278
|
+
|
|
279
|
+
| CVE | What | Scope | Log signature |
|
|
280
|
+
|-----|------|-------|---------------|
|
|
281
|
+
| CVE-2019-11043 | PHP-FPM env_path_info RCE | FPM + nginx, config-gated | yes |
|
|
282
|
+
| CVE-2023-3824 | PHAR stack overflow RCE | any SAPI, untrusted PHAR | no |
|
|
283
|
+
| CVE-2024-4577 | PHP-CGI argument injection | Windows + CGI | yes |
|
|
284
|
+
| CVE-2024-8926 | cgi.force_redirect bypass | Windows + CGI | no |
|
|
285
|
+
| CVE-2024-8927 | cgi.force_redirect bypass | CGI / FPM | no |
|
|
286
|
+
| CVE-2024-11236 | DB-quoter integer overflow | any SAPI | no |
|
|
287
|
+
| CVE-2026-109618 / 109619 | php2shell FPM memory RCE | any SAPI | yes |
|
|
231
288
|
|
|
232
289
|
## Continuous guard
|
|
233
290
|
|
|
234
291
|
`scan` and `detect` are point-in-time. For always-on containment of
|
|
235
292
|
in-progress php2shell-class attacks (OOM on a worker, then that worker reused),
|
|
236
293
|
run `phpwarden-guard`, which boots in dry-run and never acts until you promote
|
|
237
|
-
it after a burn-in. See
|
|
294
|
+
it after a burn-in. See [`VALIDATION.md`](VALIDATION.md) for what was validated.
|
|
238
295
|
|
|
239
296
|
## Honest limits
|
|
240
297
|
|
|
@@ -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 about 19,500 PHP vulnerabilities in the feed,
|
|
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
|
|
|
@@ -37,6 +37,9 @@ $ phpwarden deps composer.lock
|
|
|
37
37
|
| Composer dependency CVEs | `deps` | Snyk, `composer audit` |
|
|
38
38
|
| WordPress core, plugin and theme CVEs | `wordpress` | Patchstack, Wordfence |
|
|
39
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` | — |
|
|
40
43
|
| Exploitation signatures in logs | `detect` | SIEM content |
|
|
41
44
|
| WAF blocking rules (PHP-specific) | `rules` | the gap a generic WAF leaves |
|
|
42
45
|
| Fleet exposure dashboard | `report` | the CNAPP's PHP view |
|
|
@@ -57,7 +60,9 @@ $ phpwarden deps composer.lock
|
|
|
57
60
|
configuration is reported as *possibly* exposed with the exact condition to
|
|
58
61
|
confirm — not flagged as a certain hit, and not silently dismissed.
|
|
59
62
|
- **Offline by default.** The feed ships inside the package. Nothing leaves the
|
|
60
|
-
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.
|
|
61
66
|
|
|
62
67
|
## Install
|
|
63
68
|
|
|
@@ -65,7 +70,7 @@ $ phpwarden deps composer.lock
|
|
|
65
70
|
composer require --dev phpwarden/phpwarden # PHP projects → vendor/bin/phpwarden
|
|
66
71
|
pipx install phpwarden # Python-tooling box
|
|
67
72
|
brew install oroma-alfred/tap/phpwarden # macOS / Linux with Homebrew
|
|
68
|
-
python3 phpwarden.pyz scan
|
|
73
|
+
curl -LO https://phpwarden.io/phpwarden.pyz && python3 phpwarden.pyz scan # single file, no install
|
|
69
74
|
docker run --rm phpwarden/phpwarden scan --version 8.3.10 --sapi fpm --os linux # Docker Hub, amd64 + arm64
|
|
70
75
|
```
|
|
71
76
|
|
|
@@ -90,17 +95,61 @@ phpwarden wordpress /var/www/html # WordPress core, plugins, themes
|
|
|
90
95
|
phpwarden integrity /var/www/html # tampered or injected files (vs wordpress.org checksums)
|
|
91
96
|
phpwarden detect /var/log/nginx/access.log # exploit signatures in a log
|
|
92
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
|
|
93
103
|
phpwarden report web01.json web02.json --out fleet.html # aggregate a fleet view
|
|
94
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
|
|
95
106
|
```
|
|
96
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
|
+
|
|
97
114
|
Every command takes `--json`. Exit codes are shared across subcommands and
|
|
98
|
-
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`),
|
|
99
118
|
`2` possibly exposed (`scan`), `3` PHP version unknown (`scan`), `4` input
|
|
100
119
|
error, `5` `--report-to` upload failed on an otherwise clean run, `64` usage
|
|
101
120
|
error. Full table: `phpwarden --help`.
|
|
102
121
|
|
|
103
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 across files (helper functions and classes elsewhere in the
|
|
134
|
+
project, fixed includes, class properties); it is not a type checker like
|
|
135
|
+
PHPStan or Psalm. `phpwarden code --list-rules` lists every rule.
|
|
136
|
+
- **`phpwarden assess`**: one ranked list from every view of a site: installed
|
|
137
|
+
versions, your code, the live site from outside, and the access log, with the
|
|
138
|
+
reason for each item ("exploited, reachable, visible from outside, already
|
|
139
|
+
fetched"). `--out` then `--baseline` reports what is fixed and verified.
|
|
140
|
+
- **`--format sarif`** on `scan`, `deps`, `wordpress`, `probe` and `code`.
|
|
141
|
+
- **`rules --dry-run` and `rules --replay ACCESS_LOG`**: see what the WAF rules
|
|
142
|
+
would block before they block anything.
|
|
143
|
+
- **Free weekly feed updates**: `phpwarden feed update` without a key pulls a
|
|
144
|
+
weekly snapshot of the maintained feed. A vulnerability newly listed as known
|
|
145
|
+
exploited is released to the free channel straight away.
|
|
146
|
+
- **Coverage commitment**: in-scope CISA KEV entries are in the maintained feed
|
|
147
|
+
within 24 hours of listing, checked on every rebuild and published at
|
|
148
|
+
https://phpwarden.io/feeds/kev-status.json. Details: https://phpwarden.io/sla
|
|
149
|
+
- **Editors**: a VS Code extension in `integrations/vscode` (build and install
|
|
150
|
+
the `.vsix` from that folder; not on the Marketplace yet) and PhpStorm External Tool steps in
|
|
151
|
+
`integrations/phpstorm`.
|
|
152
|
+
|
|
104
153
|
## Replacing Wordfence CLI
|
|
105
154
|
|
|
106
155
|
Wordfence closed new licences for its CLI in October 2026; Free licences stopped working on 2026-10-14 and Premium
|
|
@@ -118,8 +167,9 @@ Full guide: https://phpwarden.io/compare/phpwarden-vs-wordfence-cli#replace
|
|
|
118
167
|
## Send results to the dashboard
|
|
119
168
|
|
|
120
169
|
Any `scan`, `deps`, `wordpress` or `detect` run can post its result to
|
|
121
|
-
[app.phpwarden.io](https://app.phpwarden.io) for history across hosts
|
|
122
|
-
|
|
170
|
+
[app.phpwarden.io](https://app.phpwarden.io) for history across hosts.
|
|
171
|
+
It marks findings that become known-exploited; email and webhook alerts are on
|
|
172
|
+
Pro and Team, Slack on Team:
|
|
123
173
|
|
|
124
174
|
```bash
|
|
125
175
|
phpwarden scan --report-to https://app.phpwarden.io/v1/reports --report-token <token>
|
|
@@ -164,7 +214,7 @@ see [phpwarden/laravel](https://packagist.org/packages/phpwarden/laravel).
|
|
|
164
214
|
```yaml
|
|
165
215
|
repos:
|
|
166
216
|
- repo: https://github.com/oroma-alfred/phpwarden
|
|
167
|
-
rev:
|
|
217
|
+
rev: v2.1.0 # pin to a tag with `pre-commit autoupdate`
|
|
168
218
|
hooks:
|
|
169
219
|
- id: phpwarden-deps
|
|
170
220
|
# args: ["--no-reachability"] # faster: skip the reachability pass
|
|
@@ -175,44 +225,51 @@ timer recipes are in [`docs/recipes.md`](docs/recipes.md).
|
|
|
175
225
|
|
|
176
226
|
## The feed
|
|
177
227
|
|
|
178
|
-
The bundled community feed (
|
|
228
|
+
The bundled community feed (2026.11.13.3 in this release) works forever, offline:
|
|
179
229
|
|
|
180
230
|
- **~21,300 advisories**: ~8,800 Composer (from [OSV.dev](https://osv.dev) and
|
|
181
231
|
[FriendsOfPHP](https://github.com/FriendsOfPHP/security-advisories)),
|
|
182
232
|
~12,300 WordPress (from [NVD](https://nvd.nist.gov): plugin, core and theme),
|
|
183
233
|
and 145 PHP engine/FPM CVEs, including hand-curated entries with per-branch
|
|
184
|
-
fixed versions, exposure conditions and detection signatures. 19,
|
|
185
|
-
EPSS score;
|
|
234
|
+
fixed versions, exposure conditions and detection signatures. About 19,700 carry an
|
|
235
|
+
EPSS score; 41 distinct vulnerabilities were in KEV when this feed was built.
|
|
186
236
|
- **License-independent by construction.** Every source is open or
|
|
187
|
-
public-domain (OSV, NVD, FriendsOfPHP, CISA KEV, FIRST EPSS). No commercial
|
|
237
|
+
public-domain (OSV, NVD, FriendsOfPHP, php-src release notes, CISA KEV, FIRST EPSS). No commercial
|
|
188
238
|
vendor's feed is used, so coverage cannot be revoked or priced away.
|
|
189
239
|
- Every build passes `feedbuilder/check_feed.py` (no open-ended range with a
|
|
190
240
|
known fix, no fixed version inside its own affected range, no duplicate
|
|
191
241
|
(package|slug, cve)). The feed is assembled by `feedbuilder/` from a curated
|
|
192
242
|
base plus bulk imports and enrichment; curated entries always win.
|
|
193
243
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
`
|
|
197
|
-
the
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
| CVE
|
|
208
|
-
|
|
244
|
+
Updating the feed is optional and has two channels:
|
|
245
|
+
|
|
246
|
+
- **Free weekly.** `phpwarden feed update` with no key downloads a weekly
|
|
247
|
+
snapshot of the maintained feed. A vulnerability newly listed as known
|
|
248
|
+
exploited is released to this channel straight away. If the server cannot be
|
|
249
|
+
reached, phpwarden keeps the feed it has and says so. Set
|
|
250
|
+
`PHPWARDEN_FREE_FEED_URL=off` to stop the no-key update from calling out.
|
|
251
|
+
- **Maintained.** A [phpwarden.io](https://phpwarden.io) subscription gives a
|
|
252
|
+
license key; `PHPWARDEN_LICENSE=... phpwarden feed update` then pulls the
|
|
253
|
+
channel that is rebuilt twice a day.
|
|
254
|
+
|
|
255
|
+
Examples of hand-curated engine CVEs:
|
|
256
|
+
|
|
257
|
+
| CVE | What | Scope | Log signature |
|
|
258
|
+
|-----|------|-------|---------------|
|
|
259
|
+
| CVE-2019-11043 | PHP-FPM env_path_info RCE | FPM + nginx, config-gated | yes |
|
|
260
|
+
| CVE-2023-3824 | PHAR stack overflow RCE | any SAPI, untrusted PHAR | no |
|
|
261
|
+
| CVE-2024-4577 | PHP-CGI argument injection | Windows + CGI | yes |
|
|
262
|
+
| CVE-2024-8926 | cgi.force_redirect bypass | Windows + CGI | no |
|
|
263
|
+
| CVE-2024-8927 | cgi.force_redirect bypass | CGI / FPM | no |
|
|
264
|
+
| CVE-2024-11236 | DB-quoter integer overflow | any SAPI | no |
|
|
265
|
+
| CVE-2026-109618 / 109619 | php2shell FPM memory RCE | any SAPI | yes |
|
|
209
266
|
|
|
210
267
|
## Continuous guard
|
|
211
268
|
|
|
212
269
|
`scan` and `detect` are point-in-time. For always-on containment of
|
|
213
270
|
in-progress php2shell-class attacks (OOM on a worker, then that worker reused),
|
|
214
271
|
run `phpwarden-guard`, which boots in dry-run and never acts until you promote
|
|
215
|
-
it after a burn-in. See
|
|
272
|
+
it after a burn-in. See [`VALIDATION.md`](VALIDATION.md) for what was validated.
|
|
216
273
|
|
|
217
274
|
## Honest limits
|
|
218
275
|
|
|
@@ -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.1"
|
|
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.1"
|