*/ public static function protect( $html ) { $html = (string) $html; $nonce = self::nonce(); $state = array( 'original' => $html, 'html' => $html, 'nonce' => $nonce, 'map' => array(), 'valid' => true, ); $block_patterns = array( '~~', '~<(script|style|template|noscript|code|pre|kbd|samp|textarea)\b[^>]*>[\s\S]*?~i', '~<\?(?:php|=)?[\s\S]*?\?>~i', '~]*/\s*>~i', ); foreach ( $block_patterns as $pattern ) { $state['html'] = preg_replace_callback( $pattern, static function ( $matches ) use ( &$state ) { return self::store( $matches[0], $state, true ); }, $state['html'] ); if ( null === $state['html'] ) { $state['html'] = $state['original']; $state['valid'] = false; return $state; } } $inline_patterns = array( '~\{\{\{[\s\S]*?\}\}\}~', '~\{\{[\s\S]*?\}\}~', '~\{%[\s\S]*?%\}~', '~\{#[\s\S]*?#\}~', '~\[(?:/?)[A-Za-z][A-Za-z0-9_-]*(?:\s[^\]\r\n]*)?\]~', ); foreach ( $inline_patterns as $pattern ) { $state['html'] = preg_replace_callback( $pattern, static function ( $matches ) use ( &$state ) { return self::store( $matches[0], $state, false ); }, $state['html'] ); if ( null === $state['html'] ) { $state['html'] = $state['original']; $state['valid'] = false; return $state; } } return $state; } /** * Restore protected content. Any missing or duplicated token invalidates the * translated response and returns the byte-identical original response. * * @param string $html Parsed or translated HTML. * @param array $state Protection state. * @return string */ public static function restore( $html, $state ) { if ( empty( $state['valid'] ) || ! isset( $state['original'], $state['map'], $state['nonce'] ) ) { return self::fail_restore( $state, $html, 'invalid_protection_state' ); } $html = (string) $html; foreach ( (array) $state['map'] as $placeholder => $original ) { if ( 1 !== substr_count( $html, (string) $placeholder ) ) { return self::fail_restore( $state, $html, 'placeholder_count_mismatch' ); } } $html = strtr( $html, (array) $state['map'] ); if ( false !== strpos( $html, 'AITMTPROTECTED_' . $state['nonce'] ) ) { return self::fail_restore( $state, $html, 'placeholder_not_restored' ); } return $html; } /** * Count protected fragments without exposing their content. * * @param array $state Protection state. * @return int */ public static function count( $state ) { return isset( $state['map'] ) && is_array( $state['map'] ) ? count( $state['map'] ) : 0; } /** * Determine whether a value contains one of this request's placeholders. * * @param string $value Candidate value. * @return bool */ public static function is_placeholder( $value ) { return false !== strpos( (string) $value, 'AITMTPROTECTED_' ); } /** * Validate the intentionally small selector grammar supported by the DOM * exclusion walker. * * @param string $selectors Comma/newline-separated selectors. * @return array{valid:array,invalid:array} */ public static function validate_selectors( $selectors ) { $result = array( 'valid' => array(), 'invalid' => array(), ); $items = preg_split( '/\r\n|\r|\n|,/', (string) $selectors ); foreach ( (array) $items as $selector ) { $selector = trim( $selector ); if ( '' === $selector ) { continue; } $is_valid = (bool) preg_match( '/^(?:\.[A-Za-z_][A-Za-z0-9_-]*|#[A-Za-z_][A-Za-z0-9_-]*|[A-Za-z][A-Za-z0-9_-]*|\[[A-Za-z_:][A-Za-z0-9_:.-]*(?:\s*=\s*(?:"[^"]*"|\'[^\']*\'))?\])$/', $selector ); $result[ $is_valid ? 'valid' : 'invalid' ][] = $selector; } return $result; } /** * Store a protected fragment and return its placeholder. * * @param string $original Original bytes. * @param array $state Protection state. * @param bool $block Whether to use an HTML comment token. * @return string */ private static function store( $original, &$state, $block ) { $index = count( $state['map'] ); $token = 'AITMTPROTECTED_' . $state['nonce'] . '_' . $index; $key = $block ? '' : '__' . $token . '__'; $state['map'][ $key ] = (string) $original; return $key; } /** * Generate a request-local unpredictable token component. * * @return string */ private static function nonce() { try { return bin2hex( random_bytes( 12 ) ); } catch ( Exception $exception ) { return hash( 'sha256', uniqid( 'aitmt', true ) . wp_rand() ); } } /** * Abort a damaged translation without logging page content. * * @param array $state Protection state. * @param string $html Candidate HTML. * @param string $reason Failure code. * @return string */ private static function fail_restore( $state, $html, $reason ) { if ( class_exists( 'AITMT_Runtime_Maintenance' ) ) { AITMT_Runtime_Maintenance::increment_counter( 'protected_restore_failed' ); } if ( function_exists( 'aitmt_fallback_log_event' ) ) { aitmt_fallback_log_event( 'pipeline', 'Protected content restoration failed; original response was returned.', array( 'reason' => sanitize_key( (string) $reason ), 'protected_count' => self::count( $state ), ) ); } return isset( $state['original'] ) ? (string) $state['original'] : (string) $html; } }