Changeset 3733310
- Timestamp:
- 10/07/2026 08:39:00 PM (12 days ago)
- Location:
- site-first-seo
- Files:
-
- 6 edited
- 1 copied
-
tags/1.1.14 (copied) (copied from site-first-seo/trunk )
-
tags/1.1.14/includes/class-sfs-botblock.php (modified) (10 diffs)
-
tags/1.1.14/readme.txt (modified) (4 diffs)
-
tags/1.1.14/site-first-seo.php (modified) (2 diffs)
-
trunk/includes/class-sfs-botblock.php (modified) (10 diffs)
-
trunk/readme.txt (modified) (4 diffs)
-
trunk/site-first-seo.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
site-first-seo/tags/1.1.14/includes/class-sfs-botblock.php
r3706009 r3733310 177 177 $ua = self::server( 'HTTP_USER_AGENT', '' ); 178 178 $ip = self::client_ip(); 179 $method = s elf::server( 'REQUEST_METHOD', 'GET');179 $method = strtoupper( self::server( 'REQUEST_METHOD', 'GET' ) ); 180 180 $referer = self::server( 'HTTP_REFERER', '' ); 181 181 $body = self::classify_request_body( $method ); 182 182 $behavior = self::track_ip_behavior( $ip, $path, $ua ); 183 $crawler = self::crawler_classification( $ua, $ip, $behavior ); 183 $exploit_rules = self::common_exploit_rules( self::server( 'REQUEST_URI', $path ) ); 184 // Do not perform crawler DNS lookups for requests already known to be probes. 185 $crawler = self::crawler_classification( $ua, $ip, $behavior, empty( $exploit_rules ) || ! (int) get_option( self::OPT_BLOCK_EXPLOITS, 1 ) ); 184 186 185 187 // 1) Deterministically block sensitive-file probes when protection is enabled. 186 if ( (int) get_option( self::OPT_BLOCK_EXPLOITS, 1 ) && self::is_common_exploit_path( $path) ) {187 self::log( 'hard_block', 'common_exploit', 100, $ua, $ip, $path, $method, $referer, 403, 0, array( 'common_exploit_path' ), $body, $crawler );188 if ( (int) get_option( self::OPT_BLOCK_EXPLOITS, 1 ) && ! empty( $exploit_rules ) ) { 189 self::log( 'hard_block', 'common_exploit', 100, $ua, $ip, $path, $method, $referer, 403, 0, $exploit_rules, $body, $crawler ); 188 190 status_header( 403 ); 189 191 exit; … … 202 204 203 205 // 3) Verified search crawlers and configured allowlists 204 if ( (int) get_option( self::OPT_ALLOW_SEARCH, 1 ) && $crawler === 'trusted_search' ) {206 if ( (int) get_option( self::OPT_ALLOW_SEARCH, 1 ) && $crawler === 'trusted_search' && in_array( $method, array( 'GET', 'HEAD' ), true ) ) { 205 207 self::schedule_log( 'allow', 'trusted_crawler', 0, $ua, $ip, $path, $method, $referer, array( 'verified_search_crawler' ), $body, $crawler ); 206 208 return; … … 208 210 209 211 $allow = self::build_allowlist(); 212 // A declared bot identity does not authorize submitting data or write requests. 213 if ( ! in_array( $method, array( 'GET', 'HEAD' ), true ) ) { $allow['ua'] = array(); } 210 214 if ( self::matches_allow( $ua, $ip, $path, $allow ) ) { 211 215 self::schedule_log( 'allow', 'allowlist', 0, $ua, $ip, $path, $method, $referer, array( 'allowlist' ), $body, $crawler ); … … 270 274 $normalized = rawurldecode( rawurldecode( $path ) ); 271 275 $normalized = str_replace( '\\', '/', $normalized ); 276 $normalized = str_replace( "\0", '', $normalized ); 272 277 $normalized = preg_replace( '#/+#', '/', $normalized ); 278 // Windows alternate data streams can follow an otherwise sensitive filename. 279 $normalized = preg_replace( '#::\\$DATA(?=/|$)#i', '', $normalized ); 273 280 if ( ! is_string( $normalized ) ) { return false; } 274 281 275 282 $patterns = array( 276 283 '#(?:^|/)\.git(?:/|$)#i', 284 '#(?:^|/)\.git(?:ignore|modules|attributes)(?:[.~_-][^/]*)?(?:/|$)#i', 277 285 '#(?:^|/)\.(?:svn|hg)(?:/|$)#i', 278 286 '#(?:^|/)\.env(?:[._-][^/]*)?(?:/|$)#i', 279 287 '#(?:^|/)(?:wp-config\.php|configuration\.php)(?:[.~_-][^/]*)?$#i', 280 288 '#(?:^|/)(?:config|configuration)/(?:secrets?|credentials?|database)(?:\.[^/]*)?$#i', 281 '#(?:^|/)\.(?:aws|ssh)/(?:credentials|config|id_rsa)(?:\.[^/]*)?$#i', 289 '#(?:^|/)\.aws(?:/|$)#i', 290 '#(?:^|/)\.ssh/(?:config|authorized_keys|known_hosts|id_(?:rsa|dsa|ecdsa|ed25519))(?:\.[^/]*)?(?:/|$)#i', 291 '#(?:^|/)\.(?:azure|config/gcloud)(?:/|$)#i', 292 '#(?:^|/)(?:alfacgiapi|alfa_data)(?:\.(?:php[0-9]?|cgi|pl|txt)(?:[.~_-][^/]*)?)?(?:/|$)#i', 293 '#(?:^|/)perl\.alfa(?:\.(?:php[0-9]?|cgi|pl|txt))?(?:/|$)#i', 294 '#(?:^|/)(?:php-cgi(?:\.exe)?|cgi-bin/php(?:[0-9]+(?:\.[0-9]+)*)?(?:-cgi)?(?:\.exe)?)(?:/|$)#i', 282 295 '#(?:^|/)(?:composer\.(?:json|lock)|auth\.json)$#i', 283 296 '#(?:^|/)vendor/phpunit/phpunit/src/util/php/eval-stdin\.php$#i', … … 288 301 } 289 302 return false; 303 } 304 305 /** Match paths and CGI command-line injection before any request allowlisting. */ 306 protected static function common_exploit_rules( string $uri ): array { 307 $parts = explode( '?', $uri, 2 ); 308 $rules = self::is_common_exploit_path( $parts[0] ) ? array( 'common_exploit_path' ) : array(); 309 $query = str_replace( '+', ' ', rawurldecode( rawurldecode( $parts[1] ?? '' ) ) ); 310 // Encoded soft hyphens are also used as CGI option delimiters on Windows. 311 $query = str_replace( array( "\xC2\xAD", "\xAD" ), '-', $query ); 312 if ( preg_match( '#(?:^|[\s&])-d\s*(?:allow_url_include|auto_prepend_file|cgi\.force_redirect|cgi\.redirect_status_env|disable_functions)\s*=#i', $query ) ) { 313 $rules[] = 'php_cgi_argument_injection'; 314 } 315 return $rules; 290 316 } 291 317 … … 430 456 $score += 5; 431 457 $rules[] = 'probe_method'; 458 if ( $hits > 10 && $ua_count > 3 ) { 459 $score += 15; 460 $rules[] = 'rotating_probe_burst'; 461 } 432 462 } 433 463 if ( in_array( $method, array( 'PUT','PATCH','DELETE' ), true ) ) { … … 444 474 $rules[] = 'post_without_referer'; 445 475 } 446 if ( $crawler === ' declared_search' || $crawler === 'declared_ai' || $crawler === 'declared_bot' ) {476 if ( $crawler === 'trusted_search' || $crawler === 'declared_search' || $crawler === 'declared_ai' || $crawler === 'declared_bot' ) { 447 477 $score += 20; 448 478 $rules[] = 'crawler_post_request'; … … 664 694 <h2><?php esc_html_e( 'Protection & Allow Toggles', 'site-first-seo' ); ?></h2> 665 695 <p><label><input type="checkbox" name="block_common_exploits" value="1" <?php checked( $blockE, 1 ); ?> /> <strong><?php esc_html_e( 'Block Common Exploits', 'site-first-seo' ); ?></strong></label></p> 666 <p class="description"><?php esc_html_e( 'Immediately returns 403 for probes targeting .git, .env, c redentials, backup configuration files, and other sensitive development files. This protection applies in every Bot Block mode.', 'site-first-seo' ); ?></p>696 <p class="description"><?php esc_html_e( 'Immediately returns 403 for probes targeting .git, .env, cloud credentials, backup configuration files, ALFA shell paths, and PHP-CGI injection attempts, including encoded and Windows alternate-data-stream variants. This protection applies in every Bot Block mode.', 'site-first-seo' ); ?></p> 667 697 <p class="description"><?php esc_html_e( 'Behavior scoring also watches one-minute IP velocity, repeated paths, rotating user agents, suspicious POST requests, and unexpected write methods.', 'site-first-seo' ); ?></p> 668 698 <p><label><input type="checkbox" name="allow_search" value="1" <?php checked( $allowS, 1 ); ?> /> <?php esc_html_e( 'Allow verified search crawlers', 'site-first-seo' ); ?></label></p> … … 956 986 'ua_count' => max( 1, min( 100, isset( $_POST['sim_ua_count'] ) ? absint( $_POST['sim_ua_count'] ) : 1 ) ), 957 987 ); 958 $crawler = self::crawler_classification( $ua, $ip, $behavior ); 959 960 if ( (int) get_option( self::OPT_BLOCK_EXPLOITS, 1 ) && self::is_common_exploit_path( $path ) ) { 988 $exploit_rules = self::common_exploit_rules( $path ); 989 $path = explode( '?', $path, 2 )[0]; 990 $crawler = self::crawler_classification( $ua, $ip, $behavior, empty( $exploit_rules ) || ! (int) get_option( self::OPT_BLOCK_EXPLOITS, 1 ) ); 991 992 if ( (int) get_option( self::OPT_BLOCK_EXPLOITS, 1 ) && ! empty( $exploit_rules ) ) { 961 993 $result = array( 962 994 'score' => 100, 963 'rules' => array( 'common_exploit_path' ),995 'rules' => $exploit_rules, 964 996 'crawler' => $crawler, 965 997 'decision' => 'hard_block', … … 1427 1459 } 1428 1460 1429 protected static function crawler_classification( string $ua, string $ip, array $behavior ): string {1461 protected static function crawler_classification( string $ua, string $ip, array $behavior, bool $verify_search = true ): string { 1430 1462 if ( trim( $ua ) === '' || $ua === '-' ) { return 'empty_ua'; } 1431 1463 1432 1464 if ( preg_match( '#(googlebot|googleother|google-inspectiontool|storebot-google|bingbot|duckduckbot|applebot|yandexbot)#i', $ua ) ) { 1433 return self::is_verified_search_crawler( $ua, $ip ) ? 'trusted_search' : 'declared_search';1465 return $verify_search && self::is_verified_search_crawler( $ua, $ip ) ? 'trusted_search' : 'declared_search'; 1434 1466 } 1435 1467 if ( preg_match( '#(gptbot|chatgpt-user|ccbot|claudebot|anthropic-ai|perplexitybot|google-extended|cohere-ai|bytespider)#i', $ua ) ) { -
site-first-seo/tags/1.1.14/readme.txt
r3706009 r3733310 5 5 Requires at least: 6.0 6 6 Tested up to: 7.0 7 Stable tag: 1.1.1 37 Stable tag: 1.1.14 8 8 Requires PHP: 8.0 9 9 License: GPLv2 or later … … 43 43 * Module Controls: enables or disables major modules from a central settings screen. 44 44 45 = Bot Block protection and traffic monitoring = 46 47 Bot Block offers Monitor, Soft block (429), and Hard block (403) modes. Monitor records suspicious behavior without enforcing score-based blocks. Enable Block Common Exploits to immediately reject recognized sensitive-file and exploit probes in every mode, before crawler and user allowlists are evaluated. 48 49 Exploit protection covers source-control metadata, .env files, cloud credentials, configuration backups, ALFA shell paths, PHP-CGI executable probes, and CGI argument-injection queries. Detection handles encoded paths, backslash separators, and Windows ::$DATA variants. Known exploit probes do not trigger crawler DNS lookups. 50 51 Google, Bing, and Apple search crawler identities use cached reverse and forward DNS verification. Automatic crawler and user-agent allowances apply to GET and HEAD requests. POST and other write methods receive behavioral scoring unless a safe endpoint or an explicit path/IP allowance applies. Scoring watches request velocity, repeated paths, rotating user agents, suspicious POST requests, and rotating HEAD/OPTIONS probe bursts. 52 53 Activity logs and CSV exports retain decisions, scores, matched rules, crawler classifications, request body types, and observed response status. Request body contents are not stored. The rule simulator supports exploit paths and PHP-CGI query probes. Cache-aware browser beacons remain available, with aggregate totals shown separately from PHP-visible bot/human percentages. 54 55 Protection applies to requests reaching WordPress; files served directly by the web server or CDN require server-level protection. A declared crawler User-Agent is not proof of crawler identity, and logged requests do not represent all cached traffic. 56 45 57 = Local and entity SEO = 46 58 … … 108 120 109 121 == Changelog == 122 123 = 1.1.14 = 124 * Expanded Bot Block protection for ALFA shell paths, PHP-CGI argument injection, cloud credentials, and encoded Windows alternate-data-stream probes before allowlisting. 125 * Avoided crawler DNS lookups for known exploit probes and retained existing request logging and verified search crawler handling. 126 * Restricted user-agent allowlisting to GET/HEAD requests and strengthened scoring for rotating HEAD/OPTIONS probe bursts. 110 127 111 128 = 1.1.13 = … … 204 221 == Upgrade Notice == 205 222 223 = 1.1.14 = 224 Hardens Bot Block exploit detection and probe handling while preserving cache-aware browser beacons and existing log settings. 225 206 226 = 1.1.13 = 207 227 Adds cache-aware browser beacons to help Bot Block account for full-page cached browser traffic without changing the existing PHP-visible bot/human metric. -
site-first-seo/tags/1.1.14/site-first-seo.php
r3706009 r3733310 3 3 * Plugin Name: Site-First SEO 4 4 * Description: On-site SEO and GEO toolkit (Dashboard/Visits, Headings & Images, Titles & Meta + SERP preview, Links Overview, AI Readiness, Indexation Monitor, Content Decay, Internal Link Manager, Local SEO Schema, Sitemaps, Robots.txt, llms.txt, Redirects & 404s, Exports, Bot Block, Settings). 5 * Version: 1.1.1 35 * Version: 1.1.14 6 6 * Author: potar 7 7 * Requires at least: 6.0 … … 18 18 define('SFSEO_BOOTSTRAPPED', true); 19 19 20 if (!defined('SFSEO_VER')) define('SFSEO_VER', '1.1.1 3'); // keep in sync with header20 if (!defined('SFSEO_VER')) define('SFSEO_VER', '1.1.14'); // keep in sync with header 21 21 if (!defined('SFSEO_CAP')) define('SFSEO_CAP', 'manage_options'); 22 22 if (!defined('SFSEO_FILE')) define('SFSEO_FILE', __FILE__); -
site-first-seo/trunk/includes/class-sfs-botblock.php
r3706009 r3733310 177 177 $ua = self::server( 'HTTP_USER_AGENT', '' ); 178 178 $ip = self::client_ip(); 179 $method = s elf::server( 'REQUEST_METHOD', 'GET');179 $method = strtoupper( self::server( 'REQUEST_METHOD', 'GET' ) ); 180 180 $referer = self::server( 'HTTP_REFERER', '' ); 181 181 $body = self::classify_request_body( $method ); 182 182 $behavior = self::track_ip_behavior( $ip, $path, $ua ); 183 $crawler = self::crawler_classification( $ua, $ip, $behavior ); 183 $exploit_rules = self::common_exploit_rules( self::server( 'REQUEST_URI', $path ) ); 184 // Do not perform crawler DNS lookups for requests already known to be probes. 185 $crawler = self::crawler_classification( $ua, $ip, $behavior, empty( $exploit_rules ) || ! (int) get_option( self::OPT_BLOCK_EXPLOITS, 1 ) ); 184 186 185 187 // 1) Deterministically block sensitive-file probes when protection is enabled. 186 if ( (int) get_option( self::OPT_BLOCK_EXPLOITS, 1 ) && self::is_common_exploit_path( $path) ) {187 self::log( 'hard_block', 'common_exploit', 100, $ua, $ip, $path, $method, $referer, 403, 0, array( 'common_exploit_path' ), $body, $crawler );188 if ( (int) get_option( self::OPT_BLOCK_EXPLOITS, 1 ) && ! empty( $exploit_rules ) ) { 189 self::log( 'hard_block', 'common_exploit', 100, $ua, $ip, $path, $method, $referer, 403, 0, $exploit_rules, $body, $crawler ); 188 190 status_header( 403 ); 189 191 exit; … … 202 204 203 205 // 3) Verified search crawlers and configured allowlists 204 if ( (int) get_option( self::OPT_ALLOW_SEARCH, 1 ) && $crawler === 'trusted_search' ) {206 if ( (int) get_option( self::OPT_ALLOW_SEARCH, 1 ) && $crawler === 'trusted_search' && in_array( $method, array( 'GET', 'HEAD' ), true ) ) { 205 207 self::schedule_log( 'allow', 'trusted_crawler', 0, $ua, $ip, $path, $method, $referer, array( 'verified_search_crawler' ), $body, $crawler ); 206 208 return; … … 208 210 209 211 $allow = self::build_allowlist(); 212 // A declared bot identity does not authorize submitting data or write requests. 213 if ( ! in_array( $method, array( 'GET', 'HEAD' ), true ) ) { $allow['ua'] = array(); } 210 214 if ( self::matches_allow( $ua, $ip, $path, $allow ) ) { 211 215 self::schedule_log( 'allow', 'allowlist', 0, $ua, $ip, $path, $method, $referer, array( 'allowlist' ), $body, $crawler ); … … 270 274 $normalized = rawurldecode( rawurldecode( $path ) ); 271 275 $normalized = str_replace( '\\', '/', $normalized ); 276 $normalized = str_replace( "\0", '', $normalized ); 272 277 $normalized = preg_replace( '#/+#', '/', $normalized ); 278 // Windows alternate data streams can follow an otherwise sensitive filename. 279 $normalized = preg_replace( '#::\\$DATA(?=/|$)#i', '', $normalized ); 273 280 if ( ! is_string( $normalized ) ) { return false; } 274 281 275 282 $patterns = array( 276 283 '#(?:^|/)\.git(?:/|$)#i', 284 '#(?:^|/)\.git(?:ignore|modules|attributes)(?:[.~_-][^/]*)?(?:/|$)#i', 277 285 '#(?:^|/)\.(?:svn|hg)(?:/|$)#i', 278 286 '#(?:^|/)\.env(?:[._-][^/]*)?(?:/|$)#i', 279 287 '#(?:^|/)(?:wp-config\.php|configuration\.php)(?:[.~_-][^/]*)?$#i', 280 288 '#(?:^|/)(?:config|configuration)/(?:secrets?|credentials?|database)(?:\.[^/]*)?$#i', 281 '#(?:^|/)\.(?:aws|ssh)/(?:credentials|config|id_rsa)(?:\.[^/]*)?$#i', 289 '#(?:^|/)\.aws(?:/|$)#i', 290 '#(?:^|/)\.ssh/(?:config|authorized_keys|known_hosts|id_(?:rsa|dsa|ecdsa|ed25519))(?:\.[^/]*)?(?:/|$)#i', 291 '#(?:^|/)\.(?:azure|config/gcloud)(?:/|$)#i', 292 '#(?:^|/)(?:alfacgiapi|alfa_data)(?:\.(?:php[0-9]?|cgi|pl|txt)(?:[.~_-][^/]*)?)?(?:/|$)#i', 293 '#(?:^|/)perl\.alfa(?:\.(?:php[0-9]?|cgi|pl|txt))?(?:/|$)#i', 294 '#(?:^|/)(?:php-cgi(?:\.exe)?|cgi-bin/php(?:[0-9]+(?:\.[0-9]+)*)?(?:-cgi)?(?:\.exe)?)(?:/|$)#i', 282 295 '#(?:^|/)(?:composer\.(?:json|lock)|auth\.json)$#i', 283 296 '#(?:^|/)vendor/phpunit/phpunit/src/util/php/eval-stdin\.php$#i', … … 288 301 } 289 302 return false; 303 } 304 305 /** Match paths and CGI command-line injection before any request allowlisting. */ 306 protected static function common_exploit_rules( string $uri ): array { 307 $parts = explode( '?', $uri, 2 ); 308 $rules = self::is_common_exploit_path( $parts[0] ) ? array( 'common_exploit_path' ) : array(); 309 $query = str_replace( '+', ' ', rawurldecode( rawurldecode( $parts[1] ?? '' ) ) ); 310 // Encoded soft hyphens are also used as CGI option delimiters on Windows. 311 $query = str_replace( array( "\xC2\xAD", "\xAD" ), '-', $query ); 312 if ( preg_match( '#(?:^|[\s&])-d\s*(?:allow_url_include|auto_prepend_file|cgi\.force_redirect|cgi\.redirect_status_env|disable_functions)\s*=#i', $query ) ) { 313 $rules[] = 'php_cgi_argument_injection'; 314 } 315 return $rules; 290 316 } 291 317 … … 430 456 $score += 5; 431 457 $rules[] = 'probe_method'; 458 if ( $hits > 10 && $ua_count > 3 ) { 459 $score += 15; 460 $rules[] = 'rotating_probe_burst'; 461 } 432 462 } 433 463 if ( in_array( $method, array( 'PUT','PATCH','DELETE' ), true ) ) { … … 444 474 $rules[] = 'post_without_referer'; 445 475 } 446 if ( $crawler === ' declared_search' || $crawler === 'declared_ai' || $crawler === 'declared_bot' ) {476 if ( $crawler === 'trusted_search' || $crawler === 'declared_search' || $crawler === 'declared_ai' || $crawler === 'declared_bot' ) { 447 477 $score += 20; 448 478 $rules[] = 'crawler_post_request'; … … 664 694 <h2><?php esc_html_e( 'Protection & Allow Toggles', 'site-first-seo' ); ?></h2> 665 695 <p><label><input type="checkbox" name="block_common_exploits" value="1" <?php checked( $blockE, 1 ); ?> /> <strong><?php esc_html_e( 'Block Common Exploits', 'site-first-seo' ); ?></strong></label></p> 666 <p class="description"><?php esc_html_e( 'Immediately returns 403 for probes targeting .git, .env, c redentials, backup configuration files, and other sensitive development files. This protection applies in every Bot Block mode.', 'site-first-seo' ); ?></p>696 <p class="description"><?php esc_html_e( 'Immediately returns 403 for probes targeting .git, .env, cloud credentials, backup configuration files, ALFA shell paths, and PHP-CGI injection attempts, including encoded and Windows alternate-data-stream variants. This protection applies in every Bot Block mode.', 'site-first-seo' ); ?></p> 667 697 <p class="description"><?php esc_html_e( 'Behavior scoring also watches one-minute IP velocity, repeated paths, rotating user agents, suspicious POST requests, and unexpected write methods.', 'site-first-seo' ); ?></p> 668 698 <p><label><input type="checkbox" name="allow_search" value="1" <?php checked( $allowS, 1 ); ?> /> <?php esc_html_e( 'Allow verified search crawlers', 'site-first-seo' ); ?></label></p> … … 956 986 'ua_count' => max( 1, min( 100, isset( $_POST['sim_ua_count'] ) ? absint( $_POST['sim_ua_count'] ) : 1 ) ), 957 987 ); 958 $crawler = self::crawler_classification( $ua, $ip, $behavior ); 959 960 if ( (int) get_option( self::OPT_BLOCK_EXPLOITS, 1 ) && self::is_common_exploit_path( $path ) ) { 988 $exploit_rules = self::common_exploit_rules( $path ); 989 $path = explode( '?', $path, 2 )[0]; 990 $crawler = self::crawler_classification( $ua, $ip, $behavior, empty( $exploit_rules ) || ! (int) get_option( self::OPT_BLOCK_EXPLOITS, 1 ) ); 991 992 if ( (int) get_option( self::OPT_BLOCK_EXPLOITS, 1 ) && ! empty( $exploit_rules ) ) { 961 993 $result = array( 962 994 'score' => 100, 963 'rules' => array( 'common_exploit_path' ),995 'rules' => $exploit_rules, 964 996 'crawler' => $crawler, 965 997 'decision' => 'hard_block', … … 1427 1459 } 1428 1460 1429 protected static function crawler_classification( string $ua, string $ip, array $behavior ): string {1461 protected static function crawler_classification( string $ua, string $ip, array $behavior, bool $verify_search = true ): string { 1430 1462 if ( trim( $ua ) === '' || $ua === '-' ) { return 'empty_ua'; } 1431 1463 1432 1464 if ( preg_match( '#(googlebot|googleother|google-inspectiontool|storebot-google|bingbot|duckduckbot|applebot|yandexbot)#i', $ua ) ) { 1433 return self::is_verified_search_crawler( $ua, $ip ) ? 'trusted_search' : 'declared_search';1465 return $verify_search && self::is_verified_search_crawler( $ua, $ip ) ? 'trusted_search' : 'declared_search'; 1434 1466 } 1435 1467 if ( preg_match( '#(gptbot|chatgpt-user|ccbot|claudebot|anthropic-ai|perplexitybot|google-extended|cohere-ai|bytespider)#i', $ua ) ) { -
site-first-seo/trunk/readme.txt
r3706009 r3733310 5 5 Requires at least: 6.0 6 6 Tested up to: 7.0 7 Stable tag: 1.1.1 37 Stable tag: 1.1.14 8 8 Requires PHP: 8.0 9 9 License: GPLv2 or later … … 43 43 * Module Controls: enables or disables major modules from a central settings screen. 44 44 45 = Bot Block protection and traffic monitoring = 46 47 Bot Block offers Monitor, Soft block (429), and Hard block (403) modes. Monitor records suspicious behavior without enforcing score-based blocks. Enable Block Common Exploits to immediately reject recognized sensitive-file and exploit probes in every mode, before crawler and user allowlists are evaluated. 48 49 Exploit protection covers source-control metadata, .env files, cloud credentials, configuration backups, ALFA shell paths, PHP-CGI executable probes, and CGI argument-injection queries. Detection handles encoded paths, backslash separators, and Windows ::$DATA variants. Known exploit probes do not trigger crawler DNS lookups. 50 51 Google, Bing, and Apple search crawler identities use cached reverse and forward DNS verification. Automatic crawler and user-agent allowances apply to GET and HEAD requests. POST and other write methods receive behavioral scoring unless a safe endpoint or an explicit path/IP allowance applies. Scoring watches request velocity, repeated paths, rotating user agents, suspicious POST requests, and rotating HEAD/OPTIONS probe bursts. 52 53 Activity logs and CSV exports retain decisions, scores, matched rules, crawler classifications, request body types, and observed response status. Request body contents are not stored. The rule simulator supports exploit paths and PHP-CGI query probes. Cache-aware browser beacons remain available, with aggregate totals shown separately from PHP-visible bot/human percentages. 54 55 Protection applies to requests reaching WordPress; files served directly by the web server or CDN require server-level protection. A declared crawler User-Agent is not proof of crawler identity, and logged requests do not represent all cached traffic. 56 45 57 = Local and entity SEO = 46 58 … … 108 120 109 121 == Changelog == 122 123 = 1.1.14 = 124 * Expanded Bot Block protection for ALFA shell paths, PHP-CGI argument injection, cloud credentials, and encoded Windows alternate-data-stream probes before allowlisting. 125 * Avoided crawler DNS lookups for known exploit probes and retained existing request logging and verified search crawler handling. 126 * Restricted user-agent allowlisting to GET/HEAD requests and strengthened scoring for rotating HEAD/OPTIONS probe bursts. 110 127 111 128 = 1.1.13 = … … 204 221 == Upgrade Notice == 205 222 223 = 1.1.14 = 224 Hardens Bot Block exploit detection and probe handling while preserving cache-aware browser beacons and existing log settings. 225 206 226 = 1.1.13 = 207 227 Adds cache-aware browser beacons to help Bot Block account for full-page cached browser traffic without changing the existing PHP-visible bot/human metric. -
site-first-seo/trunk/site-first-seo.php
r3706009 r3733310 3 3 * Plugin Name: Site-First SEO 4 4 * Description: On-site SEO and GEO toolkit (Dashboard/Visits, Headings & Images, Titles & Meta + SERP preview, Links Overview, AI Readiness, Indexation Monitor, Content Decay, Internal Link Manager, Local SEO Schema, Sitemaps, Robots.txt, llms.txt, Redirects & 404s, Exports, Bot Block, Settings). 5 * Version: 1.1.1 35 * Version: 1.1.14 6 6 * Author: potar 7 7 * Requires at least: 6.0 … … 18 18 define('SFSEO_BOOTSTRAPPED', true); 19 19 20 if (!defined('SFSEO_VER')) define('SFSEO_VER', '1.1.1 3'); // keep in sync with header20 if (!defined('SFSEO_VER')) define('SFSEO_VER', '1.1.14'); // keep in sync with header 21 21 if (!defined('SFSEO_CAP')) define('SFSEO_CAP', 'manage_options'); 22 22 if (!defined('SFSEO_FILE')) define('SFSEO_FILE', __FILE__);
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)