= 16 && $second <= 31 ) { return true; } } // 192.168.0.0/16 if ( str_starts_with( $host, '192.168.' ) ) { return true; } // 127.0.0.0/8 if ( str_starts_with( $host, '127.' ) ) { return true; } // ::1 if ( $host === '::1' ) { return true; } // fc00::/7 (unique local addresses) if ( str_starts_with( $host, 'fc' ) || str_starts_with( $host, 'fd' ) ) { return true; } // Let filter_var catch any other private/reserved ranges. return filter_var( $host, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) === false; } private static function hostname_resolves_to_private_address( string $host ): bool { if ( filter_var( $host, FILTER_VALIDATE_IP ) ) { return self::is_private_ip( $host ); } $addresses = []; if ( function_exists( 'dns_get_record' ) ) { $records = @dns_get_record( $host, DNS_A + DNS_AAAA ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- Resolves the host for the SSRF guard; a failed lookup returns false and is handled below. if ( is_array( $records ) ) { foreach ( $records as $record ) { if ( ! empty( $record['ip'] ) ) { $addresses[] = (string) $record['ip']; } if ( ! empty( $record['ipv6'] ) ) { $addresses[] = (string) $record['ipv6']; } } } } if ( empty( $addresses ) && function_exists( 'gethostbynamel' ) ) { $resolved = @gethostbynamel( $host ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- Fallback resolver for the same SSRF guard; a failed lookup returns false and is handled below. if ( is_array( $resolved ) ) { $addresses = array_merge( $addresses, $resolved ); } } foreach ( array_unique( $addresses ) as $address ) { if ( self::is_private_ip( strtolower( $address ) ) ) { return true; } } return false; } /** * Normalizes a URL for self-connection comparison: ignores scheme and a www. * prefix, drops default ports, lowercases the host and trims the trailing slash. */ public static function normalize_url_for_comparison( string $url ): string { $parsed = wp_parse_url( rtrim( $url, '/' ) ); if ( ! is_array( $parsed ) ) { return strtolower( rtrim( $url, '/' ) ); } $scheme = isset( $parsed['scheme'] ) ? strtolower( $parsed['scheme'] ) : 'https'; $host = isset( $parsed['host'] ) ? strtolower( $parsed['host'] ) : ''; // Strip leading www. if ( str_starts_with( $host, 'www.' ) ) { $host = substr( $host, 4 ); } $port = isset( $parsed['port'] ) ? (int) $parsed['port'] : null; $default = ( 'https' === $scheme ) ? 443 : 80; if ( $port === $default ) { $port = null; } $path = isset( $parsed['path'] ) ? rtrim( $parsed['path'], '/' ) : ''; $query = isset( $parsed['query'] ) ? '?' . $parsed['query'] : ''; $host_part = $host . ( null !== $port ? ':' . $port : '' ); return $host_part . $path . $query; } /** Sanitizes a non-negative integer. */ public static function int( mixed $value ): int { return absint( $value ); } /** Sanitizes a boolean-ish value from user input. */ public static function bool( mixed $value ): bool { return filter_var( $value, FILTER_VALIDATE_BOOLEAN ); } /** * Sanitizes a list of table names. * * @return array */ public static function table_names( array $tables ): array { $clean = []; foreach ( $tables as $table ) { $sanitized = sanitize_key( (string) $table ); if ( '' !== $sanitized ) { $clean[] = $sanitized; } } return array_unique( $clean ); } }