phpwarden 2.0.0__tar.gz → 2.0.2__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 (61) hide show
  1. phpwarden-2.0.2/PKG-INFO +228 -0
  2. phpwarden-2.0.2/README.md +206 -0
  3. {phpwarden-2.0.0 → phpwarden-2.0.2}/pyproject.toml +5 -3
  4. {phpwarden-2.0.0 → phpwarden-2.0.2}/src/phpwarden/__init__.py +1 -1
  5. {phpwarden-2.0.0 → phpwarden-2.0.2}/src/phpwarden/cli.py +4 -1
  6. {phpwarden-2.0.0 → phpwarden-2.0.2}/src/phpwarden/data/php-cve-feed.json +5774 -217
  7. {phpwarden-2.0.0 → phpwarden-2.0.2}/src/phpwarden/guard.py +13 -0
  8. phpwarden-2.0.2/src/phpwarden.egg-info/PKG-INFO +228 -0
  9. {phpwarden-2.0.0 → phpwarden-2.0.2}/src/phpwarden.egg-info/SOURCES.txt +5 -0
  10. phpwarden-2.0.2/tests/test_advisories.py +447 -0
  11. phpwarden-2.0.2/tests/test_connect_flow.py +103 -0
  12. phpwarden-2.0.2/tests/test_dashboard_states.py +125 -0
  13. phpwarden-2.0.2/tests/test_engine_feed.py +62 -0
  14. phpwarden-2.0.2/tests/test_google_oauth.py +191 -0
  15. phpwarden-2.0.0/PKG-INFO +0 -177
  16. phpwarden-2.0.0/README.md +0 -157
  17. phpwarden-2.0.0/src/phpwarden.egg-info/PKG-INFO +0 -177
  18. {phpwarden-2.0.0 → phpwarden-2.0.2}/LICENSE +0 -0
  19. {phpwarden-2.0.0 → phpwarden-2.0.2}/setup.cfg +0 -0
  20. {phpwarden-2.0.0 → phpwarden-2.0.2}/src/phpwarden/deps.py +0 -0
  21. {phpwarden-2.0.0 → phpwarden-2.0.2}/src/phpwarden/detect.py +0 -0
  22. {phpwarden-2.0.0 → phpwarden-2.0.2}/src/phpwarden/engine.py +0 -0
  23. {phpwarden-2.0.0 → phpwarden-2.0.2}/src/phpwarden/env.py +0 -0
  24. {phpwarden-2.0.0 → phpwarden-2.0.2}/src/phpwarden/feed.py +0 -0
  25. {phpwarden-2.0.0 → phpwarden-2.0.2}/src/phpwarden/report.py +0 -0
  26. {phpwarden-2.0.0 → phpwarden-2.0.2}/src/phpwarden/rules.py +0 -0
  27. {phpwarden-2.0.0 → phpwarden-2.0.2}/src/phpwarden/version.py +0 -0
  28. {phpwarden-2.0.0 → phpwarden-2.0.2}/src/phpwarden/vulndb.py +0 -0
  29. {phpwarden-2.0.0 → phpwarden-2.0.2}/src/phpwarden/wordpress.py +0 -0
  30. {phpwarden-2.0.0 → phpwarden-2.0.2}/src/phpwarden.egg-info/dependency_links.txt +0 -0
  31. {phpwarden-2.0.0 → phpwarden-2.0.2}/src/phpwarden.egg-info/entry_points.txt +0 -0
  32. {phpwarden-2.0.0 → phpwarden-2.0.2}/src/phpwarden.egg-info/top_level.txt +0 -0
  33. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_accounts.py +0 -0
  34. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_assignment.py +0 -0
  35. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_audit.py +0 -0
  36. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_billing.py +0 -0
  37. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_cli_edges.py +0 -0
  38. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_deps_and_rules.py +0 -0
  39. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_detection_validation.py +0 -0
  40. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_developer.py +0 -0
  41. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_discussion.py +0 -0
  42. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_e2e_readiness.py +0 -0
  43. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_email.py +0 -0
  44. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_engine.py +0 -0
  45. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_feed_license.py +0 -0
  46. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_feed_subscription.py +0 -0
  47. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_fleet.py +0 -0
  48. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_guard_live.py +0 -0
  49. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_mentions.py +0 -0
  50. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_reachability.py +0 -0
  51. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_saas.py +0 -0
  52. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_saas_billing.py +0 -0
  53. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_server.py +0 -0
  54. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_settings.py +0 -0
  55. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_shield.py +0 -0
  56. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_slack.py +0 -0
  57. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_sso.py +0 -0
  58. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_team.py +0 -0
  59. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_triage.py +0 -0
  60. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_webhook_idempotency.py +0 -0
  61. {phpwarden-2.0.0 → phpwarden-2.0.2}/tests/test_wordpress_and_report.py +0 -0
@@ -0,0 +1,228 @@
1
+ Metadata-Version: 2.4
2
+ Name: phpwarden
3
+ Version: 2.0.2
4
+ Summary: PHP exposure and exploitation guard — assess any PHP host against an evolving CVE feed, and watch PHP-FPM for active exploitation
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,php-fpm,security,cve,vulnerability,exploitation,detection,rce
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
+ # phpwarden
24
+
25
+ **PHP vulnerability scanner that ranks findings by real-world exploitation,
26
+ not CVSS.** Of the ~21,100 PHP advisories in the feed, 43 are actually being
27
+ exploited (CISA KEV, as of 2026-11-12). phpwarden puts those first, then sorts
28
+ by EPSS probability, then CVSS — so a `composer.lock` with 30 findings tells
29
+ you which one to fix today.
30
+
31
+ It scans the PHP engine/FPM version, Composer dependencies and WordPress
32
+ installs against one bundled, offline advisory feed. Zero runtime dependencies
33
+ (Python 3.8+), Apache-2.0.
34
+
35
+ ```bash
36
+ $ phpwarden deps composer.lock
37
+ 18 vulnerable package(s) in composer.lock; 0 actively exploited (KEV). Ranked by exploitation risk, then reachability:
38
+
39
+ symfony/http-kernel 5.4.0 CVE-2022-24894 medium (CVSS 5.9) [EPSS 4% (p90)]
40
+ Symfony storing cookie headers in HttpCache
41
+ fixed in: 4.4.50, 5.4.20, 6.0.20, 6.1.12, 6.2.6
42
+
43
+ phpmailer/phpmailer 6.1.5 CVE-2020-13625 high (CVSS 7.5) [EPSS 4% (p90)]
44
+ ...
45
+ ```
46
+
47
+ ## Surfaces it covers
48
+
49
+ | Surface | Command | Instead of |
50
+ |---------|---------|------------|
51
+ | PHP engine / FPM CVE exposure | `scan` | image & network scanners |
52
+ | Composer dependency CVEs | `deps` | Snyk, `composer audit` |
53
+ | WordPress core, plugin and theme CVEs | `wordpress` | Patchstack, Wordfence |
54
+ | Exploitation signatures in logs | `detect` | SIEM content |
55
+ | WAF blocking rules (PHP-specific) | `rules` | the gap a generic WAF leaves |
56
+ | Fleet exposure dashboard | `report` | the CNAPP's PHP view |
57
+ | Continuous worker containment | `phpwarden-guard` daemon | — |
58
+
59
+ ## Why it is different
60
+
61
+ - **Exploitation first.** Every advisory with a CVE is joined to
62
+ [CISA KEV](https://www.cisa.gov/known-exploited-vulnerabilities-catalog)
63
+ (exploited in the wild, used in ransomware) and
64
+ [FIRST EPSS](https://www.first.org/epss/) (probability of exploitation).
65
+ Findings are ordered KEV, then reachability (`deps`), then EPSS, then CVSS —
66
+ in the text output, the JSON and the dashboard alike.
67
+ - **Version-accurate, per branch.** PHP backports fixes per branch, so a flat
68
+ "you're on 8.3, upgrade" is wrong. phpwarden knows the fixed release for each
69
+ branch of each engine CVE and compares precisely.
70
+ - **It never overclaims.** A CVE that needs a specific SAPI (FPM/CGI), OS or
71
+ configuration is reported as *possibly* exposed with the exact condition to
72
+ confirm — not flagged as a certain hit, and not silently dismissed.
73
+ - **Offline by default.** The feed ships inside the package. Nothing leaves the
74
+ machine unless you opt in to `--report-to` or `feed update`.
75
+
76
+ ## Install
77
+
78
+ ```bash
79
+ composer require --dev phpwarden/phpwarden # PHP projects → vendor/bin/phpwarden
80
+ pipx install phpwarden # Python-tooling box
81
+ python3 phpwarden.pyz scan # single file, no install
82
+ docker build -t phpwarden . && docker run --rm phpwarden scan --version 8.3.10 --sapi fpm --os linux
83
+ ```
84
+
85
+ On modern macOS / Debian / Ubuntu a plain `pip install phpwarden` into the
86
+ system Python is blocked by [PEP 668](https://peps.python.org/pep-0668/); use
87
+ `pipx`, or a virtualenv:
88
+
89
+ ```bash
90
+ python3 -m venv ~/.venvs/phpwarden && ~/.venvs/phpwarden/bin/pip install phpwarden
91
+ ```
92
+
93
+ Docker usage (mounted projects, CI, dashboard reporting) is in
94
+ [`docs/docker.md`](docs/docker.md).
95
+
96
+ ## Use
97
+
98
+ ```bash
99
+ phpwarden scan # assess this host (auto-detects version/SAPI/OS)
100
+ phpwarden scan --version 8.3.10 --sapi fpm --os linux
101
+ phpwarden deps composer.lock # Composer dependencies, KEV → reachability → EPSS → CVSS
102
+ phpwarden wordpress /var/www/html # WordPress core, plugins, themes
103
+ phpwarden detect /var/log/nginx/access.log # exploit signatures in a log
104
+ phpwarden rules --format nginx --out php-block.conf # PHP-specific WAF rules (or modsecurity)
105
+ phpwarden report web01.json web02.json --out fleet.html # aggregate a fleet view
106
+ phpwarden feed status # which feed is active, how many advisories
107
+ ```
108
+
109
+ Every command takes `--json`. Exit codes are shared across subcommands and
110
+ built for gating: `0` clean, `1` exposed / vulnerable / signature hits,
111
+ `2` possibly exposed (`scan`), `3` PHP version unknown (`scan`), `4` input
112
+ error, `5` `--report-to` upload failed on an otherwise clean run, `64` usage
113
+ error. Full table: `phpwarden --help`.
114
+
115
+ ## Send results to the dashboard
116
+
117
+ Any `scan`, `deps`, `wordpress` or `detect` run can post its result to
118
+ [app.phpwarden.io](https://app.phpwarden.io) for history across hosts and
119
+ alerts when a finding you already carry lands in KEV:
120
+
121
+ ```bash
122
+ phpwarden scan --report-to https://app.phpwarden.io/v1/reports --report-token <token>
123
+ ```
124
+
125
+ The token comes from the project page in the dashboard; it can also be set as
126
+ `PHPWARDEN_REPORT_TOKEN` (and the URL as `PHPWARDEN_REPORT_URL`). Local output
127
+ is always printed first, so an unreachable dashboard never hides a finding.
128
+
129
+ ## In CI
130
+
131
+ **GitHub Actions** — the [phpwarden action](https://github.com/oroma-alfred/phpwarden-action)
132
+ scans `composer.lock`, a WordPress path and your production PHP version, writes
133
+ a job-summary table and `composer.lock` line annotations, can upload SARIF to
134
+ the Security tab, and can report to the dashboard:
135
+
136
+ ```yaml
137
+ steps:
138
+ - uses: actions/checkout@v4
139
+ - uses: oroma-alfred/phpwarden-action@v1
140
+ with:
141
+ fail-on: exploited # fail only on CISA-KEV findings; or critical | high | medium | exposed | never
142
+ report-url: https://app.phpwarden.io/v1/reports
143
+ report-token: ${{ secrets.PHPWARDEN_TOKEN }}
144
+ ```
145
+
146
+ `fail-on` is an input of the action. The bare CLI exits `1` on any vulnerable
147
+ package; see the templates below for how a job uses that.
148
+
149
+ **GitLab CI / Bitbucket Pipelines** — ready-made templates in
150
+ [`integrations/gitlab-ci/`](integrations/gitlab-ci/phpwarden.gitlab-ci.yml) and
151
+ [`integrations/bitbucket/`](integrations/bitbucket/bitbucket-pipelines.yml)
152
+ install the CLI, keep `phpwarden.json` as an artifact, fail the job on exit
153
+ `1` or `2`, and forward to the dashboard when a token variable is set.
154
+
155
+ **Laravel** — `composer require --dev phpwarden/laravel` adds
156
+ `php artisan phpwarden:scan` (same exit codes, plus `--fail-on=exploited`);
157
+ see [phpwarden/laravel](https://packagist.org/packages/phpwarden/laravel).
158
+
159
+ **pre-commit** — block a vulnerable `composer.lock` before it is committed:
160
+
161
+ ```yaml
162
+ repos:
163
+ - repo: https://github.com/oroma-alfred/phpwarden
164
+ rev: main # pin to a tag with `pre-commit autoupdate`
165
+ hooks:
166
+ - id: phpwarden-deps
167
+ # args: ["--no-reachability"] # faster: skip the reachability pass
168
+ ```
169
+
170
+ **Hosts and cron** — WP-CLI, Laravel Forge / Ploi, plain cron and systemd
171
+ timer recipes are in [`docs/recipes.md`](docs/recipes.md).
172
+
173
+ ## The feed
174
+
175
+ The bundled community feed (currently 2026.10.31.2) works forever, offline:
176
+
177
+ - **~21,100 advisories**: ~8,600 Composer (from [OSV.dev](https://osv.dev) and
178
+ [FriendsOfPHP](https://github.com/FriendsOfPHP/security-advisories)),
179
+ ~12,300 WordPress (from [NVD](https://nvd.nist.gov): plugin, core and theme),
180
+ and 145 PHP engine/FPM CVEs, including hand-curated entries with per-branch
181
+ fixed versions, exposure conditions and detection signatures. 19,400+ carry an
182
+ EPSS score; 42 were in KEV when this feed was built.
183
+ - **License-independent by construction.** Every source is open or
184
+ public-domain (OSV, NVD, FriendsOfPHP, CISA KEV, FIRST EPSS). No commercial
185
+ vendor's feed is used, so coverage cannot be revoked or priced away.
186
+ - Every build passes `feedbuilder/check_feed.py` (no open-ended range with a
187
+ known fix, no fixed version inside its own affected range, no duplicate
188
+ (package|slug, cve)). The feed is assembled by `feedbuilder/` from a curated
189
+ base plus bulk imports and enrichment; curated entries always win.
190
+
191
+ A [phpwarden.io](https://phpwarden.io) subscription unlocks the maintained
192
+ channel — new advisories and KEV/EPSS refreshes as they are triaged — via
193
+ `PHPWARDEN_LICENSE=... phpwarden feed update`. Without a key, phpwarden runs
194
+ the community feed and says so.
195
+
196
+ Curated engine CVEs with detection signatures:
197
+
198
+ | CVE | What | Scope |
199
+ |-----|------|-------|
200
+ | CVE-2019-11043 | PHP-FPM env_path_info RCE | FPM + nginx, config-gated |
201
+ | CVE-2023-3824 | PHAR stack overflow RCE | any SAPI, untrusted PHAR |
202
+ | CVE-2024-4577 | PHP-CGI argument injection | Windows + CGI |
203
+ | CVE-2024-8926 / 8927 | cgi.force_redirect bypass | CGI / FPM |
204
+ | CVE-2024-11236 | DB-quoter integer overflow | any SAPI |
205
+ | CVE-2026-109618 / 109619 | php2shell FPM memory RCE | any SAPI |
206
+
207
+ ## Continuous guard
208
+
209
+ `scan` and `detect` are point-in-time. For always-on containment of
210
+ in-progress php2shell-class attacks (OOM on a worker, then that worker reused),
211
+ run `phpwarden-guard`, which boots in dry-run and never acts until you promote
212
+ it after a burn-in. See the guard's DESIGN document.
213
+
214
+ ## Honest limits
215
+
216
+ - The deterministic version census is the reliable backbone; behavioural
217
+ detection is best-effort and a clean exploit can leave little trace.
218
+ - `scan` only knows what it can observe. Anything it cannot determine (SAPI, OS,
219
+ app-level conditions) is reported as *possibly* exposed, not safe.
220
+ - The KEV count above is a dated snapshot; `phpwarden feed status` and the
221
+ bundled feed are the source of truth for the version you run.
222
+
223
+ ## License
224
+
225
+ Open-core. The **engine** (`src/phpwarden/`) and the **bundled community feed**
226
+ are **Apache-2.0** (see [`LICENSE`](LICENSE)). The **hosted service** (`saas/`,
227
+ `server/`) and the **maintained feed** are proprietary. Full scope in
228
+ [`LICENSING.md`](LICENSING.md).
@@ -0,0 +1,206 @@
1
+ # phpwarden
2
+
3
+ **PHP vulnerability scanner that ranks findings by real-world exploitation,
4
+ not CVSS.** Of the ~21,100 PHP advisories in the feed, 43 are actually being
5
+ exploited (CISA KEV, as of 2026-11-12). phpwarden puts those first, then sorts
6
+ by EPSS probability, then CVSS — so a `composer.lock` with 30 findings tells
7
+ you which one to fix today.
8
+
9
+ It scans the PHP engine/FPM version, Composer dependencies and WordPress
10
+ installs against one bundled, offline advisory feed. Zero runtime dependencies
11
+ (Python 3.8+), Apache-2.0.
12
+
13
+ ```bash
14
+ $ phpwarden deps composer.lock
15
+ 18 vulnerable package(s) in composer.lock; 0 actively exploited (KEV). Ranked by exploitation risk, then reachability:
16
+
17
+ symfony/http-kernel 5.4.0 CVE-2022-24894 medium (CVSS 5.9) [EPSS 4% (p90)]
18
+ Symfony storing cookie headers in HttpCache
19
+ fixed in: 4.4.50, 5.4.20, 6.0.20, 6.1.12, 6.2.6
20
+
21
+ phpmailer/phpmailer 6.1.5 CVE-2020-13625 high (CVSS 7.5) [EPSS 4% (p90)]
22
+ ...
23
+ ```
24
+
25
+ ## Surfaces it covers
26
+
27
+ | Surface | Command | Instead of |
28
+ |---------|---------|------------|
29
+ | PHP engine / FPM CVE exposure | `scan` | image & network scanners |
30
+ | Composer dependency CVEs | `deps` | Snyk, `composer audit` |
31
+ | WordPress core, plugin and theme CVEs | `wordpress` | Patchstack, Wordfence |
32
+ | Exploitation signatures in logs | `detect` | SIEM content |
33
+ | WAF blocking rules (PHP-specific) | `rules` | the gap a generic WAF leaves |
34
+ | Fleet exposure dashboard | `report` | the CNAPP's PHP view |
35
+ | Continuous worker containment | `phpwarden-guard` daemon | — |
36
+
37
+ ## Why it is different
38
+
39
+ - **Exploitation first.** Every advisory with a CVE is joined to
40
+ [CISA KEV](https://www.cisa.gov/known-exploited-vulnerabilities-catalog)
41
+ (exploited in the wild, used in ransomware) and
42
+ [FIRST EPSS](https://www.first.org/epss/) (probability of exploitation).
43
+ Findings are ordered KEV, then reachability (`deps`), then EPSS, then CVSS —
44
+ in the text output, the JSON and the dashboard alike.
45
+ - **Version-accurate, per branch.** PHP backports fixes per branch, so a flat
46
+ "you're on 8.3, upgrade" is wrong. phpwarden knows the fixed release for each
47
+ branch of each engine CVE and compares precisely.
48
+ - **It never overclaims.** A CVE that needs a specific SAPI (FPM/CGI), OS or
49
+ configuration is reported as *possibly* exposed with the exact condition to
50
+ confirm — not flagged as a certain hit, and not silently dismissed.
51
+ - **Offline by default.** The feed ships inside the package. Nothing leaves the
52
+ machine unless you opt in to `--report-to` or `feed update`.
53
+
54
+ ## Install
55
+
56
+ ```bash
57
+ composer require --dev phpwarden/phpwarden # PHP projects → vendor/bin/phpwarden
58
+ pipx install phpwarden # Python-tooling box
59
+ python3 phpwarden.pyz scan # single file, no install
60
+ docker build -t phpwarden . && docker run --rm phpwarden scan --version 8.3.10 --sapi fpm --os linux
61
+ ```
62
+
63
+ On modern macOS / Debian / Ubuntu a plain `pip install phpwarden` into the
64
+ system Python is blocked by [PEP 668](https://peps.python.org/pep-0668/); use
65
+ `pipx`, or a virtualenv:
66
+
67
+ ```bash
68
+ python3 -m venv ~/.venvs/phpwarden && ~/.venvs/phpwarden/bin/pip install phpwarden
69
+ ```
70
+
71
+ Docker usage (mounted projects, CI, dashboard reporting) is in
72
+ [`docs/docker.md`](docs/docker.md).
73
+
74
+ ## Use
75
+
76
+ ```bash
77
+ phpwarden scan # assess this host (auto-detects version/SAPI/OS)
78
+ phpwarden scan --version 8.3.10 --sapi fpm --os linux
79
+ phpwarden deps composer.lock # Composer dependencies, KEV → reachability → EPSS → CVSS
80
+ phpwarden wordpress /var/www/html # WordPress core, plugins, themes
81
+ phpwarden detect /var/log/nginx/access.log # exploit signatures in a log
82
+ phpwarden rules --format nginx --out php-block.conf # PHP-specific WAF rules (or modsecurity)
83
+ phpwarden report web01.json web02.json --out fleet.html # aggregate a fleet view
84
+ phpwarden feed status # which feed is active, how many advisories
85
+ ```
86
+
87
+ Every command takes `--json`. Exit codes are shared across subcommands and
88
+ built for gating: `0` clean, `1` exposed / vulnerable / signature hits,
89
+ `2` possibly exposed (`scan`), `3` PHP version unknown (`scan`), `4` input
90
+ error, `5` `--report-to` upload failed on an otherwise clean run, `64` usage
91
+ error. Full table: `phpwarden --help`.
92
+
93
+ ## Send results to the dashboard
94
+
95
+ Any `scan`, `deps`, `wordpress` or `detect` run can post its result to
96
+ [app.phpwarden.io](https://app.phpwarden.io) for history across hosts and
97
+ alerts when a finding you already carry lands in KEV:
98
+
99
+ ```bash
100
+ phpwarden scan --report-to https://app.phpwarden.io/v1/reports --report-token <token>
101
+ ```
102
+
103
+ The token comes from the project page in the dashboard; it can also be set as
104
+ `PHPWARDEN_REPORT_TOKEN` (and the URL as `PHPWARDEN_REPORT_URL`). Local output
105
+ is always printed first, so an unreachable dashboard never hides a finding.
106
+
107
+ ## In CI
108
+
109
+ **GitHub Actions** — the [phpwarden action](https://github.com/oroma-alfred/phpwarden-action)
110
+ scans `composer.lock`, a WordPress path and your production PHP version, writes
111
+ a job-summary table and `composer.lock` line annotations, can upload SARIF to
112
+ the Security tab, and can report to the dashboard:
113
+
114
+ ```yaml
115
+ steps:
116
+ - uses: actions/checkout@v4
117
+ - uses: oroma-alfred/phpwarden-action@v1
118
+ with:
119
+ fail-on: exploited # fail only on CISA-KEV findings; or critical | high | medium | exposed | never
120
+ report-url: https://app.phpwarden.io/v1/reports
121
+ report-token: ${{ secrets.PHPWARDEN_TOKEN }}
122
+ ```
123
+
124
+ `fail-on` is an input of the action. The bare CLI exits `1` on any vulnerable
125
+ package; see the templates below for how a job uses that.
126
+
127
+ **GitLab CI / Bitbucket Pipelines** — ready-made templates in
128
+ [`integrations/gitlab-ci/`](integrations/gitlab-ci/phpwarden.gitlab-ci.yml) and
129
+ [`integrations/bitbucket/`](integrations/bitbucket/bitbucket-pipelines.yml)
130
+ install the CLI, keep `phpwarden.json` as an artifact, fail the job on exit
131
+ `1` or `2`, and forward to the dashboard when a token variable is set.
132
+
133
+ **Laravel** — `composer require --dev phpwarden/laravel` adds
134
+ `php artisan phpwarden:scan` (same exit codes, plus `--fail-on=exploited`);
135
+ see [phpwarden/laravel](https://packagist.org/packages/phpwarden/laravel).
136
+
137
+ **pre-commit** — block a vulnerable `composer.lock` before it is committed:
138
+
139
+ ```yaml
140
+ repos:
141
+ - repo: https://github.com/oroma-alfred/phpwarden
142
+ rev: main # pin to a tag with `pre-commit autoupdate`
143
+ hooks:
144
+ - id: phpwarden-deps
145
+ # args: ["--no-reachability"] # faster: skip the reachability pass
146
+ ```
147
+
148
+ **Hosts and cron** — WP-CLI, Laravel Forge / Ploi, plain cron and systemd
149
+ timer recipes are in [`docs/recipes.md`](docs/recipes.md).
150
+
151
+ ## The feed
152
+
153
+ The bundled community feed (currently 2026.10.31.2) works forever, offline:
154
+
155
+ - **~21,100 advisories**: ~8,600 Composer (from [OSV.dev](https://osv.dev) and
156
+ [FriendsOfPHP](https://github.com/FriendsOfPHP/security-advisories)),
157
+ ~12,300 WordPress (from [NVD](https://nvd.nist.gov): plugin, core and theme),
158
+ and 145 PHP engine/FPM CVEs, including hand-curated entries with per-branch
159
+ fixed versions, exposure conditions and detection signatures. 19,400+ carry an
160
+ EPSS score; 42 were in KEV when this feed was built.
161
+ - **License-independent by construction.** Every source is open or
162
+ public-domain (OSV, NVD, FriendsOfPHP, CISA KEV, FIRST EPSS). No commercial
163
+ vendor's feed is used, so coverage cannot be revoked or priced away.
164
+ - Every build passes `feedbuilder/check_feed.py` (no open-ended range with a
165
+ known fix, no fixed version inside its own affected range, no duplicate
166
+ (package|slug, cve)). The feed is assembled by `feedbuilder/` from a curated
167
+ base plus bulk imports and enrichment; curated entries always win.
168
+
169
+ A [phpwarden.io](https://phpwarden.io) subscription unlocks the maintained
170
+ channel — new advisories and KEV/EPSS refreshes as they are triaged — via
171
+ `PHPWARDEN_LICENSE=... phpwarden feed update`. Without a key, phpwarden runs
172
+ the community feed and says so.
173
+
174
+ Curated engine CVEs with detection signatures:
175
+
176
+ | CVE | What | Scope |
177
+ |-----|------|-------|
178
+ | CVE-2019-11043 | PHP-FPM env_path_info RCE | FPM + nginx, config-gated |
179
+ | CVE-2023-3824 | PHAR stack overflow RCE | any SAPI, untrusted PHAR |
180
+ | CVE-2024-4577 | PHP-CGI argument injection | Windows + CGI |
181
+ | CVE-2024-8926 / 8927 | cgi.force_redirect bypass | CGI / FPM |
182
+ | CVE-2024-11236 | DB-quoter integer overflow | any SAPI |
183
+ | CVE-2026-109618 / 109619 | php2shell FPM memory RCE | any SAPI |
184
+
185
+ ## Continuous guard
186
+
187
+ `scan` and `detect` are point-in-time. For always-on containment of
188
+ in-progress php2shell-class attacks (OOM on a worker, then that worker reused),
189
+ run `phpwarden-guard`, which boots in dry-run and never acts until you promote
190
+ it after a burn-in. See the guard's DESIGN document.
191
+
192
+ ## Honest limits
193
+
194
+ - The deterministic version census is the reliable backbone; behavioural
195
+ detection is best-effort and a clean exploit can leave little trace.
196
+ - `scan` only knows what it can observe. Anything it cannot determine (SAPI, OS,
197
+ app-level conditions) is reported as *possibly* exposed, not safe.
198
+ - The KEV count above is a dated snapshot; `phpwarden feed status` and the
199
+ bundled feed are the source of truth for the version you run.
200
+
201
+ ## License
202
+
203
+ Open-core. The **engine** (`src/phpwarden/`) and the **bundled community feed**
204
+ are **Apache-2.0** (see [`LICENSE`](LICENSE)). The **hosted service** (`saas/`,
205
+ `server/`) and the **maintained feed** are proprietary. Full scope in
206
+ [`LICENSING.md`](LICENSING.md).
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "phpwarden"
7
- version = "2.0.0"
7
+ version = "2.0.2"
8
8
  description = "PHP exposure and exploitation guard — assess any PHP host against an evolving CVE feed, and watch PHP-FPM for active exploitation"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
11
11
  license = "Apache-2.0"
12
12
  license-files = ["LICENSE"]
13
- authors = [{ name = "localprime" }]
13
+ authors = [{ name = "oroma-alfred" }]
14
14
  keywords = ["php", "php-fpm", "security", "cve", "vulnerability", "exploitation", "detection", "rce"]
15
15
  classifiers = [
16
16
  "Development Status :: 4 - Beta",
@@ -24,7 +24,9 @@ classifiers = [
24
24
  dependencies = [] # stdlib only
25
25
 
26
26
  [project.urls]
27
- Homepage = "https://github.com/localprime/phpwarden"
27
+ Homepage = "https://phpwarden.io"
28
+ Source = "https://github.com/oroma-alfred/phpwarden"
29
+ Issues = "https://github.com/oroma-alfred/phpwarden/issues"
28
30
 
29
31
  [project.scripts]
30
32
  phpwarden = "phpwarden.cli:main"
@@ -5,4 +5,4 @@ environment against an evolving knowledge base of PHP CVEs (the feed), and can
5
5
  watch PHP-FPM for active exploitation. The engine is open; the CVE feed is the
6
6
  part a subscription keeps current.
7
7
  """
8
- __version__ = "2.0.0"
8
+ __version__ = "2.0.2"
@@ -280,7 +280,10 @@ def _risk(known, epss, pct, ransomware=False):
280
280
  if known:
281
281
  parts.append("KEV·ransomware" if ransomware else "KEV")
282
282
  if epss is not None:
283
- parts.append(f"EPSS {epss * 100:.0f}% (p{pct * 100:.0f})")
283
+ tag = f"EPSS {epss * 100:.0f}%"
284
+ if pct is not None: # percentile is optional in the feed
285
+ tag += f" (p{pct * 100:.0f})"
286
+ parts.append(tag)
284
287
  return " [" + ", ".join(parts) + "]" if parts else ""
285
288
 
286
289