Changeset 3226477
- Timestamp:
- 01/21/2025 07:52:19 PM (15 months ago)
- Location:
- parasol
- Files:
-
- 5 added
- 3 edited
-
tags/1.1 (added)
-
tags/1.1/config.php (added)
-
tags/1.1/index.php (added)
-
tags/1.1/parasol.php (added)
-
tags/1.1/readme.txt (added)
-
trunk/config.php (modified) (2 diffs)
-
trunk/parasol.php (modified) (1 diff)
-
trunk/readme.txt (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
parasol/trunk/config.php
r3223111 r3226477 1 1 <?php 2 if (!defined('ABSPATH')) { 3 exit; 2 /** 3 * Configuration file for Parasol Plugin 4 * 5 * This file contains configuration settings for the Parasol plugin. 6 * It defines constants, API keys, URLs, and other settings used by the plugin. 7 * 8 * @package Parasol 9 */ 10 11 if ( ! defined( 'ABSPATH' ) ) { 12 exit; 4 13 } 5 14 6 define('PARASOL_API', 'https://api.parasolbrowsing.com'); 7 define('PARASOL_API_KEY', 'cf20c948293a16db47d5e5e0371ec883'); 8 define('PARASOL_CLOUDFRONT_KEY_ID', 'K2X7KRV1T2NPUT'); 9 define('PARASOL_ENV', 'prod'); 10 define('PARASOL_PRIVATE_KEY', '-----BEGIN RSA PRIVATE KEY----- 15 define( 'PARASOL_API', 'https://api.parasolbrowsing.com' ); 16 define( 'PARASOL_API_KEY', 'cf20c948293a16db47d5e5e0371ec883' ); 17 define( 'PARASOL_CLOUDFRONT_KEY_ID', 'K2X7KRV1T2NPUT' ); 18 define( 'PARASOL_ENV', 'prod' ); 19 define( 20 'PARASOL_PRIVATE_KEY', 21 '-----BEGIN RSA PRIVATE KEY----- 11 22 MIIEpQIBAAKCAQEA7DLfPUF7UtKIEB2+ZVjFf/7ATac97mmoPpI27ZKYw5Lp5fVH 12 23 nSo1X3AiA4dynhXLXmXG/k1KkIMQ58nfYBk6Gs4u/SiGOAcHfj+mOKnJvQ+Hip2S … … 34 45 MYCOmKZhBUwQlTjUgB6tmK9c72irtWjWCtPmp327hePW3fToO0QyC/TtolJzuZmI 35 46 mBCqyIJqdf/NJeQSqxUoNU7A2cb6sok0aoWwSs5Pg5WXWEBPbzJv5Gg= 36 -----END RSA PRIVATE KEY-----'); 37 define('PARASOL_VERSION', '1.0'); 38 define('PARASOL_WIDGET', 'https://widget.parasolbrowsing.com/' . PARASOL_ENV); 47 -----END RSA PRIVATE KEY-----' 48 ); 49 define( 'PARASOL_VERSION', '1.1' ); 50 define( 'PARASOL_WIDGET', 'https://widget.parasolbrowsing.com/' . PARASOL_ENV ); -
parasol/trunk/parasol.php
r3223111 r3226477 1 1 <?php 2 /* 3 Plugin Name: Parasol 4 Plugin URI: https://parasolbrowsing.com 5 Description: Monetize your website without ads or data tracking 6 Version: 1.0 7 License: GPLv2 or later 8 */ 9 10 if (!defined('ABSPATH')) { 11 exit; 12 } 13 14 if (isset($_SERVER['REQUEST_URI']) && basename(sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI']))) === 'favicon.ico') { 15 return; 16 } 17 18 if (is_admin() && !wp_doing_ajax() && !wp_doing_cron()) { 19 register_deactivation_hook(__FILE__, 'parasol_deactivation'); 20 return; 21 } 22 23 if (is_admin() || defined('DOING_AJAX') || defined('REST_REQUEST') || defined('DOING_CRON')) { 24 return; 25 } 26 27 if (session_status() === PHP_SESSION_NONE) { 28 session_start(); 29 } 30 31 require_once plugin_dir_path(__FILE__) . 'config.php'; 32 33 $parasol_signed_widget_url = parasol_generate_signed_url(PARASOL_WIDGET, PARASOL_VERSION); 34 $parasol_signed_api_url = parasol_generate_signed_url(PARASOL_API); 35 36 add_action('init', 'parasol_initialize'); 37 2 /** 3 * Plugin Name: Parasol 4 * Plugin URI: https://parasolbrowsing.com 5 * Description: Monetize your website without ads or data tracking 6 * Version: 1.1 7 * License: GPLv2 or later 8 * 9 * @package Parasol 10 */ 11 12 if ( ! defined( 'ABSPATH' ) ) { 13 exit; 14 } 15 16 if ( isset( $_SERVER['REQUEST_URI'] ) && basename( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) === 'favicon.ico' ) { 17 return; 18 } 19 20 if ( is_admin() && ! wp_doing_ajax() && ! wp_doing_cron() ) { 21 register_deactivation_hook( __FILE__, 'parasol_deactivation' ); 22 return; 23 } 24 25 if ( is_admin() || defined( 'DOING_AJAX' ) || defined( 'REST_REQUEST' ) || defined( 'DOING_CRON' ) ) { 26 return; 27 } 28 29 if ( session_status() === PHP_SESSION_NONE ) { 30 session_start(); 31 } 32 33 require_once plugin_dir_path( __FILE__ ) . 'config.php'; 34 35 $parasol_signed_api_url = parasol_generate_signed_url( PARASOL_API ); 36 $parasol_signed_widget_url = parasol_generate_signed_url( PARASOL_WIDGET, PARASOL_VERSION ); 37 38 add_action( 'init', 'parasol_initialize' ); 39 40 /** 41 * Initializes the Parasol integration. 42 * 43 * This function handles the initialization of the Parasol integration by: 44 * - Verifying OAuth nonces and processing OAuth session tokens passed via URL parameters. 45 * - Redirecting to a clean URL if OAuth processing occurs. 46 * - Disabling Parasol if certain conditions are met (e.g., `parasol_disabled` transient or `parasol_opt_out` cookie is set). 47 * - Fetching and storing whitelist domains from the Parasol API if they are not available in the transient. 48 * - Generating and setting necessary cookies, including API token and widget URL data. 49 * - Enqueuing Parasol assets (JavaScript). 50 * 51 * @global string $parasol_signed_api_url The signed API URL for the Parasol API. 52 * @global string $parasol_signed_widget_url The signed widget URL for the Parasol widget. 53 * 54 * @return void 55 */ 38 56 function parasol_initialize() { 39 global $parasol_signed_api_url; 40 41 if (isset($_GET['parasol_opt_out'])) { 42 $clean_url = remove_query_arg('parasol_opt_out'); 43 set_transient('parasol_opt_out_' . session_id(), true, 1800); 44 wp_redirect($clean_url); 45 exit; 46 } 47 48 if (isset($_GET['parasol_oauth_nonce'])) { 49 $parasol_oauth_nonce = sanitize_text_field(wp_unslash($_GET['parasol_oauth_nonce'])); 50 $clean_url = remove_query_arg('parasol_oauth_nonce'); 51 if (wp_verify_nonce($parasol_oauth_nonce, 'parasol_oauth_nonce')) { 52 if (isset($_GET['parasol_oauth_session_token'])) { 53 set_transient('parasol_session_token_' . session_id(), sanitize_text_field(wp_unslash($_GET['parasol_oauth_session_token'])), 30); 54 $clean_url = remove_query_arg('parasol_oauth_session_token', $clean_url); 55 } 56 } 57 wp_redirect($clean_url); 58 exit; 59 } 60 61 if (get_transient('parasol_disabled') || get_transient('parasol_opt_out_' . session_id())) { 62 return; 63 } 64 65 if (!get_transient('parasol_whitelist_domains')) { 66 $http_response = wp_remote_post(PARASOL_API . '/GetSite' . $parasol_signed_api_url, [ 67 'body' => wp_json_encode([ 68 'apiToken' => parasol_generate_token([]), 69 'currentVersion' => PARASOL_VERSION, 70 ]), 71 'headers' => [ 72 'Content-Type' => 'application/json', 73 ], 74 'timeout' => 10, 75 ]); 76 77 if (is_wp_error($http_response) || wp_remote_retrieve_response_code($http_response) !== 200) { 78 if (wp_remote_retrieve_response_code($http_response) == 401) { 79 set_transient('parasol_disabled', true); 80 } 81 return; 82 } 83 84 $parasol_site_data = json_decode(wp_remote_retrieve_body($http_response), true); 85 if (!$parasol_site_data) { 86 return; 87 } 88 89 set_transient('parasol_whitelist_domains', $parasol_site_data['whitelistDomains'] ?? []); 90 } 91 92 parasol_disable_domains(); 93 add_action('wp_enqueue_scripts', 'parasol_enqueue_assets'); 94 } 95 57 global $parasol_signed_api_url, $parasol_signed_widget_url; 58 59 if ( isset( $_GET['parasol_oauth_nonce'] ) ) { 60 $parasol_oauth_nonce = sanitize_text_field( wp_unslash( $_GET['parasol_oauth_nonce'] ) ); 61 $clean_url = remove_query_arg( 'parasol_oauth_nonce' ); 62 if ( wp_verify_nonce( $parasol_oauth_nonce, 'parasol_oauth_nonce' ) ) { 63 if ( isset( $_GET['parasol_oauth_session_token'] ) ) { 64 setcookie( 'parasol_session_token', sanitize_text_field( wp_unslash( $_GET['parasol_oauth_session_token'] ) ), strtotime( '+1 minute' ), '/', '', true, false ); 65 $clean_url = remove_query_arg( 'parasol_oauth_session_token', $clean_url ); 66 } 67 } 68 wp_safe_redirect( $clean_url ); 69 exit(); 70 } 71 72 if ( get_transient( 'parasol_disabled' ) || isset( $_COOKIE['parasol_opt_out'] ) ) { 73 return; 74 } 75 76 if ( ! get_transient( 'parasol_whitelist_domains' ) ) { 77 $http_response = wp_remote_post( 78 PARASOL_API . '/GetSite' . $parasol_signed_api_url, 79 array( 80 'body' => wp_json_encode( 81 array( 82 'apiToken' => parasol_generate_token( 83 array() 84 ), 85 'currentVersion' => PARASOL_VERSION, 86 ) 87 ), 88 'headers' => array( 89 'Content-Type' => 'application/json', 90 ), 91 'timeout' => 10, 92 ) 93 ); 94 95 if ( is_wp_error( $http_response ) || wp_remote_retrieve_response_code( $http_response ) !== 200 ) { 96 if ( wp_remote_retrieve_response_code( $http_response ) === 401 ) { 97 set_transient( 'parasol_disabled', true ); 98 } 99 return; 100 } 101 102 $parasol_site_data = json_decode( wp_remote_retrieve_body( $http_response ), true ); 103 if ( ! $parasol_site_data ) { 104 return; 105 } 106 107 set_transient( 'parasol_whitelist_domains', $parasol_site_data['whitelistDomains'] ?? array() ); 108 } 109 110 $api_token_payload = array( 111 'idempotencyKey' => uniqid( session_id(), true ), 112 'parasolKey' => hash( 'sha256', session_id() . '6a39c2ce25815beb37872e8f7f2eb7ae' ), 113 ); 114 115 $parasol_data = array( 116 'apiDomain' => PARASOL_API, 117 'apiToken' => parasol_generate_token( $api_token_payload ), 118 'apiUrlQueryString' => $parasol_signed_api_url, 119 'devMode' => PARASOL_ENV === 'dev', 120 'oauthNonce' => wp_create_nonce( 'parasol_oauth_nonce' ), 121 'widgetDomain' => PARASOL_WIDGET, 122 'widgetUrlQueryString' => $parasol_signed_widget_url, 123 ); 124 125 setcookie( 'parasol_data', str_replace( array( '+', '/', '=' ), array( '-', '_', '' ), base64_encode( wp_json_encode( $parasol_data ) ) ), strtotime( '+10 minute' ), '/', '', true, false ); 126 parasol_disable_domains(); 127 add_action( 'wp_enqueue_scripts', 'parasol_enqueue_assets' ); 128 } 129 130 /** 131 * Enqueues the Parasol JavaScript file. 132 * 133 * This function enqueues the Parasol JavaScript file (`parasol.js`) with a signed widget URL appended as a query parameter. 134 * The script is loaded in the footer (`true` for the last parameter) and is registered with the version defined in 135 * the `PARASOL_VERSION` constant. 136 * 137 * @global string $parasol_signed_widget_url A signed URL used as a query parameter for the script. 138 * 139 * @return void 140 */ 96 141 function parasol_enqueue_assets() { 97 global $parasol_signed_api_url; 98 99 $parasol_signed_widget_url = parasol_generate_signed_url(PARASOL_WIDGET, PARASOL_VERSION); 100 101 $api_token_payload = [ 102 'idempotencyKey' => uniqid(session_id(), true), 103 'parasolKey' => hash('sha256', session_id() . '6a39c2ce25815beb37872e8f7f2eb7ae') 104 ]; 105 106 $is_new_session = false; 107 if (!get_transient('parasol_session_' . session_id())) { 108 set_transient('parasol_session_' . session_id(), true, 1800); 109 $is_new_session = true; 110 } 111 112 wp_enqueue_style('parasol-styles', PARASOL_WIDGET . '/parasol.css' . $parasol_signed_widget_url, [], PARASOL_VERSION); 113 wp_enqueue_script('stripe-js', 'https://js.stripe.com/v3/', [], true, true); 114 wp_enqueue_script('parasol-js', PARASOL_WIDGET . '/parasol.js' . $parasol_signed_widget_url, [], PARASOL_VERSION, true); 115 wp_localize_script('parasol-js', 'parasol', [ 116 'apiDomain' => PARASOL_API, 117 'apiToken' => parasol_generate_token($api_token_payload), 118 'apiUrlQueryString' => $parasol_signed_api_url, 119 'devMode' => PARASOL_ENV === 'dev', 120 'newSession' => $is_new_session, 121 'oauthNonce' => wp_create_nonce('parasol_oauth_nonce'), 122 'sessionToken' => get_transient('parasol_session_token_' . session_id()) ?? '', 123 'widgetDomain' => PARASOL_WIDGET, 124 'widgetUrlQueryString' => $parasol_signed_widget_url, 125 ]); 126 127 delete_transient('parasol_session_token_' . session_id()); 128 } 129 130 function parasol_generate_token($payload) { 131 $payload = array_merge($payload, [ 132 'timestamp' => time(), 133 'url' => parasol_get_current_url(), 134 ]); 135 136 $payloadJson = wp_json_encode($payload); 137 $signature = hash_hmac('sha256', $payloadJson, PARASOL_API_KEY); 138 $token = [ 139 'payload' => $payloadJson, 140 'signature' => $signature 141 ]; 142 143 return base64_encode(wp_json_encode($token)); 144 } 145 142 global $parasol_signed_widget_url; 143 wp_enqueue_script( 'parasol-js', PARASOL_WIDGET . '/parasol.js' . $parasol_signed_widget_url, array(), PARASOL_VERSION, true ); 144 } 145 146 /** 147 * Generates a signed token for a given payload. 148 * 149 * This function takes a payload (an array), adds a timestamp and the current URL, then creates a 150 * JSON-encoded representation of the payload. It generates a signature using the HMAC SHA-256 151 * algorithm with the payload and a secret API key, and returns a base64-encoded token that includes 152 * the payload and its signature. 153 * 154 * @param array $payload The initial data to include in the token. 155 * 156 * @return string A base64-encoded token consisting of the payload and its signature. 157 */ 158 function parasol_generate_token( $payload ) { 159 $payload = array_merge( 160 $payload, 161 array( 162 'timestamp' => time(), 163 'url' => parasol_get_current_url(), 164 ) 165 ); 166 167 $payload_json = wp_json_encode( $payload ); 168 $signature = hash_hmac( 'sha256', $payload_json, PARASOL_API_KEY ); 169 $token = array( 170 'payload' => $payload_json, 171 'signature' => $signature, 172 ); 173 174 return base64_encode( wp_json_encode( $token ) ); 175 } 176 177 /** 178 * Retrieves the current URL based on the server's protocol, host, and request URI. 179 * 180 * This function constructs the full URL by checking if HTTPS is enabled and appending 181 * the sanitized `HTTP_HOST` and `REQUEST_URI` from the `$_SERVER` superglobal. 182 * 183 * @return string The full URL, including the protocol (http or https). 184 */ 146 185 function parasol_get_current_url() { 147 $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https://' : 'http://'; 148 return $protocol . sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'] ?? '')) . sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'] ?? '')); 149 } 150 151 function parasol_generate_signed_url($url, $version = '') { 152 $policy = wp_json_encode([ 153 "Statement" => [ 154 [ 155 "Resource" => $url . '/*' . ($version ? '?ver=' . $version : ''), 156 "Condition" => ["DateLessThan" => ["AWS:EpochTime" => time() + 600]], 157 ], 158 ], 159 ]); 160 161 $encoded_policy = parasol_url_safe_base64_encode($policy); 162 openssl_sign($policy, $signature, PARASOL_PRIVATE_KEY, OPENSSL_ALGO_SHA1); 163 $encoded_signature = parasol_url_safe_base64_encode($signature); 164 165 $query_delimiter = $version ? 'ver=' . $version . '&' : ''; 166 return "?{$query_delimiter}Policy={$encoded_policy}&Signature={$encoded_signature}&Key-Pair-Id=" . PARASOL_CLOUDFRONT_KEY_ID; 167 } 168 169 function parasol_url_safe_base64_encode($value) { 170 return str_replace(['+', '=', '/'], ['-', '_', '~'], base64_encode($value)); 171 } 172 186 $protocol = ( ! empty( $_SERVER['HTTPS'] ) && 'on' === $_SERVER['HTTPS'] ) ? 'https://' : 'http://'; 187 return $protocol . sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ?? '' ) ) . sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ?? '' ) ); 188 } 189 190 /** 191 * Generates a signed URL for accessing a resource with a specified expiration time. 192 * 193 * This function creates a signed URL for a given resource by encoding a policy 194 * that includes a condition for expiration (600 seconds from the current time). 195 * The URL is signed using a private key, and the resulting signature is included 196 * in the URL as a query parameter. 197 * 198 * @param string $url The URL of the resource to be signed. 199 * @param string $version The version of the resource (optional). 200 * 201 * @return string The signed URL with the policy and signature as query parameters. 202 */ 203 function parasol_generate_signed_url( $url, $version = '' ) { 204 $policy = wp_json_encode( 205 array( 206 'Statement' => array( 207 array( 208 'Resource' => $url . '/*' . ( $version ? '?ver=' . $version : '' ), 209 'Condition' => array( 'DateLessThan' => array( 'AWS:EpochTime' => time() + 600 ) ), 210 ), 211 ), 212 ) 213 ); 214 215 $encoded_policy = parasol_url_safe_base64_encode( $policy ); 216 openssl_sign( $policy, $signature, PARASOL_PRIVATE_KEY, OPENSSL_ALGO_SHA1 ); 217 $encoded_signature = parasol_url_safe_base64_encode( $signature ); 218 219 $query_delimiter = $version ? 'ver=' . $version . '&' : ''; 220 return "?{$query_delimiter}Policy={$encoded_policy}&Signature={$encoded_signature}&Key-Pair-Id=" . PARASOL_CLOUDFRONT_KEY_ID; 221 } 222 223 /** 224 * Encodes a value using base64 encoding and replaces URL unsafe characters. 225 * 226 * This function performs base64 encoding on the input value and then replaces 227 * the characters that are not URL-safe (`+`, `/`, `=`) with their URL-safe 228 * equivalents (`-`, `_`, `~`) to make the encoded string safe for use in URLs. 229 * 230 * @param string $value The value to be base64 encoded. 231 * 232 * @return string The URL-safe base64 encoded string. 233 */ 234 function parasol_url_safe_base64_encode( $value ) { 235 return str_replace( array( '+', '=', '/' ), array( '-', '_', '~' ), base64_encode( $value ) ); 236 } 237 238 /** 239 * Cleans up transients on plugin deactivation. 240 * 241 * This function deletes specific transients related to the Parasol plugin (`parasol_disabled` 242 * and `parasol_whitelist_domains`) to clean up any stored data when the plugin is deactivated. 243 * 244 * @return void 245 */ 173 246 function parasol_deactivation() { 174 delete_transient('parasol_disabled'); 175 delete_transient('parasol_whitelist_domains'); 176 } 177 247 delete_transient( 'parasol_disabled' ); 248 delete_transient( 'parasol_whitelist_domains' ); 249 } 250 251 /** 252 * Disables certain domains and enqueues custom actions. 253 * 254 * This function hooks into WordPress actions to disable certain domains by adding a custom 255 * content security policy and dequeueing third-party scripts. It hooks `parasol_add_custom_content_security_policy` 256 * to `send_headers` and `parasol_dequeue_third_party_scripts` to `wp_enqueue_scripts` with a priority of 100. 257 * 258 * @return void 259 */ 178 260 function parasol_disable_domains() { 179 add_action('send_headers', 'parasol_add_custom_content_security_policy'); 180 add_action('wp_enqueue_scripts', 'parasol_dequeue_third_party_scripts', 100); 181 } 182 261 add_action( 'send_headers', 'parasol_add_custom_content_security_policy' ); 262 add_action( 'wp_enqueue_scripts', 'parasol_dequeue_third_party_scripts', 100 ); 263 } 264 265 /** 266 * Adds a custom Content Security Policy (CSP) header. 267 * 268 * This function generates a custom CSP header based on the domains stored in the `parasol_whitelist_domains` 269 * transient. It defines directives for script, style, worker, and frame sources, and then adds the CSP header 270 * to the response using the `header()` function. It supports wildcard domains and includes `unsafe-inline` for 271 * script and style sources. 272 * 273 * @return void 274 */ 183 275 function parasol_add_custom_content_security_policy() { 184 $wildcard_domains = array_map(function($domain) { 185 return '*.' . $domain; 186 }, (get_transient('parasol_whitelist_domains') ?? [])); 187 188 $csp_directives = [ 189 'script-src' => array_merge(["'self'", "'unsafe-inline'"], $wildcard_domains), 190 'style-src' => array_merge(["'self'", "'unsafe-inline'"], $wildcard_domains), 191 'worker-src' => array_merge(["'self'", 'blob:'], $wildcard_domains), 192 'frame-src' => array_merge(["'self'"], $wildcard_domains), 193 ]; 194 195 $csp_header = parasol_build_csp_header($csp_directives); 196 if ($csp_header) { 197 header("Content-Security-Policy: $csp_header"); 198 } 199 } 200 201 function parasol_build_csp_header(array $directives): string { 202 $header_parts = []; 203 204 foreach ($directives as $directive => $sources) { 205 $header_parts[] = "{$directive} " . implode(' ', $sources) . ';'; 206 } 207 208 return implode(' ', $header_parts); 209 } 210 276 $wildcard_domains = array_map( 277 function ( $domain ) { 278 return '*.' . $domain; 279 }, 280 ( get_transient( 'parasol_whitelist_domains' ) ?? array() ) 281 ); 282 283 $csp_directives = array( 284 'script-src' => array_merge( array( "'self'", "'unsafe-inline'" ), $wildcard_domains ), 285 'style-src' => array_merge( array( "'self'", "'unsafe-inline'" ), $wildcard_domains ), 286 'worker-src' => array_merge( array( "'self'", 'blob:' ), $wildcard_domains ), 287 'frame-src' => array_merge( array( "'self'" ), $wildcard_domains ), 288 ); 289 290 $csp_header = parasol_build_csp_header( $csp_directives ); 291 if ( $csp_header ) { 292 header( "Content-Security-Policy: $csp_header" ); 293 } 294 } 295 296 /** 297 * Builds a Content Security Policy (CSP) header from the provided directives. 298 * 299 * This function takes an array of CSP directives, where each directive is associated with a list of sources. 300 * It formats the directives and their corresponding sources into a valid CSP header string, which can be used 301 * in an HTTP response to define security policies for various resources like scripts, styles, and frames. 302 * 303 * @param array $directives An associative array of CSP directives and their corresponding sources. 304 * 305 * @return string A string representing the formatted CSP header. 306 */ 307 function parasol_build_csp_header( array $directives ): string { 308 $header_parts = array(); 309 310 foreach ( $directives as $directive => $sources ) { 311 $header_parts[] = "{$directive} " . implode( ' ', $sources ) . ';'; 312 } 313 314 return implode( ' ', $header_parts ); 315 } 316 317 /** 318 * Dequeues third-party scripts not hosted on allowed domains. 319 * 320 * This function checks the list of currently queued scripts and dequeues any third-party scripts 321 * whose source domain is not in the list of allowed domains. The allowed domains are retrieved 322 * from the `parasol_whitelist_domains` transient, along with the site’s own domain. 323 * 324 * The function uses `parasol_is_allowed_script_domain()` to verify whether a script's domain is allowed. 325 * 326 * @return void 327 */ 211 328 function parasol_dequeue_third_party_scripts() { 212 global $wp_scripts; 213 214 $allowed_domains = array_merge((get_transient('parasol_whitelist_domains') ?? []), [wp_parse_url(get_site_url(), PHP_URL_HOST)]); 215 216 foreach ($wp_scripts->queue as $script_handle) { 217 $script_obj = $wp_scripts->registered[$script_handle] ?? null; 218 219 if ($script_obj && $script_obj->src && !parasol_is_allowed_script_domain($script_obj->src, $allowed_domains)) { 220 wp_dequeue_script($script_handle); 221 } 222 } 223 } 224 225 function parasol_is_allowed_script_domain(string $script_src, array $allowed_domains): bool { 226 $script_domain = wp_parse_url($script_src, PHP_URL_HOST); 227 return $script_domain ? in_array(get_root_domain($script_domain), $allowed_domains, true) : true; 228 } 229 230 function get_root_domain(string $domain): string { 231 $parts = explode('.', $domain); 232 233 if (count($parts) > 2) { 234 return implode('.', array_slice($parts, -2)); 235 } 236 237 return $domain; 238 } 329 global $wp_scripts; 330 331 $allowed_domains = array_merge( ( get_transient( 'parasol_whitelist_domains' ) ?? array() ), array( wp_parse_url( get_site_url(), PHP_URL_HOST ) ) ); 332 333 foreach ( $wp_scripts->queue as $script_handle ) { 334 $script_obj = $wp_scripts->registered[ $script_handle ] ?? null; 335 336 if ( $script_obj && $script_obj->src && ! parasol_is_allowed_script_domain( $script_obj->src, $allowed_domains ) ) { 337 wp_dequeue_script( $script_handle ); 338 } 339 } 340 } 341 342 /** 343 * Checks if a script's domain is allowed. 344 * 345 * This function extracts the domain from the given script URL and compares it to the list of allowed 346 * domains. It checks if the root domain of the script’s source is in the allowed domains list. If the 347 * script’s domain is not found or no domain is present in the URL, it returns `true` (indicating the script is allowed). 348 * 349 * @param string $script_src The URL of the script to check. 350 * @param array $allowed_domains The list of allowed domains. 351 * 352 * @return bool `true` if the script’s domain is allowed, otherwise `false`. 353 */ 354 function parasol_is_allowed_script_domain( string $script_src, array $allowed_domains ): bool { 355 $script_domain = wp_parse_url( $script_src, PHP_URL_HOST ); 356 return $script_domain ? in_array( get_root_domain( $script_domain ), $allowed_domains, true ) : true; 357 } 358 359 /** 360 * Extracts the root domain from a given domain. 361 * 362 * This function takes a domain and returns the root domain by extracting the last two segments 363 * (e.g., for `sub.example.com`, it will return `example.com`). If the domain has two or fewer segments, 364 * the original domain is returned. 365 * 366 * @param string $domain The domain to extract the root from. 367 * 368 * @return string The root domain (last two segments) or the original domain if it has two or fewer segments. 369 */ 370 function get_root_domain( string $domain ): string { 371 $parts = explode( '.', $domain ); 372 373 if ( count( $parts ) > 2 ) { 374 return implode( '.', array_slice( $parts, -2 ) ); 375 } 376 377 return $domain; 378 } -
parasol/trunk/readme.txt
r3224076 r3226477 5 5 Tested up to: 6.7.1 6 6 Requires PHP: 7.4 7 Stable tag: 1. 07 Stable tag: 1.1 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 87 87 == Changelog == 88 88 89 = 1.0.0 = 89 = 1.1 = 90 * Updates to Parasol asset loading. 91 92 = 1.0 = 90 93 * Initial release of Parasol. 91 94 * Added options for site visitors to access content. … … 94 97 95 98 == Upgrade Notice == 99 100 = 1.1 = 101 Updates to Parasol asset loading. 96 102 97 103 = 1.0 =
Note: See TracChangeset
for help on using the changeset viewer.