WP Manifestindependent plugin directory
manifest / security / mainwp-check-plugins-vulnerability-extension

MainWP Check Plugins Vulnerability Extension WP Registry grade D+. High-severity findings · 4 findingsWP RegistryHigh-severity findings · 4 findingsOpen the reportD+

Extension for MainWP Dashboard. Checks the child websites plugins for vulnerability using the WordFence intelligence api.

★ 11stars
1.4.8latest · 5mo ago

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/herbie4/mainwp-check-plugins-vulnerability-extension/archive/refs/heads/master.zip
Updates itself. It ships its own updater (Plugin Update Checker), so new versions appear under Dashboard → Updates.

Extension for MainWP Dashboard. Check the installed website plugins for vulnerability use the WordFence vulnerability data feed api. You don't need to have WF plugin installed on your website! This is a manual check and only runs when you do a scan from MainWP Dashboard. It will check all child sites and all plugins, so also inactive plugins on child sites.

As from version 1.4.7 You need to get your own Wordfence API key (it is free).

Get your api key here: https://www.wordfence.com/account/integrations Beware that it does have a download limit so do not update the file to often. You can find information about rate limits on the Wordfence website.

This MainWP extension can be used to manual check on vulnerabilties for plugins without using the WordFence plugin on your child sites. The current version uses only the last 2 year of records from the WordFence Intelligence API (production) data feed (https://www.wordfence.com/intelligence-documentation/v2-accessing-and-consuming-the-vulnerability-data-feed/).

Make sure you do a full sync of all child site data before scanning your plugins.

=== This plugin is still under development and new functions will be added in time. ===

To Do list

  • add option to choose 'production' or 'scanner' feed
  • add direct link to site dashboard from scanner list
  • add option to scan for vulnerable themes
  • add option to filter sites by tags
  • exclude theme data from adapted feed

Feedback

Let me know if you need some function or something appears not to be working correct.

TIP

This theme looks best in dark mode setting of MWP.

Screenshots

mainwp-extensie-check-plugin-vulnerability-scan mainwp-extensie-check-plugin-vulnerability-dashboard

Releases

25 releases.

Tag
Published
1.4.8 latest
May 19, 2026 5mo ago
Apr 24, 2026 6mo ago
Apr 23, 2026 6mo ago
Oct 17, 2024 2 years ago
Oct 12, 2024 2 years ago
Oct 12, 2024 2 years ago
Oct 10, 2024 2 years ago
Oct 10, 2024 2 years ago
Jun 25, 2024 2 years ago
May 30, 2024 2 years ago
Apr 9, 2024 2 years ago
Apr 5, 2024 2 years ago
Apr 3, 2024 2 years ago
Mar 29, 2024 2 years ago
Mar 29, 2024 2 years ago
Mar 26, 2024 2 years ago
Mar 22, 2024 2 years ago
Mar 22, 2024 2 years ago
Mar 22, 2024 2 years ago
Mar 22, 2024 2 years ago
Nov 6, 2023 2 years ago
Nov 3, 2023 2 years ago
Oct 16, 2023 3 years ago
Oct 16, 2023 3 years ago
Oct 12, 2023 3 years ago

These releases are tags only. The author does not attach a packaged zip, so there are no download counts to report.

D+ grade

Security

MainWP Check Plugins Vulnerability Extension 1.4.8 · audited by WP Registry

High-severity findings.

2 high 2 medium
Audited release
1.4.8
Findings
4
Worst severity
high
Content hash
a90db449bbfd30f452eed91a…

Findings

  • high Reflected XSS via unescaped REQUEST_URI in admin form actions

    xss_reflected

    hhdev_mwpcpv_render_page() prints three HTML forms whose action attribute is str_replace('%7E','~', $_SERVER['REQUEST_URI']) with no esc_url/esc_attr. The page is a MainWP admin screen (ManageSitesmainwp-check-plugins-vulnerability or the Extensions-* slug). A logged-in dashboard administrator who opens a crafted URL such as admin.php?page=ManageSitesmainwp-check-plugins-vulnerability&x="><img src=x onerror=…> receives that string inside action="…", breaking out of the attribute into HTML. WordPress does not kses $_SERVER. SameSite=Lax still sends auth cookies on this top-level GET, so a phishing link is sufficient. Script runs in the MainWP dashboard origin as the administrator — the same session that can update plugins, run snippets, and open child-site wp-admin — so this is dashboard/child-network takeover, not a cosmetic admin glitch. At least one of the three forms is always rendered (API-key form when _MWPCPV_api_key is empty; data-update and run-scan forms when it is set).

    class/class-dashboard.php:118-208

    Recommendation

    Use action="" or esc_url( admin_url( 'admin.php?page=' . sanitize_key( wp_unslash( $_GET['page'] ?? '' ) ) ) ). Never echo REQUEST_URI into HTML. Add a Content-Security-Policy on these screens as defense in depth.

  • high Stored XSS in scan results from child-site plugin data and unescaped feed fields

    xss_stored

    After a successful mwpcpv-run-scan POST, the renderer echoes $website->name, $plugin['name'], $plugin['version'], $plugin_data['title'], and $plugin_data['references'][0] into HTML and into an href with no esc_html/esc_attr/esc_url. $plugin['name'] and $plugin['version'] come from json_decode($website->plugins) — the plugin headers MainWP stored from each child site. A client (or attacker) with admin on any connected child can install or header-spoof a plugin whose Name is <img src=x onerror=…> and whose version matches a Wordfence affected range; when the dashboard operator runs this scanner, that payload executes in the parent dashboard origin as the administrator (full MainWP + child-site blast radius). $plugin_data['title'] and references[0] come from wp-content/hhdev-mwpcpv-data/vuln-data.json, which is written 0777-world-writable with no .htaccess, so a shared-hosting neighbor can poison the feed for the same sink. hhdev_get_groups_dropdown() also concatenates $group->name into <option> text.

    class/class-dashboard.php:291-343

    Recommendation

    Escape every scan field: esc_html() for names/titles/versions, esc_url() for reference hrefs. Treat child-site plugin JSON and the on-disk feed as untrusted. Drop world-writable permissions on the data directory so the JSON cannot be replaced out-of-band.

  • medium CSRF overwrites the Wordfence API key and triggers an unauthenticated feed download

    csrf

    1.4.8 added check_admin_referer('run-scan-check') only on the scan POST. The other two state-changing handlers in the same render function have no nonce, no current_user_can, and no sanitize. (1) POST mwpcpv-set-option=1&api_key=… runs update_option('_MWPCPV_api_key', $_REQUEST['api_key'], false) even when a key already exists (the save is above the 'please add a key' UI). Observable: the option is replaced; the next hhdev_save_api_data_file() sends Authorization: Bearer <attacker value> to wordfence.com (token theft/burn or scanner DoS; CRLF in the unsanitized key is also interpolated into the stream-context header). (2) POST mwpcpv-get-data-file=1 (when a key is already stored) calls file_get_contents() of the hardcoded Wordfence v3 URL and file_put_contents of the response plus a derived vuln-data.json under wp-content. Observable: a multi-megabyte write, Wordfence rate-limit consumption, mkdir 0777. Both are cross-site POSTs to admin.php?page=ManageSitesmainwp-check-plugins-vulnerability (or the Extensions-* slug). SameSite=Lax reduces classic cross-origin POST reliability on modern WP cookies; the missing nonce is still exploitable via same-site content, an existing XSS, or browsers/plugins that send cookies on POST, and it is the exact class 1.4.8 claimed to fix.

    class/class-dashboard.php:104-141

    Recommendation

    Add wp_nonce_field + check_admin_referer (or wp_verify_nonce) to both forms, matching the scan form. Gate with current_user_can('manage_options') or mainwp_current_user_can('extension', …) inside the handler, not only via menu registration. Sanitize the API key (sanitize_text_field; reject CR/LF) before update_option and before concatenating it into an HTTP header. Prefer wp_remote_get() over file_get_contents().

  • medium World-writable web-accessible data directory for the Wordfence feed

    insecure_config

    hhdev_save_api_data_file() creates WP_CONTENT_DIR/hhdev-mwpcpv-data with mkdir(..., 0777, true) and writes vulnerabilities.production.json (raw Wordfence body) and later vuln-data.json with no index.php and no .htaccess deny. 0777 lets every local system user replace those files (shared hosting / compromised neighboring account). Direct HTTP GET of /wp-content/hhdev-mwpcpv-data/*.json is allowed on default Apache/nginx that serve wp-content statically (directory listing if Indexes is on). A replaced vuln-data.json is the stored-XSS source for plugin_data['title'] and references[0]. A replaced PHP file in the same 0777 directory is RCE if the host executes PHP under wp-content (common except where a global php_flag engine off covers only uploads/). MainWP core independently tightened the same 0777 pattern (MWP-1558) for this reason.

    hhdev-mwpcpv.php:213-216

    Recommendation

    Create the directory with 0750/0755, write index.php and a deny-from-all .htaccess (or place files under uploads/mainwp with MainWP_System_Utility::get_mainwp_dir(..., false)). Do not use 0777. Validate that the downloaded body is JSON before saving. Consider storing the adapted feed as a non-public option/transient instead of a web-reachable file.

WP Registry hashes the installable build and reports on that exact bytes-for-bytes copy. Embargoed findings are withheld until they are disclosed, so a clean verdict means nothing public is outstanding. WP Manifest does not audit code itself.