settings = $settings; $this->classifier = $scanner->classifier; } /** * Start buffering on front-end HTML requests. */ public function register() { add_action( 'template_redirect', array( $this, 'start' ), 0 ); } /** * Begin the buffer when this request should be filtered. */ public function start() { if ( $this->should_filter() ) { ob_start( array( $this, 'filter' ) ); } } /** * Requests that must never be altered. * * @return bool */ private function should_filter() { if ( Skip::request() ) { return false; // Back office, staff and integration pages: never buffered or rewritten. } if ( is_admin() || wp_doing_ajax() || wp_doing_cron() || is_feed() || is_embed() || is_customize_preview() ) { return false; } if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { return false; } if ( Scanner::is_scan_request() ) { return false; } // Page-builder editing screens. foreach ( array( 'fl_builder', 'elementor-preview', 'et_fb', 'vc_editable', 'bricks', 'ct_builder', 'brizy-edit-iframe' ) as $key ) { if ( isset( $_GET[ $key ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- presence check only. return false; } } /** * Filter whether GoConsent filters this page's HTML. * * @param bool $filter Default true. */ return (bool) apply_filters( 'goconsent_filter_page', true ); } /** * Output-buffer callback. Fails open: on any error the page is served unchanged. * * @param string $html Page HTML. * @return string */ public function filter( $html ) { if ( ! is_string( $html ) || '' === $html ) { return $html; } $head = substr( $html, 0, 2000 ); if ( false === stripos( $head, 'settings->get( 'block' ) ) { $out = $this->hold_scripts( $out ); $out = $this->strip_noscript_pixels( $out ); } $out = MapsGate::rewrite( $out ); $out = ClickToLoad::rewrite( $out ); $out = LocalFonts::rewrite( $out ); return ( is_string( $out ) && '' !== $out ) ? $out : $html; } catch ( \Throwable $e ) { if ( defined( 'WP_DEBUG' ) && WP_DEBUG && defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- debug-only diagnostics. error_log( 'GoConsent output filter: ' . $e->getMessage() ); } return $html; } } /** * Neutralise analytics/advertising scripts until consent (type="text/plain"). * * @param string $html HTML. * @return string */ private function hold_scripts( $html ) { $out = preg_replace_callback( '#]*)>(.*?)#is', array( $this, 'rewrite_script' ), $html ); return null === $out ? $html : $out; } /** * Rewrite one script tag. * * @param array $m Regex match. * @return string */ public function rewrite_script( $m ) { $attrs = $m[1]; if ( false !== stripos( $attrs, 'data-goconsent' ) ) { return $m[0]; } $type = ''; if ( preg_match( '#\btype\s*=\s*(["\']?)([^"\'\s>]+)\1#i', $attrs, $tm ) ) { $type = strtolower( $tm[2] ); } if ( '' !== $type && ! in_array( $type, array( 'text/javascript', 'application/javascript', 'module' ), true ) ) { return $m[0]; } $cat = $this->classifier->category( $attrs . ' ' . $m[2], $attrs ); if ( '' === $cat || 'necessary' === $cat ) { return $m[0]; } $attrs = preg_replace( '#\s*\btype\s*=\s*(["\']?)[^"\'\s>]+\1#i', '', $attrs ); $extra = ' type="text/plain" data-goconsent="' . $cat . '"' . ( 'module' === $type ? ' data-goconsent-type="module"' : '' ); return '' . $m[2] . ''; } /** * Tracking pixels inside #is', function ( $m ) { if ( false !== stripos( $m[0], 'googletagmanager.com/ns.html' ) ) { return ''; } foreach ( Signatures::all() as $sig ) { if ( 'marketing' !== $sig[1] ) { continue; } foreach ( $sig[2] as $needle ) { if ( false !== stripos( $m[0], $needle ) ) { return ''; } } } return $m[0]; }, $html ); return null === $out ? $html : $out; } }