0

Looking for a way to show a list of hidden plugins - those that don't show up on the Admin, Plugins page for Admins. The code should also work on multisite installation.

Not sure if this is the proper way to do it:

$hidden_plugins = array_filter($plugins, function($plugin) {
    return !is_plugin_active($plugin['slug']) && !is_dir(WP_PLUGIN_DIR . '/' . $plugin['slug']);
});

Is there a better way to create a list of plugins that are hidden from view? This is for a security plugin I am working on.

ADDED

Assume that a plugin has managed to become 'hidden' (it can be done) so that it doesn't appear on the Admin, Plugins page. The plugin is active, however, and it's functions are running on the site. (Perhaps the plugin adds some code that is executed on every page - and that code is 'nefarious'.)

What code would a security-related plugin use to hide itself from the Admin, Plugins page so that the hidden plugin can be sensed and displayed on the security plugin's status page?

6
  • I can't think: how would a plugin end up hidden? Because it's edited itself out of the list to display, or because it's not activated for that site on a multisite (?) Or something else?
    – Rup
    Oct 28, 2023 at 19:43
  • There are ways to 'hide' a plugin, according to the googles/bings/ducks. Most often, it might be done by a hacker using the hidden plugin for nefarious purposes. I am updating a security plugin I wrote, and wanted to add a feature that would show any hidden plugins. A non-nefarious developer might want to have a plugin that cannot be disabled via the plugin screen due to it's needed features. Oct 28, 2023 at 22:00
  • can you be more specific than "hidden", mention specific methods as what you're describing is not a thing in WP core, and the code you posted would just show active plugins which is already what that page does. I suspect that this is an X Y problem and could probably be reworded as "how to detect when plugins have been hidden from the plugin page using method X Y Z", hard concrete examples are needed
    – Tom J Nowell
    Oct 28, 2023 at 23:00
  • additional information added as requested. Oct 28, 2023 at 23:26
  • I'm still not sure how it's possible, but I guess you could scan get_included_files for plugin and mu-plugin folders you're not expecting to have loaded through normal means. I suppose some other plugin or theme could require code from a not-loaded plugin. Apart from that I'd pick through 1) the normal plugin loading code to see if there are any loopholes, e.g. where it decides if a folder or file is a plugin or not (and report them to trac); 2) the plugin admin page to see if there's any way to filter the list displayed.
    – Rup
    Oct 29, 2023 at 11:31

2 Answers 2

1

The potentially nefarious plugin might filter itself out of the plugins list table by attaching a filter callback to all_plugins hook, which is used in WP_Plugins_List_Table->prepare_items().

One option could be that you inspect the global $wp_filter variable and see, if there are any suspicious (but what counts as suspicious?) callbacks attached to the above hook.

Another option is to mimic what the list table is doing, but compare the raw and filtered results of get_plugins() to see, if there are potentially hidden plugins.

$all_plugins = get_plugins();
// from wp-admin/includes/class-wp-plugins-list-table.php:104
$potentially_filtered_plugins = apply_filters( 'all_plugins', $all_plugins );

if ( count($all_plugins) !== count($potentially_filtered_plugins) ) {
    echo 'There might be hidden plugins on your site.'
}

// foreach $all_plugins to render a list of plugins in the plugins dir

These are perhaps a little naive solutions, but build upon what WP is doing already.

0

After some research and testing, and much asking of the googles/bings/ducks, I figured out the following information to answer my own question. My research is presented here as the answer to my question.

First, there is a way to create a plugin that is not shown on the Plugins list. I am not going to detail the process or code, but it can be done. I started my testing by creating a 'hidden plugin' that did not appear in the Plugins list. It is done by using the 'all_plugins' filter to unset the hidden plugin from the plugin list.

But, every plugin has a settings screen (well, usually). My Hidden Plugin had a settings screen, and therefore had a link on the Settings screen.

So the second thing the hidden plugin had to do is to remove that settings link from the Settings screen (or any location in the Admin menu). Again, no code, but it involved using an external jQuery file that manipulated the DOM object to find and remove the plugins settings URL.

So the result is that my hidden plugin did not appear, and it's Settings screen did also not appear. (Although the plugin's settings URL could be determined.)

The next step was to create a new plugin that would 'find' any hidden plugins. As mentioned in another answer, that is done by looking at the list of plugins and comparing it with the plugins that are listed on the Plugins screen.

The code to do that is this:

function get_hidden_plugins() {
    // Get the list of all plugins before the filter is applied
    $all_plugins_before = get_plugins();

    // Apply the filter to remove plugins
    $all_plugins_after = apply_filters('all_plugins', $all_plugins_before);

    // Find the hidden plugins (those removed by the filter)
    $hidden_plugins = array_diff_key($all_plugins_before, $all_plugins_after);

    // Create an unordered list of hidden plugins
    $hidden_plugins_list = '<ul>';
    foreach ($hidden_plugins as $plugin_file => $plugin_data) {
        $status = is_plugin_active($plugin_file) ? '<b>Active</b>' : '<i>Inactive</i>';

        $plugin_name = esc_html($plugin_data['Name']);
        $version     = esc_html($plugin_data['Version']);
        echo '<li  style=\'list-style-type: disc;margin-left:3%;\'><b>' . $plugin_name . '</b> (Version: ' . $version . ', Status: ' . $status . ')</li>';
    }
                 $plugin_slug = admin_url('admin.php?page=' . $plugin_data['TextDomain']);
             echo "<p> link to hidden plugin settings page: " . $plugin_slug . '</p>' . PHP_EOL;
$hidden_plugins_list .= '</ul>';

    return $hidden_plugins_list;
}

The result in my testing environment (which has the successfully hidden plugin) is a list showing all hidden plugins (just the one - my test hidden plugin), in a unordered list.

I also included in that unordered list information about the hidden plugin, including the URL of it's setting screen.

Now, there may be a way to create a hidden plugin that doesn't follow the rules of the proper items in the plugin. I have not researched that.

But the above code will show hidden plugins. Which was the point of my question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.