detect_type( $hook_extra ); if ( ! $type ) { return $source; // Not a plugin/theme operation (e.g. core update). } if ( $this->is_update( $hook_extra ) ) { return $source; // Only fresh installs are restricted, not updates. } $slug = $this->detect_slug( $source ); if ( ! $slug ) { return $this->reject( $type, __( '(unrecognized package)', 'wlock' ) ); } if ( in_array( strtolower( $slug ), $this->get_allowed( $type ), true ) ) { return $source; } return $this->reject( $type, $slug ); } private function get_allowed( $type ) { $option = 'theme' === $type ? self::OPT_THEMES : self::OPT_PLUGINS; $slugs = get_option( $option, array() ); return is_array( $slugs ) ? array_map( 'strtolower', $slugs ) : array(); } private function detect_type( $hook_extra ) { if ( isset( $hook_extra['type'] ) && in_array( $hook_extra['type'], array( 'plugin', 'theme' ), true ) ) { return $hook_extra['type']; } if ( isset( $hook_extra['plugin'] ) ) { return 'plugin'; } if ( isset( $hook_extra['theme'] ) ) { return 'theme'; } return false; } private function is_update( $hook_extra ) { return isset( $hook_extra['action'] ) && 'update' === $hook_extra['action']; } private function detect_slug( $source ) { $slug = basename( untrailingslashit( $source ) ); if ( ! $slug ) { return false; } // Defense in depth: the folder name originates from inside an // uploaded/downloaded archive. Strip anything unexpected and cap the // length before it is ever stored or echoed anywhere, even though it // is only escaped output at that point too. $slug = sanitize_text_field( $slug ); $slug = substr( $slug, 0, 100 ); return '' !== $slug ? $slug : false; } private function reject( $type, $slug ) { $this->log_block( $type, $slug ); $label = 'theme' === $type ? __( 'This theme', 'wlock' ) : __( 'This plugin', 'wlock' ); return new WP_Error( 'wlock_not_whitelisted', sprintf( /* translators: 1: "This plugin"/"This theme", 2: slug of the rejected package */ __( '%1$s ("%2$s") is not on the approved list for this site and cannot be installed. An administrator can add it under Settings → WLock.', 'wlock' ), $label, $slug ) ); } private function log_block( $type, $slug ) { $log = get_option( self::OPT_LOG, array() ); array_unshift( $log, array( 'time' => current_time( 'mysql' ), 'user' => wp_get_current_user()->user_login, 'type' => $type, 'slug' => $slug, ) ); update_option( self::OPT_LOG, array_slice( $log, 0, self::MAX_LOG ), false ); } /* ------------------------------------------------------------------ */ /* Settings page */ /* ------------------------------------------------------------------ */ public function add_settings_link( $links ) { $settings_link = sprintf( '%s', esc_url( admin_url( 'options-general.php?page=wlock' ) ), esc_html__( 'Settings', 'wlock' ) ); array_unshift( $links, $settings_link ); return $links; } public function add_settings_page() { add_options_page( __( 'WLock', 'wlock' ), __( 'WLock', 'wlock' ), 'manage_options', 'wlock', array( $this, 'render_settings_page' ) ); } public function register_settings() { register_setting( self::OPTION_GROUP, self::OPT_ENABLED, array( 'type' => 'string', 'sanitize_callback' => array( $this, 'sanitize_checkbox' ), 'default' => '0', ) ); register_setting( self::OPTION_GROUP, self::OPT_PLUGINS, array( 'type' => 'array', 'sanitize_callback' => array( $this, 'sanitize_slug_list' ), 'default' => array(), ) ); register_setting( self::OPTION_GROUP, self::OPT_THEMES, array( 'type' => 'array', 'sanitize_callback' => array( $this, 'sanitize_slug_list' ), 'default' => array(), ) ); } public function sanitize_checkbox( $value ) { return ( '1' === $value ) ? '1' : '0'; } /** * Accepts either a newline-separated string (from the textarea) or an * array, and returns a clean array of unique, lower-cased slugs. */ public function sanitize_slug_list( $value ) { if ( is_string( $value ) ) { $value = preg_split( '/[\r\n,]+/', $value ); } if ( ! is_array( $value ) ) { return array(); } $clean = array(); foreach ( $value as $item ) { $item = sanitize_title( trim( (string) $item ) ); $item = substr( $item, 0, 100 ); if ( '' !== $item ) { $clean[] = $item; } } return array_values( array_unique( $clean ) ); } public function render_settings_page() { if ( ! current_user_can( 'manage_options' ) ) { return; } $enabled = get_option( self::OPT_ENABLED, '0' ); $plugins = get_option( self::OPT_PLUGINS, array() ); $themes = get_option( self::OPT_THEMES, array() ); $log = get_option( self::OPT_LOG, array() ); ?>
|