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.
Files changed (85) hide show
  1. {phpwarden-2.1.1 → phpwarden-2.2.1}/PKG-INFO +87 -30
  2. {phpwarden-2.1.1 → phpwarden-2.2.1}/README.md +85 -28
  3. {phpwarden-2.1.1 → phpwarden-2.2.1}/pyproject.toml +2 -2
  4. phpwarden-2.2.1/src/phpwarden/__init__.py +9 -0
  5. {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/cli.py +274 -28
  6. phpwarden-2.2.1/src/phpwarden/code.py +2263 -0
  7. phpwarden-2.2.1/src/phpwarden/code_rules.py +369 -0
  8. {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/data/php-cve-feed.json +360 -37
  9. {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/feed.py +62 -9
  10. {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/guard.py +5 -4
  11. phpwarden-2.2.1/src/phpwarden/probe.py +902 -0
  12. phpwarden-2.2.1/src/phpwarden/rules.py +259 -0
  13. phpwarden-2.2.1/src/phpwarden/sarif.py +134 -0
  14. phpwarden-2.2.1/src/phpwarden/verdict.py +462 -0
  15. {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden.egg-info/PKG-INFO +87 -30
  16. {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden.egg-info/SOURCES.txt +17 -0
  17. phpwarden-2.2.1/tests/test_account_deletion.py +138 -0
  18. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_agency.py +8 -0
  19. phpwarden-2.2.1/tests/test_assess.py +152 -0
  20. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_cli_edges.py +7 -0
  21. phpwarden-2.2.1/tests/test_code.py +490 -0
  22. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_e2e_readiness.py +2 -2
  23. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_feed_license.py +2 -2
  24. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_feed_subscription.py +1 -1
  25. phpwarden-2.2.1/tests/test_free_feed.py +108 -0
  26. phpwarden-2.2.1/tests/test_kev_alert.py +97 -0
  27. phpwarden-2.2.1/tests/test_kev_gaps.py +52 -0
  28. phpwarden-2.2.1/tests/test_probe.py +196 -0
  29. phpwarden-2.2.1/tests/test_probe_cli.py +99 -0
  30. phpwarden-2.2.1/tests/test_probe_dashboard.py +312 -0
  31. phpwarden-2.2.1/tests/test_report_proof.py +53 -0
  32. phpwarden-2.2.1/tests/test_sarif.py +80 -0
  33. phpwarden-2.2.1/tests/test_shield_plan.py +36 -0
  34. phpwarden-2.2.1/tests/test_waf_dryrun.py +76 -0
  35. phpwarden-2.1.1/src/phpwarden/__init__.py +0 -8
  36. phpwarden-2.1.1/src/phpwarden/rules.py +0 -152
  37. phpwarden-2.1.1/tests/test_kev_gaps.py +0 -23
  38. {phpwarden-2.1.1 → phpwarden-2.2.1}/LICENSE +0 -0
  39. {phpwarden-2.1.1 → phpwarden-2.2.1}/setup.cfg +0 -0
  40. {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/deps.py +0 -0
  41. {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/detect.py +0 -0
  42. {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/engine.py +0 -0
  43. {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/env.py +0 -0
  44. {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/integrity.py +0 -0
  45. {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/report.py +0 -0
  46. {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/version.py +0 -0
  47. {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/vulndb.py +0 -0
  48. {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden/wordpress.py +0 -0
  49. {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden.egg-info/dependency_links.txt +0 -0
  50. {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden.egg-info/entry_points.txt +0 -0
  51. {phpwarden-2.1.1 → phpwarden-2.2.1}/src/phpwarden.egg-info/top_level.txt +0 -0
  52. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_accounts.py +0 -0
  53. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_advisories.py +0 -0
  54. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_assignment.py +0 -0
  55. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_audit.py +0 -0
  56. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_billing.py +0 -0
  57. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_business_metrics.py +0 -0
  58. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_connect_flow.py +0 -0
  59. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_dashboard_states.py +0 -0
  60. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_deps_and_rules.py +0 -0
  61. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_detection_validation.py +0 -0
  62. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_developer.py +0 -0
  63. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_discussion.py +0 -0
  64. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_email.py +0 -0
  65. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_engine.py +0 -0
  66. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_engine_feed.py +0 -0
  67. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_fleet.py +0 -0
  68. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_google_oauth.py +0 -0
  69. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_guard_live.py +0 -0
  70. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_integrity.py +0 -0
  71. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_livefeed.py +0 -0
  72. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_mentions.py +0 -0
  73. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_reachability.py +0 -0
  74. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_saas.py +0 -0
  75. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_saas_billing.py +0 -0
  76. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_server.py +0 -0
  77. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_settings.py +0 -0
  78. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_shield.py +0 -0
  79. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_slack.py +0 -0
  80. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_sso.py +0 -0
  81. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_store_connections.py +0 -0
  82. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_team.py +0 -0
  83. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_triage.py +0 -0
  84. {phpwarden-2.1.1 → phpwarden-2.2.1}/tests/test_webhook_idempotency.py +0 -0
  85. {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.1.1
4
- Summary: PHP vulnerability scanner for Composer, WordPress and the PHP engine — findings ranked by real-world exploitation (CISA KEV, then EPSS), not CVSS. Offline, zero deps.
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, 41 are known to be
27
- exploited (CISA KEV, as of 2026-11-13). phpwarden puts those first, then sorts
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 # single file, no install
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 and
144
- alerts when a finding you already carry lands in KEV:
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: main # pin to a tag with `pre-commit autoupdate`
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 (currently 2026.10.31.2) works forever, offline:
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,400+ carry an
207
- EPSS score; 42 were in KEV when this feed was built.
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
- A [phpwarden.io](https://phpwarden.io) subscription unlocks the maintained
217
- channel — new advisories and KEV/EPSS refreshes as they are triaged — via
218
- `PHPWARDEN_LICENSE=... phpwarden feed update`. Without a key, phpwarden runs
219
- the community feed and says so.
220
-
221
- Curated engine CVEs with detection signatures:
222
-
223
- | CVE | What | Scope |
224
- |-----|------|-------|
225
- | CVE-2019-11043 | PHP-FPM env_path_info RCE | FPM + nginx, config-gated |
226
- | CVE-2023-3824 | PHAR stack overflow RCE | any SAPI, untrusted PHAR |
227
- | CVE-2024-4577 | PHP-CGI argument injection | Windows + CGI |
228
- | CVE-2024-8926 / 8927 | cgi.force_redirect bypass | CGI / FPM |
229
- | CVE-2024-11236 | DB-quoter integer overflow | any SAPI |
230
- | CVE-2026-109618 / 109619 | php2shell FPM memory RCE | any SAPI |
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 the guard's DESIGN document.
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, 41 are known to be
5
- exploited (CISA KEV, as of 2026-11-13). phpwarden puts those first, then sorts
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 # single file, no install
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 and
122
- alerts when a finding you already carry lands in KEV:
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: main # pin to a tag with `pre-commit autoupdate`
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 (currently 2026.10.31.2) works forever, offline:
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,400+ carry an
185
- EPSS score; 42 were in KEV when this feed was built.
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
- A [phpwarden.io](https://phpwarden.io) subscription unlocks the maintained
195
- channel — new advisories and KEV/EPSS refreshes as they are triaged — via
196
- `PHPWARDEN_LICENSE=... phpwarden feed update`. Without a key, phpwarden runs
197
- the community feed and says so.
198
-
199
- Curated engine CVEs with detection signatures:
200
-
201
- | CVE | What | Scope |
202
- |-----|------|-------|
203
- | CVE-2019-11043 | PHP-FPM env_path_info RCE | FPM + nginx, config-gated |
204
- | CVE-2023-3824 | PHAR stack overflow RCE | any SAPI, untrusted PHAR |
205
- | CVE-2024-4577 | PHP-CGI argument injection | Windows + CGI |
206
- | CVE-2024-8926 / 8927 | cgi.force_redirect bypass | CGI / FPM |
207
- | CVE-2024-11236 | DB-quoter integer overflow | any SAPI |
208
- | CVE-2026-109618 / 109619 | php2shell FPM memory RCE | any SAPI |
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 the guard's DESIGN document.
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.1.1"
8
- description = "PHP vulnerability scanner for Composer, WordPress and the PHP engine — findings ranked by real-world exploitation (CISA KEV, then EPSS), not CVSS. Offline, zero deps."
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"