phpwarden 2.2.0__tar.gz → 2.3.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.2.0 → phpwarden-2.3.0}/PKG-INFO +23 -10
- {phpwarden-2.2.0 → phpwarden-2.3.0}/README.md +22 -9
- {phpwarden-2.2.0 → phpwarden-2.3.0}/pyproject.toml +2 -2
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden/__init__.py +1 -1
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden/cli.py +103 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden/code.py +628 -62
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden/code_rules.py +73 -3
- phpwarden-2.3.0/src/phpwarden/sast/__init__.py +10 -0
- phpwarden-2.3.0/src/phpwarden/sast/ast.py +85 -0
- phpwarden-2.3.0/src/phpwarden/sast/authz.py +284 -0
- phpwarden-2.3.0/src/phpwarden/sast/callgraph.py +627 -0
- phpwarden-2.3.0/src/phpwarden/sast/engine.py +131 -0
- phpwarden-2.3.0/src/phpwarden/sast/lexer.py +354 -0
- phpwarden-2.3.0/src/phpwarden/sast/models/__init__.py +6 -0
- phpwarden-2.3.0/src/phpwarden/sast/models/laravel.py +115 -0
- phpwarden-2.3.0/src/phpwarden/sast/models/php_builtins.py +123 -0
- phpwarden-2.3.0/src/phpwarden/sast/models/symfony.py +77 -0
- phpwarden-2.3.0/src/phpwarden/sast/models/wordpress.py +28 -0
- phpwarden-2.3.0/src/phpwarden/sast/parser.py +1565 -0
- phpwarden-2.3.0/src/phpwarden/sast/project.py +633 -0
- phpwarden-2.3.0/src/phpwarden/sast/symbols.py +184 -0
- phpwarden-2.3.0/src/phpwarden/sast/taint.py +2437 -0
- phpwarden-2.3.0/src/phpwarden/sast/types.py +432 -0
- phpwarden-2.3.0/src/phpwarden/shield/__init__.py +1 -0
- phpwarden-2.3.0/src/phpwarden/shield/agent/phpwarden-shield.php +342 -0
- phpwarden-2.3.0/src/phpwarden/shield/library.py +403 -0
- phpwarden-2.3.0/src/phpwarden/shield/matcher.py +119 -0
- phpwarden-2.3.0/src/phpwarden/shield/policy.py +208 -0
- phpwarden-2.3.0/src/phpwarden/shield/report.py +73 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden.egg-info/PKG-INFO +23 -10
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden.egg-info/SOURCES.txt +31 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_code.py +87 -4
- phpwarden-2.3.0/tests/test_report_proof.py +53 -0
- phpwarden-2.3.0/tests/test_sast_layer5.py +82 -0
- phpwarden-2.3.0/tests/test_sast_models.py +85 -0
- phpwarden-2.3.0/tests/test_sast_parser.py +237 -0
- phpwarden-2.3.0/tests/test_sast_project.py +134 -0
- phpwarden-2.3.0/tests/test_sast_taint.py +223 -0
- phpwarden-2.3.0/tests/test_shield_cli.py +51 -0
- phpwarden-2.3.0/tests/test_shield_dashboard.py +159 -0
- phpwarden-2.3.0/tests/test_shield_runtime.py +225 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/LICENSE +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/setup.cfg +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden/data/php-cve-feed.json +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden/deps.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden/detect.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden/engine.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden/env.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden/feed.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden/guard.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden/integrity.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden/probe.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden/report.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden/rules.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden/sarif.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden/verdict.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden/version.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden/vulndb.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden/wordpress.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden.egg-info/dependency_links.txt +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden.egg-info/entry_points.txt +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/src/phpwarden.egg-info/top_level.txt +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_account_deletion.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_accounts.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_advisories.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_agency.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_assess.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_assignment.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_audit.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_billing.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_business_metrics.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_cli_edges.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_connect_flow.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_dashboard_states.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_deps_and_rules.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_detection_validation.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_developer.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_discussion.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_e2e_readiness.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_email.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_engine.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_engine_feed.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_feed_license.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_feed_subscription.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_fleet.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_free_feed.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_google_oauth.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_guard_live.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_integrity.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_kev_alert.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_kev_gaps.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_livefeed.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_mentions.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_probe.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_probe_cli.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_probe_dashboard.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_reachability.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_saas.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_saas_billing.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_sarif.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_server.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_settings.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_shield.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_shield_plan.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_slack.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_sso.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_store_connections.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_team.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_triage.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_waf_dryrun.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_webhook_idempotency.py +0 -0
- {phpwarden-2.2.0 → phpwarden-2.3.0}/tests/test_wordpress_and_report.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: phpwarden
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
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
|
|
@@ -59,7 +59,8 @@ $ 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 (
|
|
62
|
+
| Your own PHP code: whole-program, Laravel/Symfony/WordPress-aware (89 rules) | `code` | Snyk Code, Semgrep, Psalm taint |
|
|
63
|
+
| Exploits of the vulnerabilities this site has, blocked inside PHP | `shield` | the WAF rule you would have to write |
|
|
63
64
|
| Live site from outside: exposed files, debug endpoints | `probe` | — |
|
|
64
65
|
| One ranked verdict across all of the above | `assess` | — |
|
|
65
66
|
| Exploitation signatures in logs | `detect` | SIEM content |
|
|
@@ -120,7 +121,8 @@ phpwarden rules --format nginx --out php-block.conf # PHP-specific WAF rules (
|
|
|
120
121
|
phpwarden rules --format nginx --dry-run # the same rules, log-only: nothing is blocked
|
|
121
122
|
phpwarden rules --replay /var/log/nginx/access.log # what the rules would have blocked in an existing log
|
|
122
123
|
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
|
|
124
|
+
phpwarden code . # your own PHP, whole program: 89 rules (injection, auth, mass assignment, Blade/Twig, WordPress)
|
|
125
|
+
phpwarden shield policy . && phpwarden shield install # block exploits of what is installed here, inside PHP (log-only first)
|
|
124
126
|
phpwarden assess . --url https://example.com --log access.log # one ranked verdict; --out / --baseline shows what is fixed and verified
|
|
125
127
|
phpwarden report web01.json web02.json --out fleet.html # aggregate a fleet view
|
|
126
128
|
phpwarden feed status # which feed is active, how many advisories
|
|
@@ -133,7 +135,7 @@ phpwarden feed update # pull a newer feed: free weekly snapshot,
|
|
|
133
135
|
`probe`, `code`, `assess`, `--format sarif`, `rules --dry-run` / `--replay` and
|
|
134
136
|
the no-key `feed update` are new in 2.2.0.
|
|
135
137
|
|
|
136
|
-
Every command takes `--json
|
|
138
|
+
Every scanning command takes `--json` (`shield install` and `shield promote` do not). Exit codes are shared across subcommands and
|
|
137
139
|
built for gating: `0` clean, `1` exposed / vulnerable / signature hits / tampered
|
|
138
140
|
files / high or critical `code` and `probe` findings (`probe` also on any
|
|
139
141
|
known-exploited finding) / anything to act on now or this week (`assess`),
|
|
@@ -142,7 +144,7 @@ error, `5` `--report-to` upload failed on an otherwise clean run, `64` usage
|
|
|
142
144
|
error. Full table: `phpwarden --help`.
|
|
143
145
|
|
|
144
146
|
|
|
145
|
-
## New in 2.2.
|
|
147
|
+
## New in 2.3.0 and 2.2
|
|
146
148
|
|
|
147
149
|
- **`phpwarden probe URL`**: a live check of a running site using GET and HEAD
|
|
148
150
|
requests only. It looks for exposed files (`.env`, `.git`, backups), open debug
|
|
@@ -150,10 +152,21 @@ error. Full table: `phpwarden --help`.
|
|
|
150
152
|
matches those against the feed. Active checks run only after you prove the
|
|
151
153
|
site is yours (DNS TXT record, a file, or a meta tag; `--print-token` shows
|
|
152
154
|
how). Without that it reads the homepage and stops.
|
|
153
|
-
- **`phpwarden code PATH
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
155
|
+
- **`phpwarden code PATH`** (whole-program engine, default in 2.3.0): parses the
|
|
156
|
+
project, resolves namespaces, Composer autoloading, inheritance and types, and
|
|
157
|
+
follows request data across files to SQL, shell, `eval`, `unserialize`,
|
|
158
|
+
includes, redirects and HTTP clients, with a trace per finding (SARIF
|
|
159
|
+
`codeFlows`). Models Laravel, Symfony and WordPress: routes with no auth check,
|
|
160
|
+
mass assignment, raw Blade/Twig output, IDOR hints, CSRF exclusions, nonces.
|
|
161
|
+
Follows data stored in the session or WordPress options (second-order).
|
|
162
|
+
89 rules. On the same pinned apps it found more than Semgrep and Psalm with far
|
|
163
|
+
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
|
+
- **`phpwarden shield`**: one PHP file loaded before your app that blocks exploit
|
|
166
|
+
attempts against the exploited vulnerabilities this site actually has, scoped
|
|
167
|
+
to their entry points. Logs until you promote rules (7-day burn-in, explicit
|
|
168
|
+
flag), fails open, has a kill switch, about 0.07 ms per request. Not an edge
|
|
169
|
+
WAF: no DDoS, bot or TLS handling.
|
|
157
170
|
- **`phpwarden assess`**: one ranked list from every view of a site: installed
|
|
158
171
|
versions, your code, the live site from outside, and the access log, with the
|
|
159
172
|
reason for each item ("exploited, reachable, visible from outside, already
|
|
@@ -235,7 +248,7 @@ see [phpwarden/laravel](https://packagist.org/packages/phpwarden/laravel).
|
|
|
235
248
|
```yaml
|
|
236
249
|
repos:
|
|
237
250
|
- repo: https://github.com/oroma-alfred/phpwarden
|
|
238
|
-
rev: v2.
|
|
251
|
+
rev: v2.3.0 # pin to a tag with `pre-commit autoupdate`
|
|
239
252
|
hooks:
|
|
240
253
|
- id: phpwarden-deps
|
|
241
254
|
# args: ["--no-reachability"] # faster: skip the reachability pass
|
|
@@ -37,7 +37,8 @@ $ 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 (
|
|
40
|
+
| Your own PHP code: whole-program, Laravel/Symfony/WordPress-aware (89 rules) | `code` | Snyk Code, Semgrep, Psalm taint |
|
|
41
|
+
| Exploits of the vulnerabilities this site has, blocked inside PHP | `shield` | the WAF rule you would have to write |
|
|
41
42
|
| Live site from outside: exposed files, debug endpoints | `probe` | — |
|
|
42
43
|
| One ranked verdict across all of the above | `assess` | — |
|
|
43
44
|
| Exploitation signatures in logs | `detect` | SIEM content |
|
|
@@ -98,7 +99,8 @@ phpwarden rules --format nginx --out php-block.conf # PHP-specific WAF rules (
|
|
|
98
99
|
phpwarden rules --format nginx --dry-run # the same rules, log-only: nothing is blocked
|
|
99
100
|
phpwarden rules --replay /var/log/nginx/access.log # what the rules would have blocked in an existing log
|
|
100
101
|
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
|
|
102
|
+
phpwarden code . # your own PHP, whole program: 89 rules (injection, auth, mass assignment, Blade/Twig, WordPress)
|
|
103
|
+
phpwarden shield policy . && phpwarden shield install # block exploits of what is installed here, inside PHP (log-only first)
|
|
102
104
|
phpwarden assess . --url https://example.com --log access.log # one ranked verdict; --out / --baseline shows what is fixed and verified
|
|
103
105
|
phpwarden report web01.json web02.json --out fleet.html # aggregate a fleet view
|
|
104
106
|
phpwarden feed status # which feed is active, how many advisories
|
|
@@ -111,7 +113,7 @@ phpwarden feed update # pull a newer feed: free weekly snapshot,
|
|
|
111
113
|
`probe`, `code`, `assess`, `--format sarif`, `rules --dry-run` / `--replay` and
|
|
112
114
|
the no-key `feed update` are new in 2.2.0.
|
|
113
115
|
|
|
114
|
-
Every command takes `--json
|
|
116
|
+
Every scanning command takes `--json` (`shield install` and `shield promote` do not). Exit codes are shared across subcommands and
|
|
115
117
|
built for gating: `0` clean, `1` exposed / vulnerable / signature hits / tampered
|
|
116
118
|
files / high or critical `code` and `probe` findings (`probe` also on any
|
|
117
119
|
known-exploited finding) / anything to act on now or this week (`assess`),
|
|
@@ -120,7 +122,7 @@ error, `5` `--report-to` upload failed on an otherwise clean run, `64` usage
|
|
|
120
122
|
error. Full table: `phpwarden --help`.
|
|
121
123
|
|
|
122
124
|
|
|
123
|
-
## New in 2.2.
|
|
125
|
+
## New in 2.3.0 and 2.2
|
|
124
126
|
|
|
125
127
|
- **`phpwarden probe URL`**: a live check of a running site using GET and HEAD
|
|
126
128
|
requests only. It looks for exposed files (`.env`, `.git`, backups), open debug
|
|
@@ -128,10 +130,21 @@ error. Full table: `phpwarden --help`.
|
|
|
128
130
|
matches those against the feed. Active checks run only after you prove the
|
|
129
131
|
site is yours (DNS TXT record, a file, or a meta tag; `--print-token` shows
|
|
130
132
|
how). Without that it reads the homepage and stops.
|
|
131
|
-
- **`phpwarden code PATH
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
- **`phpwarden code PATH`** (whole-program engine, default in 2.3.0): parses the
|
|
134
|
+
project, resolves namespaces, Composer autoloading, inheritance and types, and
|
|
135
|
+
follows request data across files to SQL, shell, `eval`, `unserialize`,
|
|
136
|
+
includes, redirects and HTTP clients, with a trace per finding (SARIF
|
|
137
|
+
`codeFlows`). Models Laravel, Symfony and WordPress: routes with no auth check,
|
|
138
|
+
mass assignment, raw Blade/Twig output, IDOR hints, CSRF exclusions, nonces.
|
|
139
|
+
Follows data stored in the session or WordPress options (second-order).
|
|
140
|
+
89 rules. On the same pinned apps it found more than Semgrep and Psalm with far
|
|
141
|
+
fewer findings on clean framework code ([comparison](https://github.com/oroma-alfred/phpwarden/tree/main/bench/sast-compare-2026-11)).
|
|
142
|
+
Data that round-trips through the database is not followed.
|
|
143
|
+
- **`phpwarden shield`**: one PHP file loaded before your app that blocks exploit
|
|
144
|
+
attempts against the exploited vulnerabilities this site actually has, scoped
|
|
145
|
+
to their entry points. Logs until you promote rules (7-day burn-in, explicit
|
|
146
|
+
flag), fails open, has a kill switch, about 0.07 ms per request. Not an edge
|
|
147
|
+
WAF: no DDoS, bot or TLS handling.
|
|
135
148
|
- **`phpwarden assess`**: one ranked list from every view of a site: installed
|
|
136
149
|
versions, your code, the live site from outside, and the access log, with the
|
|
137
150
|
reason for each item ("exploited, reachable, visible from outside, already
|
|
@@ -213,7 +226,7 @@ see [phpwarden/laravel](https://packagist.org/packages/phpwarden/laravel).
|
|
|
213
226
|
```yaml
|
|
214
227
|
repos:
|
|
215
228
|
- repo: https://github.com/oroma-alfred/phpwarden
|
|
216
|
-
rev: v2.
|
|
229
|
+
rev: v2.3.0 # pin to a tag with `pre-commit autoupdate`
|
|
217
230
|
hooks:
|
|
218
231
|
- id: phpwarden-deps
|
|
219
232
|
# args: ["--no-reachability"] # faster: skip the reachability pass
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "phpwarden"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.3.0"
|
|
8
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"
|
|
@@ -39,4 +39,4 @@ package-dir = { "" = "src" }
|
|
|
39
39
|
where = ["src"]
|
|
40
40
|
|
|
41
41
|
[tool.setuptools.package-data]
|
|
42
|
-
phpwarden = ["data/*.json"]
|
|
42
|
+
phpwarden = ["data/*.json", "shield/agent/*.php"]
|
|
@@ -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.3.0"
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
phpwarden probe URL safe live HTTP check of a site you own
|
|
14
14
|
phpwarden code PATH scan your own PHP code for high-signal security bugs
|
|
15
15
|
phpwarden assess PATH --url U --log L one ranked verdict from every view
|
|
16
|
+
phpwarden shield policy PATH protection inside PHP for the exploited vulns installed here
|
|
16
17
|
|
|
17
18
|
Exit codes (shared by every subcommand; usable as a CI gate):
|
|
18
19
|
0 nothing applies / clean
|
|
@@ -562,6 +563,85 @@ def _assess(args):
|
|
|
562
563
|
return vd.exit_code(res)
|
|
563
564
|
|
|
564
565
|
|
|
566
|
+
def _shield(args):
|
|
567
|
+
from pathlib import Path as _P
|
|
568
|
+
from phpwarden.shield import policy as sp, report as sr
|
|
569
|
+
if args.shield_cmd == "policy":
|
|
570
|
+
root = _P(args.path)
|
|
571
|
+
if not root.is_dir():
|
|
572
|
+
sys.stderr.write(f"error: not a directory: {args.path}\n")
|
|
573
|
+
return 4
|
|
574
|
+
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)
|
|
576
|
+
out = args.out or "phpwarden-shield.policy.json"
|
|
577
|
+
j, php = sp.write_policy(pol, out)
|
|
578
|
+
if args.json:
|
|
579
|
+
print(json.dumps(pol, indent=2))
|
|
580
|
+
return 0
|
|
581
|
+
print(f"wrote {j} and {php} ({len(pol['rules'])} rule(s), mode: log)")
|
|
582
|
+
for r in pol["rules"]:
|
|
583
|
+
print(f" + {r['id']:<34} {r.get('cve') or '':<16} {r['component']}: {r['why']}")
|
|
584
|
+
if not pol["rules"]:
|
|
585
|
+
print(" No exploited vulnerability with a known request shape is installed here; the policy only has")
|
|
586
|
+
print(" the generic checks, if any applied. Nothing to protect against beyond those.")
|
|
587
|
+
print("\nNext: `phpwarden shield install` shows how to load the agent. It only logs until you promote rules.")
|
|
588
|
+
return 0
|
|
589
|
+
if args.shield_cmd == "install":
|
|
590
|
+
agent = _P(__file__).resolve().parent / "shield" / "agent" / "phpwarden-shield.php"
|
|
591
|
+
dest = _P(args.dest) if args.dest else None
|
|
592
|
+
if dest:
|
|
593
|
+
try:
|
|
594
|
+
dest.mkdir(parents=True, exist_ok=True)
|
|
595
|
+
(dest / "phpwarden-shield.php").write_bytes(agent.read_bytes())
|
|
596
|
+
except OSError as e:
|
|
597
|
+
sys.stderr.write(f"error: {e}\n")
|
|
598
|
+
return 4
|
|
599
|
+
where = (dest / "phpwarden-shield.php").resolve()
|
|
600
|
+
print(f"copied the agent to {where}")
|
|
601
|
+
else:
|
|
602
|
+
where = "/path/to/phpwarden-shield.php"
|
|
603
|
+
print(f"The agent ships with phpwarden at {agent}. Copy it next to your site, or rerun with --dest DIR.")
|
|
604
|
+
print(f"""
|
|
605
|
+
Load it before your application (pick one; nothing is changed for you):
|
|
606
|
+
php.ini or .user.ini: auto_prepend_file={where}
|
|
607
|
+
WordPress: add require '{where}'; as the first line of wp-config.php
|
|
608
|
+
Laravel / other apps: add require '{where}'; at the top of public/index.php
|
|
609
|
+
|
|
610
|
+
Put phpwarden-shield.policy.php (from `phpwarden shield policy`) next to the agent, or set
|
|
611
|
+
PHPWARDEN_SHIELD_POLICY to its path. It starts in log-only mode and fails open on any error.
|
|
612
|
+
Turn it off instantly: create a file named phpwarden-shield.off next to the agent, or set
|
|
613
|
+
PHPWARDEN_SHIELD=off.""")
|
|
614
|
+
return 0
|
|
615
|
+
if args.shield_cmd == "report":
|
|
616
|
+
try:
|
|
617
|
+
with open(args.log, errors="replace") as fh:
|
|
618
|
+
summ = sr.summarize(fh)
|
|
619
|
+
except OSError as e:
|
|
620
|
+
sys.stderr.write(f"error: {e}\n")
|
|
621
|
+
return 4
|
|
622
|
+
print(json.dumps(summ, indent=2, default=list) if args.json else sr.to_text(summ))
|
|
623
|
+
return 0
|
|
624
|
+
if args.shield_cmd == "promote":
|
|
625
|
+
try:
|
|
626
|
+
pol = json.loads(_P(args.policy).read_text())
|
|
627
|
+
with open(args.log, errors="replace") as fh:
|
|
628
|
+
summ = sr.summarize(fh)
|
|
629
|
+
except (OSError, ValueError) as e:
|
|
630
|
+
sys.stderr.write(f"error: {e}\n")
|
|
631
|
+
return 4
|
|
632
|
+
pol, rep = sp.promote(pol, summ, operator_ack=args.i_understand_this_blocks_requests,
|
|
633
|
+
rule_ids=set(args.rule) if args.rule else None)
|
|
634
|
+
for rid in rep["promoted"]:
|
|
635
|
+
print(f" BLOCK {rid}")
|
|
636
|
+
for r in rep["refused"]:
|
|
637
|
+
print(f" still logging {r['id']}: {r['reason']}")
|
|
638
|
+
if rep["promoted"]:
|
|
639
|
+
sp.write_policy(pol, args.policy)
|
|
640
|
+
print(f"\nupdated {args.policy} (and its .php twin). Promoted rules now return 403.")
|
|
641
|
+
return 0
|
|
642
|
+
return EX_USAGE
|
|
643
|
+
|
|
644
|
+
|
|
565
645
|
def _integrity(args):
|
|
566
646
|
from phpwarden import integrity as integ
|
|
567
647
|
res = integ.check(args.path, locale=args.locale, plugins=not args.no_plugins)
|
|
@@ -674,6 +754,29 @@ def main(argv=None):
|
|
|
674
754
|
_report_args(wpc)
|
|
675
755
|
wpc.set_defaults(func=_wordpress)
|
|
676
756
|
|
|
757
|
+
sh = sub.add_parser("shield", help="protection inside PHP: block exploits of the vulnerabilities this site "
|
|
758
|
+
"actually has (log-only until you promote rules)")
|
|
759
|
+
shs = sh.add_subparsers(dest="shield_cmd", required=True, parser_class=_Parser)
|
|
760
|
+
shp = shs.add_parser("policy", help="build a policy for the exploited vulnerabilities installed at PATH")
|
|
761
|
+
shp.add_argument("path", nargs="?", default=".", help="project or WordPress root (default: .)")
|
|
762
|
+
shp.add_argument("--out", help="policy file (default phpwarden-shield.policy.json; a .php twin is written too)")
|
|
763
|
+
shp.add_argument("--php-version", help="the production PHP version, for engine CVE rules")
|
|
764
|
+
shp.add_argument("--php-sapi", choices=["fpm", "cgi", "cli"], help="the production SAPI")
|
|
765
|
+
shp.add_argument("--log", help="where the agent should write its log (default: next to the agent)")
|
|
766
|
+
shp.add_argument("--json", action="store_true", help="print the policy as JSON")
|
|
767
|
+
shi = shs.add_parser("install", help="show how to load the agent (and copy it with --dest)")
|
|
768
|
+
shi.add_argument("--dest", metavar="DIR", help="copy phpwarden-shield.php into this folder")
|
|
769
|
+
shr = shs.add_parser("report", help="summarise the agent's log")
|
|
770
|
+
shr.add_argument("log", help="phpwarden-shield.log")
|
|
771
|
+
shr.add_argument("--json", action="store_true", help="machine-readable JSON output")
|
|
772
|
+
shm = shs.add_parser("promote", help="switch rules from logging to blocking after a clean burn-in")
|
|
773
|
+
shm.add_argument("policy", help="the policy JSON to update")
|
|
774
|
+
shm.add_argument("--log", required=True, help="the agent log covering the burn-in")
|
|
775
|
+
shm.add_argument("--rule", action="append", metavar="ID", help="only this rule (repeatable)")
|
|
776
|
+
shm.add_argument("--i-understand-this-blocks-requests", action="store_true",
|
|
777
|
+
help="required: promoted rules return 403 to matching requests")
|
|
778
|
+
sh.set_defaults(func=_shield)
|
|
779
|
+
|
|
677
780
|
asx = sub.add_parser("assess", help="one ranked verdict from every view: installed versions, your code, "
|
|
678
781
|
"the live site from outside, and attacks in the access log")
|
|
679
782
|
asx.add_argument("path", nargs="?", help="project or site root (composer.lock, WordPress, PHP code)")
|