• 5.1.6 e6cba7ef6a

    v5.1.6 Stable

    javier released this 2026-08-23 07:10:06 +00:00 | 0 commits to main since this release

    5.1.6 - 2026-08-22

    Fixed

    • Saving the "Delete all plugin data on uninstall" preference no longer triggers a fatal error (add_settings_error() was called before the WordPress admin API was loaded).
    • Notification channels can now be disabled entirely: unchecking every channel and saving previously kept the old channels active, because unchecked checkboxes are never submitted.
    • "Reset Plugin" and "Delete all logs" now purge both log stores (API request logs and shell execution audit logs). Previously the shell audit store was never cleaned, and both deletions silently did nothing because the log post types were not registered yet when the actions ran.
    • The Debug tab API connectivity tests now use the real API routes: WordPress core collapses pre-release versions (e.g. 7.1-alpha) to their stable milestone, and plugins/themes are tested against their actual slug routes instead of nonexistent plural ones (HTTP 404).
    • Component icons now carry explicit width/height attributes and a meaningful alt text. The same icons are embedded in notification emails and Site Health, where the plugin CSS is not loaded and unsized SVGs rendered at their intrinsic 800x800 size.

    Changed

    • languages/wpvulnerability.pot regenerated with the new strings.

    Compatibility

    • WordPress: 4.7 - 7.1
    • PHP: 7.0 - 8.5
    • WP-CLI: 2.3.0 - 2.12.0

    Tests

    • PHP Coding Standards: 3.13.6 (0 errors)
    • WordPress Coding Standards: 3.4.1
    • PHPStan: 2.2.9 (level 9, 0 errors)
    • PHPUnit: 9.6.36 (54 tests)

    5.1.5 - 2026-08-22

    Changed

    • The contributor list now leads with the ROBOTSTXT organization account, followed by Javier Casares; the remaining contributors are unchanged.
    • Regenerated languages/wpvulnerability.pot so its source references match the current code. The string set is unchanged (450 strings); all shipped translations were verified against it and remain fully up to date.

    Compatibility

    • WordPress: 4.7 - 7.1
    • PHP: 7.0 - 8.5
    • WP-CLI: 2.3.0 - 2.12.0

    Tests

    • PHP Coding Standards: 3.13.6
    • WordPress Coding Standards: 3.4.1
    • PHPStan: 2.2.9 (level 9, 0 errors)
    • PHPUnit: 9.6.36 (45 tests)
      = [5.1.4] - 2026-08-22 =

    Highlights

    • The Site Health vulnerability tests work again on every WordPress version: they were silently disabled everywhere by an availability gate that could never pass. Secrets are now masked in the admin forms, and compatibility was verified against real PHP interpreters from 5.6 to 8.5.

    Fixed

    • The Site Health integration registered its tests behind class_exists( 'WP_Site_Health' ), but core loads that class lazily, after init, where the plugin loads its modules — so the guard always evaluated to false and none of the 13 vulnerability tests ever appeared in Site Health. The filter is now registered unconditionally; below WordPress 5.2 it simply never fires.
    • "Clear all caches" in the debug tools now also deletes the wpvulnerability-plugins-cache-data timestamp option, so the plugins dataset is fully refreshed afterwards.
    • The notification email "From:" address is now sanitized with sanitize_email() on all code paths.
    • The plugins list table "Last updated" column decoded the whole plugins dataset for every table row; it is now decoded once per request.
    • Removed a duplicated json_decode() call when reading the vulnerable-themes count.
    • The wp_date() compatibility polyfill no longer fatals on WordPress 4.7–5.3: core calls wp_maybe_decline_date() unconditionally, but that function only exists since 5.4; the call is now availability-guarded.
    • The core and software JSON getters return '[]' instead of an empty string when encoding fails, matching the plugins and themes getters.

    Security

    • Webhook URLs (Slack, Teams, Discord) and the Telegram bot token are masked in the admin settings forms (single-site and network), showing only their last 4 characters — previously they were rendered in full into the page HTML. The inputs are plain text fields, so masked values never trigger browser URL validation. Saving a form with unchanged masked values keeps the stored secret; clearing a field still removes it.
    • Removed the dead register_uninstall_hook() registration: uninstall.php supersedes it and the registration added a stray autoloaded option row.
    • Single-site settings registrations now explicitly set show_in_rest => false, matching the multisite configuration.

    Changed

    • Admin CSS/JS assets load only on the WPVulnerability settings pages, the dashboard and the plugins/themes list tables instead of on every wp-admin screen.
    • The shell availability probe (wpvulnerability_can_shell_exec()) result is cached per request instead of spawning a process on every call.

    Compatibility

    • WordPress: 4.7 - 7.1
    • PHP: 7.0 - 8.5
    • WP-CLI: 2.3.0 - 2.12.0
    • Verified against real PHP interpreters 5.6.40, 7.0.33, 7.1.33, 7.2.34, 7.3.33, 7.4.33, 8.0.30, 8.1.34, 8.2.33, 8.3.33, 8.4.24 and 8.5.9: every plugin file lints clean from 7.0 up and fails on 5.6, confirming the declared floor. PHPCompatibility gate 7.0-8.5 clean; wp-compat WordPress floor 4.7 clean.

    Tests

    • PHP Coding Standards: 3.13.6
    • WordPress Coding Standards: 3.4.1
    • PHPStan: 2.2.9 (level 9, 0 errors)
    • PHPUnit: 9.6.36 (45 tests)

    5.1.3 - 2026-08-22

    Added

    • wpvulnerability-compat.php: verbatim WordPress core function polyfills (wp_doing_cron, get_main_site_id, wp_is_json_request, sanitize_locale_name, determine_locale, wp_timezone_string, wp_timezone, wp_date, get_post_datetime, get_post_timestamp), each guarded by function_exists(). The plugin no longer fatals on WordPress versions that lack these functions.

    Security

    • The API connectivity test results (message, HTTP code, response time and the raw response preview) are now escaped client-side before being inserted into the page, in both single-site and network admin. Remote API responses could previously inject arbitrary HTML.
    • The debug export and the Database Options Viewer now mask webhook URLs, Telegram bot tokens/chat IDs and notification email addresses, so shared debug files never contain usable secrets.
    • The multisite "components to hide" save now routes through the strict sanitizer, so a crafted request can no longer re-enable analysis of components force-hidden with WPVULNERABILITY_HIDE_* constants.
    • API responses are only cached when the HTTP status is 200 and the body is valid JSON. Error responses (4xx/5xx, HTML error pages) were previously cached for the full TTL and blocked retries.
    • API request URLs are now built from whitelisted types and sanitized slugs/versions (wpvulnerability_get(), wpvulnerability_get_vulnerabilities()).

    Fixed

    • Cron scheduling ran wp_get_schedules() (and other plugins' cron_schedules callbacks, including their translation calls) before init on every request. Core and notification event scheduling moved to init, matching the WordPress 6.7+ translation-loading requirements.
    • Deactivation now removes the wpvulnerability-plugins-data and wpvulnerability-plugins-data-cache options created at activation.
    • The notification cron period is validated against daily/weekly before wp_schedule_event(), so a corrupted configuration value can no longer silently unschedule notifications.
    • The admin menu vulnerability counters escape the badge title attribute, and the plugin row "Settings" link escapes its URL and label.
    • Application Passwords REST authentication is gated on function_exists( 'wp_is_application_passwords_available' ) so WordPress < 5.6 no longer fatals on unauthenticated REST requests; cookie-authenticated admin sessions keep working and the Application Passwords functionality itself is simply unavailable on old versions.
    • The Site Health integration registers its tests only when WP_Site_Health exists (WordPress 5.2+); on older versions the plugin runs without the Site Health section.

    Changed

    • Minimum WordPress version lowered from 5.6 to 4.7. Whole functionalities degrade gracefully on old versions: Application Passwords REST authentication (WordPress 5.6+) and the Site Health integration (WordPress 5.2+) are only registered when available; everything else works through the polyfills.

    Compatibility

    • WordPress: 4.7 - 7.1
    • PHP: 7.0 - 8.5
    • WP-CLI: 2.3.0 - 2.12.0

    Tests

    • PHP Coding Standards: 3.13.6
    • WordPress Coding Standards: 3.4.1
    • PHPStan: 2.2.9 (level 9, 0 errors)
    • PHPUnit: 9.6.36 (37 tests)
    Downloads
  • 5.1.2 7dfff83511

    v5.1.2 Stable

    javier released this 2026-08-07 10:46:22 +00:00 | 1 commits to main since this release

    [5.1.2] - 2026-08-07

    Fixed

    • Missing load_plugin_textdomain() call caused a "Translation loading for the wpvulnerability domain was triggered too early" _doing_it_wrong() notice on WordPress 6.7+. The textdomain is now explicitly loaded on the init hook.

    Added

    • WordPress Playground blueprint (blueprint.json) with pre-installed vulnerable plugins and a theme for quick testing.

    Changed

    • Security vulnerability reporting link updated to ROBOTSTXT.
    • Contributor repository link updated to git.robotstxt.es/ROBOTSTXT/wpvulnerability.
    • The readme.txt Changelog section now shows only the latest 3 versions; the full history remains in changelog.txt.

    Compatibility

    • WordPress: 5.6 - 7.1
    • PHP: 7.0 - 8.5
    • WP-CLI: 2.3.0 - 2.12.0

    Tests

    • PHP Coding Standards: 3.13.5
    • WordPress Coding Standards: 3.3.0
    • PHPStan: 2.1.55 (level 9, 0 errors)
    • PHPUnit: 9.6.34 (25 tests)
    Downloads
  • 5.1.1 db98c4438c

    v5.1.1 Stable

    javier released this 2026-07-09 06:29:00 +00:00 | 2 commits to main since this release

    [5.1.1] - 2026-07-09

    Fixed

    • Site Health: the memcached, Redis, and SQLite vulnerability tests always returned "Invalid software type" because those components were missing from the software list. They now run correctly.
    • "Send test email" failed when no email recipients were configured: the forced-test path added the admin email but the send gate used a flag computed before the override. The test email now sends.
    • "Run notification now" reported failure when only webhook channels (Slack/Teams/Discord/Telegram) were enabled and email was disabled. The notification result now reflects whether any channel delivered successfully.
    • Weekly notification cron events were never auto-scheduled, because the weekly cron schedule was registered only at init while the on-load scheduling runs earlier. The weekly/daily schedule registration moved to the always-loaded scheduler so weekly notifications schedule correctly.
    • Deactivation deleted the per-component analysis settings (wpvulnerability-analyze), so deactivating and reactivating reset which components were hidden. Analysis settings are now preserved on deactivation (only uninstall removes them).
    • The debug "Cron Status" panel always showed the notification event as "not scheduled" because it checked the wrong hook name (wpvulnerability_send_notification instead of wpvulnerability_notification).
    • The debug "last run" timestamp was always empty because it read a wpvulnerability-logs option that is never written; it now reads the most recent API log entry.
    • Redis detection called close() twice on the success path (once in the try block, once in finally); the redundant close was removed.
    • The single-site vulnerable-themes count was hardcoded to 0 instead of reading the stored option.

    Changed

    • Single-site email recipient sanitization now uses is_email() for strict validation, matching the multisite behaviour.
    • Removed a redundant nonce field from the single-site "Reset Plugin" form (the form only submits the full-reset action).
    • Removed dead code: the unused wpvulnerability_sanitize_messages callback and its setting registration, and an unused $tools_action variable in the multisite admin.
    Downloads
  • 5.1.0 31f7e97d39

    v5.1.0 Stable

    javier released this 2026-07-09 06:21:27 +00:00 | 3 commits to main since this release

    [5.1.0] - 2026-07-08

    Security

    • wpvulnerability_validate_shell_command() now uses exact in_array() match instead of stripos() substring matching for the shell-command allowlist (defense-in-depth).
    • wpvulnerability_detect_php(), wpvulnerability_detect_curl(), and wpvulnerability_detect_webserver() now route through wpvulnerability_safe_shell_exec(), so every software-detection shell call is validated and recorded in the Shell Execution Audit Log. Previously these called shell_exec() directly, bypassing the wrapper and the audit log. As a side effect this also fixes nginx/angie version detection: escapeshellcmd() was escaping the 2>&1 redirect, so stderr (where nginx prints its version) was never captured.

    Fixed

    • Multisite uninstall fatal error: Uncaught Error: Undefined constant "WPVULNERABILITY_PLUGIN_BASE" when "Delete all plugin data on uninstall" was enabled. uninstall.php now defines the constant before loading wpvulnerability-run.php.
    • WPVULNERABILITY_HIDE_* constants now stop shell_exec detection for hidden components during scheduled scans and in the admin "Software Detection Methods" panel. Previously they only hid the results from the UI, so the audit log kept filling with "command not found" entries for components the administrator had explicitly deactivated.
    • wpvulnerability_detect_webserver() no longer shell-probes a hidden web server via the sibling path: WPVULNERABILITY_HIDE_NGINX and WPVULNERABILITY_HIDE_APACHE now fully isolate the hidden server.
    • Multisite cron requests on non-main subsites no longer load all plugin module files. No WPVulnerability cron events are scheduled on subsites, so the extra loading was wasted work.
    • LiteSpeed / OpenLiteSpeed / Caddy shell detection in the WP_DEBUG diagnostic panel now works: the commands no longer use 2>/dev/null (which the shell-command validator rejected), caddy was added to the allowlist, and which output is validated as a real path so "command not found" messages are not mistaken for a detection.

    Changed

    • Network: true added to the plugin header to declare network-aware multisite behaviour.
    • Removed the unused $plugin_status parameter from wpvulnerability_plugin_info_after(); the PHPCS suite now passes with zero warnings.
    • Multisite network dashboard: the "Site Health" footer link pointed to wp-admin/network/site-health.php, which does not exist (Site Health is a per-site screen). It now links to the main site's wp-admin/site-health.php.
    Downloads
  • 5.0.1 9aef0b4533

    v5.0.1 Stable

    javier released this 2026-06-02 13:44:09 +00:00 | 4 commits to main since this release

    [5.0.1] - 2026-06-02

    Fixed

    • wp_cache_flush() removed from wpvulnerability_update_database_data() and wpvulnerability_reset_plugin_data(). The function was wiping the entire Object Cache (Redis, Memcached, APCu) on every plugin or theme update, causing CPU spikes and database overload on high-traffic sites. The targeted wpvulnerability_delete_transients() call that already runs before it invalidates all plugin-owned cache entries; a global flush was never necessary.
    • wp_cache_flush() was called twice on plugin reset: once inside wpvulnerability_update_database_data() and once more immediately after in wpvulnerability_reset_plugin_data(). Both calls have been removed.
    Downloads
  • 5.0.0 7b62556227

    v5.0.0 Stable

    javier released this 2026-06-02 13:42:48 +00:00 | 5 commits to main since this release

    [5.0.0] - 2026-05-26 =

    Added

    • Vulnerability detail panels fully redesigned across all contexts (plugins, themes, core, server software, Site Health):
      • Bootstrap-inspired score/severity badge colour-coded by severity level (Critical/High/Medium/Low/None).
      • Source attribution pills: label is the hostname extracted from the source URL (e.g. wordfence.com, nvd.nist.gov); uniform dark-grey #50575e; hover turns black/white; linked with rel="nofollow noopener noreferrer".
      • "References:" label and pills displayed on the same flex row.
      • CVSS priority order: CVSS 4 → CVSS 3 → CVSS 2 → legacy CVSS. The highest available score and severity are always shown.
      • ssvc.exploitation: poc → " Public exploit" badge; active → merged into "⚠ Actively exploited".
      • ssvc.automatable: yes → "⚙ Automatable" badge.
      • ssvc.kev_date → date appended to "Actively exploited" (e.g. "⚠ Actively exploited · 2024-03-15").
      • epss → "EPSS X.X%" badge on the same row as the CVSS badge.
      • CVE description from source[].description shown for all vulnerability types; [en-US] / language prefix stripped automatically.
      • CWE name and description shown in server software vulnerability details (was missing).
      • Version range: leading - / * stripped when there is no lower bound (e.g. - < 1.3.28< 1.3.28).
      • Wording change: "affecting this version" → "affecting your installed version" (plugins, themes, core).
    • Core vulnerabilities on update-core.php: display rewritten to use the same two-column table with badges, description, CWE, and reference pills as plugin/theme rows (replaces the old plain table with score-only rows).
    • Theme details modal (single-site): vulnerability section injected after the Tags field. Uses wp_prepare_themes_for_js filter to embed pre-rendered HTML and a JS template patch (admin_footer) to surface it inside the Underscore.js template. Shows version range, badges, description, and references — identical layout to plugin rows.
    • Helper functions added to wpvulnerability-process.php: wpvulnerability_source_css_slug(), wpvulnerability_render_source_pills(), wpvulnerability_render_score_badge(), wpvulnerability_clean_version_range(), wpvulnerability_get_source_description().
    • Helper functions added to wpvulnerability-themes.php: wpvulnerability_theme_modal_html(), wpvulnerability_filter_prepare_themes_for_js(), wpvulnerability_theme_modal_template_patch().
    • Vulnerability severity now uses the full-word value from cvss3.severity ("critical", "high", "medium", "low", "none") when available, falling back to the legacy single-character cvss.severity code. wpvulnerability_severity() accepts both formats.
    • Known Exploited Vulnerability (KEV) badge: when a vulnerability is listed in the CISA KEV catalog (impact.ssvc.kev: true for plugins/themes/core; impact.kev: true for server software), a prominent warning is shown in the vulnerability detail panel.
    • Publication date displayed next to each source link in the vulnerability details table (source[].date field from the API).
    • Server software lifecycle status displayed in the vulnerability detail panel: a coloured dot shows whether the detected version is Supported or End of Life, together with the end-of-life date if available (data.status, data.date_end fields from the API).
    • EOL badge shown in the dashboard software grid (PHP, Apache, nginx, MariaDB, MySQL, ImageMagick, curl, memcached, Redis, SQLite) when the detected version has reached end-of-life. Lifecycle data is fetched alongside vulnerability data during the scheduled scan and stored with the software option.
    • REST API: all vulnerability endpoints now expose uuid (unique vulnerability identifier), kev (boolean), ssvc block (exploitation, automatable, technical_impact) for plugins/themes/core, and severity from cvss3 when available.
    • About tab: new Intelligence Sources table showing how many vulnerabilities each source (CVE, EUVD, JVN, Patchstack, WPScan, Wordfence) contributes, broken down by Core / Plugins / Themes. Populated from the new stats.vulnerabilities.{source} field in the API.
    • REST API: source entries now include a date field with the vulnerability publication date.

    Security

    • wpvulnerability-adminms.php: wpvulnerability_process_network_config_forms() now requires manage_network_options capability (via current_user_can()) in addition to the nonce check already present. Previously, a logged-in user who obtained a valid nonce could update plugin config and analysis options.
    • WP-CLI: all vulnerability commands (core, plugins, themes, php, apache, …) now require manage_options (or manage_network_options on multisite). Commands abort with a clear error and --user=<admin_login> hint if the check fails. Enforces the same access model as the REST API endpoints.
    • WP-CLI: all config commands (hide, email, cache, log-retention, period) now require manage_options / manage_network_options before modifying any WordPress option.
    • REST API permission check now passes strict mode (true) to base64_decode() when parsing the HTTP Basic Authorization header. Without this flag, PHP silently ignores illegal base64 characters; with it, a malformed or padding-stripped token is immediately rejected and returns an authentication failure instead of decoding unpredictable bytes.
    • Debug web server detection (LiteSpeed, OpenLiteSpeed, Caddy) now routes through the wpvulnerability_safe_shell_exec() wrapper instead of calling shell_exec() directly. This ensures the WPVULNERABILITY_DISABLE_SHELL_EXEC constant, the security mode, the command whitelist, and audit logging are respected even when WP_DEBUG is enabled.

    Fixed

    • Forced test email on a fresh install (no email address configured yet) no longer produces an E_WARNING on PHP 7.x or a TypeError on PHP 8.x. The fallback now initialises emails as an array instead of appending to a potentially empty string.
    • determine_locale() call in the About tab is now guarded by function_exists() instead of a version_compare() check, which is more forward-compatible and correctly recognised by static analysis tools.
    • preg_replace() return values (which can be null on error) in the HTML-to-plain-text converter are now handled safely, preventing a potential TypeError on corrupt input.
    • DOMDocument::$documentElement null check added in the HTML-to-plain-text converter; avoids a potential fatal error on malformed or empty HTML fragments.
    • JSON-encoded vulnerability count options are validated as strings before being passed to json_decode(), preventing potential errors when a WordPress option returns an unexpected type.
    • cron_schedules callbacks (wpvulnerability_add_every_week, wpvulnerability_add_every_day) now use did_action('init') guard before calling __(), eliminating the "Translation loading triggered too early" notice introduced in WordPress 6.7.
    • WP-CLI vulnerability commands (core, plugins, themes, php, and all software components) now display a clear success message when no vulnerabilities are found, instead of an empty table.
    • WP-CLI config period monthly now returns a proper error message instead of silently treating the value as never. Valid periods are daily, weekly, and never.
    • uninstall.php created. Plugin data is preserved by default on uninstall; a new "Delete all plugin data on uninstall" checkbox in the Tools settings tab allows opting in to full removal.
    • Large vulnerability data options are now stored with autoload=false, preventing unnecessary loading of JSON blobs on every WordPress request.

    Changed

    • Minimum required PHP version updated from 5.6 to 7.0. The null coalescing operator (??) introduced throughout the codebase for improved type safety requires PHP 7.0 or later. PHP 5.6 reached end-of-life in December 2018 and is no longer supported by any currently maintained WordPress version.
    • Minimum required WordPress version updated from 4.7 to 5.6. This enables simplification of backward-compatibility code paths and full use of Application Passwords, wp_timezone(), wp_date(), determine_locale(), and $wpdb->db_server_info() without version guards.
    • Removed wp_doing_cron() polyfill — the function has been available since WordPress 4.8 and is guaranteed at the new minimum.
    • Application Passwords authentication in the REST API no longer requires a function_exists('wp_is_application_passwords_available') guard — Application Passwords are always available at WordPress 5.6.
    • Removed fallback code paths for wp_timezone() (WordPress 5.3), wp_date() (WordPress 5.3), and determine_locale() (WordPress 5.0).
    • wpvulnerability_get_vulnerabilities() now stores the impact field in each matched vulnerability array entry, enabling KEV and severity display for all component types.
    • wpvulnerability_get_fresh_vulnerabilities() (software.php) now stores lifecycle metadata (name, status, date_start, date_end) in the software option alongside vulnerabilities and vulnerable. The transient is explicitly cleared before the refresh to guarantee a fresh API call.
    • wpvulnerability_get_cron_snapshot() now uses only public WordPress Cron API functions (wp_next_scheduled(), wp_get_schedule()) instead of the private _get_cron_array(). The function iterates over the known plugin hooks (defined in wpvulnerability_get_plugin_cron_hooks()) rather than scanning the full cron array. Duplicate-instance detection is no longer performed.
    • wpvulnerability_get_plugin(), wpvulnerability_get_theme(), wpvulnerability_get_core(), and wpvulnerability_get_vulnerabilities() now store the uuid field in each vulnerability entry.

    Developer

    • Full PHPStan level 9 compliance achieved across all 17 PHP files: 1,092 pre-existing type errors resolved; analysis now passes clean with zero errors and an empty baseline.
    • Added development tooling: composer.json with all dev dependencies, phpstan.neon (level 9), phpcs.xml (WordPress standards), phpunit.xml.dist, bin/deploy.sh, and docs/ directory.
    • Added 18 PHPUnit plugin header tests verifying consistency between wpvulnerability.php and readme.txt.
    • WordPress API stubs updated to v7.0.0 for PHPStan analysis. Added johnbillion/wp-compat extension for WordPress version compatibility analysis.
    • Added internal helpers wpvulnerability_get_component_count() and wpvulnerability_get_config() to centralise typed access to cached vulnerability counts and plugin configuration.
    • Fixed 10 WordPress version compatibility findings reported by wp-compat: wp_date(), determine_locale(), wp_authenticate_application_password(), after_core_auto_updates_settings hook, site_status_tests filter, and others.

    Compatibility

    • WordPress: 5.6 - 7.1
    • PHP: 7.0 - 8.5
    • WP-CLI: 2.3.0 - 2.12.0

    Tests

    • PHP Coding Standards: 3.13.5
    • WordPress Coding Standards: 3.3.0
    • PHPStan: 2.1.55 (level 9, 0 errors)
    • PHPUnit: 9.6.34 (18 tests)
    • Plugin Check (PCP): 1.8.0
    Downloads