Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cookiebot.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Plugin URI: https://www.cookiebot.com/
Description: Install your cookie banner in minutes. Automatically scan and block cookies to comply with the GDPR, CCPA, Google Consent Mode v2. Free plan option.
Author: Usercentrics A/S
Version: 4.6.2
Version: 4.6.3
Author URI: https://www.cookiebot.com/
Text Domain: cookiebot
Domain Path: /langs
Expand Down
15 changes: 14 additions & 1 deletion readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Tags: cookie banner, cookie consent, cookie notice, GDPR, privacy, cmp, consent‑management‑platform, google‑consent‑mode, compliance, gdpr‑compliance, ccpa, dma
* Requires at least: 4.4
* Tested up to: 6.8
* Stable tag: 4.6.2
* Stable tag: 4.6.3
* Requires PHP: 5.6
* License: GPLv2 or later

Expand Down Expand Up @@ -163,6 +163,19 @@ Usercentrics Cookiebot is fully integrated with the WP Consent API. When your vi
## Changelog ##
**Cookiebot by Usercentrics Plugin will soon no longer support PHP 5. If your website still runs on this version we recommend upgrading so you can continue enjoying the features Cookiebot by Usercentrics offers.**

### 4.6.3 ###
Release date: January 27th 2026

Cookiebot by Usercentrics version 4.6.3 is out! This release has a bugfix and an improvement

####Improvements####

* Enhanced script consent handling for WordPress 5.7+, including proper support for inline scripts

####Bugfixes####

* Fixed an issue where the consent banner could become disabled after plugin updates or hosting migrations

### 4.6.2 ###
Release date: December 17th 2025

Expand Down
8 changes: 8 additions & 0 deletions src/lib/Cookiebot_Activated.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,18 @@ private function set_to_mode_auto_when_no_cookiebot_id_is_set() {

private function set_banner_enabled_by_default() {
$enabled = get_option( 'cookiebot-banner-enabled', 'default' );
$cbid = Cookiebot_WP::get_cbid();

// Set to enabled if it's a fresh install (default)
if ( $enabled === 'default' ) {
$enabled = '1';
update_option( 'cookiebot-banner-enabled', $enabled );
}

// If banner is disabled but CBID is configured, it was disabled by hosting or plugin update. Re-enable it.
if ( $enabled === '0' && ! empty( $cbid ) ) {
update_option( 'cookiebot-banner-enabled', '1' );
}
}
Comment on lines 88 to 101
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[CRITICAL_BUG] This change forcefully re-enables the banner when option 'cookiebot-banner-enabled' === '0' and a CBID exists (lines 98-100). Two issues: (1) On multisite installations the code uses update_option() which will not update the network/site option if the setting is stored as a site option — use update_site_option() when is_multisite() to keep behavior consistent with set_to_mode_auto_when_no_cookiebot_id_is_set(). (2) This unconditional re-enable can override an administrator's intentional decision to disable the banner. Instead, scope the auto re-enable to only known-recovery scenarios (for example: during activation/migration flows protected by a transient, or when detecting a recent plugin-version change) and/or log/admin-notice the action so admins are aware. Example fix snippet: if ( $enabled === '0' && ! empty( $cbid ) && get_transient( 'cookiebot_reenable_banner_recovery' ) ) { if ( is_multisite() ) { update_site_option( 'cookiebot-banner-enabled', '1' ); } else { update_option( 'cookiebot-banner-enabled', '1' ); } }

private function set_banner_enabled_by_default() {
	$enabled = get_option( 'cookiebot-banner-enabled', 'default' );
	$cbid    = Cookiebot_WP::get_cbid();

	// Set to enabled if it's a fresh install (default)
	if ( $enabled === 'default' ) {
		$enabled = '1';
		update_option( 'cookiebot-banner-enabled', $enabled );
	}

	// Only attempt automatic recovery when explicitly in a recovery window
	if ( $enabled === '0' && ! empty( $cbid ) && get_transient( 'cookiebot_reenable_banner_recovery' ) ) {
		if ( is_multisite() ) {
			update_site_option( 'cookiebot-banner-enabled', '1' );
		} else {
			update_option( 'cookiebot-banner-enabled', '1' );
		}

		Cookiebot_WP::debug_log( 'Banner was automatically re-enabled as part of recovery flow.' );
	}
}


/**
Expand Down
1 change: 0 additions & 1 deletion src/lib/Cookiebot_Deactivated.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ class Cookiebot_Deactivated {
*/
public function run() {
$this->run_addons_deactivation_hooks();
$this->disable_banner();
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/lib/Cookiebot_WP.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public static function debug_log( $message ) {
}
}

const COOKIEBOT_PLUGIN_VERSION = '4.6.2';
const COOKIEBOT_PLUGIN_VERSION = '4.6.3';
const COOKIEBOT_MIN_PHP_VERSION = '5.6.0';

/**
Expand Down
56 changes: 53 additions & 3 deletions src/lib/script_loader_tag/Script_Loader_Tag.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,24 @@ function ( $tag ) use ( $handle ) {
}

/**
* Modifies script tags to add the consent ignore attribute.
* Modifies script tags to add the consent attributes for WordPress 5.7+.
* Handles both consent-required scripts (from add_tag) and ignore scripts.
*
* @param array $attributes List of the attributes for the tag.
*
* @return array List of the attributes for the tag.
*/
public function cookiebot_add_consent_attribute_to_script_tag( $attributes ) {
// First, check if this script requires consent (registered via add_tag)
$handle = $this->extract_handle_from_attributes( $attributes );

if ( $handle && array_key_exists( $handle, $this->tags ) && ! empty( $this->tags[ $handle ] ) ) {
$attributes['type'] = 'text/plain';
$attributes['data-cookieconsent'] = implode( ',', $this->tags[ $handle ] );
return $attributes;
}
Comment on lines 111 to +119
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[CRITICAL_BUG] In cookiebot_add_consent_attribute_to_script_tag you force attributes['type'] = 'text/plain' for scripts that require consent (lines 116-117). Overriding the type unconditionally can break legitimate script types (e.g. type='module') or other non-JS content. Change the logic to: (1) detect and preserve module scripts (if isset($attributes['type']) && $attributes['type'] === 'module' then skip or handle differently), (2) only override type when no type is set or when it's safe to change, and (3) consider adding a whitelist/opt-in for types you can convert to text/plain. Example: if ( empty( $attributes['type'] ) || $attributes['type'] === 'text/javascript' ) { $attributes['type'] = 'text/plain'; }

public function cookiebot_add_consent_attribute_to_script_tag( $attributes ) {
	$handle = $this->extract_handle_from_attributes( $attributes );

	if ( $handle && array_key_exists( $handle, $this->tags ) && ! empty( $this->tags[ $handle ] ) ) {
		// Preserve module scripts and other explicit non-JS types
		if ( empty( $attributes['type'] ) || $attributes['type'] === 'text/javascript' || $attributes['type'] === 'application/javascript' ) {
			$attributes['type'] = 'text/plain';
		}

		$attributes['data-cookieconsent'] = implode( ',', $this->tags[ $handle ] );
		return $attributes;
	}

	if ( isset( $attributes['src'] ) && $this->check_ignore_script( $attributes['src'] ) ) {
		$attributes['data-cookieconsent'] = 'ignore';
	}

	return $attributes;
}


// Then check if this script should be ignored (for admin scripts, etc.)
if ( isset( $attributes['src'] ) && $this->check_ignore_script( $attributes['src'] ) ) {
$attributes['data-cookieconsent'] = 'ignore';
}
Expand All @@ -116,14 +127,29 @@ public function cookiebot_add_consent_attribute_to_script_tag( $attributes ) {
}

/**
* Modifies inline script tags to add the consent ignore attribute.
* Modifies inline script tags to add the consent attributes for WordPress 5.7+.
* Handles both consent-required inline scripts and ignored inline scripts.
*
* @param array $attributes List of the attributes for the tag.
*
* @return array List of the attributes for the tag.
*/
public function cookiebot_add_consent_attribute_to_inline_script_tag( $attributes ) {
if ( isset( $attributes['id'] ) && $this->is_inline_of_ignored_script( $attributes['id'] ) ) {
if ( ! isset( $attributes['id'] ) ) {
return $attributes;
}

// Check if inline script belongs to a consent-required parent script
$base_handle = $this->extract_base_id_from_inline_id( $attributes['id'] );

if ( $base_handle && array_key_exists( $base_handle, $this->tags ) && ! empty( $this->tags[ $base_handle ] ) ) {
$attributes['type'] = 'text/plain';
$attributes['data-cookieconsent'] = implode( ',', $this->tags[ $base_handle ] );
return $attributes;
}

// Check if inline script belongs to an ignored parent script
if ( $this->is_inline_of_ignored_script( $attributes['id'] ) ) {
$attributes['data-cookieconsent'] = 'ignore';
}

Expand Down Expand Up @@ -179,6 +205,30 @@ private function extract_base_id_from_inline_id( $inline_script_id ) {
return preg_replace( '/-js-(extra|after|before)$/', '', $inline_script_id );
}

/**
* Extract the script handle from attributes array.
* WordPress typically sets the id attribute as "{handle}-js".
*
* @param array $attributes Script attributes.
*
* @return string|null Script handle or null if not found.
*/
private function extract_handle_from_attributes( $attributes ) {
if ( ! isset( $attributes['id'] ) ) {
return null;
}

$id = $attributes['id'];

// Remove the '-js' suffix that WordPress adds
if ( substr( $id, -3 ) === '-js' ) {
return substr( $id, 0, -3 );
}

// If no -js suffix, return the ID as-is (some scripts may not follow convention)
return $id;
}
Comment on lines +216 to +230
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[REFACTORING] extract_handle_from_attributes strips only the literal '-js' suffix (lines 223-225) and returns the id as-is for other patterns. Improve robustness by removing '-js' and optional extra/after/before suffixes in one step (same as extract_base_id_from_inline_id suggestion). Example: $id = preg_replace('/-js(?:-(?:extra|after|before))?$/', '', $id); return $id !== '' ? $id : null; This will better handle varied ID conventions and make handle extraction more reliable.

private function extract_handle_from_attributes( $attributes ) {
	if ( ! isset( $attributes['id'] ) ) {
		return null;
	}

	$id = $attributes['id'];

	// Remove the '-js' suffix and optional inline suffixes that WordPress may add
	$handle = preg_replace( '/-js(?:-(?:extra|after|before))?$/', '', $id );

	return $handle !== '' ? $handle : null;
}


/**
* Check if the script tag attributes are valid for the injection of the consent ignore attribute.
*
Expand Down
Loading