phpwarden 2.3.0__tar.gz → 2.4.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.3.0/src/phpwarden.egg-info → phpwarden-2.4.0}/PKG-INFO +31 -12
- phpwarden-2.3.0/PKG-INFO → phpwarden-2.4.0/README.md +28 -31
- {phpwarden-2.3.0 → phpwarden-2.4.0}/pyproject.toml +4 -4
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/__init__.py +1 -1
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/cli.py +71 -2
- phpwarden-2.4.0/src/phpwarden/sast/dbflow.py +720 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/sast/engine.py +11 -4
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/sast/taint.py +50 -3
- phpwarden-2.4.0/src/phpwarden/shield/EDGE.md +108 -0
- phpwarden-2.4.0/src/phpwarden/shield/README.md +111 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/shield/agent/phpwarden-shield.php +237 -7
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/shield/policy.py +56 -10
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/shield/report.py +19 -3
- phpwarden-2.4.0/src/phpwarden/thirdparty/__init__.py +1 -0
- phpwarden-2.4.0/src/phpwarden/thirdparty/trivy.py +188 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/verdict.py +56 -4
- phpwarden-2.3.0/README.md → phpwarden-2.4.0/src/phpwarden.egg-info/PKG-INFO +50 -9
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden.egg-info/SOURCES.txt +7 -0
- phpwarden-2.4.0/tests/test_sast_dbflow.py +106 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_shield_dashboard.py +9 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_shield_runtime.py +155 -0
- phpwarden-2.4.0/tests/test_trivy_import.py +131 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/LICENSE +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/setup.cfg +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/code.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/code_rules.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/data/php-cve-feed.json +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/deps.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/detect.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/engine.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/env.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/feed.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/guard.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/integrity.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/probe.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/report.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/rules.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/sarif.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/sast/__init__.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/sast/ast.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/sast/authz.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/sast/callgraph.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/sast/lexer.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/sast/models/__init__.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/sast/models/laravel.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/sast/models/php_builtins.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/sast/models/symfony.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/sast/models/wordpress.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/sast/parser.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/sast/project.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/sast/symbols.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/sast/types.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/shield/__init__.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/shield/library.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/shield/matcher.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/version.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/vulndb.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden/wordpress.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden.egg-info/dependency_links.txt +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden.egg-info/entry_points.txt +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/src/phpwarden.egg-info/top_level.txt +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_account_deletion.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_accounts.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_advisories.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_agency.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_assess.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_assignment.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_audit.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_billing.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_business_metrics.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_cli_edges.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_code.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_connect_flow.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_dashboard_states.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_deps_and_rules.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_detection_validation.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_developer.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_discussion.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_e2e_readiness.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_email.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_engine.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_engine_feed.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_feed_license.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_feed_subscription.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_fleet.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_free_feed.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_google_oauth.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_guard_live.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_integrity.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_kev_alert.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_kev_gaps.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_livefeed.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_mentions.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_probe.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_probe_cli.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_probe_dashboard.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_reachability.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_report_proof.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_saas.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_saas_billing.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_sarif.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_sast_layer5.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_sast_models.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_sast_parser.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_sast_project.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_sast_taint.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_server.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_settings.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_shield.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_shield_cli.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_shield_plan.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_slack.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_sso.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_store_connections.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_team.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_triage.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_waf_dryrun.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_webhook_idempotency.py +0 -0
- {phpwarden-2.3.0 → phpwarden-2.4.0}/tests/test_wordpress_and_report.py +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: phpwarden
|
|
3
|
-
Version: 2.
|
|
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.
|
|
3
|
+
Version: 2.4.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; blocks exploits inside PHP (shield). Offline, zero dependencies.
|
|
5
5
|
Author: oroma-alfred
|
|
6
6
|
License-Expression: Apache-2.0
|
|
7
7
|
Project-URL: Homepage, https://phpwarden.io
|
|
8
8
|
Project-URL: Source, https://github.com/oroma-alfred/phpwarden
|
|
9
9
|
Project-URL: Issues, https://github.com/oroma-alfred/phpwarden/issues
|
|
10
|
-
Keywords: php,composer,wordpress,php-fpm,security,vulnerability,cve,kev,epss,sca,scanner
|
|
10
|
+
Keywords: php,composer,wordpress,php-fpm,security,vulnerability,cve,kev,epss,sca,sast,waf,scanner
|
|
11
11
|
Classifier: Development Status :: 4 - Beta
|
|
12
12
|
Classifier: Environment :: Console
|
|
13
13
|
Classifier: Intended Audience :: System Administrators
|
|
@@ -28,8 +28,9 @@ 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
|
|
|
31
|
-
It scans the PHP engine/FPM version, Composer dependencies
|
|
32
|
-
|
|
31
|
+
It scans the PHP engine/FPM version, Composer dependencies, WordPress installs
|
|
32
|
+
and your own PHP code, checks live sites from outside, and can block exploits
|
|
33
|
+
inside PHP, against one bundled, offline advisory feed. Zero runtime dependencies
|
|
33
34
|
(Python 3.8+), Apache-2.0.
|
|
34
35
|
|
|
35
36
|
**Reproducible benchmark:** on four real PHP stacks, phpwarden found 295 of 295
|
|
@@ -144,7 +145,26 @@ error, `5` `--report-to` upload failed on an otherwise clean run, `64` usage
|
|
|
144
145
|
error. Full table: `phpwarden --help`.
|
|
145
146
|
|
|
146
147
|
|
|
147
|
-
## New in 2.3.0 and 2.2
|
|
148
|
+
## New in 2.4.0, 2.3.0 and 2.2
|
|
149
|
+
|
|
150
|
+
- **2.4.0: `phpwarden code` follows data through the database and callbacks.**
|
|
151
|
+
Request data saved with raw SQL, `$wpdb`, Eloquent, the query builder or
|
|
152
|
+
Doctrine and read back from the same table is traced to where it is printed
|
|
153
|
+
or queried (stored XSS, second-order SQL injection), and callbacks passed to
|
|
154
|
+
`array_map`, `usort`, `preg_replace_callback`, `add_filter` and the like,
|
|
155
|
+
`$f()` and first-class callables are followed. DVWA: 8/8/7 of 8 types under
|
|
156
|
+
the comparison's scorer (was 7/7/6); clean-code counts unchanged.
|
|
157
|
+
- **2.4.0: shield rate limits.** Per-client limits on WordPress login, password
|
|
158
|
+
reset, xmlrpc and `system.multicall`, Laravel login and reset, component
|
|
159
|
+
writes, and a burst guard. Log-only until promoted, then 429; APCu or file
|
|
160
|
+
store; fails open. `--trust-proxy CIDR`, `--no-limits`, `--limit-path REGEX`.
|
|
161
|
+
0 trips on 6,000 ordinary requests; 6 of 6 abuse scenarios caught. Pairing
|
|
162
|
+
with Cloudflare or nginx: [EDGE.md](src/phpwarden/shield/EDGE.md).
|
|
163
|
+
- **2.4.0: `phpwarden trivy-import` and `assess --trivy FILE` / `--run-trivy`.**
|
|
164
|
+
Trivy's JSON (npm/JS, OS packages, images, IaC, secrets) in the same ranked
|
|
165
|
+
list as PHP findings: KEV, then EPSS (`--kev`, `--epss`), then severity.
|
|
166
|
+
Secrets by file and line only. Never downloads Trivy. See
|
|
167
|
+
[src/phpwarden/thirdparty/README.md](src/phpwarden/thirdparty/README.md).
|
|
148
168
|
|
|
149
169
|
- **`phpwarden probe URL`**: a live check of a running site using GET and HEAD
|
|
150
170
|
requests only. It looks for exposed files (`.env`, `.git`, backups), open debug
|
|
@@ -161,7 +181,6 @@ error. Full table: `phpwarden --help`.
|
|
|
161
181
|
Follows data stored in the session or WordPress options (second-order).
|
|
162
182
|
89 rules. On the same pinned apps it found more than Semgrep and Psalm with far
|
|
163
183
|
fewer findings on clean framework code ([comparison](https://github.com/oroma-alfred/phpwarden/tree/main/bench/sast-compare-2026-11)).
|
|
164
|
-
Data that round-trips through the database is not followed.
|
|
165
184
|
- **`phpwarden shield`**: one PHP file loaded before your app that blocks exploit
|
|
166
185
|
attempts against the exploited vulnerabilities this site actually has, scoped
|
|
167
186
|
to their entry points. Logs until you promote rules (7-day burn-in, explicit
|
|
@@ -248,7 +267,7 @@ see [phpwarden/laravel](https://packagist.org/packages/phpwarden/laravel).
|
|
|
248
267
|
```yaml
|
|
249
268
|
repos:
|
|
250
269
|
- repo: https://github.com/oroma-alfred/phpwarden
|
|
251
|
-
rev: v2.
|
|
270
|
+
rev: v2.4.0 # pin to a tag with `pre-commit autoupdate`
|
|
252
271
|
hooks:
|
|
253
272
|
- id: phpwarden-deps
|
|
254
273
|
# args: ["--no-reachability"] # faster: skip the reachability pass
|
|
@@ -259,14 +278,14 @@ timer recipes are in [`docs/recipes.md`](docs/recipes.md).
|
|
|
259
278
|
|
|
260
279
|
## The feed
|
|
261
280
|
|
|
262
|
-
The bundled community feed (2026.11.
|
|
281
|
+
The bundled community feed (2026.11.14.2 in this release) works forever, offline:
|
|
263
282
|
|
|
264
283
|
- **~21,300 advisories**: ~8,800 Composer (from [OSV.dev](https://osv.dev) and
|
|
265
284
|
[FriendsOfPHP](https://github.com/FriendsOfPHP/security-advisories)),
|
|
266
285
|
~12,300 WordPress (from [NVD](https://nvd.nist.gov): plugin, core and theme),
|
|
267
|
-
and
|
|
268
|
-
fixed versions, exposure conditions and detection signatures. About 19,
|
|
269
|
-
EPSS score;
|
|
286
|
+
and 146 PHP engine/FPM CVEs, including hand-curated entries with per-branch
|
|
287
|
+
fixed versions, exposure conditions and detection signatures. About 19,800 carry an
|
|
288
|
+
EPSS score; 54 distinct vulnerabilities were in KEV when this feed was built.
|
|
270
289
|
- **License-independent by construction.** Every source is open or
|
|
271
290
|
public-domain (OSV, NVD, FriendsOfPHP, php-src release notes, CISA KEV, FIRST EPSS). No commercial
|
|
272
291
|
vendor's feed is used, so coverage cannot be revoked or priced away.
|
|
@@ -1,25 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: phpwarden
|
|
3
|
-
Version: 2.3.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
|
-
Author: oroma-alfred
|
|
6
|
-
License-Expression: Apache-2.0
|
|
7
|
-
Project-URL: Homepage, https://phpwarden.io
|
|
8
|
-
Project-URL: Source, https://github.com/oroma-alfred/phpwarden
|
|
9
|
-
Project-URL: Issues, https://github.com/oroma-alfred/phpwarden/issues
|
|
10
|
-
Keywords: php,composer,wordpress,php-fpm,security,vulnerability,cve,kev,epss,sca,scanner
|
|
11
|
-
Classifier: Development Status :: 4 - Beta
|
|
12
|
-
Classifier: Environment :: Console
|
|
13
|
-
Classifier: Intended Audience :: System Administrators
|
|
14
|
-
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
-
Classifier: Topic :: Security
|
|
17
|
-
Classifier: Topic :: System :: Monitoring
|
|
18
|
-
Requires-Python: >=3.8
|
|
19
|
-
Description-Content-Type: text/markdown
|
|
20
|
-
License-File: LICENSE
|
|
21
|
-
Dynamic: license-file
|
|
22
|
-
|
|
23
1
|
# phpwarden
|
|
24
2
|
|
|
25
3
|
**PHP vulnerability scanner that ranks findings by real-world exploitation,
|
|
@@ -28,8 +6,9 @@ exploited (CISA KEV, as of 2026-11-14). phpwarden puts those first, then sorts
|
|
|
28
6
|
by EPSS probability, then CVSS — so a `composer.lock` with 30 findings tells
|
|
29
7
|
you which one to fix today.
|
|
30
8
|
|
|
31
|
-
It scans the PHP engine/FPM version, Composer dependencies
|
|
32
|
-
|
|
9
|
+
It scans the PHP engine/FPM version, Composer dependencies, WordPress installs
|
|
10
|
+
and your own PHP code, checks live sites from outside, and can block exploits
|
|
11
|
+
inside PHP, against one bundled, offline advisory feed. Zero runtime dependencies
|
|
33
12
|
(Python 3.8+), Apache-2.0.
|
|
34
13
|
|
|
35
14
|
**Reproducible benchmark:** on four real PHP stacks, phpwarden found 295 of 295
|
|
@@ -144,7 +123,26 @@ error, `5` `--report-to` upload failed on an otherwise clean run, `64` usage
|
|
|
144
123
|
error. Full table: `phpwarden --help`.
|
|
145
124
|
|
|
146
125
|
|
|
147
|
-
## New in 2.3.0 and 2.2
|
|
126
|
+
## New in 2.4.0, 2.3.0 and 2.2
|
|
127
|
+
|
|
128
|
+
- **2.4.0: `phpwarden code` follows data through the database and callbacks.**
|
|
129
|
+
Request data saved with raw SQL, `$wpdb`, Eloquent, the query builder or
|
|
130
|
+
Doctrine and read back from the same table is traced to where it is printed
|
|
131
|
+
or queried (stored XSS, second-order SQL injection), and callbacks passed to
|
|
132
|
+
`array_map`, `usort`, `preg_replace_callback`, `add_filter` and the like,
|
|
133
|
+
`$f()` and first-class callables are followed. DVWA: 8/8/7 of 8 types under
|
|
134
|
+
the comparison's scorer (was 7/7/6); clean-code counts unchanged.
|
|
135
|
+
- **2.4.0: shield rate limits.** Per-client limits on WordPress login, password
|
|
136
|
+
reset, xmlrpc and `system.multicall`, Laravel login and reset, component
|
|
137
|
+
writes, and a burst guard. Log-only until promoted, then 429; APCu or file
|
|
138
|
+
store; fails open. `--trust-proxy CIDR`, `--no-limits`, `--limit-path REGEX`.
|
|
139
|
+
0 trips on 6,000 ordinary requests; 6 of 6 abuse scenarios caught. Pairing
|
|
140
|
+
with Cloudflare or nginx: [EDGE.md](src/phpwarden/shield/EDGE.md).
|
|
141
|
+
- **2.4.0: `phpwarden trivy-import` and `assess --trivy FILE` / `--run-trivy`.**
|
|
142
|
+
Trivy's JSON (npm/JS, OS packages, images, IaC, secrets) in the same ranked
|
|
143
|
+
list as PHP findings: KEV, then EPSS (`--kev`, `--epss`), then severity.
|
|
144
|
+
Secrets by file and line only. Never downloads Trivy. See
|
|
145
|
+
[src/phpwarden/thirdparty/README.md](src/phpwarden/thirdparty/README.md).
|
|
148
146
|
|
|
149
147
|
- **`phpwarden probe URL`**: a live check of a running site using GET and HEAD
|
|
150
148
|
requests only. It looks for exposed files (`.env`, `.git`, backups), open debug
|
|
@@ -161,7 +159,6 @@ error. Full table: `phpwarden --help`.
|
|
|
161
159
|
Follows data stored in the session or WordPress options (second-order).
|
|
162
160
|
89 rules. On the same pinned apps it found more than Semgrep and Psalm with far
|
|
163
161
|
fewer findings on clean framework code ([comparison](https://github.com/oroma-alfred/phpwarden/tree/main/bench/sast-compare-2026-11)).
|
|
164
|
-
Data that round-trips through the database is not followed.
|
|
165
162
|
- **`phpwarden shield`**: one PHP file loaded before your app that blocks exploit
|
|
166
163
|
attempts against the exploited vulnerabilities this site actually has, scoped
|
|
167
164
|
to their entry points. Logs until you promote rules (7-day burn-in, explicit
|
|
@@ -248,7 +245,7 @@ see [phpwarden/laravel](https://packagist.org/packages/phpwarden/laravel).
|
|
|
248
245
|
```yaml
|
|
249
246
|
repos:
|
|
250
247
|
- repo: https://github.com/oroma-alfred/phpwarden
|
|
251
|
-
rev: v2.
|
|
248
|
+
rev: v2.4.0 # pin to a tag with `pre-commit autoupdate`
|
|
252
249
|
hooks:
|
|
253
250
|
- id: phpwarden-deps
|
|
254
251
|
# args: ["--no-reachability"] # faster: skip the reachability pass
|
|
@@ -259,14 +256,14 @@ timer recipes are in [`docs/recipes.md`](docs/recipes.md).
|
|
|
259
256
|
|
|
260
257
|
## The feed
|
|
261
258
|
|
|
262
|
-
The bundled community feed (2026.11.
|
|
259
|
+
The bundled community feed (2026.11.14.2 in this release) works forever, offline:
|
|
263
260
|
|
|
264
261
|
- **~21,300 advisories**: ~8,800 Composer (from [OSV.dev](https://osv.dev) and
|
|
265
262
|
[FriendsOfPHP](https://github.com/FriendsOfPHP/security-advisories)),
|
|
266
263
|
~12,300 WordPress (from [NVD](https://nvd.nist.gov): plugin, core and theme),
|
|
267
|
-
and
|
|
268
|
-
fixed versions, exposure conditions and detection signatures. About 19,
|
|
269
|
-
EPSS score;
|
|
264
|
+
and 146 PHP engine/FPM CVEs, including hand-curated entries with per-branch
|
|
265
|
+
fixed versions, exposure conditions and detection signatures. About 19,800 carry an
|
|
266
|
+
EPSS score; 54 distinct vulnerabilities were in KEV when this feed was built.
|
|
270
267
|
- **License-independent by construction.** Every source is open or
|
|
271
268
|
public-domain (OSV, NVD, FriendsOfPHP, php-src release notes, CISA KEV, FIRST EPSS). No commercial
|
|
272
269
|
vendor's feed is used, so coverage cannot be revoked or priced away.
|
|
@@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "phpwarden"
|
|
7
|
-
version = "2.
|
|
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."
|
|
7
|
+
version = "2.4.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; blocks exploits inside PHP (shield). Offline, zero dependencies."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
11
11
|
license = "Apache-2.0"
|
|
12
12
|
license-files = ["LICENSE"]
|
|
13
13
|
authors = [{ name = "oroma-alfred" }]
|
|
14
|
-
keywords = ["php", "composer", "wordpress", "php-fpm", "security", "vulnerability", "cve", "kev", "epss", "sca", "scanner"]
|
|
14
|
+
keywords = ["php", "composer", "wordpress", "php-fpm", "security", "vulnerability", "cve", "kev", "epss", "sca", "sast", "waf", "scanner"]
|
|
15
15
|
classifiers = [
|
|
16
16
|
"Development Status :: 4 - Beta",
|
|
17
17
|
"Environment :: Console",
|
|
@@ -39,4 +39,4 @@ package-dir = { "" = "src" }
|
|
|
39
39
|
where = ["src"]
|
|
40
40
|
|
|
41
41
|
[tool.setuptools.package-data]
|
|
42
|
-
phpwarden = ["data/*.json", "shield/agent/*.php"]
|
|
42
|
+
phpwarden = ["data/*.json", "shield/agent/*.php", "shield/*.md"]
|
|
@@ -6,4 +6,4 @@ real-world exploitation (CISA KEV, then EPSS, then CVSS), and can watch PHP-FPM
|
|
|
6
6
|
for active exploitation. The engine and the bundled feed are open; free users
|
|
7
7
|
get weekly feed updates, and a subscription adds the feed rebuilt twice a day.
|
|
8
8
|
"""
|
|
9
|
-
__version__ = "2.
|
|
9
|
+
__version__ = "2.4.0"
|
|
@@ -546,8 +546,29 @@ def _assess(args):
|
|
|
546
546
|
if any("homepage unreachable" in n for n in probe_result.get("notes", [])):
|
|
547
547
|
sys.stderr.write(f"error: could not reach {url}\n")
|
|
548
548
|
return 4
|
|
549
|
+
trivy_fs, trivy_note = None, None
|
|
550
|
+
if args.trivy or args.run_trivy:
|
|
551
|
+
from phpwarden.thirdparty import trivy as tv
|
|
552
|
+
try:
|
|
553
|
+
kev, epss = tv.load_kev(args.kev), tv.load_epss(args.epss)
|
|
554
|
+
docs = [tv.load_report(f) for f in (args.trivy or [])]
|
|
555
|
+
except (OSError, ValueError) as e:
|
|
556
|
+
sys.stderr.write(f"error: {e}\n")
|
|
557
|
+
return 4
|
|
558
|
+
if args.run_trivy and args.path:
|
|
559
|
+
doc, msg = tv.run_trivy_fs(args.path)
|
|
560
|
+
if doc is not None:
|
|
561
|
+
docs.append(doc)
|
|
562
|
+
else:
|
|
563
|
+
trivy_note = msg
|
|
564
|
+
sys.stderr.write(msg + "\n")
|
|
565
|
+
trivy_fs = [f for d in docs for f in tv.findings(d, kev, epss)]
|
|
566
|
+
if docs and not (args.kev or args.epss):
|
|
567
|
+
trivy_note = (trivy_note + "; " if trivy_note else "") + \
|
|
568
|
+
"non-PHP findings ranked by severity only (pass --kev and --epss for exploitation data)"
|
|
549
569
|
try:
|
|
550
|
-
res = vd.assess(args.path, probe_result=probe_result, logfile=args.log, run_code=not args.no_code
|
|
570
|
+
res = vd.assess(args.path, probe_result=probe_result, logfile=args.log, run_code=not args.no_code,
|
|
571
|
+
trivy_findings=trivy_fs, trivy_note=trivy_note)
|
|
551
572
|
except OSError as e: # unreadable access log
|
|
552
573
|
sys.stderr.write(f"error: {e}\n")
|
|
553
574
|
return 4
|
|
@@ -563,6 +584,31 @@ def _assess(args):
|
|
|
563
584
|
return vd.exit_code(res)
|
|
564
585
|
|
|
565
586
|
|
|
587
|
+
def _trivy_import(args):
|
|
588
|
+
from phpwarden.thirdparty import trivy as tv
|
|
589
|
+
try:
|
|
590
|
+
kev, epss = tv.load_kev(args.kev), tv.load_epss(args.epss)
|
|
591
|
+
fs = [f for p in args.files for f in tv.findings(tv.load_report(p), kev, epss)]
|
|
592
|
+
except (OSError, ValueError) as e:
|
|
593
|
+
sys.stderr.write(f"error: {e}\n")
|
|
594
|
+
return 4
|
|
595
|
+
doc = tv.to_report_doc(fs, args.files)
|
|
596
|
+
if args.json:
|
|
597
|
+
print(json.dumps(doc, indent=2))
|
|
598
|
+
else:
|
|
599
|
+
kev_n = sum(1 for f in fs if f["known_exploited"])
|
|
600
|
+
print(f"{len(fs)} finding(s) from {len(args.files)} Trivy report(s); {kev_n} known exploited. "
|
|
601
|
+
f"Ranked by exploitation{'' if (args.kev or args.epss) else ' (no --kev/--epss: by severity)'}:")
|
|
602
|
+
for f in fs[:50]:
|
|
603
|
+
tag = " [KEV]" if f["known_exploited"] else (f" [EPSS {f['epss'] * 100:.0f}%]" if f.get("epss") else "")
|
|
604
|
+
dev = " (dev)" if f["dev"] else ""
|
|
605
|
+
print(f" {f['severity']:<8} {tv.ident(f)}{tag}{dev} {f['title'][:90]}")
|
|
606
|
+
if len(fs) > 50:
|
|
607
|
+
print(f" ... and {len(fs) - 50} more; use --json for all")
|
|
608
|
+
rc = 1 if any(f["known_exploited"] or f["severity"] in ("critical", "high") for f in fs) else 0
|
|
609
|
+
return _combine(rc, _maybe_report(args, doc))
|
|
610
|
+
|
|
611
|
+
|
|
566
612
|
def _shield(args):
|
|
567
613
|
from pathlib import Path as _P
|
|
568
614
|
from phpwarden.shield import policy as sp, report as sr
|
|
@@ -572,7 +618,9 @@ def _shield(args):
|
|
|
572
618
|
sys.stderr.write(f"error: not a directory: {args.path}\n")
|
|
573
619
|
return 4
|
|
574
620
|
pol = sp.build_policy_for_path(str(root), FeedManager().current(), php_version=args.php_version,
|
|
575
|
-
php_sapi=args.php_sapi, log_path=args.log
|
|
621
|
+
php_sapi=args.php_sapi, log_path=args.log,
|
|
622
|
+
limits=not args.no_limits, limit_paths=args.limit_path or [],
|
|
623
|
+
trust_proxy=args.trust_proxy or [])
|
|
576
624
|
out = args.out or "phpwarden-shield.policy.json"
|
|
577
625
|
j, php = sp.write_policy(pol, out)
|
|
578
626
|
if args.json:
|
|
@@ -754,6 +802,15 @@ def main(argv=None):
|
|
|
754
802
|
_report_args(wpc)
|
|
755
803
|
wpc.set_defaults(func=_wordpress)
|
|
756
804
|
|
|
805
|
+
tvi = sub.add_parser("trivy-import", help="read Trivy JSON reports (npm/JS, OS packages, images, IaC), "
|
|
806
|
+
"rank them by exploitation, optionally send them to the dashboard")
|
|
807
|
+
tvi.add_argument("files", nargs="+", metavar="TRIVY_JSON", help="output of trivy fs/image/config --format json")
|
|
808
|
+
tvi.add_argument("--kev", metavar="FILE", help="CISA KEV catalogue JSON")
|
|
809
|
+
tvi.add_argument("--epss", metavar="FILE", help="FIRST EPSS CSV (.csv or .csv.gz)")
|
|
810
|
+
tvi.add_argument("--json", action="store_true", help="machine-readable JSON output")
|
|
811
|
+
_report_args(tvi)
|
|
812
|
+
tvi.set_defaults(func=_trivy_import)
|
|
813
|
+
|
|
757
814
|
sh = sub.add_parser("shield", help="protection inside PHP: block exploits of the vulnerabilities this site "
|
|
758
815
|
"actually has (log-only until you promote rules)")
|
|
759
816
|
shs = sh.add_subparsers(dest="shield_cmd", required=True, parser_class=_Parser)
|
|
@@ -763,6 +820,12 @@ def main(argv=None):
|
|
|
763
820
|
shp.add_argument("--php-version", help="the production PHP version, for engine CVE rules")
|
|
764
821
|
shp.add_argument("--php-sapi", choices=["fpm", "cgi", "cli"], help="the production SAPI")
|
|
765
822
|
shp.add_argument("--log", help="where the agent should write its log (default: next to the agent)")
|
|
823
|
+
shp.add_argument("--no-limits", action="store_true",
|
|
824
|
+
help="leave out the rate limits (login, password reset, xmlrpc, burst guard)")
|
|
825
|
+
shp.add_argument("--limit-path", action="append", metavar="REGEX",
|
|
826
|
+
help="also rate-limit requests to this path (repeatable)")
|
|
827
|
+
shp.add_argument("--trust-proxy", action="append", metavar="CIDR",
|
|
828
|
+
help="trust forwarded client IPs only from this proxy range, e.g. your CDN (repeatable)")
|
|
766
829
|
shp.add_argument("--json", action="store_true", help="print the policy as JSON")
|
|
767
830
|
shi = shs.add_parser("install", help="show how to load the agent (and copy it with --dest)")
|
|
768
831
|
shi.add_argument("--dest", metavar="DIR", help="copy phpwarden-shield.php into this folder")
|
|
@@ -789,6 +852,12 @@ def main(argv=None):
|
|
|
789
852
|
asx.add_argument("--baseline", metavar="FILE",
|
|
790
853
|
help="an earlier --out file: report what is fixed and verified, still open, and new")
|
|
791
854
|
asx.add_argument("--json", action="store_true", help="machine-readable JSON output")
|
|
855
|
+
asx.add_argument("--trivy", metavar="FILE", action="append",
|
|
856
|
+
help="add a Trivy JSON report (npm/JS, OS packages, images, IaC); repeatable")
|
|
857
|
+
asx.add_argument("--run-trivy", action="store_true",
|
|
858
|
+
help="run `trivy fs` on the path if Trivy is installed (never downloads it)")
|
|
859
|
+
asx.add_argument("--kev", metavar="FILE", help="CISA KEV catalogue JSON, to rank non-PHP CVEs by exploitation")
|
|
860
|
+
asx.add_argument("--epss", metavar="FILE", help="FIRST EPSS CSV (.csv or .csv.gz) for non-PHP CVEs")
|
|
792
861
|
asx.set_defaults(func=_assess)
|
|
793
862
|
|
|
794
863
|
cd = sub.add_parser("code", help="scan your own PHP code for high-signal security bugs "
|