$pass, 'ip' => Rabbitproof_Ip::visitor( $_SERVER, (bool) Rabbitproof_Options::get( 'proxy' ) ), 'path' => self::path( isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '/' ), 'sender' => self::sender( $_SERVER ), 'signed_in' => function_exists( 'is_user_logged_in' ) && is_user_logged_in(), 'ua' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? substr( sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ), 0, 500 ) : '', 'ch' => isset( $_SERVER['HTTP_SEC_CH_UA'] ) ? substr( sanitize_text_field( wp_unslash( $_SERVER['HTTP_SEC_CH_UA'] ) ), 0, 200 ) : '', 'edge' => self::edge_key(), 'form' => preg_match( '/^[a-z0-9]{1,20}$/', (string) $form ) ? (string) $form : '', ), Rabbitproof_Api::CHECK_TIMEOUT ); if ( is_wp_error( $answer ) || 200 !== $answer['_status'] ) { self::unreachable(); return false; } self::$decided = ! empty( $answer['block'] ); self::tally( $form, self::$decided || ! empty( $answer['would'] ) ); self::$ref = self::$decided && isset( $answer['ref'] ) && is_string( $answer['ref'] ) && 1 === preg_match( '/^[0-9a-f]{6}$/', $answer['ref'] ) ? $answer['ref'] : ''; return self::$decided; } public static function own_server( array $server ) { if ( ( function_exists( 'wp_doing_cron' ) && wp_doing_cron() ) || ( defined( 'DOING_CRON' ) && DOING_CRON ) ) { return true; } $ip = Rabbitproof_Ip::visitor( $server, false ); $self = array( '127.0.0.1', '::1' ); if ( ! empty( $server['SERVER_ADDR'] ) ) { $self[] = (string) $server['SERVER_ADDR']; } return '' !== $ip && in_array( $ip, $self, true ); } const SENDERS = array( 'HTTP_STRIPE_SIGNATURE' => 'Stripe', 'HTTP_PAYPAL_TRANSMISSION_SIG' => 'PayPal', 'HTTP_X_SQUARE_HMACSHA256_SIGNATURE' => 'Square', 'HTTP_X_SHOPIFY_HMAC_SHA256' => 'Shopify', 'HTTP_X_WC_WEBHOOK_SIGNATURE' => 'WooCommerce', 'HTTP_PADDLE_SIGNATURE' => 'Paddle', 'HTTP_X_GITHUB_EVENT' => 'GitHub', 'HTTP_X_TWILIO_SIGNATURE' => 'Twilio', 'HTTP_X_XERO_SIGNATURE' => 'Xero', ); public static function sender( $server ) { foreach ( self::SENDERS as $header => $name ) { if ( isset( $server[ $header ] ) ) { return $name; } } $ua = isset( $server['HTTP_USER_AGENT'] ) ? (string) $server['HTTP_USER_AGENT'] : ''; return 0 === strpos( $ua, 'PayPal IPN' ) ? 'PayPal' : ''; } public static function path( $uri ) { $path = wp_parse_url( $uri, PHP_URL_PATH ); $path = is_string( $path ) && 0 === strpos( $path, '/' ) && 0 !== strpos( $path, '//' ) ? $path : '/'; $query = wp_parse_url( $uri, PHP_URL_QUERY ); $names = array(); if ( is_string( $query ) && '' !== $query ) { foreach ( explode( '&', $query ) as $pair ) { $name = rawurldecode( explode( '=', $pair, 2 )[0] ); if ( 1 === preg_match( '/^[\w.-]{1,40}$/', $name ) ) { $names[ $name ] = true; } } } return substr( $path, 0, 300 ) . ( $names ? '?' . implode( '&', array_keys( $names ) ) : '' ); } public static function unreachable() { $last = (int) get_option( 'rabbitproof_unreachable', 0 ); if ( time() - $last > 60 ) { update_option( 'rabbitproof_unreachable', time(), false ); } } private static function tally( $form, $spam ) { $days = get_option( self::TALLY, array() ); $days = is_array( $days ) ? $days : array(); $today = gmdate( 'Y-m-d' ); $was = isset( $days[ $today ][ $form ] ) && is_array( $days[ $today ][ $form ] ) ? $days[ $today ][ $form ] : array( 0, 0 ); $now = array( (int) ( isset( $was[0] ) ? $was[0] : 0 ), (int) ( isset( $was[1] ) ? $was[1] : 0 ) ); $now[ $spam ? 0 : 1 ]++; $days[ $today ][ $form ] = $now; $from = gmdate( 'Y-m-d', time() - 6 * 86400 ); $days = array_filter( $days, function ( $d ) use ( $from ) { return is_string( $d ) && $d >= $from; }, ARRAY_FILTER_USE_KEY ); update_option( self::TALLY, $days, false ); } public static function tally_week() { $days = get_option( self::TALLY, array() ); $from = gmdate( 'Y-m-d', time() - 6 * 86400 ); $out = array(); foreach ( is_array( $days ) ? $days : array() as $day => $forms ) { if ( ! is_string( $day ) || $day < $from || ! is_array( $forms ) ) { continue; } foreach ( $forms as $form => $n ) { $was = isset( $out[ $form ] ) ? $out[ $form ] : array( 0, 0 ); $out[ $form ] = array( $was[0] + (int) ( is_array( $n ) && isset( $n[0] ) ? $n[0] : 0 ), $was[1] + (int) ( is_array( $n ) && isset( $n[1] ) ? $n[1] : 0 ) ); } } return $out; } public static function ref() { return self::$ref; } public static function reset() { self::$decided = null; self::$ref = ''; } public static function comment( $approved, $data = array() ) { unset( $data ); if ( is_wp_error( $approved ) || in_array( $approved, array( 'spam', 'trash' ), true ) || ! self::blocked( 'comments' ) ) { return $approved; } return 'spam'; } public static function comment_kept( $id, $approved ) { if ( 'spam' === $approved && true === self::$decided && '' !== self::$ref ) { add_comment_meta( (int) $id, 'rabbitproof_ref', self::$ref, true ); } } public static function comment_row( $actions, $comment ) { $ref = is_object( $comment ) && isset( $comment->comment_ID ) ? get_comment_meta( (int) $comment->comment_ID, 'rabbitproof_ref', true ) : ''; if ( is_string( $ref ) && '' !== $ref ) { /* translators: %s: a short reference code */ $actions['rabbitproof'] = '' . esc_html( sprintf( __( 'Rabbitproof: spam bot, reference %s', 'rabbitproof' ), $ref ) ) . ''; } return $actions; } public static function errors_first( $errors ) { if ( is_wp_error( $errors ) && ! $errors->has_errors() && self::blocked( 'signups' ) ) { $errors->add( 'rabbitproof_refused', self::message() ); } return $errors; } public static function lost_password( $errors ) { self::errors_first( $errors ); } public static function login( $user, $username = '', $password = '' ) { unset( $password ); if ( '' === (string) $username || is_wp_error( $user ) || ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) { return $user; } return self::blocked( 'login' ) ? new WP_Error( 'rabbitproof_refused', self::message() ) : $user; } public static function woo_checkout( $data, $errors ) { unset( $data ); if ( is_wp_error( $errors ) && ! $errors->has_errors() && self::blocked( 'woocommerce' ) ) { $errors->add( 'rabbitproof_refused', self::message() ); } } public static function woo_block_checkout( $order, $errors ) { self::woo_checkout( $order, $errors ); } public static function woo_register( $username, $email, $errors ) { unset( $username, $email ); self::woo_checkout( null, $errors ); } public static function cf7( $spam, $submission = null ) { if ( $spam || ! self::blocked( 'cf7' ) ) { return $spam; } if ( Rabbitproof_Caught::kept_here( 'cf7' ) && is_object( $submission ) && method_exists( $submission, 'get_posted_data' ) ) { $posted = $submission->get_posted_data(); $kept = array(); foreach ( is_array( $posted ) ? $posted : array() as $name => $value ) { if ( is_string( $name ) && 0 !== strpos( $name, '_' ) ) { $kept[ ucfirst( str_replace( array( '-', '_' ), ' ', preg_replace( '/^your-/', '', $name ) ) ) ] = $value; } } Rabbitproof_Caught::keep( 'cf7', $kept, self::$ref ); } return true; } public static function cf7_message( $message, $status = '' ) { return 'spam' === $status && true === self::$decided ? self::message() : $message; } public static function wpforms( $errors, $form_data = array() ) { $errors = is_array( $errors ) ? $errors : array(); if ( empty( $form_data['id'] ) ) { return $errors; } $id = $form_data['id']; if ( ! empty( $errors[ $id ] ) || ! self::blocked( 'wpforms' ) ) { return $errors; } $raw = isset( $_POST['wpforms']['fields'] ) && is_array( $_POST['wpforms']['fields'] ) ? map_deep( wp_unslash( $_POST['wpforms']['fields'] ), 'sanitize_textarea_field' ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- a public form's own send, read only to keep the refused message for the website's owner. $kept = array(); foreach ( isset( $form_data['fields'] ) && is_array( $form_data['fields'] ) ? $form_data['fields'] : array() as $f ) { if ( ! is_array( $f ) || ! isset( $f['id'] ) || ! isset( $raw[ $f['id'] ] ) ) { continue; } $v = $raw[ $f['id'] ]; $v = is_array( $v ) ? trim( implode( ' ', array_filter( array_map( 'strval', $v ) ) ) ) : $v; $kept[ isset( $f['label'] ) && '' !== (string) $f['label'] ? (string) $f['label'] : 'Field ' . $f['id'] ] = $v; } Rabbitproof_Caught::keep( 'wpforms', $kept, self::$ref ); $errors[ $id ]['header'] = self::message(); return $errors; } public static function gravity( $is_spam, $form = array() ) { unset( $form ); if ( ! self::blocked( 'gravity' ) ) { return $is_spam; } return true; } public static function elementor( $record, $ajax_handler ) { if ( ! is_object( $ajax_handler ) || ! method_exists( $ajax_handler, 'add_error_message' ) ) { return; } if ( ( isset( $ajax_handler->is_success ) && false === $ajax_handler->is_success ) || ! empty( $ajax_handler->errors ) || ! self::blocked( 'elementor' ) ) { return; } $kept = array(); if ( is_object( $record ) && method_exists( $record, 'get' ) ) { foreach ( (array) $record->get( 'fields' ) as $f ) { if ( is_array( $f ) && isset( $f['value'] ) ) { $kept[ isset( $f['title'] ) && '' !== $f['title'] ? (string) $f['title'] : ( isset( $f['id'] ) ? (string) $f['id'] : 'Field' ) ] = $f['value']; } } } Rabbitproof_Caught::keep( 'elementor', $kept, self::$ref ); $ajax_handler->add_error_message( self::message() ); } public static function fluent( $errors, $form_data = array(), $form = null ) { if ( ! empty( $errors ) ) { return $errors; } if ( self::blocked( 'fluent' ) ) { $kept = array(); foreach ( is_array( $form_data ) ? $form_data : array() as $name => $value ) { if ( is_string( $name ) && 0 !== strpos( $name, '_' ) && ( is_scalar( $value ) || is_array( $value ) ) ) { $kept[ ucfirst( str_replace( '_', ' ', $name ) ) ] = $value; } } Rabbitproof_Caught::keep( 'fluent', $kept, self::$ref ); Rabbitproof_Caught::fluent_spam( $form, is_array( $form_data ) ? $form_data : array() ); $errors = is_array( $errors ) ? $errors : array(); $errors['rabbitproof'] = array( self::message() ); } return $errors; } }