├── .github └── workflows │ └── amplify.yml ├── .wordpress-org ├── banner-1544x500.png ├── banner-772x250.png ├── icon-128x128.png ├── icon-256x256.png ├── screenshot-1.png ├── screenshot-2.png └── screenshot-3.png ├── assets ├── admin.css ├── banner-1544x500.png ├── banner-772x250.png ├── icon-128x128.png ├── icon-256x256.png ├── icon-apache.svg ├── icon-curl.svg ├── icon-imagemagick.svg ├── icon-mariadb.svg ├── icon-memcached.svg ├── icon-mysql.svg ├── icon-nginx.svg ├── icon-php.svg ├── icon-plugin.svg ├── icon-redis.svg ├── icon-sqlite.svg ├── icon-theme.svg ├── icon-wordpress.svg ├── logo16.png ├── logo512.png ├── logo64.png ├── screenshot-1.png ├── screenshot-2.png └── screenshot-3.png ├── changelog.txt ├── languages └── wpvulnerability.pot ├── readme.txt ├── wpvulnerability-admin.php ├── wpvulnerability-adminms.php ├── wpvulnerability-api.php ├── wpvulnerability-cli.php ├── wpvulnerability-core.php ├── wpvulnerability-general.php ├── wpvulnerability-notifications.php ├── wpvulnerability-plugins.php ├── wpvulnerability-process.php ├── wpvulnerability-run.php ├── wpvulnerability-schedule.php ├── wpvulnerability-sitehealth.php ├── wpvulnerability-software.php ├── wpvulnerability-themes.php └── wpvulnerability.php /.github/workflows/amplify.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Amplify Security 3 | on: 4 | pull_request: {} 5 | workflow_dispatch: {} 6 | push: 7 | branches: ["master", "main"] 8 | 9 | permissions: 10 | contents: read 11 | id-token: write 12 | 13 | jobs: 14 | amplify-security-scan: 15 | name: Amplify Security Scan 16 | runs-on: ubuntu-latest 17 | if: (github.actor != 'dependabot[bot]') 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v4 21 | - name: Amplify Runner 22 | uses: amplify-security/runner-action@v0.1.0 23 | -------------------------------------------------------------------------------- /.wordpress-org/banner-1544x500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javiercasares/wpvulnerability/41f0f84fb04f96828b1962072ad1d2ac560c0629/.wordpress-org/banner-1544x500.png -------------------------------------------------------------------------------- /.wordpress-org/banner-772x250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javiercasares/wpvulnerability/41f0f84fb04f96828b1962072ad1d2ac560c0629/.wordpress-org/banner-772x250.png -------------------------------------------------------------------------------- /.wordpress-org/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javiercasares/wpvulnerability/41f0f84fb04f96828b1962072ad1d2ac560c0629/.wordpress-org/icon-128x128.png -------------------------------------------------------------------------------- /.wordpress-org/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javiercasares/wpvulnerability/41f0f84fb04f96828b1962072ad1d2ac560c0629/.wordpress-org/icon-256x256.png -------------------------------------------------------------------------------- /.wordpress-org/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javiercasares/wpvulnerability/41f0f84fb04f96828b1962072ad1d2ac560c0629/.wordpress-org/screenshot-1.png -------------------------------------------------------------------------------- /.wordpress-org/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javiercasares/wpvulnerability/41f0f84fb04f96828b1962072ad1d2ac560c0629/.wordpress-org/screenshot-2.png -------------------------------------------------------------------------------- /.wordpress-org/screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javiercasares/wpvulnerability/41f0f84fb04f96828b1962072ad1d2ac560c0629/.wordpress-org/screenshot-3.png -------------------------------------------------------------------------------- /assets/admin.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --red-dark: #b32d2e; 3 | --red-mediun: #D54E21; 4 | --red-light: #FAEDE8; 5 | } 6 | .blink { 7 | animation: blinker 2s linear infinite; 8 | } 9 | @keyframes blinker { 10 | 50% { 11 | opacity: 0; 12 | } 13 | } 14 | 15 | /* Admin plugins table styles */ 16 | 17 | .plugins-php .vulnerability { 18 | background-color: var(--red-mediun); 19 | padding: 4px; 20 | } 21 | .plugins-php .vulnerability .alert { 22 | color: white; 23 | } 24 | .plugins tr.wpvulnerability td, .plugins tr.wpvulnerability.active td { 25 | background-color: var(--red-light); 26 | } 27 | .plugins tr.wpvulnerability:before { 28 | background-color: var(--red-light); 29 | content: ""; 30 | display: table-cell; 31 | } 32 | .plugins tr.wpvulnerability.active::before { 33 | border-left: 4px solid var( --red-mediun ); 34 | } 35 | .plugins tr.wpvulnerability p.text-red, .plugins tr.wpvulnerability.active p.text-red { 36 | color: var(--red-mediun) 37 | } 38 | 39 | /* Admin core table styles */ 40 | 41 | .update-core-php table.wpvulnerability td { 42 | background-color: var(--red-light); 43 | } 44 | .update-core-php table.wpvulnerability tr:before { 45 | background-color: var(--red-light); 46 | content: ""; 47 | display: table-cell; 48 | } 49 | .update-core-php table.wpvulnerability tr.active::before { 50 | border-left: 4px solid var( --red-mediun ); 51 | } 52 | .update-core-php p.text-red { 53 | color: var(--red-mediun) 54 | } 55 | 56 | /* Configuration header */ 57 | 58 | .wpvulnerability-header { 59 | background-color: #1d73be; 60 | margin-left: -20px; 61 | padding: 20px; 62 | display: flex; 63 | justify-content: space-between; 64 | color: white; 65 | } 66 | .wpvulnerability-header .logo { 67 | min-width: 20%; 68 | } 69 | .wpvulnerability-header h2 { 70 | float: right; 71 | color: white; 72 | padding: 25px 0 0 0; 73 | margin: 0; 74 | } 75 | 76 | /* Configuration flex */ 77 | 78 | .wpvulnerability-container { 79 | display: flex; 80 | flex-direction: row; 81 | gap: 20px; 82 | } 83 | 84 | .wpvulnerability-column { 85 | flex: 1; 86 | } 87 | 88 | /* Diseño móvil */ 89 | @media (max-width: 1280px) { 90 | .wpvulnerability-container { 91 | flex-direction: column; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /assets/banner-1544x500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javiercasares/wpvulnerability/41f0f84fb04f96828b1962072ad1d2ac560c0629/assets/banner-1544x500.png -------------------------------------------------------------------------------- /assets/banner-772x250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javiercasares/wpvulnerability/41f0f84fb04f96828b1962072ad1d2ac560c0629/assets/banner-772x250.png -------------------------------------------------------------------------------- /assets/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javiercasares/wpvulnerability/41f0f84fb04f96828b1962072ad1d2ac560c0629/assets/icon-128x128.png -------------------------------------------------------------------------------- /assets/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javiercasares/wpvulnerability/41f0f84fb04f96828b1962072ad1d2ac560c0629/assets/icon-256x256.png -------------------------------------------------------------------------------- /assets/icon-apache.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | file_type_apache -------------------------------------------------------------------------------- /assets/icon-curl.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/icon-mariadb.svg: -------------------------------------------------------------------------------- 1 | 2 | file_type_mariadb -------------------------------------------------------------------------------- /assets/icon-memcached.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /assets/icon-mysql.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/icon-nginx.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | file_type_nginx -------------------------------------------------------------------------------- /assets/icon-php.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | file_type_php3 -------------------------------------------------------------------------------- /assets/icon-plugin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /assets/icon-redis.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/icon-sqlite.svg: -------------------------------------------------------------------------------- 1 | 2 | file_type_sqlite -------------------------------------------------------------------------------- /assets/icon-theme.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /assets/icon-wordpress.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /assets/logo16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javiercasares/wpvulnerability/41f0f84fb04f96828b1962072ad1d2ac560c0629/assets/logo16.png -------------------------------------------------------------------------------- /assets/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javiercasares/wpvulnerability/41f0f84fb04f96828b1962072ad1d2ac560c0629/assets/logo512.png -------------------------------------------------------------------------------- /assets/logo64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javiercasares/wpvulnerability/41f0f84fb04f96828b1962072ad1d2ac560c0629/assets/logo64.png -------------------------------------------------------------------------------- /assets/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javiercasares/wpvulnerability/41f0f84fb04f96828b1962072ad1d2ac560c0629/assets/screenshot-1.png -------------------------------------------------------------------------------- /assets/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javiercasares/wpvulnerability/41f0f84fb04f96828b1962072ad1d2ac560c0629/assets/screenshot-2.png -------------------------------------------------------------------------------- /assets/screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javiercasares/wpvulnerability/41f0f84fb04f96828b1962072ad1d2ac560c0629/assets/screenshot-3.png -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | == Changelog == 2 | 3 | = [4.0.4] - 2025-04-07 = 4 | 5 | **Added** 6 | 7 | * Extra sanitizations. 8 | 9 | **Changed** 10 | 11 | * Translation improvements. 12 | 13 | **Fixed** 14 | 15 | * Plugin and translation load. 16 | 17 | **Compatibility** 18 | 19 | * WordPress: 4.1 - 6.8 20 | * PHP: 5.6 - 8.4 21 | * WP-CLI: 2.3.0 - 2.11.0 22 | 23 | **Tests** 24 | 25 | * PHP Coding Standards: 3.12.1 26 | * WordPress Coding Standards: 3.1.0 27 | * Plugin Check (PCP): 1.4.0 28 | * SonarCloud Code Review 29 | 30 | = [4.0.3] - 2024-10-28 = 31 | 32 | * Recreation of the 4.0.2 version. Something did not created the 4.0.2 version. 33 | 34 | = [4.0.2] - 2024-10-25 = 35 | 36 | **Fixed** 37 | 38 | * ImageMagick: it crashes in some cases where the hosting does not have ImageMagick. 39 | 40 | **Compatibility** 41 | 42 | * WordPress: 4.1 - 6.7 43 | * PHP: 5.6 - 8.4 44 | * WP-CLI: 2.3.0 - 2.11.0 45 | 46 | **Tests** 47 | 48 | * PHP Coding Standards: 3.10.3 49 | * WordPress Coding Standards: 3.1.0 50 | * Plugin Check (PCP): 1.1.0 51 | * SonarCloud Code Review 52 | 53 | = [4.0.1] - 2024-10-04 = 54 | 55 | **Fixed** 56 | 57 | * API endpoints: some API endpoints were failing. 58 | * CLI endpoints: some CLI endpoints were failing. 59 | 60 | **Compatibility** 61 | 62 | * WordPress: 4.1 - 6.7 63 | * PHP: 5.6 - 8.4 64 | * WP-CLI: 2.3.0 - 2.11.0 65 | 66 | **Tests** 67 | 68 | * PHP Coding Standards: 3.10.3 69 | * WordPress Coding Standards: 3.1.0 70 | * Plugin Check (PCP): 1.1.0 71 | * SonarCloud Code Review 72 | 73 | = [4.0.0] - 2024-10-01 = 74 | 75 | **Added** 76 | 77 | * ImageMagic vulnerabilities (Site Health + WP-CLI + API + mail). 78 | * curl vulnerabilities (Site Health + WP-CLI + API + mail). 79 | * memcached vulnerabilities (Site Health + WP-CLI + API + mail). 80 | * Redis vulnerabilities (Site Health + WP-CLI + API + mail). 81 | * SQLite vulnerabilities (Site Health + WP-CLI + API + mail). 82 | 83 | **Fixed** 84 | 85 | * Test email without email. 86 | * Improved MariaDB 11.x detection. 87 | * Improved versions detection (major-minor.patch-build). 88 | * WordPress < 5.3: use of wp_date(). 89 | * WordPress < 5.0: locale detection. 90 | * Dashboard widget only for users with capabilities. 91 | * WordPress < 5.2: link to Site Health 92 | 93 | **Changed** 94 | 95 | * Big refactory. 96 | * Less files, less size, improved code quality. 97 | 98 | **Compatibility** 99 | 100 | * WordPress: 4.1 - 6.7 101 | * PHP: 5.6 - 8.4 102 | * WP-CLI: 2.3.0 - 2.11.0 103 | 104 | **Tests** 105 | 106 | * Manual Testing: 107 | * WordPress 6.7 / PHP 8.4 108 | * WordPress 6.6 / PHP 8.3 109 | * WordPress 6.4 / PHP 8.2 110 | * WordPress 6.1 / PHP 8.1 111 | * WordPress 5.8 / PHP 8.0 112 | * WordPress 5.5 / PHP 7.4 113 | * WordPress 5.3 / PHP 7.3 114 | * WordPress 4.9 / PHP 7.2 115 | * WordPress 4.8 / PHP 7.1 116 | * WordPress 4.6 / PHP 7.0 117 | * WordPress 4.1 / PHP 5.6 118 | * PHP Coding Standards: 3.10.3 119 | * WordPress Coding Standards: 3.1.0 120 | * Plugin Check (PCP): 1.1.0 121 | * SonarCloud Code Review 122 | 123 | = [3.4.1] - 2024-08-23 = 124 | 125 | **Fixed** 126 | 127 | * The number of vulnerabilities for core is incorrect. 128 | 129 | **Compatibility** 130 | 131 | * WordPress: 4.1 - 6.7 132 | * PHP: 5.6 - 8.3 133 | * WP-CLI: 2.3.0 - 2.11.0 134 | 135 | **Tests** 136 | 137 | * PHP Coding Standards: 3.10.2 138 | * WordPress Coding Standards: 3.1.0 139 | * Plugin Check (PCP): 1.0.2 140 | * SonarCloud Code Review 141 | 142 | = [3.4.0] - 2024-08-16 = 143 | 144 | **Added** 145 | 146 | * New checks for MariaDB vulnerabilities. 147 | * New checks for MySQL vulnerabilities. 148 | * WPVulnerability statistics in the configuration page. 149 | * WPVulnerability contributors in the configuration page. 150 | 151 | **Changed** 152 | 153 | * Code improvement. 154 | * Better UI for the configuration page. 155 | * Web server version detection improved. 156 | 157 | **Fixed** 158 | 159 | * Get the statistics information the right way. 160 | 161 | **Compatibility** 162 | 163 | * WordPress: 4.1 - 6.7 164 | * PHP: 5.6 - 8.3 165 | * WP-CLI: 2.3.0 - 2.11.0 166 | 167 | **Tests** 168 | 169 | * PHP Coding Standards: 3.10.2 170 | * WordPress Coding Standards: 3.1.0 171 | * Plugin Check (PCP): 1.0.2 172 | * SonarCloud Code Review 173 | 174 | = [3.3.5] - 2024-08-14 = 175 | 176 | **Added** 177 | 178 | * Add counters for Core, Plugins, and Themes. 179 | * Add a Vulnerabilities filter in the Plugin list (WordPress and WordPress Multisite). 180 | * Add a Vulnerabilities filter in the Themes list (WordPress Multisite). 181 | 182 | **Compatibility** 183 | 184 | * WordPress: 4.1 - 6.7 185 | * PHP: 5.6 - 8.3 186 | * WP-CLI: 2.3.0 - 2.11.0 187 | 188 | **Tests** 189 | 190 | * PHP Coding Standards: 3.10.2 191 | * WordPress Coding Standards: 3.1.0 192 | * Plugin Check (PCP): 1.0.2 193 | * SonarCloud Code Review 194 | 195 | = [3.3.4] - 2024-08-12 = 196 | 197 | **Fixed** 198 | 199 | * The "Last updated on" column in the plugin list is available again. 200 | 201 | **Compatibility** 202 | 203 | * WordPress: 4.1 - 6.7 204 | * PHP: 5.6 - 8.3 205 | * WP-CLI: 2.3.0 - 2.11.0 206 | 207 | **Tests** 208 | 209 | * PHP Coding Standards: 3.10.2 210 | * WordPress Coding Standards: 3.1.0 211 | * Plugin Check (PCP): 1.0.2 212 | * SonarCloud Code Review 213 | 214 | = [3.3.3] - 2024-08-05 = 215 | 216 | **Fixed** 217 | 218 | * The Dashboard panel is availbale, again. 219 | 220 | **Compatibility** 221 | 222 | * WordPress: 4.1 - 6.7 223 | * PHP: 5.6 - 8.3 224 | * WP-CLI: 2.3.0 - 2.10.0 225 | 226 | **Tests** 227 | 228 | * PHP Coding Standards: 3.10.2 229 | * WordPress Coding Standards: 3.1.0 230 | * Plugin Check (PCP): 1.0.2 231 | * SonarCloud Code Review 232 | 233 | = [3.3.1] - 2024-08-02 = 234 | 235 | **Fixed** 236 | 237 | * Delete the wp_is_rest_endpoint check. Does not need it. 238 | 239 | **Compatibility** 240 | 241 | * WordPress: 4.1 - 6.7 242 | * PHP: 5.6 - 8.3 243 | * WP-CLI: 2.3.0 - 2.10.0 244 | 245 | **Tests** 246 | 247 | * PHP Coding Standards: 3.10.2 248 | * WordPress Coding Standards: 3.1.0 249 | * Plugin Check (PCP): 1.0.2 250 | * SonarCloud Code Review 251 | 252 | = [3.3.0] - 2024-08-02 = 253 | 254 | **Added** 255 | 256 | * Ability to exclude of vulnerability types at a global level. 257 | * WP-CLI commands formats (--format=[table,json]). 258 | * REST API endpoints (requires Application Password). 259 | 260 | **Changed** 261 | 262 | * README file. 263 | 264 | **Compatibility** 265 | 266 | * WordPress: 4.1 - 6.7 267 | * PHP: 5.6 - 8.3 268 | * WP-CLI: 2.3.0 - 2.10.0 269 | 270 | **Tests** 271 | 272 | * PHP Coding Standards: 3.10.2 273 | * WordPress Coding Standards: 3.1.0 274 | * Plugin Check (PCP): 1.0.2 275 | * SonarCloud Code Review 276 | 277 | = [3.2.2] - 2024-07-27 = 278 | 279 | **Added** 280 | 281 | * Ability to configure a different From: email address for sending vulnerability notifications via `wp-config.php`. 282 | 283 | **Changed** 284 | 285 | * The URL for the website now uses its own domain name. 286 | * Dashboard visibility is restricted to users with specific capabilities, similar to Site Health. 287 | 288 | **Fixed** 289 | 290 | * Various minor fixes to prevent warnings and potential errors due to misconfigured WordPress setups. 291 | * Allow loading of some necessary libraries. 292 | 293 | **Compatibility** 294 | 295 | * WordPress: 4.1 - 6.6 296 | * PHP: 5.6 - 8.3 297 | * WP-CLI: 2.3.0 - 2.10.0 298 | 299 | **Tests** 300 | 301 | * WordPress Coding Standards: 3.1.0 302 | * Plugin Check (PCP): 1.0.2 303 | * SonarCloud Code Review 304 | 305 | = [3.2.0] - 2024-05-08 = 306 | 307 | **Added** 308 | 309 | * Apache HTTPD vulnerabilities (Site Health). 310 | * nginx vulnerabilities (Site Health). 311 | 312 | **Changed** 313 | 314 | * License updated to GPL 2.0 or later. 315 | 316 | **Compatibility** 317 | 318 | * WordPress 4.1 - WordPress 6.6. 319 | * PHP 5.6 - PHP 8.3. 320 | * WordPress Coding Standards 3.1.0. 321 | * WP-CLI 2.3.0 - WP-CLI 2.10.0. 322 | * Plugin Check (PCP) 323 | 324 | = [3.1.2] - 2024-05-08 = 325 | 326 | **Fixed** 327 | 328 | * In some cases (when calling it directly, or wget), the cron was not working and gave an error. 329 | * The license had a non-compliance ID. Now, same license but working. 330 | * General improvements. 331 | 332 | **Changed** 333 | 334 | * The URL from the API is using its own domain name. 335 | 336 | **Compatibility** 337 | 338 | * WordPress 4.1 - WordPress 6.6. 339 | * PHP 5.6 - PHP 8.3. 340 | * WordPress Coding Standards 3.1.0. 341 | * WP-CLI 2.3.0 - WP-CLI 2.10.0. 342 | * Plugin Check (PCP) 343 | 344 | = [3.1.1] - 2024-02-11 = 345 | 346 | **Fixed** 347 | 348 | * Fixes some possible PHP warnings when retrieving data from the API. 349 | * Delete old schedules when unistalling the plugin. 350 | * Fix how is printed the High severity. 351 | 352 | **Deleted** 353 | 354 | * The plugin will not show the Exploitability information. 355 | 356 | **Compatibility** 357 | 358 | * Compatibility: WordPress 4.1 - WordPress 6.5. 359 | * Compatibility: PHP 5.6 - PHP 8.3. 360 | * Compatibility: WordPress Coding Standards 3.0.1. 361 | * Compatibility: WP-CLI 2.3.0 - WP-CLI 2.10.0. 362 | 363 | = [3.1.0] - 2024-02-04 = 364 | 365 | **Added** 366 | 367 | * A new column in the plugin list, with the last updated day (and diff). 368 | * A notice if the plugin is closed in the WordPress.org repo. 369 | 370 | **Fixed** 371 | 372 | * Fixes the schedule in some cases. 373 | * Fixes the PHP format (using always the n.n / n.n.n format). 374 | 375 | **Compatibility** 376 | 377 | * Compatibility: WordPress 4.1 - WordPress 6.5. 378 | * Compatibility: PHP 5.6 - PHP 8.3. 379 | * Compatibility: WordPress Coding Standards 3.0.1. 380 | * Compatibility: WP-CLI 2.3.0 - WP-CLI 2.9.0. 381 | 382 | = [3.0.2] - 2024-01-27 = 383 | 384 | **Fixed** 385 | 386 | * Fixes the WordPress Multisite saving options. 387 | 388 | **Compatibility** 389 | 390 | * Compatibility: WordPress 4.1 - WordPress 6.5. 391 | * Compatibility: PHP 5.6 - PHP 8.3. 392 | * Compatibility: WordPress Coding Standards 3.0.1. 393 | * Compatibility: WP-CLI 2.3.0 - WP-CLI 2.9.0. 394 | 395 | = [3.0.1] - 2023-12-19 = 396 | 397 | **Fixed** 398 | 399 | * Test email with the actual vulnerabilities (or a test message), now forced when the button is clicked. 400 | * Fixed some strings (thanks @alexclassroom). 401 | * WordPress Coding Standards 3.0.1 up-to-date. 402 | 403 | **Compatibility** 404 | 405 | * Compatibility: WordPress 4.1 - WordPress 6.4. 406 | * Compatibility: PHP 5.6 - PHP 8.3. 407 | * Compatibility: WordPress Coding Standards 3.0.1. 408 | * Compatibility: WP-CLI 2.3.0 - WP-CLI 2.9.0. 409 | 410 | = [3.0.0] - 2023-12-09 = 411 | 412 | **Added** 413 | 414 | * WordPress Multisite support. 415 | * PHP vulnerabilities (Site Health). 416 | * Reload the data from source. 417 | * Test email with the actual vulnerabilities. 418 | 419 | **Changed** 420 | 421 | * Loading the data in better way. 422 | 423 | **Compatibility** 424 | 425 | * Compatibility: WordPress 4.1 - WordPress 6.4. 426 | * Compatibility: PHP 5.6 - PHP 8.3. 427 | * Compatibility: WordPress Coding Standards 3.0.1. 428 | * Compatibility: WP-CLI 2.3 - WP-CLI 2.9.0. 429 | 430 | = [2.2.1] - 2023-10-02 = 431 | 432 | **Added** 433 | 434 | * New security information (at WordPress.org plugin page). 435 | * New privacy information (at WordPress.org plugin page). 436 | * New compatibility information (at WordPress.org plugin page). 437 | * New vulnerabilities information (at WordPress.org plugin page). 438 | * New profiling information (at WordPress.org plugin page). 439 | 440 | **Changed** 441 | 442 | * Promoted dashboard. 443 | * Performance improvement: only load the plugin in the admin area. 444 | 445 | **Compatibility** 446 | 447 | * Compatibility: WordPress 4.1 - WordPress 6.4. 448 | * Compatibility: PHP 5.6 - PHP 8.3. 449 | * Compatibility: WordPress Coding Standards 3.0.1. 450 | * Compatibility: WP-CLI 2.3 - WP-CLI 2.8.1. 451 | 452 | = [2.2.0] - 2023-09-14 = 453 | 454 | **Added** 455 | 456 | * New Dashboard, with a Vulnerability summary and products affected. 457 | 458 | **Compatibility** 459 | 460 | * Compatibility: WordPress 4.1 - WordPress 6.3. 461 | * Compatibility: PHP 5.6 - PHP 8.3. 462 | * Compatibility: WordPress Coding Standards 3.0.0. 463 | * Compatibility: WP-CLI 2.3 - WP-CLI 2.8. 464 | 465 | = [2.1.0] - 2023-09-11 = 466 | 467 | **Changed** 468 | 469 | * Improved detection of plugins folders. This shpould reduce the false positives in some plugins, and Pro/Premium plugins. 470 | 471 | **Compatibility** 472 | 473 | * Compatibility: WordPress 4.1 - WordPress 6.3. 474 | * Compatibility: PHP 5.6 - PHP 8.3. 475 | * Compatibility: WordPress Coding Standards 3.0.0. 476 | * Compatibility: WP-CLI 2.3 - WP-CLI 2.7. 477 | 478 | = [2.0.4] - 2023-09-10 = 479 | 480 | **Compatibility** 481 | 482 | * WordPress Coding Standards 3.0.0 compatible. 483 | 484 | = [2.0.3] - 2023-07-27 = 485 | 486 | **Added** 487 | 488 | * Validate secure requests to the API. 489 | 490 | **Changed** 491 | 492 | * Reduce API timeout request time from 10.0 seconds to 2.5 seconds. 493 | 494 | **Compatibility** 495 | 496 | * Compatibility: WordPress 4.1 - WordPress 6.3. 497 | * Compatibility: PHP 5.6 - PHP 8.3. 498 | 499 | = [2.0.2] - 2023-04-24 = 500 | 501 | **Fixed** 502 | 503 | * Fix the Notification system. 504 | 505 | = [2.0.1] - 2023-04-20 = 506 | 507 | **Added** 508 | 509 | * Added new options to cache the vulnerability counter. 510 | 511 | **Changed** 512 | 513 | * Update the readme.txt. 514 | 515 | **Fixed** 516 | 517 | * Fix the Site Health messages. 518 | 519 | = [2.0.0] - 2023-04-15 = 520 | 521 | **Added** 522 | 523 | * If the WordPress version supports it, vulnerabilities are displayed in the Core update screen. 524 | * Calls can be made from WP-CLI `wp help wpvulnerability` to list vulnerabilities in Core `wp wpvulnerability core`, Plugins `wp wpvulnerability plugins` and Themes `wp wpvulnerability themes`. Before only Plugins. 525 | * Site Health shows core vulnerabilities, which were not previously shown. 526 | 527 | **Changed** 528 | 529 | * The plugin has been completely refactored. 530 | 531 | **Compatibility** 532 | 533 | * Compatibility: WordPress 4.1 - WordPress 6.2 534 | * Compatibility: PHP 5.6 - PHP 8.2 535 | * Compatibility: WP-CLI 2.3 - 2.7 536 | 537 | = [1.3.2] - 2023-03-22 = 538 | 539 | **Changed** 540 | 541 | * Code security improvements 542 | 543 | **Fixed** 544 | 545 | * Fix some PHP errors 546 | 547 | = [1.3.2] - 2023-03-22 = 548 | 549 | **Changed** 550 | 551 | * Code security improvements 552 | 553 | **Fixed** 554 | 555 | * Fix some PHP errors 556 | 557 | = [1.3.1] - 2023-02-27 = 558 | 559 | **Changed** 560 | 561 | * Code security improvements 562 | * Fix the Severity value 563 | * A better Site Health information 564 | 565 | **Compatibility** 566 | 567 | * Compatibility: WordPress 5.2 - WordPress 6.2 568 | * Compatibility: PHP 7.2 - PHP 8.1 569 | 570 | = [1.3.0] - 2023-02-27 = 571 | 572 | **Added** 573 | 574 | * Information, when available, about the vulnerability, in a simplified way. Only in the plugin list. 575 | * Information, when available, about the potential severity and exploitability. Only in the plugin list. 576 | * Links to sources to get additional information. Only in the plugin list. 577 | 578 | **Changed** 579 | 580 | * Improved security in code. 581 | 582 | = [1.2.4] - 2023-02-20 = 583 | 584 | **Compatibility** 585 | 586 | * Compatibility: WordPress 5.2 - WordPress 6.2 587 | * Compatibility: PHP 7.2 - PHP 8.1 588 | 589 | = [1.2.3] - 2023-01-30 = 590 | 591 | **Fixed** 592 | 593 | * Fix WP_Error object. 594 | 595 | = [1.2.2] - 2023-01-30 = 596 | 597 | **Fixed** 598 | 599 | * Fix WP_Error object. 600 | 601 | = [1.2.1] - 2023-01-09 = 602 | 603 | **Fixed** 604 | 605 | * Some fixed to improve the operators. 606 | 607 | = [1.2.0] - 2022-12-15 = 608 | 609 | **Added** 610 | 611 | * Sends email periodically. You can choose who is going to receive the emails. 612 | * First approach to WPCLI Commands (thanks to @lbonomo). 613 | 614 | = [1.1.0] - 2022-05-18 = 615 | 616 | **Fixed** 617 | 618 | * Fix: Prevents text domain not given correctly. 619 | * Fix: strings not translated. 620 | 621 | = [1.0.1] - 2022-05-17 = 622 | 623 | **Fixed** 624 | 625 | * Fix: strings not translated. 626 | 627 | = [1.0.0] - 2022-05-16 = 628 | 629 | **Added** 630 | 631 | * Added tabs in Health check. 632 | 633 | = [0.2.0] - 2022-05-07 = 634 | 635 | **Added** 636 | 637 | * Improved the information in plugins list. 638 | 639 | = [0.1.0] - 2022-05-06 = 640 | 641 | **Added** 642 | 643 | * Notification in the plugins list. 644 | * First release. -------------------------------------------------------------------------------- /languages/wpvulnerability.pot: -------------------------------------------------------------------------------- 1 | #, fuzzy 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: WPVulnerability\n" 5 | "POT-Creation-Date: 2024-10-01 19:01+0200\n" 6 | "PO-Revision-Date: 2023-09-12 06:56+0200\n" 7 | "Last-Translator: \n" 8 | "Language-Team: \n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" 13 | "X-Generator: Poedit 3.4.4\n" 14 | "X-Poedit-Basepath: ..\n" 15 | "X-Poedit-Flags-xgettext: --add-comments=translators:\n" 16 | "X-Poedit-WPHeader: wpvulnerability.php\n" 17 | "X-Poedit-SourceCharset: UTF-8\n" 18 | "X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;" 19 | "esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;" 20 | "_n_noop:1,2;_nx_noop:3c,1,2;__ngettext_noop:1,2\n" 21 | "X-Poedit-SearchPath-0: .\n" 22 | "X-Poedit-SearchPathExcluded-0: *.min.js\n" 23 | 24 | #: wpvulnerability-admin.php:54 wpvulnerability-adminms.php:236 25 | msgid "Data from source has been reloaded." 26 | msgstr "" 27 | 28 | #: wpvulnerability-admin.php:80 wpvulnerability-adminms.php:261 29 | msgid "Test email has been sent." 30 | msgstr "" 31 | 32 | #: wpvulnerability-admin.php:82 wpvulnerability-adminms.php:265 33 | msgid "Test email has failed. Please, check your email settings." 34 | msgstr "" 35 | 36 | #: wpvulnerability-admin.php:103 wpvulnerability-adminms.php:277 37 | msgid "WPVulnerability settings" 38 | msgstr "" 39 | 40 | #: wpvulnerability-admin.php:146 wpvulnerability-adminms.php:331 41 | msgid "Reload the data from source" 42 | msgstr "" 43 | 44 | #: wpvulnerability-admin.php:149 wpvulnerability-adminms.php:334 45 | msgid "" 46 | "Reload all Core, Plugins, Themes and other components information directly " 47 | "from the API to have updated data." 48 | msgstr "" 49 | 50 | #: wpvulnerability-admin.php:152 wpvulnerability-adminms.php:337 51 | msgid "Reload Data" 52 | msgstr "" 53 | 54 | #: wpvulnerability-admin.php:158 wpvulnerability-adminms.php:344 55 | msgid "Email test" 56 | msgstr "" 57 | 58 | #: wpvulnerability-admin.php:167 wpvulnerability-adminms.php:353 59 | msgid "The mail will be sent from (set on WPVULNERABILITY_MAIL): " 60 | msgstr "" 61 | 62 | #: wpvulnerability-admin.php:174 wpvulnerability-adminms.php:360 63 | msgid "The mail will be sent from: " 64 | msgstr "" 65 | 66 | #: wpvulnerability-admin.php:178 wpvulnerability-adminms.php:364 67 | msgid "Send an email with the vulnerabilities (or empty)." 68 | msgstr "" 69 | 70 | #: wpvulnerability-admin.php:181 wpvulnerability-adminms.php:367 71 | msgid "Send email" 72 | msgstr "" 73 | 74 | #: wpvulnerability-admin.php:192 wpvulnerability-adminms.php:377 75 | msgid "WPVulnerability Statistics" 76 | msgstr "" 77 | 78 | #: wpvulnerability-admin.php:198 wpvulnerability-admin.php:703 79 | #: wpvulnerability-adminms.php:383 wpvulnerability-adminms.php:881 80 | msgid "Plugins" 81 | msgstr "" 82 | 83 | #. translators: number of vulnerabilities. 84 | #: wpvulnerability-admin.php:206 wpvulnerability-admin.php:232 85 | #: wpvulnerability-admin.php:258 wpvulnerability-admin.php:278 86 | #: wpvulnerability-admin.php:298 wpvulnerability-admin.php:318 87 | #: wpvulnerability-admin.php:342 wpvulnerability-admin.php:362 88 | #: wpvulnerability-admin.php:382 wpvulnerability-admin.php:402 89 | #: wpvulnerability-admin.php:422 wpvulnerability-admin.php:442 90 | #: wpvulnerability-adminms.php:392 wpvulnerability-adminms.php:418 91 | #: wpvulnerability-adminms.php:444 wpvulnerability-adminms.php:464 92 | #: wpvulnerability-adminms.php:484 wpvulnerability-adminms.php:504 93 | #: wpvulnerability-adminms.php:528 wpvulnerability-adminms.php:548 94 | #: wpvulnerability-adminms.php:568 wpvulnerability-adminms.php:588 95 | #: wpvulnerability-adminms.php:608 wpvulnerability-adminms.php:628 96 | #, php-format 97 | msgid "%s vulnerability" 98 | msgid_plural "%s vulnerabilities" 99 | msgstr[0] "" 100 | msgstr[1] "" 101 | 102 | #. translators: number of plugins. 103 | #: wpvulnerability-admin.php:212 wpvulnerability-adminms.php:398 104 | #, php-format 105 | msgid " (%s plugin)" 106 | msgid_plural " (%s plugins)" 107 | msgstr[0] "" 108 | msgstr[1] "" 109 | 110 | #: wpvulnerability-admin.php:220 wpvulnerability-admin.php:246 111 | #: wpvulnerability-admin.php:266 wpvulnerability-admin.php:286 112 | #: wpvulnerability-admin.php:306 wpvulnerability-admin.php:326 113 | #: wpvulnerability-admin.php:350 wpvulnerability-admin.php:370 114 | #: wpvulnerability-admin.php:390 wpvulnerability-admin.php:410 115 | #: wpvulnerability-admin.php:430 wpvulnerability-admin.php:450 116 | #: wpvulnerability-admin.php:497 wpvulnerability-admin.php:513 117 | #: wpvulnerability-adminms.php:406 wpvulnerability-adminms.php:432 118 | #: wpvulnerability-adminms.php:452 wpvulnerability-adminms.php:472 119 | #: wpvulnerability-adminms.php:492 wpvulnerability-adminms.php:512 120 | #: wpvulnerability-adminms.php:536 wpvulnerability-adminms.php:556 121 | #: wpvulnerability-adminms.php:576 wpvulnerability-adminms.php:596 122 | #: wpvulnerability-adminms.php:616 wpvulnerability-adminms.php:636 123 | #: wpvulnerability-adminms.php:682 wpvulnerability-adminms.php:699 124 | msgid "Data not available." 125 | msgstr "" 126 | 127 | #: wpvulnerability-admin.php:224 wpvulnerability-admin.php:708 128 | #: wpvulnerability-adminms.php:410 wpvulnerability-adminms.php:886 129 | msgid "Themes" 130 | msgstr "" 131 | 132 | #. translators: number of themes. 133 | #: wpvulnerability-admin.php:238 wpvulnerability-adminms.php:424 134 | #, php-format 135 | msgid " (%s theme)" 136 | msgid_plural " (%s themes)" 137 | msgstr[0] "" 138 | msgstr[1] "" 139 | 140 | #: wpvulnerability-admin.php:250 wpvulnerability-admin.php:713 141 | #: wpvulnerability-adminms.php:436 wpvulnerability-adminms.php:891 142 | #: wpvulnerability-sitehealth.php:178 143 | msgid "PHP" 144 | msgstr "" 145 | 146 | #: wpvulnerability-admin.php:270 wpvulnerability-admin.php:718 147 | #: wpvulnerability-adminms.php:456 wpvulnerability-adminms.php:896 148 | #: wpvulnerability-sitehealth.php:179 149 | msgid "Apache HTTPD" 150 | msgstr "" 151 | 152 | #: wpvulnerability-admin.php:290 wpvulnerability-admin.php:723 153 | #: wpvulnerability-adminms.php:476 wpvulnerability-adminms.php:901 154 | msgid "nginx" 155 | msgstr "" 156 | 157 | #: wpvulnerability-admin.php:310 wpvulnerability-admin.php:728 158 | #: wpvulnerability-adminms.php:496 wpvulnerability-adminms.php:906 159 | #: wpvulnerability-sitehealth.php:181 160 | msgid "MariaDB" 161 | msgstr "" 162 | 163 | #: wpvulnerability-admin.php:334 wpvulnerability-admin.php:733 164 | #: wpvulnerability-adminms.php:520 wpvulnerability-adminms.php:911 165 | #: wpvulnerability-sitehealth.php:182 166 | msgid "MySQL" 167 | msgstr "" 168 | 169 | #: wpvulnerability-admin.php:354 wpvulnerability-admin.php:738 170 | #: wpvulnerability-adminms.php:540 wpvulnerability-adminms.php:916 171 | #: wpvulnerability-sitehealth.php:183 172 | msgid "ImageMagick" 173 | msgstr "" 174 | 175 | #: wpvulnerability-admin.php:374 wpvulnerability-admin.php:743 176 | #: wpvulnerability-adminms.php:560 wpvulnerability-adminms.php:921 177 | #: wpvulnerability-sitehealth.php:184 178 | msgid "curl" 179 | msgstr "" 180 | 181 | #: wpvulnerability-admin.php:394 wpvulnerability-admin.php:749 182 | #: wpvulnerability-adminms.php:580 wpvulnerability-adminms.php:926 183 | msgid "memcached" 184 | msgstr "" 185 | 186 | #: wpvulnerability-admin.php:414 wpvulnerability-admin.php:754 187 | #: wpvulnerability-adminms.php:600 wpvulnerability-adminms.php:931 188 | msgid "Redis" 189 | msgstr "" 190 | 191 | #: wpvulnerability-admin.php:434 wpvulnerability-admin.php:759 192 | #: wpvulnerability-adminms.php:620 wpvulnerability-adminms.php:936 193 | msgid "SQLite" 194 | msgstr "" 195 | 196 | #. translators: date of last update. 197 | #: wpvulnerability-admin.php:469 wpvulnerability-adminms.php:655 198 | #, php-format 199 | msgid "Updated: %s" 200 | msgstr "" 201 | 202 | #: wpvulnerability-admin.php:482 wpvulnerability-adminms.php:668 203 | msgid "Behind the Project" 204 | msgstr "" 205 | 206 | #: wpvulnerability-admin.php:487 wpvulnerability-adminms.php:672 207 | msgid "Sponsors" 208 | msgstr "" 209 | 210 | #: wpvulnerability-admin.php:503 wpvulnerability-adminms.php:689 211 | msgid "Contributors" 212 | msgstr "" 213 | 214 | #. Plugin Name of the plugin/theme 215 | #: wpvulnerability-admin.php:539 wpvulnerability-admin.php:540 216 | #: wpvulnerability-adminms.php:723 wpvulnerability-adminms.php:724 217 | msgid "WPVulnerability" 218 | msgstr "" 219 | 220 | #: wpvulnerability-admin.php:558 wpvulnerability-adminms.php:741 221 | msgid "Configure and save these settings to receive email notifications." 222 | msgstr "" 223 | 224 | #: wpvulnerability-admin.php:571 wpvulnerability-adminms.php:783 225 | msgid "Configure and save these settings to hide vulnerabilities." 226 | msgstr "" 227 | 228 | #: wpvulnerability-admin.php:599 wpvulnerability-adminms.php:768 229 | msgid "Default administrator email" 230 | msgstr "" 231 | 232 | #: wpvulnerability-admin.php:627 wpvulnerability-adminms.php:807 233 | msgid "Daily" 234 | msgstr "" 235 | 236 | #: wpvulnerability-admin.php:632 wpvulnerability-adminms.php:812 237 | msgid "Weekly" 238 | msgstr "" 239 | 240 | #: wpvulnerability-admin.php:698 wpvulnerability-adminms.php:876 241 | msgid "Core" 242 | msgstr "" 243 | 244 | #. translators: Show the number of vulnerabilities in a WP-Admin dashboard 245 | #: wpvulnerability-admin.php:870 wpvulnerability-adminms.php:956 246 | #, php-format 247 | msgid "Core: %d vulnerability" 248 | msgid_plural "Core: %d vulnerabilities" 249 | msgstr[0] "" 250 | msgstr[1] "" 251 | 252 | #. translators: Show the number of vulnerabilities in a WP-Admin dashboard 253 | #: wpvulnerability-admin.php:878 wpvulnerability-adminms.php:964 254 | #, php-format 255 | msgid "Themes: %d vulnerability" 256 | msgid_plural "Themes: %d vulnerabilities" 257 | msgstr[0] "" 258 | msgstr[1] "" 259 | 260 | #. translators: Show the number of vulnerabilities in a WP-Admin dashboard 261 | #: wpvulnerability-admin.php:886 wpvulnerability-adminms.php:972 262 | #, php-format 263 | msgid "Plugins: %d vulnerability" 264 | msgid_plural "Plugins: %d vulnerabilities" 265 | msgstr[0] "" 266 | msgstr[1] "" 267 | 268 | #. translators: Show the number of vulnerabilities in a WP-Admin dashboard 269 | #: wpvulnerability-admin.php:895 wpvulnerability-adminms.php:981 270 | #, php-format 271 | msgid "PHP %s: " 272 | msgstr "" 273 | 274 | #: wpvulnerability-admin.php:895 wpvulnerability-admin.php:911 275 | #: wpvulnerability-admin.php:928 wpvulnerability-admin.php:945 276 | #: wpvulnerability-admin.php:962 wpvulnerability-admin.php:977 277 | #: wpvulnerability-admin.php:992 wpvulnerability-admin.php:1007 278 | #: wpvulnerability-admin.php:1022 wpvulnerability-admin.php:1037 279 | #: wpvulnerability-adminms.php:981 wpvulnerability-adminms.php:997 280 | #: wpvulnerability-adminms.php:1014 wpvulnerability-adminms.php:1031 281 | #: wpvulnerability-adminms.php:1048 wpvulnerability-adminms.php:1063 282 | #: wpvulnerability-adminms.php:1078 wpvulnerability-adminms.php:1093 283 | #: wpvulnerability-adminms.php:1108 wpvulnerability-adminms.php:1123 284 | #, php-format 285 | msgid "%d vulnerability" 286 | msgid_plural "%d vulnerabilities" 287 | msgstr[0] "" 288 | msgstr[1] "" 289 | 290 | #. translators: Show the number of vulnerabilities in a WP-Admin dashboard 291 | #: wpvulnerability-admin.php:911 wpvulnerability-adminms.php:997 292 | #, php-format 293 | msgid "Apache %s: " 294 | msgstr "" 295 | 296 | #. translators: Show the number of vulnerabilities in a WP-Admin dashboard 297 | #: wpvulnerability-admin.php:928 wpvulnerability-adminms.php:1014 298 | #, php-format 299 | msgid "nginx %s: " 300 | msgstr "" 301 | 302 | #. translators: Show the number of vulnerabilities in a WP-Admin dashboard 303 | #: wpvulnerability-admin.php:945 wpvulnerability-adminms.php:1031 304 | #, php-format 305 | msgid "MariaDB %s: " 306 | msgstr "" 307 | 308 | #. translators: Show the number of vulnerabilities in a WP-Admin dashboard 309 | #: wpvulnerability-admin.php:962 wpvulnerability-adminms.php:1048 310 | #, php-format 311 | msgid "MySQL %s: " 312 | msgstr "" 313 | 314 | #. translators: Show the number of vulnerabilities in a WP-Admin dashboard 315 | #: wpvulnerability-admin.php:977 wpvulnerability-adminms.php:1063 316 | #, php-format 317 | msgid "ImageMagick %s: " 318 | msgstr "" 319 | 320 | #. translators: Show the number of vulnerabilities in a WP-Admin dashboard 321 | #: wpvulnerability-admin.php:992 wpvulnerability-adminms.php:1078 322 | #, php-format 323 | msgid "curl %s: " 324 | msgstr "" 325 | 326 | #. translators: Show the number of vulnerabilities in a WP-Admin dashboard 327 | #: wpvulnerability-admin.php:1007 wpvulnerability-adminms.php:1093 328 | #, php-format 329 | msgid "memcached %s: " 330 | msgstr "" 331 | 332 | #. translators: Show the number of vulnerabilities in a WP-Admin dashboard 333 | #: wpvulnerability-admin.php:1022 wpvulnerability-adminms.php:1108 334 | #, php-format 335 | msgid "Redis %s: " 336 | msgstr "" 337 | 338 | #. translators: Show the number of vulnerabilities in a WP-Admin dashboard 339 | #: wpvulnerability-admin.php:1037 wpvulnerability-adminms.php:1123 340 | #, php-format 341 | msgid "SQLite %s: " 342 | msgstr "" 343 | 344 | #: wpvulnerability-admin.php:1041 wpvulnerability-adminms.php:1127 345 | msgid "Vulnerability analysis of your WordPress installation:" 346 | msgstr "" 347 | 348 | #: wpvulnerability-admin.php:1147 wpvulnerability-adminms.php:1233 349 | msgid "More information? Visit" 350 | msgstr "" 351 | 352 | #: wpvulnerability-admin.php:1147 wpvulnerability-adminms.php:1233 353 | msgid "Site Health" 354 | msgstr "" 355 | 356 | #: wpvulnerability-admin.php:1164 wpvulnerability-adminms.php:1251 357 | msgid "WPVulnerability Status" 358 | msgstr "" 359 | 360 | #: wpvulnerability-admin.php:1193 wpvulnerability-adminms.php:1286 361 | msgid "Receive notifications in your email" 362 | msgstr "" 363 | 364 | #: wpvulnerability-admin.php:1201 wpvulnerability-adminms.php:1294 365 | msgid "Email addresses to notify (separated by commas)" 366 | msgstr "" 367 | 368 | #: wpvulnerability-admin.php:1210 wpvulnerability-adminms.php:1303 369 | msgid "How often you want to receive notifications" 370 | msgstr "" 371 | 372 | #: wpvulnerability-admin.php:1226 wpvulnerability-adminms.php:1318 373 | msgid "Vulnerabilities to hide" 374 | msgstr "" 375 | 376 | #: wpvulnerability-admin.php:1234 wpvulnerability-adminms.php:1326 377 | msgid "What do you want to hide?" 378 | msgstr "" 379 | 380 | #: wpvulnerability-adminms.php:124 wpvulnerability-adminms.php:218 381 | msgid "Settings saved." 382 | msgstr "" 383 | 384 | #: wpvulnerability-adminms.php:307 wpvulnerability-adminms.php:321 385 | msgid "Save Changes" 386 | msgstr "" 387 | 388 | #. translators: 1: core version 389 | #: wpvulnerability-core.php:31 390 | #, php-format 391 | msgid "" 392 | "WordPress %1$s has a known vulnerability that may be affecting this version." 393 | msgstr "" 394 | 395 | #: wpvulnerability-core.php:69 wpvulnerability-plugins.php:106 396 | #: wpvulnerability-process.php:65 wpvulnerability-process.php:99 397 | #: wpvulnerability-themes.php:104 398 | msgid "Global score: " 399 | msgstr "" 400 | 401 | #: wpvulnerability-core.php:72 wpvulnerability-plugins.php:109 402 | #: wpvulnerability-process.php:68 wpvulnerability-process.php:102 403 | #: wpvulnerability-themes.php:107 404 | msgid "Severity: " 405 | msgstr "" 406 | 407 | #: wpvulnerability-general.php:632 408 | msgid "None" 409 | msgstr "" 410 | 411 | #: wpvulnerability-general.php:633 412 | msgid "Low" 413 | msgstr "" 414 | 415 | #: wpvulnerability-general.php:634 416 | msgid "Medium" 417 | msgstr "" 418 | 419 | #: wpvulnerability-general.php:635 420 | msgid "High" 421 | msgstr "" 422 | 423 | #: wpvulnerability-general.php:636 424 | msgid "Critical" 425 | msgstr "" 426 | 427 | #: wpvulnerability-notifications.php:29 428 | msgid "Every week" 429 | msgstr "" 430 | 431 | #: wpvulnerability-notifications.php:55 432 | msgid "Every day" 433 | msgstr "" 434 | 435 | #. translators: %1$s the website of Database, %2$s database site name. 436 | #: wpvulnerability-notifications.php:136 437 | #, php-format 438 | msgid "" 439 | "Learn more about the WordPress Vulnerability Database API at %2$s" 441 | msgstr "" 442 | 443 | #: wpvulnerability-notifications.php:223 444 | msgid "No vulnerabilities found" 445 | msgstr "" 446 | 447 | #: wpvulnerability-notifications.php:224 448 | msgid "This is likely a test. The site does not have vulnerabilities." 449 | msgstr "" 450 | 451 | #: wpvulnerability-notifications.php:229 452 | msgid "Core vulnerabilities" 453 | msgstr "" 454 | 455 | #: wpvulnerability-notifications.php:235 456 | msgid "Plugins vulnerabilities" 457 | msgstr "" 458 | 459 | #: wpvulnerability-notifications.php:241 460 | msgid "Themes vulnerabilities" 461 | msgstr "" 462 | 463 | #: wpvulnerability-notifications.php:247 464 | msgid "PHP vulnerabilities" 465 | msgstr "" 466 | 467 | #: wpvulnerability-notifications.php:253 468 | msgid "Apache HTTPD vulnerabilities" 469 | msgstr "" 470 | 471 | #: wpvulnerability-notifications.php:259 472 | msgid "Nginx vulnerabilities" 473 | msgstr "" 474 | 475 | #: wpvulnerability-notifications.php:265 476 | msgid "MariaDB vulnerabilities" 477 | msgstr "" 478 | 479 | #: wpvulnerability-notifications.php:271 480 | msgid "MySQL vulnerabilities" 481 | msgstr "" 482 | 483 | #: wpvulnerability-notifications.php:277 484 | msgid "ImageMagick vulnerabilities" 485 | msgstr "" 486 | 487 | #: wpvulnerability-notifications.php:283 488 | msgid "curl vulnerabilities" 489 | msgstr "" 490 | 491 | #: wpvulnerability-notifications.php:289 492 | msgid "memcached vulnerabilities" 493 | msgstr "" 494 | 495 | #: wpvulnerability-notifications.php:295 496 | msgid "Redis vulnerabilities" 497 | msgstr "" 498 | 499 | #: wpvulnerability-notifications.php:301 500 | msgid "SQLite vulnerabilities" 501 | msgstr "" 502 | 503 | #. translators: Site name. 504 | #: wpvulnerability-notifications.php:324 505 | #, php-format 506 | msgid "Vulnerability found: %s" 507 | msgstr "" 508 | 509 | #: wpvulnerability-notifications.php:328 510 | msgid "Vulnerability found" 511 | msgstr "" 512 | 513 | #. translators: 1: Plugin name 514 | #. translators: 1: theme name 515 | #: wpvulnerability-plugins.php:47 wpvulnerability-themes.php:41 516 | #, php-format 517 | msgid "%1$s has a known vulnerability that may be affecting this version." 518 | msgstr "" 519 | 520 | #: wpvulnerability-plugins.php:89 wpvulnerability-process.php:50 521 | msgid "This plugin is closed. Please replace it with another." 522 | msgstr "" 523 | 524 | #: wpvulnerability-plugins.php:92 wpvulnerability-process.php:53 525 | msgid "" 526 | "This vulnerability appears to be unpatched. Stay tuned for upcoming plugin " 527 | "updates." 528 | msgstr "" 529 | 530 | #: wpvulnerability-plugins.php:439 531 | msgid "It hasn't been updated in over a year." 532 | msgstr "" 533 | 534 | #: wpvulnerability-plugins.php:445 535 | msgid "It may no longer be available (closed?)." 536 | msgstr "" 537 | 538 | #: wpvulnerability-plugins.php:483 wpvulnerability-plugins.php:490 539 | msgid "Last updated on" 540 | msgstr "" 541 | 542 | #. translators: the number of vulnerabilities. 543 | #: wpvulnerability-plugins.php:623 wpvulnerability-themes.php:368 544 | #, php-format 545 | msgid "Vulnerabilities (%d)" 546 | msgstr "" 547 | 548 | #. translators: the type of vulnerability. 549 | #: wpvulnerability-process.php:171 550 | #, php-format 551 | msgid "%s running" 552 | msgstr "" 553 | 554 | #: wpvulnerability-process.php:198 555 | msgid "Plugin" 556 | msgstr "" 557 | 558 | #: wpvulnerability-process.php:259 559 | msgid "Theme" 560 | msgstr "" 561 | 562 | #: wpvulnerability-run.php:30 563 | msgid "Network Settings" 564 | msgstr "" 565 | 566 | #: wpvulnerability-run.php:33 567 | msgid "Settings" 568 | msgstr "" 569 | 570 | #: wpvulnerability-run.php:533 wpvulnerability-run.php:582 571 | #: wpvulnerability-run.php:592 wpvulnerability-run.php:636 572 | msgid "Vulnerabilities" 573 | msgstr "" 574 | 575 | #: wpvulnerability-sitehealth.php:22 576 | msgid "There aren't plugins vulnerabilities" 577 | msgstr "" 578 | 579 | #: wpvulnerability-sitehealth.php:25 wpvulnerability-sitehealth.php:77 580 | #: wpvulnerability-sitehealth.php:129 wpvulnerability-sitehealth.php:210 581 | msgid "Security" 582 | msgstr "" 583 | 584 | #: wpvulnerability-sitehealth.php:30 585 | msgid "Shows possible vulnerabilities that exist in installed plugins." 586 | msgstr "" 587 | 588 | #. translators: Number of plugins vulnerabilities. 589 | #: wpvulnerability-sitehealth.php:43 590 | #, php-format 591 | msgid "There is %d plugin with vulnerabilities" 592 | msgid_plural "There are %d plugins with vulnerabilities" 593 | msgstr[0] "" 594 | msgstr[1] "" 595 | 596 | #: wpvulnerability-sitehealth.php:49 597 | msgid "" 598 | "We've detected potential vulnerabilities in installed plugins. Please check " 599 | "them and keep them updated." 600 | msgstr "" 601 | 602 | #: wpvulnerability-sitehealth.php:57 603 | msgid "Update plugins" 604 | msgstr "" 605 | 606 | #: wpvulnerability-sitehealth.php:74 607 | msgid "There aren't themes vulnerabilities" 608 | msgstr "" 609 | 610 | #: wpvulnerability-sitehealth.php:82 611 | msgid "Shows possible vulnerabilities that exist in installed themes." 612 | msgstr "" 613 | 614 | #. translators: Number of themes vulnerabilities. 615 | #: wpvulnerability-sitehealth.php:95 616 | #, php-format 617 | msgid "There is %d theme with vulnerabilities" 618 | msgid_plural "There are %d themes with vulnerabilities" 619 | msgstr[0] "" 620 | msgstr[1] "" 621 | 622 | #: wpvulnerability-sitehealth.php:101 623 | msgid "" 624 | "We've detected potential vulnerabilities in installed themes. Please check " 625 | "them and keep them updated." 626 | msgstr "" 627 | 628 | #: wpvulnerability-sitehealth.php:109 629 | msgid "Update themes" 630 | msgstr "" 631 | 632 | #: wpvulnerability-sitehealth.php:126 633 | msgid "There aren't WordPress vulnerabilities" 634 | msgstr "" 635 | 636 | #: wpvulnerability-sitehealth.php:134 637 | msgid "Shows possible vulnerabilities existing in the WordPress core." 638 | msgstr "" 639 | 640 | #. translators: Number of core vulnerabilities. 641 | #: wpvulnerability-sitehealth.php:147 642 | #, php-format 643 | msgid "There is %d core vulnerability" 644 | msgid_plural "There are %d core vulnerabilities" 645 | msgstr[0] "" 646 | msgstr[1] "" 647 | 648 | #: wpvulnerability-sitehealth.php:153 649 | msgid "" 650 | "We've detected potential vulnerabilities in this WordPress installation. " 651 | "Please check them and keep your installation updated." 652 | msgstr "" 653 | 654 | #: wpvulnerability-sitehealth.php:161 655 | msgid "Update WordPress" 656 | msgstr "" 657 | 658 | #: wpvulnerability-sitehealth.php:180 659 | msgid "Nginx" 660 | msgstr "" 661 | 662 | #: wpvulnerability-sitehealth.php:189 663 | msgid "Invalid software type" 664 | msgstr "" 665 | 666 | #: wpvulnerability-sitehealth.php:192 667 | msgid "Error" 668 | msgstr "" 669 | 670 | #: wpvulnerability-sitehealth.php:197 671 | msgid "The specified software type is not valid." 672 | msgstr "" 673 | 674 | #. translators: name of the software. 675 | #: wpvulnerability-sitehealth.php:207 676 | #, php-format 677 | msgid "There aren't %s vulnerabilities" 678 | msgstr "" 679 | 680 | #. translators: software with vulnerabilities. 681 | #: wpvulnerability-sitehealth.php:216 682 | #, php-format 683 | msgid "Shows possible vulnerabilities existing in %s." 684 | msgstr "" 685 | 686 | #. translators: Software and number of vulnerabilities. 687 | #: wpvulnerability-sitehealth.php:229 688 | #, php-format 689 | msgid "There is %1$d %2$s vulnerability" 690 | msgid_plural "There are %1$d %2$s vulnerabilities" 691 | msgstr[0] "" 692 | msgstr[1] "" 693 | 694 | #. translators: software with vulnerabilities. 695 | #: wpvulnerability-sitehealth.php:237 696 | #, php-format 697 | msgid "" 698 | "We've detected potential vulnerabilities in %s. Please check them and keep " 699 | "your installation updated." 700 | msgstr "" 701 | 702 | #: wpvulnerability-sitehealth.php:246 703 | msgid "How to update PHP" 704 | msgstr "" 705 | 706 | #: wpvulnerability-sitehealth.php:361 707 | msgid "WPVulnerability Core" 708 | msgstr "" 709 | 710 | #: wpvulnerability-sitehealth.php:369 711 | msgid "WPVulnerability Themes" 712 | msgstr "" 713 | 714 | #: wpvulnerability-sitehealth.php:377 715 | msgid "WPVulnerability Plugins" 716 | msgstr "" 717 | 718 | #: wpvulnerability-sitehealth.php:385 719 | msgid "WPVulnerability PHP" 720 | msgstr "" 721 | 722 | #: wpvulnerability-sitehealth.php:393 723 | msgid "WPVulnerability Apache HTTPD" 724 | msgstr "" 725 | 726 | #: wpvulnerability-sitehealth.php:401 727 | msgid "WPVulnerability Nginx" 728 | msgstr "" 729 | 730 | #: wpvulnerability-sitehealth.php:409 731 | msgid "WPVulnerability MariaDB" 732 | msgstr "" 733 | 734 | #: wpvulnerability-sitehealth.php:417 735 | msgid "WPVulnerability MySQL" 736 | msgstr "" 737 | 738 | #: wpvulnerability-sitehealth.php:425 739 | msgid "WPVulnerability ImageMagick" 740 | msgstr "" 741 | 742 | #: wpvulnerability-sitehealth.php:433 743 | msgid "WPVulnerability curl" 744 | msgstr "" 745 | 746 | #: wpvulnerability-sitehealth.php:441 747 | msgid "WPVulnerability memcached" 748 | msgstr "" 749 | 750 | #: wpvulnerability-sitehealth.php:449 751 | msgid "WPVulnerability redis" 752 | msgstr "" 753 | 754 | #: wpvulnerability-sitehealth.php:457 755 | msgid "WPVulnerability sqlite" 756 | msgstr "" 757 | 758 | #: wpvulnerability-themes.php:87 759 | msgid "This theme is closed. Please replace it with another." 760 | msgstr "" 761 | 762 | #: wpvulnerability-themes.php:90 763 | msgid "" 764 | "This vulnerability appears to be unpatched. Stay tuned for upcoming theme " 765 | "updates." 766 | msgstr "" 767 | 768 | #. Plugin URI of the plugin/theme 769 | msgid "https://www.wpvulnerability.com/" 770 | msgstr "" 771 | 772 | #. Description of the plugin/theme 773 | msgid "" 774 | "Receive information about possible vulnerabilities in your WordPress from " 775 | "WordPress Vulnerability Database API." 776 | msgstr "" 777 | 778 | #. Author of the plugin/theme 779 | msgid "Javier Casares" 780 | msgstr "" 781 | 782 | #. Author URI of the plugin/theme 783 | msgid "https://www.javiercasares.com/" 784 | msgstr "" 785 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === WPVulnerability === 2 | Contributors: javiercasares, davidperez, lbonomo, alexclassroom 3 | Tags: security, vulnerability, site-health 4 | Requires at least: 4.1 5 | Tested up to: 6.8 6 | Stable tag: 4.0.4 7 | Requires PHP: 5.6 8 | Version: 4.0.4 9 | License: GPL-2.0-or-later 10 | License URI: https://spdx.org/licenses/GPL-2.0-or-later.html 11 | 12 | Get WordPress vulnerability alerts from the [WPVulnerability Database API](https://www.wpvulnerability.com/). 13 | 14 | == Description == 15 | 16 | This plugin integrates with the WPVulnerability API to provide real-time vulnerability assessments for your WordPress core, plugins, themes, PHP version, Apache HTTPD, nginx, MariaDB, MySQL, ImageMagick, curl, memcached, Redis, and SQLite. 17 | 18 | It delivers detailed reports directly within your WordPress dashboard, helping you stay aware of potential security risks. Configure the plugin to send periodic notifications about your site's security status, ensuring you remain informed without being overwhelmed. Designed for ease of use, it supports proactive security measures without storing or retrieving any personal data from your site. 19 | 20 | = Data reliability = 21 | 22 | The information provided by the information database comes from different sources that have been reviewed by third parties. There is no liability of any kind for the information. Act at your own risk. 23 | 24 | == Using the plugin == 25 | 26 | = WP-CLI = 27 | 28 | You can use the following WP-CLI commands to manage and check vulnerabilities: 29 | 30 | * Core: `wp wpvulnerability core` 31 | * Plugins: `wp wpvulnerability plugins` 32 | * Themes: `wp wpvulnerability themes` 33 | * PHP: `wp wpvulnerability php` 34 | * Apache HTTPD: `wp wpvulnerability apache` 35 | * nginx: `wp wpvulnerability nginx` 36 | * MariaDB: `wp wpvulnerability mariadb` 37 | * MySQL: `wp wpvulnerability mysql` 38 | * ImageMagick: `wp wpvulnerability imagemagick` 39 | * curl: `wp wpvulnerability curl` 40 | * memcached: `wp wpvulnerability memcached` 41 | * Redis: `wp wpvulnerability redis` 42 | * SQLite: `wp wpvulnerability sqlite` 43 | 44 | All commands support the `--format` option to specify the output format: 45 | 46 | * `--format=table`: Displays the results in a table format (default). 47 | * `--format=json`: Displays the results in JSON format. 48 | 49 | Need help? 50 | 51 | * `wp wpvulnerability --help`: Displays help information for WPVulnerability commands. 52 | * `wp wpvulnerability [command] --help`: Displays help information for a WPVulnerability command. 53 | 54 | = REST API = 55 | 56 | The WPVulnerability plugin provides several **REST API endpoints** to fetch vulnerability information for different components of your WordPress site. 57 | 58 | * Core: `/wpvulnerability/v1/core` 59 | * Plugins: `/wpvulnerability/v1/plugins` 60 | * Themes: `/wpvulnerability/v1/themes` 61 | * PHP: `/wpvulnerability/v1/php` 62 | * Apache HTTPD: `/wpvulnerability/v1/apache` 63 | * nginx: `/wpvulnerability/v1/nginx` 64 | * MariaDB: `/wpvulnerability/v1/mariadb` 65 | * MySQL: `/wpvulnerability/v1/mysql` 66 | * ImageMagick: `/wpvulnerability/v1/imagemagick` 67 | * curl: `/wpvulnerability/v1/curl` 68 | * memcached: `/wpvulnerability/v1/memcached` 69 | * Redis: `/wpvulnerability/v1/redis` 70 | * SQLite: `/wpvulnerability/v1/sqlite` 71 | 72 | The WPVulnerability REST API uses **Application Passwords** for authentication. You need to include a valid Application Password in the Authorization header of your requests. 73 | 74 | Example Request with Authentication 75 | 76 | `curl -X GET https://example.com/wp-json/wpvulnerability/v1/plugins -u username:application_password` 77 | 78 | Replace username with your WordPress `username` and `application_password` with your [Application Password](https://make.wordpress.org/core/2020/11/05/application-passwords-integration-guide/). 79 | 80 | == Extra Configurations == 81 | 82 | = "From:" mail (since: 3.2.2) = 83 | 84 | If, for some reason, you need the emails sent by the plugin to have a From different from the site administrator, you can change it from the `wp-config.php` by adding a constant: 85 | 86 | `define( 'WPVULNERABILITY_MAIL', 'sender@example.com' );` 87 | 88 | If the constant is active, it will be visible in the configuration screen. 89 | 90 | == Installation == 91 | 92 | = Automatic download = 93 | 94 | Visit the plugin section in your WordPress, search for [wpvulnerability]; download and install the plugin. 95 | 96 | = Manual download = 97 | 98 | Extract the contents of the ZIP and upload the contents to the `/wp-content/plugins/wpvulnerability/` directory. Once uploaded, it will appear in your plugin list. 99 | 100 | == Frequently Asked Questions == 101 | 102 | = Where does the vulnerability information come from? = 103 | 104 | The origin is in the WPVulnerability.com API. The vulnerabilities that appear in this API come from different sources, such as CVEs. 105 | 106 | = Is data from my site sent anywhere? = 107 | 108 | No. Never. Your privacy is very important to us. We do not commercialize with your data. 109 | 110 | = What vulnerabilities will I find? = 111 | 112 | Vulnerabilities in WordPress Core, Plugins, Themes, PHP, Apache HTTPD, nginx, MariaDB, MySQL, ImageMagick, curl, memcached, Redis, and SQLite are documented. 113 | 114 | = What do I do if my site has a vulnerability? = 115 | 116 | First of all, peace of mind. Investigate what the vulnerability is and, above all, check that you have the latest version of the compromised element. We actively recommend that you keep all your WordPress and its plugins up to date. Contact your hosting provider to patch non-WordPress vulnerabilities (like web server, databases, and other software). 117 | 118 | == Screenshots == 119 | 120 | 1. WP-Admin Dashboard widget. 121 | 2. Vulnerability list at Plugins list. 122 | 3. Vulnerability list at Site Health. 123 | 124 | == Compatibility == 125 | 126 | * WordPress: 4.1 - 6.8 127 | * PHP: 5.6 - 8.4 128 | * WP-CLI: 2.3.0 - 2.11.0 129 | 130 | == Changelog == 131 | 132 | = [4.0.4] - 2025-04-07 = 133 | 134 | **Added** 135 | 136 | * Extra sanitizations. 137 | 138 | **Changed** 139 | 140 | * Translation improvements. 141 | 142 | **Fixed** 143 | 144 | * Plugin and translation load. 145 | 146 | **Compatibility** 147 | 148 | * WordPress: 4.1 - 6.8 149 | * PHP: 5.6 - 8.4 150 | * WP-CLI: 2.3.0 - 2.11.0 151 | 152 | **Tests** 153 | 154 | * PHP Coding Standards: 3.12.1 155 | * WordPress Coding Standards: 3.1.0 156 | * Plugin Check (PCP): 1.4.0 157 | * SonarCloud Code Review 158 | 159 | = [4.0.3] - 2024-10-28 = 160 | 161 | * Recreation of the 4.0.2 version. Something did not created the 4.0.2 version. 162 | 163 | = [4.0.2] - 2024-10-25 = 164 | 165 | **Fixed** 166 | 167 | * ImageMagick: it crashes in some cases where the hosting does not have ImageMagick. 168 | 169 | **Compatibility** 170 | 171 | * WordPress: 4.1 - 6.7 172 | * PHP: 5.6 - 8.4 173 | * WP-CLI: 2.3.0 - 2.11.0 174 | 175 | **Tests** 176 | 177 | * PHP Coding Standards: 3.10.3 178 | * WordPress Coding Standards: 3.1.0 179 | * Plugin Check (PCP): 1.1.0 180 | * SonarCloud Code Review 181 | 182 | = [4.0.1] - 2024-10-04 = 183 | 184 | **Fixed** 185 | 186 | * API endpoints: some API endpoints were failing. 187 | * CLI endpoints: some CLI endpoints were failing. 188 | 189 | **Compatibility** 190 | 191 | * WordPress: 4.1 - 6.7 192 | * PHP: 5.6 - 8.4 193 | * WP-CLI: 2.3.0 - 2.11.0 194 | 195 | **Tests** 196 | 197 | * PHP Coding Standards: 3.10.3 198 | * WordPress Coding Standards: 3.1.0 199 | * Plugin Check (PCP): 1.1.0 200 | * SonarCloud Code Review 201 | 202 | = [4.0.0] - 2024-10-01 = 203 | 204 | **Added** 205 | 206 | * ImageMagic vulnerabilities (Site Health + WP-CLI + API + mail). 207 | * curl vulnerabilities (Site Health + WP-CLI + API + mail). 208 | * memcached vulnerabilities (Site Health + WP-CLI + API + mail). 209 | * Redis vulnerabilities (Site Health + WP-CLI + API + mail). 210 | * SQLite vulnerabilities (Site Health + WP-CLI + API + mail). 211 | 212 | **Fixed** 213 | 214 | * Test email without email. 215 | * Improved MariaDB 11.x detection. 216 | * Improved versions detection (major-minor.patch-build). 217 | * WordPress < 5.3: use of wp_date(). 218 | * WordPress < 5.0: locale detection. 219 | * Dashboard widget only for users with capabilities. 220 | * WordPress < 5.2: link to Site Health 221 | 222 | **Changed** 223 | 224 | * Big refactory. 225 | * Less files, less size, improved code quality. 226 | 227 | **Compatibility** 228 | 229 | * WordPress: 4.1 - 6.7 230 | * PHP: 5.6 - 8.4 231 | * WP-CLI: 2.3.0 - 2.11.0 232 | 233 | **Tests** 234 | 235 | * Manual Testing: 236 | * WordPress 6.7 / PHP 8.4 237 | * WordPress 6.6 / PHP 8.3 238 | * WordPress 6.4 / PHP 8.2 239 | * WordPress 6.1 / PHP 8.1 240 | * WordPress 5.8 / PHP 8.0 241 | * WordPress 5.5 / PHP 7.4 242 | * WordPress 5.3 / PHP 7.3 243 | * WordPress 4.9 / PHP 7.2 244 | * WordPress 4.8 / PHP 7.1 245 | * WordPress 4.6 / PHP 7.0 246 | * WordPress 4.1 / PHP 5.6 247 | * PHP Coding Standards: 3.10.3 248 | * WordPress Coding Standards: 3.1.0 249 | * Plugin Check (PCP): 1.1.0 250 | * SonarCloud Code Review 251 | 252 | = Previous versions = 253 | 254 | If you want to see the full changelog, visit the [changelog.txt](https://plugins.trac.wordpress.org/browser/wpvulnerability/trunk/changelog.txt) file. 255 | 256 | == Security == 257 | 258 | This plugin adheres to the following security measures and review protocols for each version: 259 | 260 | * [WordPress Plugin Handbook](https://developer.wordpress.org/plugins/) 261 | * [WordPress Plugin Security](https://developer.wordpress.org/plugins/wordpress-org/plugin-security/) 262 | * [WordPress APIs Security](https://developer.wordpress.org/apis/security/) 263 | * [WordPress Coding Standards](https://github.com/WordPress/WordPress-Coding-Standards) 264 | * [Plugin Check (PCP)](https://wordpress.org/plugins/plugin-check/) 265 | * [SonarCloud Code Review](https://www.sonarsource.com/products/sonarcloud/) 266 | 267 | == Privacy == 268 | 269 | * This plugin or the WordPress Vulnerability Database API does not collect any information about your site, your identity, the plugins, themes or content the site has. 270 | 271 | == Vulnerabilities == 272 | 273 | * No vulnerabilities have been published up to version 4.0.4. 274 | 275 | Found a security vulnerability? Please report it to us privately at the [WPVulnerability GitHub repository](https://github.com/javiercasares/wpvulnerability/security/advisories/new). 276 | 277 | == Contributors == 278 | 279 | You can contribute to this plugin at the [WPVulnerability GitHub repository](https://github.com/javiercasares/wpvulnerability). 280 | -------------------------------------------------------------------------------- /wpvulnerability-api.php: -------------------------------------------------------------------------------- 1 | trim( html_entity_decode( wp_kses( (string) $vulnerability_cwe['name'], 'strip' ) ) ), 52 | 'description' => trim( html_entity_decode( wp_kses( (string) $vulnerability_cwe['description'], 'strip' ) ) ), 53 | ); 54 | } 55 | } 56 | 57 | // Process CVSS score. 58 | $core_complete_temp['score'] = null; 59 | if ( isset( $vulnerability['impact']['cvss']['score'] ) ) { 60 | $core_complete_temp['score'] = number_format( (float) $vulnerability['impact']['cvss']['score'], 1, '.', '' ); 61 | } 62 | 63 | // Process vulnerability sources. 64 | $core_complete_temp['source'] = array(); 65 | if ( isset( $vulnerability['source'] ) && count( $vulnerability['source'] ) ) { 66 | foreach ( $vulnerability['source'] as $vulnerability_source ) { 67 | $core_complete_temp['source'][] = array( 68 | 'name' => trim( html_entity_decode( wp_kses( (string) $vulnerability_source['name'], 'strip' ) ) ), 69 | 'link' => esc_url_raw( (string) $vulnerability_source['link'], 'strip' ), 70 | 'description' => trim( html_entity_decode( wp_kses( (string) $vulnerability_source['description'], 'strip' ) ) ), 71 | ); 72 | } 73 | } 74 | 75 | $core_complete[] = $core_complete_temp; 76 | unset( $core_complete_temp ); 77 | } 78 | } 79 | 80 | // Return the vulnerabilities in the response. 81 | return new WP_REST_Response( $core_complete, 200 ); 82 | } 83 | 84 | /** 85 | * Handle the plugins vulnerabilities REST API request. 86 | * 87 | * This function handles the request for retrieving plugins vulnerabilities. 88 | * It includes the necessary files and fetches the vulnerabilities data. 89 | * 90 | * @since 3.3.0 91 | * 92 | * @return WP_REST_Response Plugins vulnerabilities data or a message if none found. 93 | */ 94 | function wpvulnerability_rest_plugins_vulnerabilities() { 95 | // Include the files containing the functions to get plugins vulnerabilities. 96 | require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-general.php'; 97 | require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-plugins.php'; 98 | 99 | // Get the plugins vulnerabilities. 100 | $plugins_vulnerabilities = wpvulnerability_plugin_get_vulnerabilities(); 101 | 102 | $plugins_complete = array(); 103 | 104 | // Loop through each plugin vulnerability. 105 | foreach ( $plugins_vulnerabilities as $plugin ) { 106 | // Check if the plugin is vulnerable. 107 | if ( 1 === $plugin['vulnerable'] ) { 108 | $plugins_complete_temp = array(); 109 | 110 | // Process plugin name and slug. 111 | $plugins_complete_temp['name'] = trim( html_entity_decode( wp_kses( (string) $plugin['Name'], 'strip' ) ) ); 112 | $plugins_complete_temp['slug'] = trim( html_entity_decode( wp_kses( (string) $plugin['slug'], 'strip' ) ) ); 113 | 114 | // Prepare the vulnerabilities array for output. 115 | foreach ( $plugin['vulnerabilities'] as $vulnerability ) { 116 | $plugins_complete_temp_vulnerabilities = array(); 117 | 118 | // Process vulnerability severity. 119 | $plugins_complete_temp_vulnerabilities['severity'] = null; 120 | if ( isset( $vulnerability['impact']['cvss']['severity'] ) ) { 121 | $plugins_complete_temp_vulnerabilities['severity'] = wpvulnerability_severity( $vulnerability['impact']['cvss']['severity'] ); 122 | } 123 | 124 | // Process vulnerability details. 125 | $plugins_complete_temp_vulnerabilities['version'] = trim( html_entity_decode( wp_kses( (string) $vulnerability['version'], 'strip' ) ) ); 126 | $plugins_complete_temp_vulnerabilities['affected'] = trim( html_entity_decode( wp_kses( (string) $vulnerability['versions'], 'strip' ) ) ); 127 | $plugins_complete_temp_vulnerabilities['name'] = trim( html_entity_decode( wp_kses( (string) $vulnerability['name'], 'strip' ) ) ); 128 | $plugins_complete_temp_vulnerabilities['closed'] = (int) $vulnerability['closed']; 129 | $plugins_complete_temp_vulnerabilities['unfixed'] = (int) $vulnerability['unfixed']; 130 | 131 | // Process CWE details. 132 | $plugins_complete_temp_vulnerabilities['cwe'] = array(); 133 | if ( isset( $vulnerability['impact']['cwe'] ) && count( $vulnerability['impact']['cwe'] ) ) { 134 | foreach ( $vulnerability['impact']['cwe'] as $vulnerability_cwe ) { 135 | $plugins_complete_temp_vulnerabilities['cwe'][] = array( 136 | 'name' => trim( html_entity_decode( wp_kses( (string) $vulnerability_cwe['name'], 'strip' ) ) ), 137 | 'description' => trim( html_entity_decode( wp_kses( (string) $vulnerability_cwe['description'], 'strip' ) ) ), 138 | ); 139 | } 140 | } 141 | 142 | // Process CVSS score. 143 | $plugins_complete_temp_vulnerabilities['score'] = null; 144 | if ( isset( $vulnerability['impact']['cvss']['score'] ) ) { 145 | $plugins_complete_temp_vulnerabilities['score'] = number_format( (float) $vulnerability['impact']['cvss']['score'], 1, '.', '' ); 146 | } 147 | 148 | // Process vulnerability sources. 149 | $plugins_complete_temp_vulnerabilities['source'] = array(); 150 | if ( isset( $vulnerability['source'] ) && count( $vulnerability['source'] ) ) { 151 | foreach ( $vulnerability['source'] as $vulnerability_source ) { 152 | $plugins_complete_temp_vulnerabilities['source'][] = array( 153 | 'name' => trim( html_entity_decode( wp_kses( (string) $vulnerability_source['name'], 'strip' ) ) ), 154 | 'link' => esc_url_raw( (string) $vulnerability_source['link'], 'strip' ), 155 | ); 156 | } 157 | } 158 | 159 | // Add processed vulnerability to the temporary array. 160 | $plugins_complete_temp['vulnerabilities'][] = $plugins_complete_temp_vulnerabilities; 161 | } 162 | 163 | // Add processed plugin data to the complete array. 164 | $plugins_complete[] = $plugins_complete_temp; 165 | } 166 | } 167 | 168 | // Return the vulnerabilities in the response. 169 | return new WP_REST_Response( $plugins_complete, 200 ); 170 | } 171 | 172 | /** 173 | * Handle the themes vulnerabilities REST API request. 174 | * 175 | * This function handles the request for retrieving themes vulnerabilities. 176 | * It includes the necessary files and fetches the vulnerabilities data. 177 | * 178 | * @since 3.3.0 179 | * 180 | * @return WP_REST_Response Themes vulnerabilities data or a message if none found. 181 | */ 182 | function wpvulnerability_rest_themes_vulnerabilities() { 183 | // Include the file containing the function to get themes vulnerabilities. 184 | require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-general.php'; 185 | require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-themes.php'; 186 | 187 | // Get the themes vulnerabilities. 188 | $themes_vulnerabilities = wpvulnerability_theme_get_vulnerabilities(); 189 | 190 | $themes_complete = array(); 191 | 192 | // Loop through each theme vulnerability. 193 | foreach ( $themes_vulnerabilities as $theme ) { 194 | // Check if the theme is vulnerable. 195 | if ( 1 === $theme['wpvulnerability']['vulnerable'] ) { 196 | $themes_complete_temp = array(); 197 | 198 | // Process theme name and slug. 199 | $themes_complete_temp['name'] = trim( html_entity_decode( wp_kses( (string) $theme['wpvulnerability']['name'], 'strip' ) ) ); 200 | $themes_complete_temp['slug'] = trim( html_entity_decode( wp_kses( (string) $theme['wpvulnerability']['slug'], 'strip' ) ) ); 201 | 202 | // Prepare the vulnerabilities array for output. 203 | foreach ( $theme['wpvulnerability']['vulnerabilities'] as $vulnerability ) { 204 | $themes_complete_temp_vulnerabilities = array(); 205 | 206 | // Process vulnerability severity. 207 | $themes_complete_temp_vulnerabilities['severity'] = null; 208 | if ( isset( $vulnerability['impact']['cvss']['severity'] ) ) { 209 | $themes_complete_temp_vulnerabilities['severity'] = wpvulnerability_severity( $vulnerability['impact']['cvss']['severity'] ); 210 | } 211 | 212 | // Process vulnerability details. 213 | $themes_complete_temp_vulnerabilities['version'] = trim( html_entity_decode( wp_kses( (string) $vulnerability['version'], 'strip' ) ) ); 214 | $themes_complete_temp_vulnerabilities['affected'] = trim( html_entity_decode( wp_kses( (string) $vulnerability['versions'], 'strip' ) ) ); 215 | $themes_complete_temp_vulnerabilities['name'] = trim( html_entity_decode( wp_kses( (string) $vulnerability['name'], 'strip' ) ) ); 216 | $themes_complete_temp_vulnerabilities['closed'] = (int) $vulnerability['closed']; 217 | $themes_complete_temp_vulnerabilities['unfixed'] = (int) $vulnerability['unfixed']; 218 | 219 | // Process CWE details. 220 | $themes_complete_temp_vulnerabilities['cwe'] = array(); 221 | if ( isset( $vulnerability['impact']['cwe'] ) && count( $vulnerability['impact']['cwe'] ) ) { 222 | foreach ( $vulnerability['impact']['cwe'] as $vulnerability_cwe ) { 223 | $themes_complete_temp_vulnerabilities['cwe'][] = array( 224 | 'name' => trim( html_entity_decode( wp_kses( (string) $vulnerability_cwe['name'], 'strip' ) ) ), 225 | 'description' => trim( html_entity_decode( wp_kses( (string) $vulnerability_cwe['description'], 'strip' ) ) ), 226 | ); 227 | } 228 | } 229 | 230 | // Process CVSS score. 231 | $themes_complete_temp_vulnerabilities['score'] = null; 232 | if ( isset( $vulnerability['impact']['cvss']['score'] ) ) { 233 | $themes_complete_temp_vulnerabilities['score'] = number_format( (float) $vulnerability['impact']['cvss']['score'], 1, '.', '' ); 234 | } 235 | 236 | // Process vulnerability sources. 237 | $themes_complete_temp_vulnerabilities['source'] = array(); 238 | if ( isset( $vulnerability['source'] ) && count( $vulnerability['source'] ) ) { 239 | foreach ( $vulnerability['source'] as $vulnerability_source ) { 240 | $themes_complete_temp_vulnerabilities['source'][] = array( 241 | 'name' => trim( html_entity_decode( wp_kses( (string) $vulnerability_source['name'], 'strip' ) ) ), 242 | 'link' => esc_url_raw( (string) $vulnerability_source['link'], 'strip' ), 243 | ); 244 | } 245 | } 246 | 247 | // Add processed vulnerability to the temporary array. 248 | $themes_complete_temp['vulnerabilities'][] = $themes_complete_temp_vulnerabilities; 249 | } 250 | 251 | // Add processed theme data to the complete array. 252 | $themes_complete[] = $themes_complete_temp; 253 | } 254 | } 255 | 256 | // Return the vulnerabilities in the response. 257 | return new WP_REST_Response( $themes_complete, 200 ); 258 | } 259 | 260 | /** 261 | * Handle vulnerabilities REST API request for different software types. 262 | * 263 | * This function processes the request to retrieve vulnerabilities for the specified software type. 264 | * It loads the necessary files and fetches the vulnerability data, then returns the data in a structured format. 265 | * 266 | * @since 3.5.0 267 | * 268 | * @param string $software_type The type of software to retrieve vulnerabilities for. 269 | * @return WP_REST_Response The vulnerabilities data or an empty array if none found. 270 | */ 271 | function wpvulnerability_rest_software_vulnerabilities( $software_type ) { 272 | // Include the general file for retrieving vulnerabilities. 273 | require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-general.php'; 274 | require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-software.php'; 275 | 276 | // Get vulnerabilities based on the software type. 277 | $vulnerabilities = array(); 278 | switch ( $software_type ) { 279 | case 'php': 280 | case 'apache': 281 | case 'nginx': 282 | case 'mariadb': 283 | case 'mysql': 284 | case 'imagemagick': 285 | case 'curl': 286 | case 'memcached': 287 | case 'redis': 288 | case 'sqlite': 289 | $vulnerabilities = wpvulnerability_get_vulnerabilities( $software_type, wpvulnerability_get_software_version( $software_type ) ); 290 | break; 291 | default: 292 | WP_REST_Response( array(), 400 ); // Invalid software type. 293 | } 294 | 295 | $complete_vulnerabilities = array(); 296 | 297 | if ( isset( $vulnerabilities ) && is_array( $vulnerabilities ) ) { 298 | // Process each vulnerability. 299 | foreach ( $vulnerabilities as $vulnerability ) { 300 | $temp = array(); 301 | 302 | $temp['version'] = trim( html_entity_decode( wp_kses( (string) $vulnerability['version'], 'strip' ) ) ); 303 | $temp['affected'] = trim( html_entity_decode( wp_kses( (string) $vulnerability['versions'], 'strip' ) ) ); 304 | $temp['unfixed'] = (int) $vulnerability['unfixed']; 305 | 306 | // Process vulnerability sources. 307 | $temp['source'] = array(); 308 | if ( isset( $vulnerability['source'] ) && count( $vulnerability['source'] ) ) { 309 | foreach ( $vulnerability['source'] as $source ) { 310 | $temp['source'][] = array( 311 | 'name' => trim( html_entity_decode( wp_kses( (string) $source['id'], 'strip' ) ) ), 312 | 'description' => trim( html_entity_decode( wp_kses( (string) $source['description'], 'strip' ) ) ), 313 | 'link' => esc_url_raw( (string) $source['link'], 'strip' ), 314 | ); 315 | } 316 | } 317 | 318 | // Add processed vulnerability to the complete array. 319 | $complete_vulnerabilities[] = $temp; 320 | } 321 | } 322 | 323 | // Return the vulnerabilities in the response. 324 | return new WP_REST_Response( $complete_vulnerabilities, 200 ); 325 | } 326 | 327 | /** 328 | * Handle the PHP vulnerabilities REST API request. 329 | * 330 | * @since 3.3.0 331 | * 332 | * @return WP_REST_Response PHP vulnerabilities data or a message if none found. 333 | */ 334 | function wpvulnerability_rest_php_vulnerabilities() { 335 | return wpvulnerability_rest_software_vulnerabilities( 'php' ); 336 | } 337 | 338 | /** 339 | * Handle the Apache vulnerabilities REST API request. 340 | * 341 | * @since 3.3.0 342 | * 343 | * @return WP_REST_Response Apache vulnerabilities data or a message if none found. 344 | */ 345 | function wpvulnerability_rest_apache_vulnerabilities() { 346 | return wpvulnerability_rest_software_vulnerabilities( 'apache' ); 347 | } 348 | 349 | /** 350 | * Handle the Nginx vulnerabilities REST API request. 351 | * 352 | * @since 3.3.0 353 | * 354 | * @return WP_REST_Response Nginx vulnerabilities data or a message if none found. 355 | */ 356 | function wpvulnerability_rest_nginx_vulnerabilities() { 357 | return wpvulnerability_rest_software_vulnerabilities( 'nginx' ); 358 | } 359 | 360 | /** 361 | * Handle the MariaDB vulnerabilities REST API request. 362 | * 363 | * @since 3.4.0 364 | * 365 | * @return WP_REST_Response MariaDB vulnerabilities data or an empty array if none found. 366 | */ 367 | function wpvulnerability_rest_mariadb_vulnerabilities() { 368 | return wpvulnerability_rest_software_vulnerabilities( 'mariadb' ); 369 | } 370 | 371 | /** 372 | * Handle the MySQL vulnerabilities REST API request. 373 | * 374 | * @since 3.4.0 375 | * 376 | * @return WP_REST_Response MySQL vulnerabilities data or an empty array if none found. 377 | */ 378 | function wpvulnerability_rest_mysql_vulnerabilities() { 379 | return wpvulnerability_rest_software_vulnerabilities( 'mysql' ); 380 | } 381 | 382 | /** 383 | * Handle the ImageMagick vulnerabilities REST API request. 384 | * 385 | * @since 3.5.0 386 | * 387 | * @return WP_REST_Response ImageMagick vulnerabilities data or an empty array if none found. 388 | */ 389 | function wpvulnerability_rest_imagemagick_vulnerabilities() { 390 | return wpvulnerability_rest_software_vulnerabilities( 'imagemagick' ); 391 | } 392 | 393 | /** 394 | * Handle the curl vulnerabilities REST API request. 395 | * 396 | * @since 3.5.0 397 | * 398 | * @return WP_REST_Response curl vulnerabilities data or an empty array if none found. 399 | */ 400 | function wpvulnerability_rest_curl_vulnerabilities() { 401 | return wpvulnerability_rest_software_vulnerabilities( 'curl' ); 402 | } 403 | 404 | /** 405 | * Handle the memcached vulnerabilities REST API request. 406 | * 407 | * @since 3.5.0 408 | * 409 | * @return WP_REST_Response memcached vulnerabilities data or an empty array if none found. 410 | */ 411 | function wpvulnerability_rest_memcached_vulnerabilities() { 412 | return wpvulnerability_rest_software_vulnerabilities( 'memcached' ); 413 | } 414 | 415 | /** 416 | * Handle the Redis vulnerabilities REST API request. 417 | * 418 | * @since 3.5.0 419 | * 420 | * @return WP_REST_Response Redis vulnerabilities data or an empty array if none found. 421 | */ 422 | function wpvulnerability_rest_redis_vulnerabilities() { 423 | return wpvulnerability_rest_software_vulnerabilities( 'redis' ); 424 | } 425 | 426 | /** 427 | * Handle the SQLite vulnerabilities REST API request. 428 | * 429 | * @since 3.5.0 430 | * 431 | * @return WP_REST_Response SQLite vulnerabilities data or an empty array if none found. 432 | */ 433 | function wpvulnerability_rest_sqlite_vulnerabilities() { 434 | return wpvulnerability_rest_software_vulnerabilities( 'sqlite' ); 435 | } 436 | 437 | /** 438 | * Custom permission check for the WPVulnerability REST API. 439 | * 440 | * This function checks if the request is authenticated using an Application Password. 441 | * 442 | * @since 3.3.0 443 | * 444 | * @param WP_REST_Request $request The REST API request. 445 | * 446 | * @return bool True if the user has permission, false otherwise. 447 | */ 448 | function wpvulnerability_permission_check( WP_REST_Request $request ) { 449 | 450 | // Check if application passwords are available. 451 | if ( wp_is_application_passwords_available() ) { 452 | $authorization_header = $request->get_header( 'authorization' ); 453 | 454 | // Check if the authorization header is present and properly formatted. 455 | if ( $authorization_header && preg_match( '/^Basic\s(.+)$/i', $authorization_header, $matches ) ) { 456 | $auth_string = base64_decode( (string) $matches[1] ); // phpcs:ignore 457 | list( $user, $password ) = explode( ':', $auth_string ); 458 | 459 | // Authenticate the user using the application password. 460 | if ( wp_authenticate_application_password( null, $user, $password ) instanceof WP_User ) { 461 | return true; 462 | } 463 | } 464 | } 465 | 466 | return false; 467 | } 468 | 469 | /** 470 | * Registers REST API routes for WPVulnerability. 471 | * 472 | * This function sets up the REST API routes for WPVulnerability to handle requests 473 | * related to vulnerabilities in various components like core, plugins, themes, PHP, and more. 474 | * 475 | * @since 3.3.0 476 | * 477 | * @return void 478 | */ 479 | function wpvulnerability_register_rest_routes() { 480 | 481 | // Define the endpoints to be registered. 482 | $endpoints = array( 483 | 'core', 484 | 'plugins', 485 | 'themes', 486 | 'php', 487 | 'apache', 488 | 'nginx', 489 | 'mariadb', 490 | 'mysql', 491 | 'imagemagick', 492 | 'curl', 493 | 'memcached', 494 | 'redis', 495 | 'sqlite', 496 | ); 497 | 498 | // Loop through each endpoint and register it. 499 | foreach ( $endpoints as $endpoint ) { 500 | register_rest_route( 501 | 'wpvulnerability/v1', // Namespace and version. 502 | '/' . $endpoint, // Endpoint URL. 503 | array( 504 | 'methods' => 'GET', // HTTP method. 505 | 'callback' => 'wpvulnerability_rest_' . $endpoint . '_vulnerabilities', // Callback function. 506 | 'permission_callback' => 'wpvulnerability_permission_check', // Permission check callback. 507 | ) 508 | ); 509 | } 510 | } 511 | 512 | // Hook to initialize REST API endpoints. 513 | add_action( 'rest_api_init', 'wpvulnerability_register_rest_routes' ); 514 | -------------------------------------------------------------------------------- /wpvulnerability-core.php: -------------------------------------------------------------------------------- 1 | ' . $message . '

'; 36 | $information .= ''; 37 | 38 | // Loop through all vulnerabilities for the current version and add their details to the table row HTML markup. 39 | if ( is_array( $core_vulnerabilities ) ) { 40 | foreach ( $core_vulnerabilities as $vulnerability ) { 41 | 42 | $what = array(); 43 | if ( isset( $vulnerability['impact']['cwe'] ) ) { 44 | foreach ( $vulnerability['impact']['cwe'] as $vulnerability_cwe ) { 45 | $what[] = '
' . wp_kses( (string) $vulnerability_cwe['name'], 'strip' ) . '
' . wp_kses_post( (string) $vulnerability_cwe['description'] ) . '
'; 46 | } 47 | } 48 | 49 | $sources = array(); 50 | if ( isset( $vulnerability['source'] ) ) { 51 | foreach ( $vulnerability['source'] as $vulnerability_source ) { 52 | $sources[] = '[+] ' . wp_kses( (string) $vulnerability_source['name'], 'strip' ); 53 | } 54 | } 55 | $source = count( $sources ) ? '
' . implode( '
', $sources ) . '
' : ''; 56 | 57 | $score = isset( $vulnerability['impact']['cvss']['score'] ) ? number_format( (float) $vulnerability['impact']['cvss']['score'], 1, '.', '' ) : null; 58 | $severity = isset( $vulnerability['impact']['cvss']['severity'] ) ? wpvulnerability_severity( $vulnerability['impact']['cvss']['severity'] ) : null; 59 | 60 | $information .= ''; 61 | $information .= ''; 62 | $information .= ''; 78 | $information .= ''; 79 | } 80 | } 81 | 82 | $information .= '
WordPress ' . wp_kses( (string) $vulnerability['name'], 'strip' ) . ''; 63 | if ( count( $what ) ) { 64 | $information .= '
' . implode( '', $what ) . '
'; 65 | } 66 | if ( ! is_null( $score ) || ! is_null( $severity ) ) { 67 | $information .= '
'; 68 | if ( ! is_null( $score ) ) { 69 | $information .= '
' . __( 'Global score: ', 'wpvulnerability' ) . $score . ' / 10
'; 70 | } 71 | if ( ! is_null( $severity ) ) { 72 | $information .= '
' . __( 'Severity: ', 'wpvulnerability' ) . $severity . '
'; 73 | } 74 | $information .= '
'; 75 | } 76 | $information .= wp_kses( (string) $source, 'post' ); 77 | $information .= '
'; 83 | 84 | echo $information; // phpcs:ignore 85 | } 86 | 87 | /** 88 | * Retrieves vulnerabilities for a given WordPress core version and updates its data. 89 | * 90 | * @since 2.0.0 91 | * 92 | * @return array|false The updated core data array or false if no vulnerabilities are found. 93 | */ 94 | function wpvulnerability_get_fresh_core_vulnerabilities() { 95 | 96 | // Get the core version and sanitize it. 97 | $version = wpvulnerability_sanitize_version( get_bloginfo( 'version' ) ); 98 | 99 | // Retrieve vulnerabilities for the core version. 100 | $response = wpvulnerability_get_core( $version, 0 ); 101 | 102 | $core_data = array(); 103 | 104 | // If no vulnerabilities are found, return false. 105 | if ( empty( $response ) ) { 106 | return false; 107 | } 108 | 109 | // If vulnerabilities are found, update the core data. 110 | foreach ( $response as $v ) { 111 | if ( isset( $v['name'], $v['source'], $v['impact'] ) ) { // Ensure expected keys exist. 112 | $core_data[] = array( 113 | 'name' => wp_kses( (string) $v['name'], 'strip' ), 114 | 'source' => $v['source'], 115 | 'impact' => $v['impact'], 116 | ); 117 | } 118 | } 119 | 120 | return ! empty( $core_data ) ? $core_data : false; // Return false if core_data is empty. 121 | } 122 | 123 | /** 124 | * Get Vulnerabilities 125 | * 126 | * Retrieves and caches the vulnerabilities for the installed WordPress core version. 127 | * 128 | * @since 2.0.0 129 | * 130 | * @return string JSON-encoded array of core data with vulnerabilities and vulnerable status. 131 | */ 132 | function wpvulnerability_core_get_installed() { 133 | 134 | $wpvulnerability_core_vulnerable = 0; 135 | 136 | // Get fresh core vulnerabilities. 137 | $core = wpvulnerability_get_fresh_core_vulnerabilities(); 138 | 139 | // Check if vulnerabilities were found and count them. 140 | if ( is_array( $core ) && count( $core ) > 0 ) { 141 | $wpvulnerability_core_vulnerable = count( $core ); 142 | } 143 | 144 | // Cache the vulnerability data and the timestamp for cache expiration. 145 | if ( is_multisite() ) { 146 | update_site_option( 'wpvulnerability-core', wp_json_encode( $core ) ); 147 | update_site_option( 'wpvulnerability-core-vulnerable', wp_json_encode( number_format( $wpvulnerability_core_vulnerable, 0, '.', '' ) ) ); 148 | update_site_option( 'wpvulnerability-core-cache', wp_json_encode( number_format( time() + ( 3600 * WPVULNERABILITY_CACHE_HOURS ), 0, '.', '' ) ) ); 149 | } else { 150 | update_option( 'wpvulnerability-core', wp_json_encode( $core ) ); 151 | update_option( 'wpvulnerability-core-vulnerable', wp_json_encode( number_format( $wpvulnerability_core_vulnerable, 0, '.', '' ) ) ); 152 | update_option( 'wpvulnerability-core-cache', wp_json_encode( number_format( time() + ( 3600 * WPVULNERABILITY_CACHE_HOURS ), 0, '.', '' ) ) ); 153 | } 154 | 155 | // Return the JSON-encoded array of core vulnerabilities. 156 | return wp_json_encode( $core ); 157 | } 158 | 159 | /** 160 | * Get the cached vulnerabilities or update the cache if it's stale or missing. 161 | * 162 | * @since 2.0.0 163 | * 164 | * @return array Array of core with their vulnerabilities. 165 | */ 166 | function wpvulnerability_core_get_vulnerabilities() { 167 | 168 | // Initialize variables. 169 | $core_data_cache = null; 170 | $core_data = null; 171 | 172 | if ( is_multisite() ) { 173 | // Get the cached core data and decode it for multisite. 174 | $core_data_cache = json_decode( get_site_option( 'wpvulnerability-core-cache' ), true ); 175 | $core_data = json_decode( get_site_option( 'wpvulnerability-core' ), true ); 176 | } else { 177 | // Get the cached core data and decode it for single site. 178 | $core_data_cache = json_decode( get_option( 'wpvulnerability-core-cache' ), true ); 179 | $core_data = json_decode( get_option( 'wpvulnerability-core' ), true ); 180 | } 181 | 182 | // If the cache is stale or the core data is empty, update the cache. 183 | if ( ( null !== $core_data_cache && $core_data_cache < time() ) || empty( $core_data ) ) { 184 | $core_data = json_decode( wpvulnerability_core_get_installed(), true ); 185 | } 186 | 187 | // Return the core data with vulnerabilities. 188 | return $core_data; 189 | } 190 | 191 | /** 192 | * Update the core cache and remove any old cache data. 193 | * 194 | * @since 2.0.0 195 | * 196 | * @return void 197 | */ 198 | function wpvulnerability_core_get_vulnerabilities_clean() { 199 | wpvulnerability_clear_cache( 'core' ); 200 | wpvulnerability_core_get_installed(); 201 | } 202 | 203 | /** 204 | * Adds vulnerability information after the core version and notices on the update-core.php page. 205 | * 206 | * @since 2.0.0 207 | * 208 | * @return void 209 | */ 210 | function wpvulnerability_core_page() { 211 | 212 | // Check if the current page is the update-core.php page. 213 | global $pagenow; 214 | if ( wpvulnerability_analyze_filter( 'core' ) && 'update-core.php' === $pagenow && wpvulnerability_capabilities() ) { 215 | 216 | // Get the vulnerabilities for the core. 217 | $core = wpvulnerability_core_get_vulnerabilities(); 218 | 219 | // If there are vulnerabilities, add an action to display them after the core auto updates settings. 220 | if ( is_array( $core ) && ! empty( $core ) ) { 221 | add_action( 'after_core_auto_updates_settings', 'wpvulnerability_core_info_after' ); 222 | } 223 | } 224 | } 225 | // Add notices for vulnerable core on the core page. 226 | add_action( 'admin_head', 'wpvulnerability_core_page' ); 227 | -------------------------------------------------------------------------------- /wpvulnerability-notifications.php: -------------------------------------------------------------------------------- 1 | 604800, 29 | 'display' => __( 'Every week', 'wpvulnerability' ), 30 | ); 31 | 32 | // Return the modified list of schedules. 33 | return $schedules; 34 | } 35 | // Hook the function to the 'cron_schedules' filter to add the custom schedule. 36 | add_filter( 'cron_schedules', 'wpvulnerability_add_every_week' ); 37 | 38 | /** 39 | * Adds a custom schedule for daily events. 40 | * 41 | * This function adds a new schedule interval of one day (86400 seconds) 42 | * to the system's available cron schedules. It allows tasks to be scheduled 43 | * to run every day using the 'daily' interval. 44 | * 45 | * @since 2.0.0 46 | * 47 | * @param array $schedules List of available schedules. 48 | * 49 | * @return array Modified list of available schedules with the added daily interval. 50 | */ 51 | function wpvulnerability_add_every_day( $schedules ) { 52 | // Define a new schedule with a 24 hour interval. 53 | $schedules['daily'] = array( 54 | 'interval' => 86400, 55 | 'display' => __( 'Every day', 'wpvulnerability' ), 56 | ); 57 | 58 | // Return the modified list of schedules. 59 | return $schedules; 60 | } 61 | // Hook the function to the 'cron_schedules' filter to add the custom schedule. 62 | add_filter( 'cron_schedules', 'wpvulnerability_add_every_day' ); 63 | 64 | /** 65 | * Prepares the HTML email message. 66 | * 67 | * This function generates an HTML email message with the given title and content. 68 | * It includes basic styling and structure to ensure compatibility with most email clients. 69 | * 70 | * @since 2.0.0 71 | * 72 | * @param string $title The title of the email message. 73 | * @param string $content The content of the email message. 74 | * 75 | * @return string The prepared HTML email message. 76 | */ 77 | function wpvulnerability_email_prepare( $title, $content ) { 78 | 79 | $message = ''; 80 | $message .= '' . "\n"; 81 | $message .= '' . "\n"; 82 | $message .= '' . "\n"; 83 | $message .= ' ' . "\n"; 84 | $message .= ' ' . "\n"; 85 | $message .= ' WPVulnerability' . "\n"; 86 | $message .= ' ' . "\n"; 99 | $message .= '' . "\n"; 100 | $message .= '' . "\n"; 101 | $message .= ' ' . "\n"; 102 | $message .= ' ' . "\n"; 103 | $message .= ' ' . "\n"; 104 | $message .= ' ' . "\n"; 159 | $message .= ' ' . "\n"; 160 | $message .= '
' . "\n"; 105 | $message .= '
' . "\n"; 106 | $message .= ' ' . "\n"; 107 | $message .= ' ' . "\n"; 108 | $message .= ' ' . "\n"; 154 | $message .= ' ' . "\n"; 155 | $message .= ' ' . "\n"; 156 | $message .= '
' . "\n"; 109 | $message .= ' ' . "\n"; 110 | $message .= ' ' . "\n"; 111 | $message .= ' ' . "\n"; 123 | $message .= ' ' . "\n"; 124 | $message .= ' ' . "\n"; 125 | $message .= ' ' . "\n"; 128 | $message .= ' ' . "\n"; 129 | $message .= '
' . "\n"; 112 | $message .= ' WPVulnerability' . "\n"; 113 | $message .= '

' . esc_html( $title ) . '

' . "\n"; 114 | 115 | // Add the site URL based on the multisite configuration. 116 | if ( is_multisite() ) { 117 | $message .= '

' . esc_html( network_site_url() ) . '

' . "\n"; 118 | } else { 119 | $message .= '

' . esc_html( site_url() ) . '

' . "\n"; 120 | } 121 | 122 | $message .= '
' . "\n"; 126 | $message .= $content; // Add the main content of the email. 127 | $message .= '
' . "\n"; 130 | $message .= ' ' . "\n"; 153 | $message .= '
' . "\n"; 157 | $message .= '
' . "\n"; 158 | $message .= '
' . "\n"; 161 | $message .= '' . "\n"; 162 | $message .= ''; 163 | 164 | // Return the prepared HTML email message. 165 | return $message; 166 | } 167 | 168 | /** 169 | * Executes the vulnerability notification process for a WordPress site. 170 | * 171 | * This function checks for vulnerabilities in the WordPress core, plugins, themes, PHP environment, and web server components. 172 | * It generates an HTML email report detailing any vulnerabilities found. If the function is called with 173 | * the $forced parameter set to true, it will send an email even if no vulnerabilities are found, which is useful for testing purposes. 174 | * 175 | * @since 2.0.0 176 | * 177 | * @param bool $forced Optional. If set to true, forces the sending of a notification email regardless of whether vulnerabilities are found. Default false. 178 | * @return string|false Returns the email content if the email was successfully sent, false otherwise. 179 | */ 180 | function wpvulnerability_execute_notification( $forced = false ) { 181 | $email_content = ''; 182 | $wpvulnerability_settings = is_multisite() ? get_site_option( 'wpvulnerability-config' ) : get_option( 'wpvulnerability-config' ); 183 | 184 | // Check if forced email sending is not required. 185 | if ( ! $forced && ( empty( $wpvulnerability_settings['emails'] ) || empty( $wpvulnerability_settings['period'] ) ) ) { 186 | return false; 187 | } 188 | 189 | // Generate HTML for core, plugins, and themes vulnerabilities. 190 | $html_core = wpvulnerability_analyze_filter( 'core' ) && ( is_multisite() ? json_decode( get_site_option( 'wpvulnerability-core-vulnerable' ) ) : json_decode( get_option( 'wpvulnerability-core-vulnerable' ) ) ) ? wpvulnerability_html_core() : null; 191 | 192 | $html_plugins = wpvulnerability_analyze_filter( 'plugins' ) && ( is_multisite() ? json_decode( get_site_option( 'wpvulnerability-plugins-vulnerable' ) ) : json_decode( get_option( 'wpvulnerability-plugins-vulnerable' ) ) ) ? wpvulnerability_html_plugins() : null; 193 | 194 | $html_themes = wpvulnerability_analyze_filter( 'themes' ) && ( is_multisite() ? json_decode( get_site_option( 'wpvulnerability-themes-vulnerable' ) ) : json_decode( get_option( 'wpvulnerability-themes-vulnerable' ) ) ) ? wpvulnerability_html_themes() : null; 195 | 196 | // Generate HTML for PHP, Apache, Nginx, MariaDB, MySQL... vulnerabilities. 197 | $html_php = wpvulnerability_analyze_filter( 'php' ) && ( is_multisite() ? json_decode( get_site_option( 'wpvulnerability-php-vulnerable' ) ) : json_decode( get_option( 'wpvulnerability-php-vulnerable' ) ) ) ? wpvulnerability_html_software( 'php' ) : null; 198 | 199 | $html_apache = wpvulnerability_analyze_filter( 'apache' ) && ( is_multisite() ? json_decode( get_site_option( 'wpvulnerability-apache-vulnerable' ) ) : json_decode( get_option( 'wpvulnerability-apache-vulnerable' ) ) ) ? wpvulnerability_html_software( 'apache' ) : null; 200 | 201 | $html_nginx = wpvulnerability_analyze_filter( 'nginx' ) && ( is_multisite() ? json_decode( get_site_option( 'wpvulnerability-nginx-vulnerable' ) ) : json_decode( get_option( 'wpvulnerability-nginx-vulnerable' ) ) ) ? wpvulnerability_html_software( 'nginx' ) : null; 202 | 203 | $html_mariadb = wpvulnerability_analyze_filter( 'mariadb' ) && ( is_multisite() ? json_decode( get_site_option( 'wpvulnerability-mariadb-vulnerable' ) ) : json_decode( get_option( 'wpvulnerability-mariadb-vulnerable' ) ) ) ? wpvulnerability_html_software( 'mariadb' ) : null; 204 | 205 | $html_mysql = wpvulnerability_analyze_filter( 'mysql' ) && ( is_multisite() ? json_decode( get_site_option( 'wpvulnerability-mysql-vulnerable' ) ) : json_decode( get_option( 'wpvulnerability-mysql-vulnerable' ) ) ) ? wpvulnerability_html_software( 'mysql' ) : null; 206 | 207 | $html_imagemagick = wpvulnerability_analyze_filter( 'imagemagick' ) && ( is_multisite() ? json_decode( get_site_option( 'wpvulnerability-imagemagick-vulnerable' ) ) : json_decode( get_option( 'wpvulnerability-imagemagick-vulnerable' ) ) ) ? wpvulnerability_html_software( 'imagemagick' ) : null; 208 | 209 | $html_curl = wpvulnerability_analyze_filter( 'curl' ) && ( is_multisite() ? json_decode( get_site_option( 'wpvulnerability-curl-vulnerable' ) ) : json_decode( get_option( 'wpvulnerability-curl-vulnerable' ) ) ) ? wpvulnerability_html_software( 'curl' ) : null; 210 | 211 | $html_memcached = wpvulnerability_analyze_filter( 'memcached' ) && ( is_multisite() ? json_decode( get_site_option( 'wpvulnerability-memcached-vulnerable' ) ) : json_decode( get_option( 'wpvulnerability-memcached-vulnerable' ) ) ) ? wpvulnerability_html_software( 'memcached' ) : null; 212 | 213 | $html_redis = wpvulnerability_analyze_filter( 'redis' ) && ( is_multisite() ? json_decode( get_site_option( 'wpvulnerability-redis-vulnerable' ) ) : json_decode( get_option( 'wpvulnerability-redis-vulnerable' ) ) ) ? wpvulnerability_html_software( 'redis' ) : null; 214 | 215 | $html_sqlite = wpvulnerability_analyze_filter( 'sqlite' ) && ( is_multisite() ? json_decode( get_site_option( 'wpvulnerability-sqlite-vulnerable' ) ) : json_decode( get_option( 'wpvulnerability-sqlite-vulnerable' ) ) ) ? wpvulnerability_html_software( 'sqlite' ) : null; 216 | 217 | $all_empty = ( empty( $html_core ) && empty( $html_plugins ) && empty( $html_themes ) && empty( $html_php ) && empty( $html_apache ) && empty( $html_nginx ) && empty( $html_mariadb ) && empty( $html_mysql ) && empty( $html_imagemagick ) && empty( $html_curl ) && empty( $html_memcached ) && empty( $html_redis ) && empty( $html_sqlite ) ); 218 | 219 | // If forced email sending is not enabled and no vulnerabilities were found, exit the function. 220 | if ( ! $forced && $all_empty ) { 221 | return false; 222 | } elseif ( $forced && $all_empty ) { 223 | $email_content .= '

' . esc_html__( 'No vulnerabilities found', 'wpvulnerability' ) . '

'; 224 | $email_content .= '

' . esc_html__( 'This is likely a test. The site does not have vulnerabilities.', 'wpvulnerability' ) . '

'; 225 | } 226 | 227 | // Append core vulnerabilities HTML to the email content. 228 | if ( ! empty( $html_core ) ) { 229 | $email_content .= '

' . esc_html__( 'Core vulnerabilities', 'wpvulnerability' ) . '

'; 230 | $email_content .= $html_core; 231 | } 232 | 233 | // Append plugins vulnerabilities HTML to the email content. 234 | if ( ! empty( $html_plugins ) ) { 235 | $email_content .= '

' . esc_html__( 'Plugins vulnerabilities', 'wpvulnerability' ) . '

'; 236 | $email_content .= $html_plugins; 237 | } 238 | 239 | // Append themes vulnerabilities HTML to the email content. 240 | if ( ! empty( $html_themes ) ) { 241 | $email_content .= '

' . esc_html__( 'Themes vulnerabilities', 'wpvulnerability' ) . '

'; 242 | $email_content .= $html_themes; 243 | } 244 | 245 | // Append PHP vulnerabilities HTML to the email content. 246 | if ( ! empty( $html_php ) ) { 247 | $email_content .= '

' . esc_html__( 'PHP vulnerabilities', 'wpvulnerability' ) . '

'; 248 | $email_content .= $html_php; 249 | } 250 | 251 | // Append Apache vulnerabilities HTML to the email content. 252 | if ( ! empty( $html_apache ) ) { 253 | $email_content .= '

' . esc_html__( 'Apache HTTPD vulnerabilities', 'wpvulnerability' ) . '

'; 254 | $email_content .= $html_apache; 255 | } 256 | 257 | // Append Nginx vulnerabilities HTML to the email content. 258 | if ( ! empty( $html_nginx ) ) { 259 | $email_content .= '

' . esc_html__( 'Nginx vulnerabilities', 'wpvulnerability' ) . '

'; 260 | $email_content .= $html_nginx; 261 | } 262 | 263 | // Append MariaDB vulnerabilities HTML to the email content. 264 | if ( ! empty( $html_mariadb ) ) { 265 | $email_content .= '

' . esc_html__( 'MariaDB vulnerabilities', 'wpvulnerability' ) . '

'; 266 | $email_content .= $html_mariadb; 267 | } 268 | 269 | // Append MySQL vulnerabilities HTML to the email content. 270 | if ( ! empty( $html_mysql ) ) { 271 | $email_content .= '

' . esc_html__( 'MySQL vulnerabilities', 'wpvulnerability' ) . '

'; 272 | $email_content .= $html_mysql; 273 | } 274 | 275 | // Append ImageMagick vulnerabilities HTML to the email content. 276 | if ( ! empty( $html_imagemagick ) ) { 277 | $email_content .= '

' . esc_html__( 'ImageMagick vulnerabilities', 'wpvulnerability' ) . '

'; 278 | $email_content .= $html_imagemagick; 279 | } 280 | 281 | // Append curl vulnerabilities HTML to the email content. 282 | if ( ! empty( $html_curl ) ) { 283 | $email_content .= '

' . esc_html__( 'curl vulnerabilities', 'wpvulnerability' ) . '

'; 284 | $email_content .= $html_curl; 285 | } 286 | 287 | // Append memcached vulnerabilities HTML to the email content. 288 | if ( ! empty( $html_memcached ) ) { 289 | $email_content .= '

' . esc_html__( 'memcached vulnerabilities', 'wpvulnerability' ) . '

'; 290 | $email_content .= $html_memcached; 291 | } 292 | 293 | // Append Redis vulnerabilities HTML to the email content. 294 | if ( ! empty( $html_redis ) ) { 295 | $email_content .= '

' . esc_html__( 'Redis vulnerabilities', 'wpvulnerability' ) . '

'; 296 | $email_content .= $html_redis; 297 | } 298 | 299 | // Append SQLite vulnerabilities HTML to the email content. 300 | if ( ! empty( $html_sqlite ) ) { 301 | $email_content .= '

' . esc_html__( 'SQLite vulnerabilities', 'wpvulnerability' ) . '

'; 302 | $email_content .= $html_sqlite; 303 | } 304 | 305 | // Get the site name. 306 | $admin_site = is_multisite() ? get_site_option( 'network_name_option' ) : get_bloginfo( 'name' ); 307 | 308 | // Get the admin email. 309 | $admin_email = is_multisite() ? get_site_option( 'admin_email' ) : get_bloginfo( 'admin_email' ); 310 | $from_email = $admin_email; 311 | 312 | // Check if WPVULNERABILITY_MAIL is defined and valid, and use it if available. 313 | if ( defined( 'WPVULNERABILITY_MAIL' ) ) { 314 | $wpvulnerability_sender_email = sanitize_email( trim( WPVULNERABILITY_MAIL ) ); 315 | if ( is_email( $wpvulnerability_sender_email ) ) { 316 | $from_email = $wpvulnerability_sender_email; 317 | } 318 | unset( $wpvulnerability_sender_email ); 319 | } 320 | 321 | // Prepare email subject and content. 322 | $email_subject = sprintf( 323 | // translators: Site name. 324 | __( 'Vulnerability found: %s', 'wpvulnerability' ), 325 | $admin_site 326 | ); 327 | 328 | $email_prepared = wpvulnerability_email_prepare( esc_html__( 'Vulnerability found', 'wpvulnerability' ), $email_content ); 329 | 330 | // Prepare email headers. 331 | $email_headers = array(); 332 | $email_headers[] = 'From: WPVulnerability <' . $from_email . '>'; 333 | $email_headers[] = 'Content-Type: text/html; charset=UTF-8'; 334 | 335 | if ( $forced && ( empty( $wpvulnerability_settings['emails'] ) ) ) { 336 | // Determine the recipient email. 337 | $wpvulnerability_settings['emails'][] = $admin_email; 338 | } 339 | 340 | // Send the email. 341 | $wpmail = wp_mail( $wpvulnerability_settings['emails'], $email_subject, $email_prepared, $email_headers ); 342 | 343 | return $wpmail; 344 | } 345 | -------------------------------------------------------------------------------- /wpvulnerability-process.php: -------------------------------------------------------------------------------- 1 | ' . wp_kses( (string) $vulnerability_cwe['name'], 'strip' ) . '
' . wp_kses_post( (string) $vulnerability_cwe['description'] ) . '
'; 31 | } 32 | } 33 | 34 | $sources = array(); 35 | if ( isset( $vulnerability['source'] ) ) { 36 | foreach ( $vulnerability['source'] as $vulnerability_source ) { 37 | $sources[] = '[+] ' . wp_kses( (string) $vulnerability_source['name'], 'strip' ); 38 | } 39 | } 40 | 41 | $source = count( $sources ) ? '
' . implode( '
', $sources ) . '
' : ''; 42 | 43 | $score = isset( $vulnerability['impact']['cvss']['score'] ) ? number_format( (float) $vulnerability['impact']['cvss']['score'], 1, '.', '' ) : null; 44 | $severity = isset( $vulnerability['impact']['cvss']['severity'] ) ? wpvulnerability_severity( $vulnerability['impact']['cvss']['severity'] ) : null; 45 | 46 | $html .= '

' . wp_kses( (string) $vulnerability['name'], 'strip' ) . '

'; 47 | if ( (int) $vulnerability['closed'] || (int) $vulnerability['unfixed'] ) { 48 | $html .= '
'; 49 | if ( (int) $vulnerability['closed'] ) { 50 | $html .= '
' . __( 'This plugin is closed. Please replace it with another.', 'wpvulnerability' ) . '
'; 51 | } 52 | if ( (int) $vulnerability['unfixed'] ) { 53 | $html .= '
' . __( 'This vulnerability appears to be unpatched. Stay tuned for upcoming plugin updates.', 'wpvulnerability' ) . '
'; 54 | } 55 | $html .= '
'; 56 | } 57 | 58 | if ( count( $what ) ) { 59 | $html .= '
' . implode( '', $what ) . '
'; 60 | } 61 | 62 | if ( ! is_null( $score ) || ! is_null( $severity ) ) { 63 | $html .= '
'; 64 | if ( ! is_null( $score ) ) { 65 | $html .= '
' . __( 'Global score: ', 'wpvulnerability' ) . $score . ' / 10
'; 66 | } 67 | if ( ! is_null( $severity ) ) { 68 | $html .= '
' . __( 'Severity: ', 'wpvulnerability' ) . $severity . '
'; 69 | } 70 | $html .= '
'; 71 | } 72 | 73 | $html .= wp_kses( (string) $source, 'post' ); 74 | } 75 | } elseif ( 'core' === $type ) { 76 | foreach ( $vulnerabilities as $vulnerability ) { 77 | $what = array(); 78 | foreach ( $vulnerability['impact']['cwe'] as $vulnerability_cwe ) { 79 | $what[] = '
' . wp_kses( (string) $vulnerability_cwe['name'], 'strip' ) . '
' . wp_kses_post( (string) $vulnerability_cwe['description'] ) . '
'; 80 | } 81 | 82 | $sources = array(); 83 | foreach ( $vulnerability['source'] as $vulnerability_source ) { 84 | $sources[] = '[+] ' . wp_kses( (string) $vulnerability_source['name'], 'strip' ); 85 | } 86 | $source = '
' . implode( '
', $sources ) . '
'; 87 | 88 | $score = isset( $vulnerability['impact']['cvss']['score'] ) ? number_format( (float) $vulnerability['impact']['cvss']['score'], 1, '.', '' ) : null; 89 | $severity = isset( $vulnerability['impact']['cvss']['severity'] ) ? wpvulnerability_severity( $vulnerability['impact']['cvss']['severity'] ) : null; 90 | 91 | $html .= '

WordPress ' . wp_kses( (string) $vulnerability['name'], 'strip' ) . '

'; 92 | if ( count( $what ) ) { 93 | $html .= '
' . implode( '', $what ) . '
'; 94 | } 95 | 96 | if ( ! is_null( $score ) || ! is_null( $severity ) ) { 97 | $html .= '
'; 98 | if ( ! is_null( $score ) ) { 99 | $html .= '
' . __( 'Global score: ', 'wpvulnerability' ) . $score . ' / 10
'; 100 | } 101 | if ( ! is_null( $severity ) ) { 102 | $html .= '
' . __( 'Severity: ', 'wpvulnerability' ) . $severity . '
'; 103 | } 104 | $html .= '
'; 105 | } 106 | 107 | $html .= wp_kses( (string) $source, 'post' ); 108 | } 109 | } elseif ( in_array( $type, array( 'php', 'apache', 'nginx', 'mariadb', 'mysql', 'imagemagick', 'curl', 'memcached', 'redis', 'sqlite' ), true ) ) { 110 | foreach ( $vulnerabilities as $vulnerability ) { 111 | $sources = array(); 112 | foreach ( $vulnerability['source'] as $vulnerability_source ) { 113 | $sources[] = '[+] ' . wp_kses( (string) $vulnerability_source['id'], 'strip' ) . '
' . wp_kses( (string) $vulnerability_source['description'], 'strip' ); 114 | } 115 | $source = '
' . implode( '
', $sources ) . '
'; 116 | 117 | $html .= '

' . wp_kses( (string) $vulnerability['name'], 'strip' ) . '

'; 118 | $html .= '
'; 119 | $html .= wp_kses( (string) $source, 'post' ); 120 | } 121 | } 122 | 123 | return $html; 124 | } 125 | 126 | /** 127 | * Convert vulnerabilities into HTML format. 128 | * 129 | * @version 3.5.0 130 | * 131 | * @param string $type Type of software (php, apache, nginx, mariadb, mysql, imagemagick, curl). 132 | * @return string|false The HTML output if vulnerabilities were found, false otherwise. 133 | */ 134 | function wpvulnerability_html_software( $type ) { 135 | $html = ''; 136 | $found = false; 137 | $software_name = null; 138 | 139 | // Include the software vulnerabilities file. 140 | require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-software.php'; 141 | 142 | // Map software types to their names. 143 | $software_names = array( 144 | 'php' => 'PHP', 145 | 'apache' => 'Apache HTTP', 146 | 'nginx' => 'Nginx', 147 | 'mariadb' => 'MariaDB', 148 | 'mysql' => 'MySQL', 149 | 'imagemagick' => 'ImageMagick', 150 | 'curl' => 'curl', 151 | 'memcached' => 'memcached', 152 | 'redis' => 'redis', 153 | 'sqlite' => 'sqlite', 154 | ); 155 | 156 | // Check if the type is valid and get the software name. 157 | if ( isset( $software_names[ $type ] ) ) { 158 | $software_name = $software_names[ $type ]; 159 | } else { 160 | return false; // Invalid type. 161 | } 162 | 163 | $version = wpvulnerability_sanitize_and_validate_version( wpvulnerability_get_software_version( $type ) ); 164 | $vulnerabilities = wpvulnerability_get_vulnerabilities( $type, $version ); 165 | 166 | // Check if vulnerabilities were found. 167 | if ( is_array( $vulnerabilities ) && 0 < count( $vulnerabilities ) ) { 168 | $found = true; 169 | 170 | // translators: the type of vulnerability. 171 | $html .= '

' . sprintf( esc_html__( '%s running', 'wpvulnerability' ), esc_html( $software_name ) ) . ': ' . wp_kses( (string) $version, 'strip' ) . '

'; 172 | $html .= wpvulnerability_html( $type, $vulnerabilities ); 173 | 174 | } 175 | 176 | return $found ? $html : false; 177 | } 178 | 179 | /** 180 | * Convert plugin vulnerabilities into HTML format. 181 | * 182 | * @version 2.0.0 183 | * 184 | * @return string|false The HTML output if plugin vulnerabilities were found, false otherwise. 185 | */ 186 | function wpvulnerability_html_plugins() { 187 | $html = ''; 188 | $found = false; 189 | 190 | $plugins = wpvulnerability_plugin_get_vulnerabilities(); 191 | 192 | foreach ( $plugins as $file_path => $plugin_data ) { 193 | // Check if the plugin is marked as vulnerable. 194 | if ( isset( $plugin_data['vulnerable'] ) && 1 === $plugin_data['vulnerable'] ) { 195 | $found = true; 196 | 197 | // Generate HTML markup for the plugin vulnerability. 198 | $html .= '

' . esc_html__( 'Plugin', 'wpvulnerability' ) . ': ' . wp_kses( (string) $plugin_data['Name'], 'strip' ) . '

'; 199 | $html .= wpvulnerability_html( 'plugin', $plugin_data['vulnerabilities'] ); 200 | } 201 | } 202 | 203 | // Return the HTML if vulnerabilities were found. 204 | return $found ? $html : false; 205 | } 206 | 207 | /** 208 | * Convert plugin vulnerabilities into list format. 209 | * 210 | * @version 2.2.0 211 | * 212 | * @return string|false The HTML output if plugin vulnerabilities were found, false otherwise. 213 | */ 214 | function wpvulnerability_list_plugins() { 215 | $html = ''; 233 | 234 | // Return the HTML if vulnerabilities were found. 235 | return $found ? $html : false; 236 | } 237 | 238 | /** 239 | * Convert theme vulnerabilities into HTML format. 240 | * 241 | * @version 2.0.0 242 | * 243 | * @return string|false The HTML output if theme vulnerabilities were found, false otherwise. 244 | */ 245 | function wpvulnerability_html_themes() { 246 | $html = ''; 247 | $found = false; 248 | 249 | // Get vulnerabilities data for themes. 250 | $themes = wpvulnerability_theme_get_vulnerabilities(); 251 | 252 | // Iterate through each theme's data. 253 | foreach ( $themes as $theme_data ) { 254 | // Check if the theme is marked as vulnerable. 255 | if ( isset( $theme_data['wpvulnerability']['vulnerable'] ) && 1 === $theme_data['wpvulnerability']['vulnerable'] ) { 256 | $found = true; 257 | 258 | // Generate HTML markup for the theme vulnerability. 259 | $html .= '

' . esc_html__( 'Theme', 'wpvulnerability' ) . ': ' . wp_kses( (string) $theme_data['wpvulnerability']['name'], 'strip' ) . '

'; 260 | $html .= wpvulnerability_html( 'theme', $theme_data['wpvulnerability']['vulnerabilities'] ); 261 | } 262 | } 263 | 264 | // Return the HTML if vulnerabilities were found. 265 | return $found ? $html : false; 266 | } 267 | 268 | /** 269 | * Convert theme vulnerabilities into list format. 270 | * 271 | * @version 2.2.0 272 | * 273 | * @return string|false The HTML output if theme vulnerabilities were found, false otherwise. 274 | */ 275 | function wpvulnerability_list_themes() { 276 | $html = ''; 294 | 295 | // Return the HTML if vulnerabilities were found. 296 | return $found ? $html : false; 297 | } 298 | 299 | /** 300 | * Convert core vulnerabilities into HTML format. 301 | * 302 | * @version 2.0.0 303 | * 304 | * @return string|false The HTML output if core vulnerabilities were found, false otherwise. 305 | */ 306 | function wpvulnerability_html_core() { 307 | $html = ''; 308 | $found = false; 309 | 310 | // Get vulnerabilities data for WordPress core. 311 | $core = wpvulnerability_core_get_vulnerabilities(); 312 | 313 | // Check if there are any vulnerabilities. 314 | if ( is_array( $core ) && count( $core ) ) { 315 | $found = true; 316 | 317 | // Generate HTML markup for the core vulnerabilities. 318 | $html .= wpvulnerability_html( 'core', $core ); 319 | } 320 | 321 | // Return the HTML if vulnerabilities were found. 322 | return $found ? $html : false; 323 | } 324 | -------------------------------------------------------------------------------- /wpvulnerability-run.php: -------------------------------------------------------------------------------- 1 | ' . __( 'Network Settings', 'wpvulnerability' ) . ''; 31 | } elseif ( ! is_multisite() && is_admin() ) { 32 | // Standard settings link for single site. 33 | $links[] = '' . __( 'Settings', 'wpvulnerability' ) . ''; 34 | } 35 | } 36 | return $links; 37 | } 38 | 39 | // Hook the function to the appropriate filters. 40 | if ( is_multisite() ) { 41 | add_filter( 'network_admin_plugin_action_links_' . WPVULNERABILITY_PLUGIN_BASE, 'wpvulnerability_add_settings_link' ); 42 | } else { 43 | add_filter( 'plugin_action_links_' . WPVULNERABILITY_PLUGIN_BASE, 'wpvulnerability_add_settings_link' ); 44 | } 45 | 46 | /** 47 | * Updates the plugin's vulnerability data. 48 | * 49 | * This function updates the vulnerability data for WordPress core, plugins, themes, PHP, Apache, nginx, MariaDB, and MySQL. 50 | * It ensures that the required functions are available by including the necessary files. 51 | * After updating the vulnerabilities, it flushes the WordPress cache. 52 | * 53 | * @since 2.0.0 54 | * 55 | * @return void 56 | */ 57 | function wpvulnerability_update_database_data() { 58 | 59 | // Ensure necessary files are included for core, plugins, and themes. 60 | require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-core.php'; 61 | require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-plugins.php'; 62 | require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-themes.php'; 63 | require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-software.php'; 64 | 65 | wpvulnerability_delete_transients(); 66 | 67 | // Update core, plugins, and themes vulnerabilities. 68 | wpvulnerability_core_get_vulnerabilities_clean(); 69 | wpvulnerability_plugin_get_vulnerabilities_clean(); 70 | wpvulnerability_theme_get_vulnerabilities_clean(); 71 | 72 | // Array of software types to update. 73 | $software_types = array( 'php', 'apache', 'nginx', 'mariadb', 'mysql', 'imagemagick', 'curl', 'memcached', 'redis', 'sqlite' ); 74 | 75 | // Update vulnerabilities for each software type. 76 | foreach ( $software_types as $software ) { 77 | wpvulnerability_get_vulnerabilities_clean( $software ); 78 | } 79 | 80 | wpvulnerability_statistics_get(); 81 | 82 | // Clean the WordPress cache. 83 | wp_cache_flush(); 84 | } 85 | 86 | /** 87 | * Updates the plugin's vulnerability data if the cache has expired. 88 | * 89 | * This function checks if the cached vulnerability data for various components (core, plugins, themes, PHP, Apache, nginx, MariaDB, MySQL) has expired and updates it accordingly. 90 | * It ensures that the required functions are available by including the necessary files. 91 | * The function handles both multisite and single site installations. 92 | * 93 | * @since 3.0.0 94 | * 95 | * @return void 96 | */ 97 | function wpvulnerability_expired_database_data() { 98 | 99 | // Ensure necessary files are included for core, plugins, and themes. 100 | require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-core.php'; 101 | require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-plugins.php'; 102 | require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-themes.php'; 103 | require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-software.php'; 104 | 105 | // Current time for cache expiration comparison. 106 | $cache_time = time(); 107 | 108 | // Check and update core, plugins, and themes vulnerabilities if cache has expired. 109 | $components = array( 110 | 'core' => 'wpvulnerability-core-cache', 111 | 'plugin' => 'wpvulnerability-plugins-cache', 112 | 'theme' => 'wpvulnerability-themes-cache', 113 | ); 114 | 115 | foreach ( $components as $component => $cache_key ) { 116 | $cache_value = is_multisite() ? get_site_option( $cache_key ) : get_option( $cache_key ); 117 | 118 | if ( json_decode( $cache_value ) < $cache_time ) { 119 | call_user_func( "wpvulnerability_{$component}_get_vulnerabilities_clean" ); 120 | } 121 | } 122 | 123 | // Array of software types to update. 124 | $software_types = array( 'php', 'apache', 'nginx', 'mariadb', 'mysql', 'imagemagick', 'curl', 'memcached', 'redis', 'sqlite' ); 125 | 126 | // Ensure necessary files are included and update vulnerabilities for each software type. 127 | foreach ( $software_types as $software ) { 128 | if ( is_multisite() ) { 129 | if ( json_decode( get_site_option( 'wpvulnerability-' . $software . '-cache' ) ) < $cache_time ) { 130 | wpvulnerability_get_vulnerabilities_clean( $software ); 131 | } 132 | } elseif ( json_decode( get_option( 'wpvulnerability-' . $software . '-cache' ) ) < $cache_time ) { 133 | wpvulnerability_get_vulnerabilities_clean( $software ); 134 | } 135 | } 136 | 137 | wpvulnerability_statistics_get(); 138 | 139 | unset( $cache_time ); 140 | } 141 | 142 | /** 143 | * Callback function for when the plugin is activated. 144 | * Adds plugin data options if they are not already created. 145 | * 146 | * @since 2.0.0 147 | * 148 | * @return void 149 | */ 150 | function wpvulnerability_activation() { 151 | $is_multisite = is_multisite(); 152 | $config_key = $is_multisite ? 'get_site_option' : 'get_option'; 153 | $add_option = $is_multisite ? 'add_site_option' : 'add_option'; 154 | $update_option = $is_multisite ? 'update_site_option' : 'update_option'; 155 | 156 | wpvulnerability_delete_transients(); 157 | 158 | // Add wpvulnerability-config option if it does not exist. 159 | if ( ! $config_key( 'wpvulnerability-config' ) ) { 160 | $default_config = array( 161 | 'emails' => get_bloginfo( 'admin_email' ), 162 | 'period' => 'weekly', 163 | ); 164 | $add_option( 'wpvulnerability-config', $default_config ); 165 | } 166 | 167 | // Add other options if they do not exist. 168 | $options = array( 169 | 'wpvulnerability-plugins' => '', 170 | 'wpvulnerability-plugins-cache' => 0, 171 | 'wpvulnerability-plugins-vulnerable' => 0, 172 | 'wpvulnerability-plugins-data' => '', 173 | 'wpvulnerability-plugins-data-cache' => 0, 174 | 'wpvulnerability-themes' => '', 175 | 'wpvulnerability-themes-cache' => 0, 176 | 'wpvulnerability-themes-vulnerable' => 0, 177 | 'wpvulnerability-core' => '', 178 | 'wpvulnerability-core-cache' => 0, 179 | 'wpvulnerability-core-vulnerable' => 0, 180 | 'wpvulnerability-php' => '', 181 | 'wpvulnerability-php-cache' => 0, 182 | 'wpvulnerability-php-vulnerable' => 0, 183 | 'wpvulnerability-apache' => '', 184 | 'wpvulnerability-apache-cache' => 0, 185 | 'wpvulnerability-apache-vulnerable' => 0, 186 | 'wpvulnerability-nginx' => '', 187 | 'wpvulnerability-nginx-cache' => 0, 188 | 'wpvulnerability-nginx-vulnerable' => 0, 189 | 'wpvulnerability-mariadb' => '', 190 | 'wpvulnerability-mariadb-cache' => 0, 191 | 'wpvulnerability-mariadb-vulnerable' => 0, 192 | 'wpvulnerability-mysql' => '', 193 | 'wpvulnerability-mysql-cache' => 0, 194 | 'wpvulnerability-mysql-vulnerable' => 0, 195 | 'wpvulnerability-imagemagick' => '', 196 | 'wpvulnerability-imagemagick-cache' => 0, 197 | 'wpvulnerability-imagemagick-vulnerable' => 0, 198 | 'wpvulnerability-curl' => '', 199 | 'wpvulnerability-curl-cache' => 0, 200 | 'wpvulnerability-curl-vulnerable' => 0, 201 | 'wpvulnerability-memcached' => '', 202 | 'wpvulnerability-memcached-cache' => 0, 203 | 'wpvulnerability-memcached-vulnerable' => 0, 204 | 'wpvulnerability-redis' => '', 205 | 'wpvulnerability-redis-cache' => 0, 206 | 'wpvulnerability-redis-vulnerable' => 0, 207 | 'wpvulnerability-sqlite' => '', 208 | 'wpvulnerability-sqlite-cache' => 0, 209 | 'wpvulnerability-sqlite-vulnerable' => 0, 210 | 'wpvulnerability-statistics' => '', 211 | 'wpvulnerability-statistics-cache' => 0, 212 | ); 213 | 214 | foreach ( $options as $key => $value ) { 215 | if ( ! $config_key( $key ) ) { 216 | $add_option( $key, $value ); 217 | } 218 | } 219 | 220 | // Add wpvulnerability-analyze option if it does not exist. 221 | if ( ! $config_key( 'wpvulnerability-analyze' ) ) { 222 | $default_analyze = array( 223 | 'core' => 0, 224 | 'plugins' => 0, 225 | 'themes' => 0, 226 | 'php' => 0, 227 | 'apache' => 0, 228 | 'nginx' => 0, 229 | 'mariadb' => 0, 230 | 'mysql' => 0, 231 | 'imagemagick' => 0, 232 | 'curl' => 0, 233 | 'memcached' => 0, 234 | 'redis' => 0, 235 | 'sqlite' => 0, 236 | ); 237 | $current_option = $config_key( 'wpvulnerability-analyze' ); 238 | 239 | if ( false === $current_option ) { 240 | $add_option( 'wpvulnerability-analyze', $default_analyze ); 241 | } else { 242 | $updated_option = array_merge( $default_analyze, $current_option ); 243 | if ( $updated_option !== $current_option ) { 244 | $update_option( 'wpvulnerability-analyze', $updated_option ); 245 | } 246 | } 247 | } 248 | } 249 | 250 | /** 251 | * Callback function to run when the plugin is deactivated. 252 | * Deletes options and removes scheduled wp-cron jobs. 253 | * 254 | * @since 2.0.0 255 | * 256 | * @return void 257 | */ 258 | function wpvulnerability_deactivation() { 259 | $options = array( 260 | 'wpvulnerability_settings', 261 | 'wpvulnerability-data', 262 | 'wpvulnerability-analyze', 263 | 'wpvulnerability-themes', 264 | 'wpvulnerability-themes-cache', 265 | 'wpvulnerability-themes-vulnerable', 266 | 'wpvulnerability-plugins', 267 | 'wpvulnerability-plugins-cache', 268 | 'wpvulnerability-plugins-vulnerable', 269 | 'wpvulnerability-core', 270 | 'wpvulnerability-core-cache', 271 | 'wpvulnerability-core-vulnerable', 272 | 'wpvulnerability-php', 273 | 'wpvulnerability-php-cache', 274 | 'wpvulnerability-php-vulnerable', 275 | 'wpvulnerability-apache', 276 | 'wpvulnerability-apache-cache', 277 | 'wpvulnerability-apache-vulnerable', 278 | 'wpvulnerability-nginx', 279 | 'wpvulnerability-nginx-cache', 280 | 'wpvulnerability-nginx-vulnerable', 281 | 'wpvulnerability-mariadb', 282 | 'wpvulnerability-mariadb-cache', 283 | 'wpvulnerability-mariadb-vulnerable', 284 | 'wpvulnerability-mysql', 285 | 'wpvulnerability-mysql-cache', 286 | 'wpvulnerability-mysql-vulnerable', 287 | 'wpvulnerability-imagemagick', 288 | 'wpvulnerability-imagemagick-cache', 289 | 'wpvulnerability-imagemagick-vulnerable', 290 | 'wpvulnerability-curl', 291 | 'wpvulnerability-curl-cache', 292 | 'wpvulnerability-curl-vulnerable', 293 | 'wpvulnerability-memcached', 294 | 'wpvulnerability-memcached-cache', 295 | 'wpvulnerability-memcached-vulnerable', 296 | 'wpvulnerability-redis', 297 | 'wpvulnerability-redis-cache', 298 | 'wpvulnerability-redis-vulnerable', 299 | 'wpvulnerability-sqlite', 300 | 'wpvulnerability-sqlite-cache', 301 | 'wpvulnerability-sqlite-vulnerable', 302 | 'wpvulnerability-statistics', 303 | 'wpvulnerability-statistics-cache', 304 | ); 305 | 306 | // Delete options based on the installation type. 307 | $delete_option_func = is_multisite() ? 'delete_site_option' : 'delete_option'; 308 | foreach ( $options as $option ) { 309 | $delete_option_func( $option ); 310 | } 311 | 312 | wpvulnerability_delete_transients(); 313 | 314 | // Unschedule and remove scheduled wp-cron jobs. 315 | $cron_jobs = array( 316 | 'wpvulnerability_notification', 317 | 'wpvulnerability_update_database', 318 | 'wpvulnerability_pull_db_data_event', 319 | ); 320 | foreach ( $cron_jobs as $job ) { 321 | wp_unschedule_event( wp_next_scheduled( $job ), $job ); 322 | wp_clear_scheduled_hook( $job ); 323 | } 324 | } 325 | 326 | /** 327 | * Deletes all transients that start with 'wpvulnerability_'. 328 | * 329 | * @since 3.5.0 330 | * 331 | * @return void 332 | */ 333 | function wpvulnerability_delete_transients() { 334 | global $wpdb; 335 | 336 | // Determine if the site is multisite. 337 | $is_multisite = is_multisite(); 338 | 339 | // Define the prefix according to whether it is multisite or not. 340 | $transient_prefix = $is_multisite ? '_site_transient_wpvulnerability_' : '_transient_wpvulnerability_'; 341 | 342 | // Prepare the LIKE pattern securely. 343 | $like_pattern = $wpdb->esc_like( $transient_prefix ) . '%'; 344 | 345 | // Try to get transients from cache first. 346 | $cache_key = 'wpvulnerability_transients_list'; 347 | $transients = wp_cache_get( $cache_key ); 348 | 349 | if ( false === $transients ) { 350 | // If cache is empty, query the database for matching transients. 351 | $transients = $wpdb->get_col( // phpcs:ignore 352 | $wpdb->prepare( 353 | "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s", 354 | $like_pattern 355 | ) 356 | ); // phpcs:ignore 357 | 358 | // Store the result in cache for future use. 359 | wp_cache_set( $cache_key, $transients, '', HOUR_IN_SECONDS ); 360 | } 361 | 362 | // If no transients are found, exit early. 363 | if ( empty( $transients ) ) { 364 | return; 365 | } 366 | 367 | foreach ( $transients as $transient ) { 368 | if ( $is_multisite ) { 369 | // For multisite, delete using delete_site_transient. 370 | $transient_name = str_replace( '_site_transient_', '', $transient ); 371 | delete_site_transient( $transient_name ); 372 | } else { 373 | // For single sites, delete using delete_transient. 374 | $transient_name = str_replace( '_transient_', '', $transient ); 375 | delete_transient( $transient_name ); 376 | } 377 | } 378 | 379 | // Optionally clear the cache after deletion. 380 | wp_cache_delete( $cache_key ); 381 | } 382 | 383 | /** 384 | * Callback function to run when the plugin is uninstalled. 385 | * Deletes options and removes scheduled wp-cron jobs. 386 | * 387 | * @since 3.0.0 388 | * 389 | * @return void 390 | */ 391 | function wpvulnerability_uninstall() { 392 | // Delete deprecated options. 393 | delete_option( 'wpvulnerability_settings' ); 394 | delete_option( 'wpvulnerability-data' ); 395 | 396 | // Options to delete for both single site and multisite. 397 | $options = array( 398 | 'wpvulnerability-themes', 399 | 'wpvulnerability-themes-cache', 400 | 'wpvulnerability-themes-vulnerable', 401 | 'wpvulnerability-plugins', 402 | 'wpvulnerability-plugins-cache', 403 | 'wpvulnerability-plugins-vulnerable', 404 | 'wpvulnerability-core', 405 | 'wpvulnerability-core-cache', 406 | 'wpvulnerability-core-vulnerable', 407 | 'wpvulnerability-php', 408 | 'wpvulnerability-php-cache', 409 | 'wpvulnerability-php-vulnerable', 410 | 'wpvulnerability-apache', 411 | 'wpvulnerability-apache-cache', 412 | 'wpvulnerability-apache-vulnerable', 413 | 'wpvulnerability-nginx', 414 | 'wpvulnerability-nginx-cache', 415 | 'wpvulnerability-nginx-vulnerable', 416 | 'wpvulnerability-mariadb', 417 | 'wpvulnerability-mariadb-cache', 418 | 'wpvulnerability-mariadb-vulnerable', 419 | 'wpvulnerability-mysql', 420 | 'wpvulnerability-mysql-cache', 421 | 'wpvulnerability-mysql-vulnerable', 422 | 'wpvulnerability-imagemagick', 423 | 'wpvulnerability-imagemagick-cache', 424 | 'wpvulnerability-imagemagick-vulnerable', 425 | 'wpvulnerability-curl', 426 | 'wpvulnerability-curl-cache', 427 | 'wpvulnerability-curl-vulnerable', 428 | 'wpvulnerability-memcached', 429 | 'wpvulnerability-memcached-cache', 430 | 'wpvulnerability-memcached-vulnerable', 431 | 'wpvulnerability-redis', 432 | 'wpvulnerability-redis-cache', 433 | 'wpvulnerability-redis-vulnerable', 434 | 'wpvulnerability-sqlite', 435 | 'wpvulnerability-sqlite-cache', 436 | 'wpvulnerability-sqlite-vulnerable', 437 | 'wpvulnerability-statistics', 438 | 'wpvulnerability-statistics-cache', 439 | 'wpvulnerability-analyze', 440 | ); 441 | 442 | // Delete options based on the installation type. 443 | $delete_option_func = is_multisite() ? 'delete_site_option' : 'delete_option'; 444 | foreach ( $options as $option ) { 445 | $delete_option_func( $option ); 446 | } 447 | 448 | // Delete config data. 449 | $delete_option_func( 'wpvulnerability-config' ); 450 | 451 | wpvulnerability_delete_transients(); 452 | 453 | // Unschedule and remove scheduled wp-cron jobs. 454 | $cron_jobs = array( 455 | 'wpvulnerability_notification', 456 | 'wpvulnerability_update_database', 457 | ); 458 | foreach ( $cron_jobs as $job ) { 459 | wp_unschedule_event( wp_next_scheduled( $job ), $job ); 460 | wp_clear_scheduled_hook( $job ); 461 | } 462 | } 463 | 464 | /** 465 | * Filters and returns the WPVulnerability analysis setting for a given type. 466 | * 467 | * This function retrieves the WPVulnerability analysis settings, either from 468 | * the single site or the multisite network, depending on the WordPress setup. 469 | * It returns false if the specified type ('core', 'plugins', 'themes', 470 | * 'php', 'apache', 'nginx', 'mariadb', 'mysql') is set. If the type is not set or is invalid, it returns true. 471 | * 472 | * @since 3.3.0 473 | * 474 | * @param string $type The type of analysis setting to retrieve ('core', 'plugins', 'themes', 'php', 'apache', 'nginx', 'mariadb', 'mysql'). 475 | * 476 | * @return bool False if the specified type is set, true if not set or invalid. 477 | */ 478 | function wpvulnerability_analyze_filter( $type ) { 479 | // Retrieve the analysis settings based on the WordPress setup. 480 | $wpvulnerability_analyze = is_multisite() ? get_site_option( 'wpvulnerability-analyze', array() ) : get_option( 'wpvulnerability-analyze', array() ); 481 | 482 | // Define the valid types for analysis. 483 | $valid_types = array( 'core', 'plugins', 'themes', 'php', 'apache', 'nginx', 'mariadb', 'mysql', 'imagemagick', 'curl', 'memcached', 'redis', 'sqlite' ); 484 | 485 | if ( in_array( $type, $valid_types, true ) ) { 486 | return ! ( isset( $wpvulnerability_analyze[ $type ] ) && (int) $wpvulnerability_analyze[ $type ] ); 487 | } 488 | 489 | return true; // Return true for invalid types. 490 | } 491 | 492 | /** 493 | * Clean the cache after an update. 494 | * 495 | * This function is triggered after a plugin or theme update to clean the cache 496 | * and refresh the vulnerability data. 497 | * 498 | * @since 2.0.0 499 | * 500 | * @return void 501 | */ 502 | add_action( 'upgrader_process_complete', 'wpvulnerability_update_database_data', 10, 2 ); 503 | 504 | /** 505 | * Adds a notification count to the Plugins menu item in the WordPress admin if there are vulnerable plugins. 506 | * 507 | * This function retrieves the number of vulnerable plugins from the cache, either from a single site 508 | * or a multisite setup, and displays the count in the WordPress admin menu next to the Plugins menu item. 509 | * The count is shown with a gold background (#FFD700) and black text. 510 | * 511 | * @since 3.3.5 512 | * 513 | * @return void 514 | */ 515 | function wpvulnerability_counter_plugins() { 516 | 517 | if ( ! wpvulnerability_analyze_filter( 'plugins' ) ) { 518 | return; // Skip if plugin analysis is disabled. 519 | } 520 | 521 | // Retrieve the number of vulnerable plugins from cache. 522 | $wpvulnerability_plugins_count = is_multisite() && is_network_admin() 523 | ? get_site_option( 'wpvulnerability-plugins-vulnerable' ) 524 | : get_option( 'wpvulnerability-plugins-vulnerable' ); 525 | 526 | // Decode the count from JSON, default to 0 if not set. 527 | $wpvulnerability_plugins_total = $wpvulnerability_plugins_count ? json_decode( $wpvulnerability_plugins_count ) : 0; 528 | 529 | if ( $wpvulnerability_plugins_total > 0 ) { 530 | global $menu; 531 | foreach ( $menu as $key => $value ) { 532 | if ( 'plugins.php' === $menu[ $key ][2] ) { 533 | $menu[ $key ][0] .= ' ' . esc_html( $wpvulnerability_plugins_total ) . ''; // phpcs:ignore 534 | break; 535 | } 536 | } 537 | } 538 | } 539 | 540 | // Hook into the appropriate admin menu action based on the site type. 541 | if ( is_multisite() && is_network_admin() ) { 542 | add_action( 'network_admin_menu', 'wpvulnerability_counter_plugins' ); 543 | } elseif ( ! is_multisite() ) { 544 | add_action( 'admin_menu', 'wpvulnerability_counter_plugins' ); 545 | } 546 | 547 | /** 548 | * Adds a notification count to the Themes menu item in the WordPress admin if there are vulnerable themes. 549 | * 550 | * This function retrieves the number of vulnerable themes from the cache, either from a single site 551 | * or a multisite setup, and displays the count in the WordPress admin menu next to the Themes menu item. 552 | * The count is displayed with a gold background (#FFD700) and black text. 553 | * 554 | * @since 3.3.5 555 | * 556 | * @return void 557 | */ 558 | function wpvulnerability_counter_themes() { 559 | 560 | if ( ! wpvulnerability_analyze_filter( 'themes' ) ) { 561 | return; // Skip if theme analysis is disabled. 562 | } 563 | 564 | // Retrieve the number of theme vulnerabilities from cache. 565 | $wpvulnerability_themes_count = ( is_multisite() && is_network_admin() ) 566 | ? get_site_option( 'wpvulnerability-themes-vulnerable' ) 567 | : get_option( 'wpvulnerability-themes-vulnerable' ); 568 | 569 | // Decode the count from JSON, default to 0 if not set. 570 | $wpvulnerability_themes_total = $wpvulnerability_themes_count ? json_decode( $wpvulnerability_themes_count ) : 0; 571 | 572 | if ( $wpvulnerability_themes_total > 0 ) { 573 | 574 | // Check if we are in a multisite setup or not. 575 | if ( ! is_multisite() ) { 576 | global $submenu; 577 | 578 | // Check if the submenu for themes exists. 579 | if ( isset( $submenu['themes.php'] ) ) { 580 | foreach ( $submenu['themes.php'] as $key => $value ) { 581 | if ( 'themes.php' === $submenu['themes.php'][ $key ][2] ) { 582 | $submenu['themes.php'][ $key ][0] .= ' ' . esc_html( $wpvulnerability_themes_total ) . ''; // phpcs:ignore 583 | break; 584 | } 585 | } 586 | } 587 | } elseif ( is_network_admin() ) { 588 | global $menu; 589 | 590 | foreach ( $menu as $key => $value ) { 591 | if ( 'themes.php' === $menu[ $key ][2] ) { 592 | $menu[ $key ][0] .= ' ' . esc_html( $wpvulnerability_themes_total ) . ''; // phpcs:ignore 593 | break; 594 | } 595 | } 596 | } 597 | } 598 | } 599 | 600 | // Hook into the appropriate admin menu action based on the site type. 601 | if ( is_multisite() && is_network_admin() ) { 602 | add_action( 'network_admin_menu', 'wpvulnerability_counter_themes' ); 603 | } elseif ( ! is_multisite() ) { 604 | add_action( 'admin_menu', 'wpvulnerability_counter_themes' ); 605 | } 606 | 607 | /** 608 | * Adds a notification count to the Updates submenu item under Dashboard in the WordPress admin if there are core updates. 609 | * 610 | * This function checks for core updates and then displays the count in the WordPress admin submenu 611 | * next to the Updates menu item with a gold background (#FFD700) and black text. 612 | * 613 | * @since 3.3.5 614 | * 615 | * @return void 616 | */ 617 | function wpvulnerability_counter_core() { 618 | 619 | if ( ! wpvulnerability_analyze_filter( 'core' ) ) { 620 | return; // Skip if core analysis is disabled. 621 | } 622 | 623 | // Retrieve the number of core vulnerabilities from cache. 624 | $wpvulnerability_core_count = is_multisite() && is_network_admin() 625 | ? get_site_option( 'wpvulnerability-core-vulnerable' ) 626 | : get_option( 'wpvulnerability-core-vulnerable' ); 627 | 628 | // Decode the count from JSON, default to 0 if not set. 629 | $wpvulnerability_core_total = $wpvulnerability_core_count ? json_decode( $wpvulnerability_core_count ) : 0; 630 | 631 | if ( $wpvulnerability_core_total > 0 ) { 632 | global $submenu; 633 | if ( isset( $submenu['index.php'] ) ) { 634 | foreach ( $submenu['index.php'] as $key => $value ) { 635 | if ( 'update-core.php' === $submenu['index.php'][ $key ][2] ) { 636 | $submenu['index.php'][ $key ][0] .= ' ' . esc_html( $wpvulnerability_core_total ) . ''; // phpcs:ignore 637 | break; 638 | } 639 | } 640 | } 641 | } 642 | } 643 | 644 | // Hook into the appropriate admin menu action based on the site type. 645 | if ( is_multisite() && is_network_admin() ) { 646 | add_action( 'network_admin_menu', 'wpvulnerability_counter_core' ); 647 | } elseif ( ! is_multisite() ) { 648 | add_action( 'admin_menu', 'wpvulnerability_counter_core' ); 649 | } 650 | -------------------------------------------------------------------------------- /wpvulnerability-schedule.php: -------------------------------------------------------------------------------- 1 | __( 'There aren\'t plugins vulnerabilities', 'wpvulnerability' ), 23 | 'status' => 'good', 24 | 'badge' => array( 25 | 'label' => __( 'Security', 'wpvulnerability' ), 26 | 'color' => 'green', 27 | ), 28 | 'description' => sprintf( 29 | '

%s

', 30 | __( 'Shows possible vulnerabilities that exist in installed plugins.', 'wpvulnerability' ) 31 | ), 32 | 'actions' => '', 33 | 'test' => 'wpvulnerability_plugins', 34 | ); 35 | 36 | // Check if any plugin vulnerabilities were found. 37 | $wpvulnerability_test_plugins_counter = is_multisite() ? json_decode( get_site_option( 'wpvulnerability-plugins-vulnerable' ) ) : json_decode( get_option( 'wpvulnerability-plugins-vulnerable' ) ); 38 | 39 | if ( $wpvulnerability_test_plugins_counter ) { 40 | $result['status'] = 'critical'; 41 | $result['label'] = sprintf( 42 | // translators: Number of plugins vulnerabilities. 43 | _n( 'There is %d plugin with vulnerabilities', 'There are %d plugins with vulnerabilities', $wpvulnerability_test_plugins_counter, 'wpvulnerability' ), 44 | $wpvulnerability_test_plugins_counter 45 | ); 46 | $result['badge']['color'] = 'red'; 47 | $result['description'] = sprintf( 48 | '

%1$s

%2$s', 49 | __( 'We\'ve detected potential vulnerabilities in installed plugins. Please check them and keep them updated.', 'wpvulnerability' ), 50 | wpvulnerability_html_plugins() 51 | ); 52 | 53 | // Add action links to update plugins. 54 | $result['actions'] .= sprintf( 55 | '

%s

', 56 | esc_url( is_multisite() ? network_admin_url( 'plugins.php' ) : admin_url( 'plugins.php' ) ), 57 | __( 'Update plugins', 'wpvulnerability' ) 58 | ); 59 | } 60 | 61 | return $result; 62 | } 63 | 64 | /** 65 | * Tests for vulnerabilities in installed themes. 66 | * 67 | * @version 2.0.0 68 | * 69 | * @return array Returns an array with the results of the vulnerability test. 70 | */ 71 | function wpvulnerability_test_themes() { 72 | // Define the initial test result values. 73 | $result = array( 74 | 'label' => __( 'There aren\'t themes vulnerabilities', 'wpvulnerability' ), 75 | 'status' => 'good', 76 | 'badge' => array( 77 | 'label' => __( 'Security', 'wpvulnerability' ), 78 | 'color' => 'green', 79 | ), 80 | 'description' => sprintf( 81 | '

%s

', 82 | __( 'Shows possible vulnerabilities that exist in installed themes.', 'wpvulnerability' ) 83 | ), 84 | 'actions' => '', 85 | 'test' => 'wpvulnerability_themes', 86 | ); 87 | 88 | // Check if any theme vulnerabilities were found. 89 | $wpvulnerability_test_themes_counter = is_multisite() ? json_decode( get_site_option( 'wpvulnerability-themes-vulnerable' ) ) : json_decode( get_option( 'wpvulnerability-themes-vulnerable' ) ); 90 | 91 | if ( $wpvulnerability_test_themes_counter ) { 92 | $result['status'] = 'critical'; 93 | $result['label'] = sprintf( 94 | // translators: Number of themes vulnerabilities. 95 | _n( 'There is %d theme with vulnerabilities', 'There are %d themes with vulnerabilities', $wpvulnerability_test_themes_counter, 'wpvulnerability' ), 96 | $wpvulnerability_test_themes_counter 97 | ); 98 | $result['badge']['color'] = 'red'; 99 | $result['description'] = sprintf( 100 | '

%1$s

%2$s', 101 | __( 'We\'ve detected potential vulnerabilities in installed themes. Please check them and keep them updated.', 'wpvulnerability' ), 102 | wpvulnerability_html_themes() 103 | ); 104 | 105 | // Add action links to update themes. 106 | $result['actions'] .= sprintf( 107 | '

%s

', 108 | esc_url( is_multisite() ? network_admin_url( 'themes.php' ) : admin_url( 'themes.php' ) ), 109 | __( 'Update themes', 'wpvulnerability' ) 110 | ); 111 | } 112 | 113 | return $result; 114 | } 115 | 116 | /** 117 | * Tests for vulnerabilities in core. 118 | * 119 | * @version 2.0.0 120 | * 121 | * @return array Returns an array with the results of the vulnerability test. 122 | */ 123 | function wpvulnerability_test_core() { 124 | // Define the initial test result values. 125 | $result = array( 126 | 'label' => __( 'There aren\'t WordPress vulnerabilities', 'wpvulnerability' ), 127 | 'status' => 'good', 128 | 'badge' => array( 129 | 'label' => __( 'Security', 'wpvulnerability' ), 130 | 'color' => 'green', 131 | ), 132 | 'description' => sprintf( 133 | '

%s

', 134 | __( 'Shows possible vulnerabilities existing in the WordPress core.', 'wpvulnerability' ) 135 | ), 136 | 'actions' => '', 137 | 'test' => 'wpvulnerability_core', 138 | ); 139 | 140 | // Check if any core vulnerabilities were found. 141 | $wpvulnerability_test_core_counter = is_multisite() ? json_decode( get_site_option( 'wpvulnerability-core-vulnerable' ) ) : json_decode( get_option( 'wpvulnerability-core-vulnerable' ) ); 142 | 143 | if ( $wpvulnerability_test_core_counter ) { 144 | $result['status'] = 'critical'; 145 | $result['label'] = sprintf( 146 | // translators: Number of core vulnerabilities. 147 | _n( 'There is %d core vulnerability', 'There are %d core vulnerabilities', $wpvulnerability_test_core_counter, 'wpvulnerability' ), 148 | $wpvulnerability_test_core_counter 149 | ); 150 | $result['badge']['color'] = 'red'; 151 | $result['description'] = sprintf( 152 | '

%1$s

%2$s', 153 | __( 'We\'ve detected potential vulnerabilities in this WordPress installation. Please check them and keep your installation updated.', 'wpvulnerability' ), 154 | wpvulnerability_html_core() 155 | ); 156 | 157 | // Add action links to update WordPress. 158 | $result['actions'] .= sprintf( 159 | '

%s

', 160 | esc_url( is_multisite() ? network_admin_url( 'update-core.php' ) : admin_url( 'update-core.php' ) ), 161 | __( 'Update WordPress', 'wpvulnerability' ) 162 | ); 163 | } 164 | 165 | return $result; 166 | } 167 | 168 | /** 169 | * Tests for vulnerabilities in a specified software component. 170 | * 171 | * @version 3.5.0 172 | * 173 | * @param string $software The type of software to test (php, apache, nginx, mariadb, mysql). 174 | * @return array Returns an array with the results of the vulnerability test. 175 | */ 176 | function wpvulnerability_test_software( $software ) { 177 | $software_list = array( 178 | 'php' => __( 'PHP', 'wpvulnerability' ), 179 | 'apache' => __( 'Apache HTTPD', 'wpvulnerability' ), 180 | 'nginx' => __( 'Nginx', 'wpvulnerability' ), 181 | 'mariadb' => __( 'MariaDB', 'wpvulnerability' ), 182 | 'mysql' => __( 'MySQL', 'wpvulnerability' ), 183 | 'imagemagick' => __( 'ImageMagick', 'wpvulnerability' ), 184 | 'curl' => __( 'curl', 'wpvulnerability' ), 185 | ); 186 | 187 | if ( ! array_key_exists( $software, $software_list ) ) { 188 | return array( 189 | 'label' => __( 'Invalid software type', 'wpvulnerability' ), 190 | 'status' => 'error', 191 | 'badge' => array( 192 | 'label' => __( 'Error', 'wpvulnerability' ), 193 | 'color' => 'red', 194 | ), 195 | 'description' => sprintf( 196 | '

%s

', 197 | __( 'The specified software type is not valid.', 'wpvulnerability' ) 198 | ), 199 | 'actions' => '', 200 | 'test' => 'wpvulnerability_' . $software, 201 | ); 202 | } 203 | 204 | // Define the initial test result values. 205 | $result = array( 206 | // translators: name of the software. 207 | 'label' => sprintf( __( 'There aren\'t %s vulnerabilities', 'wpvulnerability' ), $software_list[ $software ] ), 208 | 'status' => 'good', 209 | 'badge' => array( 210 | 'label' => __( 'Security', 'wpvulnerability' ), 211 | 'color' => 'green', 212 | ), 213 | 'description' => sprintf( 214 | '

%s

', 215 | // translators: software with vulnerabilities. 216 | sprintf( __( 'Shows possible vulnerabilities existing in %s.', 'wpvulnerability' ), $software_list[ $software ] ) 217 | ), 218 | 'actions' => '', 219 | 'test' => 'wpvulnerability_' . $software, 220 | ); 221 | 222 | // Check if any vulnerabilities were found. 223 | $vulnerability_counter = is_multisite() ? json_decode( get_site_option( 'wpvulnerability-' . $software . '-vulnerable' ) ) : json_decode( get_option( 'wpvulnerability-' . $software . '-vulnerable' ) ); 224 | 225 | if ( $vulnerability_counter ) { 226 | $result['status'] = 'critical'; 227 | $result['label'] = sprintf( 228 | // translators: Software and number of vulnerabilities. 229 | _n( 'There is %1$d %2$s vulnerability', 'There are %1$d %2$s vulnerabilities', $vulnerability_counter, 'wpvulnerability' ), 230 | $vulnerability_counter, 231 | $software_list[ $software ] 232 | ); 233 | $result['badge']['color'] = 'red'; 234 | $result['description'] = sprintf( 235 | '

%1$s

%2$s', 236 | // translators: software with vulnerabilities. 237 | sprintf( __( 'We\'ve detected potential vulnerabilities in %s. Please check them and keep your installation updated.', 'wpvulnerability' ), $software_list[ $software ] ), 238 | wpvulnerability_html_software( $software ) 239 | ); 240 | 241 | // Add specific action links if necessary. 242 | if ( 'php' === $software ) { 243 | $result['actions'] .= sprintf( 244 | '

%s

', 245 | esc_url( wp_get_update_php_url() ), 246 | __( 'How to update PHP', 'wpvulnerability' ) 247 | ); 248 | } 249 | } 250 | 251 | return $result; 252 | } 253 | 254 | /** 255 | * Tests for vulnerabilities in MySQL. 256 | * 257 | * @return array Returns an array with the results of the vulnerability test. 258 | */ 259 | function wpvulnerability_test_mysql() { 260 | return wpvulnerability_test_software( 'mysql' ); 261 | } 262 | 263 | /** 264 | * Tests for vulnerabilities in MariaDB. 265 | * 266 | * @return array Returns an array with the results of the vulnerability test. 267 | */ 268 | function wpvulnerability_test_mariadb() { 269 | return wpvulnerability_test_software( 'mariadb' ); 270 | } 271 | 272 | /** 273 | * Tests for vulnerabilities in Apache. 274 | * 275 | * @return array Returns an array with the results of the vulnerability test. 276 | */ 277 | function wpvulnerability_test_apache() { 278 | return wpvulnerability_test_software( 'apache' ); 279 | } 280 | 281 | /** 282 | * Tests for vulnerabilities in Nginx. 283 | * 284 | * @return array Returns an array with the results of the vulnerability test. 285 | */ 286 | function wpvulnerability_test_nginx() { 287 | return wpvulnerability_test_software( 'nginx' ); 288 | } 289 | 290 | /** 291 | * Tests for vulnerabilities in PHP. 292 | * 293 | * @return array Returns an array with the results of the vulnerability test. 294 | */ 295 | function wpvulnerability_test_php() { 296 | return wpvulnerability_test_software( 'php' ); 297 | } 298 | 299 | /** 300 | * Tests for vulnerabilities in ImageMagick. 301 | * 302 | * @return array Returns an array with the results of the vulnerability test. 303 | */ 304 | function wpvulnerability_test_imagemagick() { 305 | return wpvulnerability_test_software( 'imagemagick' ); 306 | } 307 | 308 | /** 309 | * Tests for vulnerabilities in curl. 310 | * 311 | * @return array Returns an array with the results of the vulnerability test. 312 | */ 313 | function wpvulnerability_test_curl() { 314 | return wpvulnerability_test_software( 'curl' ); 315 | } 316 | 317 | /** 318 | * Tests for vulnerabilities in memcached. 319 | * 320 | * @return array Returns an array with the results of the vulnerability test. 321 | */ 322 | function wpvulnerability_test_memcached() { 323 | return wpvulnerability_test_software( 'memcached' ); 324 | } 325 | 326 | /** 327 | * Tests for vulnerabilities in Redis. 328 | * 329 | * @return array Returns an array with the results of the vulnerability test. 330 | */ 331 | function wpvulnerability_test_redis() { 332 | return wpvulnerability_test_software( 'redis' ); 333 | } 334 | 335 | /** 336 | * Tests for vulnerabilities in SQLite. 337 | * 338 | * @return array Returns an array with the results of the vulnerability test. 339 | */ 340 | function wpvulnerability_test_sqlite() { 341 | return wpvulnerability_test_software( 'sqlite' ); 342 | } 343 | 344 | /** 345 | * Adds vulnerability tests to the Health Check & Troubleshooting page. 346 | * 347 | * This function registers various vulnerability tests for different components of the site, such as 348 | * WordPress core, themes, plugins, PHP, Apache, Nginx, MariaDB, and MySQL, to the Site Health status page. 349 | * 350 | * @since 2.0.0 351 | * 352 | * @param array $tests Array of current site status tests. 353 | * 354 | * @return array The updated array of site status tests. 355 | */ 356 | function wpvulnerability_tests( $tests ) { 357 | 358 | if ( wpvulnerability_analyze_filter( 'core' ) ) { 359 | // Add test for Core WordPress vulnerabilities. 360 | $tests['direct']['wpvulnerability_core'] = array( 361 | 'label' => __( 'WPVulnerability Core', 'wpvulnerability' ), 362 | 'test' => 'wpvulnerability_test_core', 363 | ); 364 | } 365 | 366 | if ( wpvulnerability_analyze_filter( 'themes' ) ) { 367 | // Add test for Theme vulnerabilities. 368 | $tests['direct']['wpvulnerability_themes'] = array( 369 | 'label' => __( 'WPVulnerability Themes', 'wpvulnerability' ), 370 | 'test' => 'wpvulnerability_test_themes', 371 | ); 372 | } 373 | 374 | if ( wpvulnerability_analyze_filter( 'plugins' ) ) { 375 | // Add test for Plugin vulnerabilities. 376 | $tests['direct']['wpvulnerability_plugins'] = array( 377 | 'label' => __( 'WPVulnerability Plugins', 'wpvulnerability' ), 378 | 'test' => 'wpvulnerability_test_plugins', 379 | ); 380 | } 381 | 382 | if ( wpvulnerability_analyze_filter( 'php' ) ) { 383 | // Add test for PHP vulnerabilities. 384 | $tests['direct']['wpvulnerability_php'] = array( 385 | 'label' => __( 'WPVulnerability PHP', 'wpvulnerability' ), 386 | 'test' => 'wpvulnerability_test_php', 387 | ); 388 | } 389 | 390 | if ( wpvulnerability_analyze_filter( 'apache' ) ) { 391 | // Add test for Apache vulnerabilities. 392 | $tests['direct']['wpvulnerability_apache'] = array( 393 | 'label' => __( 'WPVulnerability Apache HTTPD', 'wpvulnerability' ), 394 | 'test' => 'wpvulnerability_test_apache', 395 | ); 396 | } 397 | 398 | if ( wpvulnerability_analyze_filter( 'nginx' ) ) { 399 | // Add test for Nginx vulnerabilities. 400 | $tests['direct']['wpvulnerability_nginx'] = array( 401 | 'label' => __( 'WPVulnerability Nginx', 'wpvulnerability' ), 402 | 'test' => 'wpvulnerability_test_nginx', 403 | ); 404 | } 405 | 406 | if ( wpvulnerability_analyze_filter( 'mariadb' ) ) { 407 | // Add test for MariaDB vulnerabilities. 408 | $tests['direct']['wpvulnerability_mariadb'] = array( 409 | 'label' => __( 'WPVulnerability MariaDB', 'wpvulnerability' ), 410 | 'test' => 'wpvulnerability_test_mariadb', 411 | ); 412 | } 413 | 414 | if ( wpvulnerability_analyze_filter( 'mysql' ) ) { 415 | // Add test for MySQL vulnerabilities. 416 | $tests['direct']['wpvulnerability_mysql'] = array( 417 | 'label' => __( 'WPVulnerability MySQL', 'wpvulnerability' ), 418 | 'test' => 'wpvulnerability_test_mysql', 419 | ); 420 | } 421 | 422 | if ( wpvulnerability_analyze_filter( 'imagemagick' ) ) { 423 | // Add test for ImageMagick vulnerabilities. 424 | $tests['direct']['wpvulnerability_imagemagick'] = array( 425 | 'label' => __( 'WPVulnerability ImageMagick', 'wpvulnerability' ), 426 | 'test' => 'wpvulnerability_test_imagemagick', 427 | ); 428 | } 429 | 430 | if ( wpvulnerability_analyze_filter( 'curl' ) ) { 431 | // Add test for curl vulnerabilities. 432 | $tests['direct']['wpvulnerability_curl'] = array( 433 | 'label' => __( 'WPVulnerability curl', 'wpvulnerability' ), 434 | 'test' => 'wpvulnerability_test_curl', 435 | ); 436 | } 437 | 438 | if ( wpvulnerability_analyze_filter( 'memcached' ) ) { 439 | // Add test for memcached vulnerabilities. 440 | $tests['direct']['wpvulnerability_memcached'] = array( 441 | 'label' => __( 'WPVulnerability memcached', 'wpvulnerability' ), 442 | 'test' => 'wpvulnerability_test_memcached', 443 | ); 444 | } 445 | 446 | if ( wpvulnerability_analyze_filter( 'redis' ) ) { 447 | // Add test for Redis vulnerabilities. 448 | $tests['direct']['wpvulnerability_redis'] = array( 449 | 'label' => __( 'WPVulnerability redis', 'wpvulnerability' ), 450 | 'test' => 'wpvulnerability_test_redis', 451 | ); 452 | } 453 | 454 | if ( wpvulnerability_analyze_filter( 'sqlite' ) ) { 455 | // Add test for SQLite vulnerabilities. 456 | $tests['direct']['wpvulnerability_sqlite'] = array( 457 | 'label' => __( 'WPVulnerability sqlite', 'wpvulnerability' ), 458 | 'test' => 'wpvulnerability_test_sqlite', 459 | ); 460 | } 461 | 462 | return $tests; 463 | } 464 | 465 | // Adds the vulnerability tests to the site status tests. 466 | add_filter( 'site_status_tests', 'wpvulnerability_tests' ); 467 | -------------------------------------------------------------------------------- /wpvulnerability-software.php: -------------------------------------------------------------------------------- 1 | null, 82 | 'vulnerable' => 0, 83 | ); 84 | 85 | switch ( $software ) { 86 | case 'php': 87 | case 'apache': 88 | case 'nginx': 89 | case 'mysql': 90 | case 'mariadb': 91 | case 'imagemagick': 92 | case 'curl': 93 | case 'memcached': 94 | case 'redis': 95 | case 'sqlite': 96 | $version = wpvulnerability_get_software_version( $software ); 97 | break; 98 | 99 | default: 100 | return $data; 101 | } 102 | 103 | if ( $version ) { 104 | switch ( $software ) { 105 | case 'php': 106 | case 'apache': 107 | case 'nginx': 108 | case 'mysql': 109 | case 'mariadb': 110 | case 'imagemagick': 111 | case 'curl': 112 | case 'memcached': 113 | case 'redis': 114 | case 'sqlite': 115 | $api_response = wpvulnerability_get_vulnerabilities( $software, $version, 0 ); 116 | break; 117 | } 118 | 119 | if ( ! empty( $api_response ) ) { 120 | $data['vulnerabilities'] = $api_response; 121 | $data['vulnerable'] = 1; 122 | } 123 | } 124 | 125 | return $data; 126 | } 127 | 128 | 129 | /** 130 | * Get Installed Software 131 | * 132 | * Retrieves the list of installed software versions, checks for vulnerabilities, 133 | * caches the data, and sends an email notification if vulnerabilities are detected. 134 | * 135 | * @since 3.5.0 136 | * 137 | * @param string $software The software name (e.g., 'php', 'apache'). 138 | * 139 | * @return string JSON-encoded array of software data with vulnerabilities and vulnerable status. 140 | */ 141 | function wpvulnerability_get_installed( $software ) { 142 | 143 | $wpvulnerability_software_vulnerable = 0; 144 | 145 | // Retrieve fresh vulnerabilities for the installed software version. 146 | $data = wpvulnerability_get_fresh_vulnerabilities( $software ); 147 | 148 | // Check if the software version is vulnerable and count the vulnerabilities. 149 | if ( isset( $data['vulnerable'] ) && (int) $data['vulnerable'] ) { 150 | $wpvulnerability_software_vulnerable = count( $data['vulnerabilities'] ); 151 | } 152 | 153 | // Cache the vulnerability data and the timestamp for cache expiration. 154 | if ( is_multisite() ) { 155 | update_site_option( 'wpvulnerability-' . $software, wp_json_encode( $data ) ); 156 | update_site_option( 'wpvulnerability-' . $software . '-vulnerable', wp_json_encode( number_format( $wpvulnerability_software_vulnerable, 0, '.', '' ) ) ); 157 | update_site_option( 'wpvulnerability-' . $software . '-cache', wp_json_encode( number_format( time() + ( 3600 * WPVULNERABILITY_CACHE_HOURS ), 0, '.', '' ) ) ); 158 | } else { 159 | update_option( 'wpvulnerability-' . $software, wp_json_encode( $data ) ); 160 | update_option( 'wpvulnerability-' . $software . '-vulnerable', wp_json_encode( number_format( $wpvulnerability_software_vulnerable, 0, '.', '' ) ) ); 161 | update_option( 'wpvulnerability-' . $software . '-cache', wp_json_encode( number_format( time() + ( 3600 * WPVULNERABILITY_CACHE_HOURS ), 0, '.', '' ) ) ); 162 | } 163 | 164 | // Return the JSON-encoded array of software data. 165 | return wp_json_encode( $data ); 166 | } 167 | 168 | /** 169 | * Get the cached vulnerabilities or update the cache if it's stale or missing. 170 | * 171 | * @since 3.5.0 172 | * 173 | * @param string $software The software name (e.g., 'php', 'apache'). 174 | * 175 | * @return array|null Array of software data with vulnerabilities, or null if software is invalid. 176 | */ 177 | function wpvulnerability_software_get_vulnerabilities( $software ) { 178 | 179 | $valid_software = array( 'php', 'apache', 'mariadb', 'mysql', 'nginx', 'imagemagick', 'curl', 'memcached', 'redis', 'sqlite' ); 180 | 181 | // Use strict comparison for in_array. 182 | if ( in_array( $software, $valid_software, true ) ) { 183 | if ( is_multisite() ) { 184 | 185 | // Get the cached data and decode it. 186 | $data_cache = json_decode( get_site_option( 'wpvulnerability-' . $software . '-cache' ) ); 187 | 188 | // Get the installed data and decode it. 189 | $data = json_decode( get_site_option( 'wpvulnerability-' . $software ), true ); 190 | 191 | } else { 192 | 193 | // Get the cached data and decode it. 194 | $data_cache = json_decode( get_option( 'wpvulnerability-' . $software . '-cache' ) ); 195 | 196 | // Get the installed data and decode it. 197 | $data = json_decode( get_option( 'wpvulnerability-' . $software ), true ); 198 | 199 | } 200 | 201 | // If the cache is stale or the data is empty, update the cache. 202 | if ( $data_cache < time() || empty( $data ) ) { 203 | 204 | // Get the installed data and update the cache. 205 | $data = json_decode( wpvulnerability_get_installed( $software ), true ); 206 | 207 | } 208 | 209 | return $data; 210 | } else { 211 | return null; 212 | } 213 | } 214 | 215 | /** 216 | * Update the software cache and remove any old cache data. 217 | * 218 | * @since 3.0.0 219 | * 220 | * @param string $software The software name (e.g., 'php', 'apache'). 221 | * 222 | * @return void 223 | */ 224 | function wpvulnerability_get_vulnerabilities_clean( $software ) { 225 | 226 | // Update the installed software cache. 227 | wpvulnerability_get_installed( $software ); 228 | } 229 | -------------------------------------------------------------------------------- /wpvulnerability-themes.php: -------------------------------------------------------------------------------- 1 | get_stylesheet() ) { 35 | $tr_class .= 'active'; 36 | } 37 | 38 | // Generate the vulnerability notice message with the theme name. 39 | $message = sprintf( 40 | /* translators: 1: theme name */ 41 | __( '%1$s has a known vulnerability that may be affecting this version.', 'wpvulnerability' ), 42 | wp_kses( (string) $theme_data->get( 'Name' ), 'strip' ) 43 | ); 44 | 45 | // Begin generating the table row HTML markup with appropriate CSS classes and the vulnerability notice message. 46 | $information = ''; 47 | $information .= ''; 48 | $information .= '

' . esc_html( $message ) . '

'; 49 | $information .= ''; 50 | 51 | // Loop through all vulnerabilities for the current theme and add their details to the table row HTML markup. 52 | $vulnerabilities = $theme_vulnerabilities[ $theme_file ]['wpvulnerability']['vulnerabilities']; 53 | 54 | foreach ( $vulnerabilities as $vulnerability ) { 55 | $what = array(); 56 | if ( isset( $vulnerability['impact']['cwe'] ) ) { 57 | foreach ( $vulnerability['impact']['cwe'] as $vulnerability_cwe ) { 58 | $what[] = '
' . wp_kses( (string) $vulnerability_cwe['name'], 'strip' ) . '
' . wp_kses_post( (string) $vulnerability_cwe['description'] ) . '
'; 59 | } 60 | } 61 | 62 | $sources = array(); 63 | if ( isset( $vulnerability['source'] ) ) { 64 | foreach ( $vulnerability['source'] as $vulnerability_source ) { 65 | $sources[] = '[+] ' . wp_kses( (string) $vulnerability_source['name'], 'strip' ); 66 | } 67 | } 68 | if ( count( $sources ) ) { 69 | $source = '
' . implode( '
', $sources ) . '
'; 70 | } 71 | 72 | $score = null; 73 | if ( isset( $vulnerability['impact']['cvss']['score'] ) ) { 74 | $score = number_format( (float) $vulnerability['impact']['cvss']['score'], 1, '.', '' ); 75 | } 76 | $severity = null; 77 | if ( isset( $vulnerability['impact']['cvss']['severity'] ) ) { 78 | $severity = wpvulnerability_severity( $vulnerability['impact']['cvss']['severity'] ); 79 | } 80 | 81 | $information .= ''; 82 | $information .= ''; 83 | $information .= ''; 113 | $information .= ''; 114 | } 115 | 116 | $information .= '
' . wp_kses( (string) $vulnerability['versions'], 'strip' ) . ''; 84 | if ( (int) $vulnerability['closed'] || (int) $vulnerability['unfixed'] ) { 85 | $information .= '
'; 86 | if ( (int) $vulnerability['closed'] ) { 87 | $information .= '
' . __( 'This theme is closed. Please replace it with another.', 'wpvulnerability' ) . '
'; 88 | } 89 | if ( (int) $vulnerability['unfixed'] ) { 90 | $information .= '
' . __( 'This vulnerability appears to be unpatched. Stay tuned for upcoming theme updates.', 'wpvulnerability' ) . '
'; 91 | } 92 | $information .= '
'; 93 | } 94 | if ( count( $what ) ) { 95 | $information .= '
'; 96 | foreach ( $what as $w ) { 97 | $information .= $w; 98 | } 99 | $information .= '
'; 100 | } 101 | if ( ! is_null( $score ) || ! is_null( $severity ) ) { 102 | $information .= '
'; 103 | if ( ! is_null( $score ) ) { 104 | $information .= '
' . __( 'Global score: ', 'wpvulnerability' ) . $score . ' / 10
'; 105 | } 106 | if ( ! is_null( $severity ) ) { 107 | $information .= '
' . __( 'Severity: ', 'wpvulnerability' ) . $severity . '
'; 108 | } 109 | $information .= '
'; 110 | } 111 | $information .= wp_kses( (string) $source, 'post' ); 112 | $information .= '
'; 117 | $information .= ''; 118 | $information .= ''; 119 | 120 | echo $information; // phpcs:ignore 121 | } 122 | 123 | /** 124 | * Retrieves vulnerabilities for a given theme and updates its data. 125 | * 126 | * @since 2.0.0 127 | * 128 | * @param array $theme_data The theme data array. 129 | * @param string $theme_slug The slug to the theme. 130 | * 131 | * @return array The updated theme data array. 132 | */ 133 | function wpvulnerability_get_fresh_theme_vulnerabilities( $theme_data, $theme_slug ) { 134 | 135 | // Get the theme version and slug from the theme data. 136 | $theme_version = wp_kses( (string) $theme_data['data']->get( 'Version' ), 'strip' ); 137 | $theme_data_v['slug'] = $theme_slug; 138 | 139 | // Initialize vulnerability related fields. 140 | $theme_data_v['name'] = isset( $theme_data['data'] ) ? $theme_data['data']->get( 'Name' ) : ''; 141 | $theme_data_v['vulnerabilities'] = null; 142 | $theme_data_v['vulnerable'] = 0; 143 | 144 | // Retrieve vulnerabilities for the theme using its slug and version. 145 | if ( isset( $theme_slug ) && ! empty( $theme_slug ) ) { 146 | 147 | $theme_api_response = wpvulnerability_get_theme( $theme_slug, $theme_version, 0 ); 148 | 149 | // If vulnerabilities are found, update the theme data accordingly. 150 | if ( ! empty( $theme_api_response ) ) { 151 | 152 | $theme_data_v['vulnerabilities'] = $theme_api_response; 153 | $theme_data_v['vulnerable'] = 1; 154 | 155 | } 156 | } 157 | 158 | return $theme_data_v; 159 | } 160 | 161 | /** 162 | * Get Installed Themes 163 | * Retrieves the list of installed themes, checks for vulnerabilities in each of them, caches the data, and sends an email notification if vulnerabilities are detected. 164 | * 165 | * @since 2.0.0 166 | * 167 | * @return string JSON-encoded array of theme data with vulnerabilities and vulnerable status. 168 | */ 169 | function wpvulnerability_theme_get_installed() { 170 | 171 | $wpvulnerability_themes_vulnerable = 0; 172 | 173 | $themes_v = array(); 174 | $themes = wp_get_themes(); 175 | 176 | foreach ( $themes as $slug => $theme_data ) { 177 | 178 | // Store the theme data. 179 | $themes_v[ $slug ]['data'] = $theme_data; 180 | 181 | // Get fresh vulnerabilities for the theme. 182 | $themes_v[ $slug ]['wpvulnerability'] = wpvulnerability_get_fresh_theme_vulnerabilities( $themes_v[ $slug ], $slug ); 183 | 184 | // If the theme is vulnerable, increment the vulnerable themes counter. 185 | if ( isset( $themes_v[ $slug ]['wpvulnerability']['vulnerable'] ) && 1 === (int) $themes_v[ $slug ]['wpvulnerability']['vulnerable'] ) { 186 | ++$wpvulnerability_themes_vulnerable; 187 | } 188 | } 189 | 190 | // Update options for multisite installations. 191 | if ( is_multisite() ) { 192 | update_site_option( 'wpvulnerability-themes', wp_json_encode( $themes_v ) ); 193 | update_site_option( 'wpvulnerability-themes-vulnerable', wp_json_encode( number_format( $wpvulnerability_themes_vulnerable, 0, '.', '' ) ) ); 194 | update_site_option( 'wpvulnerability-themes-cache', wp_json_encode( number_format( time() + ( 3600 * WPVULNERABILITY_CACHE_HOURS ), 0, '.', '' ) ) ); 195 | 196 | // Update options for single site installations. 197 | } else { 198 | update_option( 'wpvulnerability-themes', wp_json_encode( $themes_v ) ); 199 | update_option( 'wpvulnerability-themes-vulnerable', wp_json_encode( number_format( $wpvulnerability_themes_vulnerable, 0, '.', '' ) ) ); 200 | update_option( 'wpvulnerability-themes-cache', wp_json_encode( number_format( time() + ( 3600 * WPVULNERABILITY_CACHE_HOURS ), 0, '.', '' ) ) ); 201 | } 202 | 203 | return wp_json_encode( $themes_v ); 204 | } 205 | 206 | /** 207 | * Get the cached themes vulnerabilities or update the cache if it's stale or missing. 208 | * 209 | * @since 2.0.0 210 | * 211 | * @return array Array of installed themes with their vulnerabilities. 212 | */ 213 | function wpvulnerability_theme_get_vulnerabilities() { 214 | 215 | if ( is_multisite() ) { 216 | // Get the cached theme data and decode it. 217 | $theme_data_cache = json_decode( get_site_option( 'wpvulnerability-themes-cache' ) ); 218 | 219 | // Get the installed theme data and decode it. 220 | $theme_data = json_decode( get_site_option( 'wpvulnerability-themes' ), true ); 221 | 222 | } else { 223 | // Get the cached theme data and decode it. 224 | $theme_data_cache = json_decode( get_option( 'wpvulnerability-themes-cache' ) ); 225 | 226 | // Get the installed theme data and decode it. 227 | $theme_data = json_decode( get_option( 'wpvulnerability-themes' ), true ); 228 | } 229 | 230 | // If the cache is stale or the theme data is empty, update the cache. 231 | if ( ( isset( $theme_data_cache ) && $theme_data_cache < time() ) || empty( $theme_data ) ) { 232 | // Get the installed theme data and update the cache. 233 | $theme_data = json_decode( wpvulnerability_theme_get_installed(), true ); 234 | } 235 | 236 | return $theme_data; 237 | } 238 | 239 | /** 240 | * Update the installed themes cache and remove any old cache data. 241 | * 242 | * @since 2.0.0 243 | * 244 | * @return void 245 | */ 246 | function wpvulnerability_theme_get_vulnerabilities_clean() { 247 | wpvulnerability_clear_cache( 'themes' ); 248 | wpvulnerability_theme_get_installed(); 249 | } 250 | 251 | /** 252 | * Admin Head 253 | * Adds vulnerability information after the theme row and notices on the theme page based on the installed theme cache. 254 | * 255 | * @since 2.0.0 256 | * 257 | * @return void 258 | */ 259 | function wpvulnerability_theme_page() { 260 | 261 | // Check if the current page is the themes page. 262 | global $pagenow; 263 | if ( wpvulnerability_analyze_filter( 'themes' ) && 'themes.php' === $pagenow && wpvulnerability_capabilities() ) { 264 | 265 | // Get the vulnerabilities for the installed themes. 266 | $themes = wpvulnerability_theme_get_vulnerabilities(); 267 | 268 | // Loop through the themes and add vulnerability information after the theme row for vulnerable themes. 269 | foreach ( $themes as $theme_file => $theme_data ) { 270 | 271 | if ( isset( $theme_data['wpvulnerability']['vulnerable'] ) && 1 === (int) $theme_data['wpvulnerability']['vulnerable'] ) { 272 | add_action( 'after_theme_row_' . esc_attr( $theme_file ), 'wpvulnerability_theme_info_after', 10, 2 ); 273 | } 274 | } 275 | } 276 | } 277 | // Add notices for vulnerable themes on the theme page. 278 | add_action( 'admin_head', 'wpvulnerability_theme_page' ); 279 | 280 | /** 281 | * Filters the themes list to show only vulnerable themes when the "Vulnerable" tab is selected. 282 | * 283 | * This function hooks into the WordPress themes listing in the network admin to filter the displayed themes 284 | * based on their vulnerability status. When the "Vulnerable" tab is selected (identified by the `theme_status=vulnerable` 285 | * query parameter), it filters the themes list to include only those themes with known vulnerabilities. 286 | * 287 | * The function retrieves the vulnerabilities for all themes from the WordPress options table and compares 288 | * them against the active list of themes. Themes without vulnerabilities are removed from the list, leaving 289 | * only those that are considered vulnerable. 290 | * 291 | * @since 3.3.5 292 | * 293 | * @global object $wp_list_table The WordPress list table object for managing themes. 294 | * 295 | * @return void 296 | */ 297 | function wpvulnerability_themes_filter() { 298 | if ( isset( $_GET['theme_status'] ) && 'vulnerable' === $_GET['theme_status'] ) { // phpcs:ignore 299 | 300 | global $wp_list_table; 301 | 302 | // Retrieve the vulnerabilities for all themes from the options table and decode the JSON. 303 | $theme_vulnerabilities = is_multisite() 304 | ? json_decode( get_site_option( 'wpvulnerability-themes' ), true ) 305 | : json_decode( get_option( 'wpvulnerability-themes' ), true ); 306 | 307 | // Loop through the items in the themes list table. 308 | foreach ( $wp_list_table->items as $theme_file => $theme_data ) { 309 | 310 | if ( ! isset( $theme_vulnerabilities[ $theme_file ]['wpvulnerability']['vulnerable'] ) || 311 | 0 === (int) $theme_vulnerabilities[ $theme_file ]['wpvulnerability']['vulnerable'] ) { 312 | 313 | unset( $wp_list_table->items[ $theme_file ] ); 314 | } 315 | } 316 | } 317 | } 318 | 319 | /** 320 | * Initializes the vulnerability filtering for the themes list in the network admin area of a multisite installation. 321 | * 322 | * This function checks if the current environment is a multisite network and whether the user is in the network 323 | * admin area. If both conditions are met, it hooks into the 'admin_head-themes.php' action to apply a filter that 324 | * shows only vulnerable themes in the themes list. 325 | * 326 | * @since 3.3.5 327 | * 328 | * @return void 329 | */ 330 | function wpvulnerability_themes_filter_init() { 331 | if ( is_multisite() && is_network_admin() ) { 332 | add_action( 'admin_head-themes.php', 'wpvulnerability_themes_filter' ); 333 | } 334 | } 335 | add_action( 'network_admin_menu', 'wpvulnerability_themes_filter_init' ); 336 | 337 | /** 338 | * Adds a "Vulnerable" tab to the WordPress themes page that displays the count of vulnerable themes. 339 | * 340 | * This function checks the cache for the number of vulnerable themes and adds a new tab to the themes 341 | * management page in the WordPress admin area. The tab displays the count of vulnerable themes and highlights it 342 | * if it is currently active. The tab is added only in the network admin area of a multisite installation. 343 | * 344 | * @since 3.3.5 345 | * 346 | * @param array $views An array of existing theme views (tabs) in the WordPress admin themes page. 347 | * 348 | * @return array The modified array of views including the "Vulnerable" tab. 349 | */ 350 | function wpvulnerability_themes_view( $views ) { 351 | if ( ! wpvulnerability_analyze_filter( 'themes' ) ) { 352 | return $views; 353 | } 354 | 355 | $wpvulnerability_themes_count = is_multisite() ? get_site_option( 'wpvulnerability-themes-vulnerable' ) : null; 356 | 357 | $wpvulnerability_themes_total = 0; 358 | if ( $wpvulnerability_themes_count ) { 359 | $wpvulnerability_themes_total = json_decode( $wpvulnerability_themes_count ); 360 | } 361 | 362 | if ( is_multisite() && is_network_admin() ) { 363 | $views['vulnerable'] = sprintf( 364 | '%s', 365 | network_admin_url( 'themes.php?theme_status=vulnerable' ), 366 | ( isset( $_GET['theme_status'] ) && 'vulnerable' === $_GET['theme_status'] ? ' class="current"' : '' ), // phpcs:ignore 367 | // translators: the number of vulnerabilities. 368 | sprintf( __( 'Vulnerabilities (%d)', 'wpvulnerability' ), $wpvulnerability_themes_total ) 369 | ); 370 | } 371 | 372 | return $views; 373 | } 374 | 375 | /** 376 | * Adds a custom filter to the themes page in the WordPress admin to display a tab for vulnerable themes. 377 | * 378 | * This function hooks into the 'views_themes-network' filter to add a custom tab or view for displaying vulnerable themes 379 | * on the themes management page in the WordPress network admin area. The tab is added only in a multisite setup 380 | * and specifically in the network admin context. 381 | * 382 | * @since 3.3.5 383 | * 384 | * @return void 385 | */ 386 | function wpvulnerability_themes_add_tab() { 387 | if ( is_multisite() && is_network_admin() ) { 388 | add_filter( 'views_themes-network', 'wpvulnerability_themes_view' ); 389 | } 390 | } 391 | add_action( 'admin_head', 'wpvulnerability_themes_add_tab' ); 392 | -------------------------------------------------------------------------------- /wpvulnerability.php: -------------------------------------------------------------------------------- 1 |