Skip to content

Conversation

@oxyc
Copy link

@oxyc oxyc commented Jan 20, 2026

User description

User description

Issue #359. This might not be complete but a WIP


PR Type

Enhancement


Description

  • Replace hardcoded HTML script tags with WordPress core API functions

  • Use wp_print_script_tag() for external scripts with attributes

  • Use wp_print_inline_script_tag() for inline scripts with output buffering

  • Refactor script attributes into associative arrays for better maintainability


Diagram Walkthrough

flowchart LR
  A["Hardcoded HTML script tags"] -->|"Replace with"| B["wp_print_script_tag()"]
  A -->|"Replace with"| C["wp_print_inline_script_tag()"]
  D["Inline HTML attributes"] -->|"Refactor to"| E["Associative arrays"]
  E -->|"Pass to"| B
  E -->|"Pass to"| C
Loading

File Walkthrough

Relevant files
Enhancement
cookiebot-js.php
Refactor Cookiebot script to use WordPress API                     

src/view/frontend/scripts/cb_frame/cookiebot-js.php

  • Convert hardcoded <script> tag to wp_print_script_tag() function call
  • Build script attributes dynamically in an associative array
  • Simplify conditional logic for data attributes (tcf, georegions,
    layer-name, culture, blockingmode)
  • Properly escape and handle tag attributes using array structure
+35/-33 
google-consent-mode-js.php
Convert Google Consent Mode script to WordPress API           

src/view/frontend/scripts/common/google-consent-mode-js.php

  • Replace inline <script> tag with wp_print_inline_script_tag() function
  • Build script attributes array with type and optional
    data-cookieconsent
  • Use output buffering (ob_start() and ob_get_clean()) to capture inline
    script content
  • Improve attribute handling and escaping through structured array
    approach
+10/-2   
google-tag-manager-js.php
Refactor Google Tag Manager script to WordPress API           

src/view/frontend/scripts/common/google-tag-manager-js.php

  • Replace inline <script> tag with wp_print_inline_script_tag() function
  • Build script attributes array with type and optional
    data-cookieconsent
  • Use output buffering to capture GTM inline script content
  • Maintain existing script logic while improving attribute management
+12/-2   


CodeAnt-AI Description

Replace hardcoded <script> tags with WordPress core script API for analytics and Cookiebot

What Changed

  • Google Tag Manager and Google Consent Mode inline scripts are now emitted via WordPress core functions so their script type and optional consent attribute are included consistently
  • Cookiebot external script is now emitted via WordPress core function with its attributes built from data (cbid, georegions, framework, data-layer-name, culture, blocking mode) instead of hand-assembled HTML
  • Script attributes that depend on settings (data-cookieconsent, data-georegions, data-layer-name, data-culture, data-blockingmode) are populated only when present, producing consistent and properly escaped attributes

Impact

✅ Fewer malformed script attributes on pages
✅ Clearer consent attribute presence for third-party scripts
✅ More consistent Cookiebot and GTM script output

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@codeant-ai
Copy link

codeant-ai bot commented Jan 20, 2026

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@qodo-code-review
Copy link

qodo-code-review bot commented Jan 20, 2026

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🟡
🎫 #359
🟢 Replace hardcoded <script> tag output with WordPress core script-tag APIs (e.g.,
wp_get_inline_script_tag() / wp_print_inline_script_tag()) so script tags are generated
through core.
Ensure the approach supports strict CSP by enabling others to hook in and inject
attributes (e.g., a nonce) into the printed script tags.
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status:
Misnamed variable: The code assigns the data-cookieconsent attribute to an undefined/mismatched variable
($attributes instead of $script_attributes), making intent unclear and likely breaking
expected behavior.

Referred Code
$script_attributes = array(
    'type' => $script_type,
);
if ($consent_attribute) {
    $attributes['data-cookieconsent'] = $consent_attribute;
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Missing edge handling: The consent attribute branch writes to $attributes (not passed to
wp_print_inline_script_tag()), so the consent attribute may be silently omitted without
any fallback or warning when $consent_attribute is set.

Referred Code
$script_attributes = array(
    'type' => $script_type,
);
if ($consent_attribute) {
    $attributes['data-cookieconsent'] = $consent_attribute;
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Attribute value validation: The PR passes externally-derived values like $script_type and $consent_attribute into
$script_attributes without showing allowlist validation in the diff, so it is unclear if
only safe/expected attribute values can be emitted.

Referred Code
$script_attributes = array(
    'type' => $script_type,
);
if ($consent_attribute) {
    $script_attributes['data-cookieconsent'] = $consent_attribute;
}

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@codeant-ai codeant-ai bot added the size:M This PR changes 30-99 lines, ignoring generated files label Jan 20, 2026
@qodo-code-review
Copy link

qodo-code-review bot commented Jan 20, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Fix an incorrect variable name
Suggestion Impact:The commit updates the variable used for the data-cookieconsent attribute assignment from $attributes to $script_attributes, ensuring the attribute is applied to the script tag.

code diff:

 if ($consent_attribute) {
-    $attributes['data-cookieconsent'] = $consent_attribute;
+    $script_attributes['data-cookieconsent'] = $consent_attribute;
 }

Fix a typo in the google-tag-manager-js.php file by changing the variable
$attributes to $script_attributes to ensure the data-cookieconsent attribute is
correctly assigned.

src/view/frontend/scripts/common/google-tag-manager-js.php [13-15]

 if ($consent_attribute) {
-    $attributes['data-cookieconsent'] = $consent_attribute;
+    $script_attributes['data-cookieconsent'] = $consent_attribute;
 }

[Suggestion processed]

Suggestion importance[1-10]: 10

__

Why: This suggestion correctly identifies a critical bug where a typo ($attributes instead of $script_attributes) would cause the data-cookieconsent attribute to be omitted, breaking functionality.

High
High-level
Consider using wp_add_inline_script instead

Instead of directly printing scripts in template files, use the standard
WordPress functions wp_enqueue_script for external scripts and
wp_add_inline_script for inline ones. This centralizes script management and
follows WordPress best practices.

Examples:

src/view/frontend/scripts/cb_frame/cookiebot-js.php [11-45]
$script_attributes = array(
	'type'                 => 'text/javascript',
	'id'                   => 'Cookiebot',
	'src'                  => 'https://consent.cookiebot.com/uc.js',
	'data-implementation' => 'wp',
	'data-cbid'            => $cbid,
);

if ( $tcf ) {
	$script_attributes['data-framework'] = $tcf;

 ... (clipped 25 lines)
src/view/frontend/scripts/common/google-consent-mode-js.php [9-42]
$script_attributes = array(
    'type' => $script_type,
);
if ($consent_attribute) {
    $script_attributes['data-cookieconsent'] = $consent_attribute;
}

ob_start();
?>
	window.<?php echo esc_js( $data_layer ); ?> = window.<?php echo esc_js( $data_layer ); ?> || [];

 ... (clipped 24 lines)

Solution Walkthrough:

Before:

// src/view/frontend/scripts/cb_frame/cookiebot-js.php
$script_attributes = [
    'src' => 'https://consent.cookiebot.com/uc.js',
    'data-cbid' => $cbid,
    // ... other attributes
];
wp_print_script_tag($script_attributes);

// src/view/frontend/scripts/common/google-consent-mode-js.php
ob_start();
?>
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
// ... more inline JS
<?php
wp_print_inline_script_tag(ob_get_clean(), $script_attributes);

After:

// In a centralized function hooked to 'wp_enqueue_scripts'

// For the external script
wp_enqueue_script(
    'cookiebot-uc',
    'https://consent.cookiebot.com/uc.js',
    [],
    null
);
// Use 'script_loader_tag' filter to add data attributes

// For the inline scripts
$inline_script_content = 'window.dataLayer = ...';
wp_add_inline_script('some-script-handle', $inline_script_content);
// Use 'wp_inline_script_attributes' filter to add attributes
Suggestion importance[1-10]: 9

__

Why: This is a significant architectural suggestion that correctly points out that using the WordPress enqueue system (wp_enqueue_script/wp_add_inline_script) is the standard and most robust method for managing scripts, improving maintainability and compatibility.

High
  • Update

@pantoaibot
Copy link

pantoaibot bot commented Jan 20, 2026

PR Summary:

Replace hardcoded <script> tags with WordPress core script APIs and build attribute arrays for safer, standardized output.

Changes:

  • General

    • Replaced manual script tag construction with wp_print_script_tag() for external scripts and wp_print_inline_script_tag() for inline scripts.
    • Script attributes are now built in $script_attributes arrays (safer escaping and consistent output).
    • Inline script bodies are captured with ob_start() and passed to wp_print_inline_script_tag().
  • src/view/frontend/scripts/cb_frame/cookiebot-js.php

    • Replaced direct echoing of a <script> tag with a $script_attributes array and wp_print_script_tag().
    • Builds data-georegions as an imploded array string.
    • Adds data-layer-name, data-culture (uppercased), and data-blockingmode via the attributes array.
    • Behavior change: previous $tag_attr was echoed verbatim; now it is added as an attribute key with value true (boolean-attribute style) — may change how that attribute is rendered/consumed.
  • src/view/frontend/scripts/common/google-consent-mode-js.php

    • Wrapped the inline gtag consent initialization in ob_start() and output via wp_print_inline_script_tag().
    • Adds data-cookieconsent (when provided) via the attributes array.
  • src/view/frontend/scripts/common/google-tag-manager-js.php

    • Converted inline GTM script to use ob_start() + wp_print_inline_script_tag().
    • Intends to add data-cookieconsent via attributes, but currently assigns it to $attributes (typo) instead of $script_attributes — this is a likely bug causing the data-cookieconsent attribute not to be emitted.

Potential breaking/behavior changes to review:

  • Rendering of $tag_attr in cookiebot-js.php changed from a verbatim attribute string to a boolean attribute — confirm downstream consumers accept this format.
  • Check and fix the variable-name bug in google-tag-manager-js.php so data-cookieconsent is applied correctly.

Reviewed by Panto AI

@codeant-ai
Copy link

codeant-ai bot commented Jan 20, 2026

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • Unescaped attribute values
    Several attribute values are assigned directly from variables or get_option() without ensuring they're escaped/sanitized. These values end up in HTML attributes and could lead to XSS if any of the inputs are attacker-controlled. Verify escaping strategy and whether wp_print_script_tag already handles escaping; if not, escape before assignment.

  • Unsanitized attribute name
    The code uses the variable $tag_attr as an attribute name ($script_attributes[$tag_attr] = true;) without validating or sanitizing it. Arbitrary attribute names could break HTML or introduce security/formatting issues. Restrict to a whitelist of known boolean attributes (e.g., async, defer) or sanitize the key.

  • Wrong variable name
    The code adds the cookie-consent attribute into $attributes[...] but the attributes array used with wp_print_inline_script_tag() is $script_attributes. This means the data-cookieconsent key will never be present and PHP may raise an undefined-variable notice. Fix the variable name so the attribute is added to the array actually passed to wp_print_inline_script_tag().

  • Attribute sanitization
    Attributes for the script tag (type and data-cookieconsent) are taken from $script_type and $consent_attribute without explicit sanitization/validation before being passed to wp_print_inline_script_tag(). Confirm these values are validated/whitelisted or sanitized to avoid attribute injection or unexpected values being rendered in the tag.

  • Manual data-georegions formatting
    data-georegions is constructed by concatenating string fragments that embed single quotes inside the attribute value. This manual assembly is fragile and may produce invalid attribute contents if region or cbid contain unexpected characters. Consider building a JSON array and passing an encoded/escaped string.

'id' => 'Cookiebot',
'src' => 'https://consent.cookiebot.com/uc.js',
'data-implementation' => 'wp',
'data-cbid' => $cbid,
Copy link

Choose a reason for hiding this comment

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

Suggestion: Unsanitized assignment to data-cbid: the value from $cbid is assigned directly to attributes array without escaping, which can lead to unsafe output; escape the value with esc_attr() when building attributes. [security]

Severity Level: Critical 🚨
- ❌ Cookiebot frontend attribute may enable XSS.
- ⚠️ Plugin admin-entered cbid values are risk vectors.
Suggested change
'data-cbid' => $cbid,
'data-cbid' => esc_attr( $cbid ),
Steps of Reproduction ✅
1. In a test WordPress environment or simple PHP harness, set $cbid to a value containing
special characters, e.g. $cbid = '" onmouseover="alert(1)'. Save this value as if it were
coming from plugin configuration.

2. Include the template file: src/view/frontend/scripts/cb_frame/cookiebot-js.php. The
attribute array is built at lines 11-17 and includes 'data-cbid' => $cbid (line 16).

3. The template calls wp_print_script_tag() at line 45, which will emit the <script> with
attributes composed from $script_attributes.

4. Inspect the page source / rendered output: if $cbid is not escaped before being passed
into the attributes array, attribute content may be injected verbatim, demonstrating
unescaped attribute output and potential XSS.

Note: This reproduction references the exact assignment (line 16) and the emission point
wp_print_script_tag() (line 45) in the same file.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/view/frontend/scripts/cb_frame/cookiebot-js.php
**Line:** 16:16
**Comment:**
	*Security: Unsanitized assignment to `data-cbid`: the value from `$cbid` is assigned directly to attributes array without escaping, which can lead to unsafe output; escape the value with `esc_attr()` when building attributes.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

Comment on lines +31 to +32
if ( (bool) get_option( 'cookiebot-gtm' ) !== false && ! empty( get_option( 'cookiebot-data-layer' ) ) ) {
$script_attributes['data-layer-name'] = get_option( 'cookiebot-data-layer' );
Copy link

Choose a reason for hiding this comment

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

Suggestion: Unescaped option value used for data-layer-name: get_option( 'cookiebot-data-layer' ) is assigned directly to attributes array without escaping; fetch the option once, check it, and escape it with esc_attr() before use. [security]

Severity Level: Critical 🚨
- ❌ Admin-set option may inject unsafe frontend attribute.
- ⚠️ Data layer integration could be exploited.
Suggested change
if ( (bool) get_option( 'cookiebot-gtm' ) !== false && ! empty( get_option( 'cookiebot-data-layer' ) ) ) {
$script_attributes['data-layer-name'] = get_option( 'cookiebot-data-layer' );
$layer = get_option( 'cookiebot-data-layer' );
if ( (bool) get_option( 'cookiebot-gtm' ) !== false && ! empty( $layer ) ) {
$script_attributes['data-layer-name'] = esc_attr( $layer );
Steps of Reproduction ✅
1. As a site admin, set the option 'cookiebot-data-layer' to a payload value via the WP
options UI or direct DB write (e.g. data like '" onclick="alert(1)').

2. Load a frontend page that renders the plugin template
src/view/frontend/scripts/cb_frame/cookiebot-js.php.

3. The conditional at line 31 checks options and (when true) assigns
get_option('cookiebot-data-layer') directly into $script_attributes at line 32.

4. The template emits the script attributes with wp_print_script_tag() at line 45; because
the option value was not escaped before assignment, the output can contain unescaped
content, demonstrating possible XSS via an admin-configured option.

Note: Steps reference concrete code locations: option fetch and assignment (lines 31-32)
and emission at line 45.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/view/frontend/scripts/cb_frame/cookiebot-js.php
**Line:** 31:32
**Comment:**
	*Security: Unescaped option value used for `data-layer-name`: `get_option( 'cookiebot-data-layer' )` is assigned directly to attributes array without escaping; fetch the option once, check it, and escape it with `esc_attr()` before use.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

Copy link
Author

Choose a reason for hiding this comment

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

See wp_sanitize_script_attributes() which runs esc_attr() on all passed atribtues

Choose a reason for hiding this comment

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

Question:

See wp_sanitize_script_attributes() which runs esc_attr() on all passed atribtues

Answer:

If wp_print_script_tag() (via wp_sanitize_script_attributes()) already escapes all attribute values with esc_attr(), then the “unescaped attribute → XSS” claim is likely a false positive (you’d have double-escaping at worst, not a security fix).

To reduce these kinds of WP-specific false positives in /improve suggestions, add an instruction to verify whether output is already sanitized/escaped by the WordPress API used at render time before flagging a security issue or proposing esc_*() changes.

[pr_code_suggestions]
extra_instructions = """\
- For WordPress/PHP code: before suggesting esc_attr()/esc_html()/esc_url() (or tagging as security/XSS), verify whether the output API already sanitizes/escapes (e.g., wp_print_script_tag() -> wp_sanitize_script_attributes()).
- If escaping is already applied by the sink function, do not recommend redundant escaping; instead, briefly mention why it's already safe.
- Prefer referencing the escaping/sanitization function in the rationale when concluding it's safe.
"""

#### Relevant Sources:

> - https://docs.qodo.ai/qodo-documentation/qodo-merge/tools/improve#extra-instructions-and-best-practices
> - https://docs.qodo.ai/qodo-documentation/qodo-merge/faq/index#-note-"q:-i-received-an-incorrect-or-irrelevant-suggestion-why"
> - https://docs.qodo.ai/qodo-documentation/qodo-merge/usage-guide/additional_configurations#extra-instructions

Comment on lines 13 to 14
if ($consent_attribute) {
$attributes['data-cookieconsent'] = $consent_attribute;
Copy link

Choose a reason for hiding this comment

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

Suggestion: Undefined variable bug: the code assigns the cookie-consent attribute into $attributes, but the attributes array is named $script_attributes, so $attributes is undefined and the consent attribute is never added; also sanitize the consent value before output to avoid injecting untrusted data into HTML attributes. [null pointer]

Severity Level: Critical 🚨
- ⚠️ data-cookieconsent attribute missing on GTM script.
- ⚠️ PHP notice "Undefined variable: attributes" logged.
- ⚠️ Consent-driven script behavior may be affected.
Suggested change
if ($consent_attribute) {
$attributes['data-cookieconsent'] = $consent_attribute;
if ( $consent_attribute ) {
$script_attributes['data-cookieconsent'] = sanitize_text_field( $consent_attribute );
Steps of Reproduction ✅
1. Render the frontend template that includes this script template file:
src/view/frontend/scripts/common/google-tag-manager-js.php. The template content at lines
10-15 sets up $script_attributes and conditionally writes the consent attribute.

2. Ensure the template is executed with a truthy $consent_attribute value (the file's
docblock shows @var bool|string $consent_attribute; the including code must provide this
variable). When that happens, execution reaches lines 13-14 where the code attempts to
assign the consent attribute.

3. At lines 13-14 the code writes to $attributes['data-cookieconsent'] while the
associative array in use is $script_attributes (lines 10-12). Because $attributes is
undefined, PHP emits an "Undefined variable: attributes" notice and the consent value is
not stored on $script_attributes.

4. Later the same file calls wp_print_inline_script_tag( ob_get_clean(),
$script_attributes ) (lines 37-38). Because the consent key was never added to
$script_attributes, the produced inline <script> will be missing the data-cookieconsent
attribute and PHP logs a notice. This is a real execution path entirely inside this
template file (no external callers must be inferred beyond the template including
mechanism).
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/view/frontend/scripts/common/google-tag-manager-js.php
**Line:** 13:14
**Comment:**
	*Null Pointer: Undefined variable bug: the code assigns the cookie-consent attribute into `$attributes`, but the attributes array is named `$script_attributes`, so `$attributes` is undefined and the consent attribute is never added; also sanitize the consent value before output to avoid injecting untrusted data into HTML attributes.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

Copy link
Author

Choose a reason for hiding this comment

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

The undefined variable bug has been fixed but sanitization would be out of scope and change the current functionality. Also note that core apis sanitize the value with esc_attr() as mentioned in another comment.

Choose a reason for hiding this comment

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

Question:

The undefined variable bug has been fixed but sanitization would be out of scope and change the current functionality. Also note that core apis sanitize the value with esc_attr() as mentioned in another comment.

Answer:

To reduce “sanitize/escape this” recommendations in cases like this (where output is already escaped by a core API such as esc_attr() inside wp_print_inline_script_tag(...), and adding sanitization would be out-of-scope / change behavior), guide the /improve tool with pr_code_suggestions.extra_instructions.

Add to your .pr_agent.toml (repo / wiki / global):

[pr_code_suggestions]
extra_instructions = """\
- Avoid suggesting additional sanitization/escaping for HTML attributes if the value is already escaped by the sink (e.g., WordPress core escapes attributes via esc_attr() / APIs like wp_print_inline_script_tag()).
- Do not propose “security hardening” changes that would alter current behavior or are out-of-scope for the PR (e.g., adding sanitize_text_field() when the variable may be bool|string or when sanitization policy is handled elsewhere).
- Prioritize functional correctness and concrete runtime bugs (e.g., undefined variable / missing attribute) over speculative security suggestions.
"""

Note: this won’t eliminate all false-positive security suggestions (LLM limitation), but it typically reduces them and makes the model align better with your project’s escaping/sanitization policy.

Relevant Sources:

@codeant-ai
Copy link

codeant-ai bot commented Jan 20, 2026

CodeAnt AI finished reviewing your PR.

Comment on lines +41 to +43
} elseif ( ! empty( $tag_attr ) ) {
$script_attributes[ $tag_attr ] = true;
}
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 line sets $script_attributes[ $tag_attr ] = true; but $tag_attr in the original code was a prebuilt attribute string (e.g. 'type="text/plain" data-cookieconsent="..."'). Using it as an array key will produce an invalid attribute and break output. Restore the original behavior by either: (a) parsing $tag_attr into key/value pairs and merging into $script_attributes, or (b) when $cookie_blocking_mode !== 'auto' output the raw $tag_attr string instead of using wp_print_script_tag. Example actionable fix: parse attributes with preg_match_all('/([\w:-]+)(?:\s*=\s*"([^"]*)")?/', $tag_attr, $m) and set $script_attributes[$m[1][$i]] = $m[2][$i] for each match before calling wp_print_script_tag.

if ( $cookie_blocking_mode === 'auto' ) {
	$script_attributes['data-blockingmode'] = 'auto';
} elseif ( ! empty( $tag_attr ) ) {
	// Preserve legacy behavior for non-auto mode by parsing composite attributes.
	if ( preg_match_all( '/([\w:-]+)(?:\s*=\s*"([^"]*)")?/', $tag_attr, $matches, PREG_SET_ORDER ) ) {
		foreach ( $matches as $match ) {
			$attr_name  = $match[1];
			$attr_value = isset( $match[2] ) ? $match[2] : true;
			$script_attributes[ $attr_name ] = $attr_value;
		}
	}
}

wp_print_script_tag( $script_attributes );

Copy link
Author

Choose a reason for hiding this comment

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

This is wrong. The tag_attr comes from the database option. which can be empty, defer or async.

wp_print_script_tag() handles booleans correctly: https://github.com/WordPress/wordpress-develop/blob/6.9/src/wp-includes/script-loader.php#L2884-L2888

Choose a reason for hiding this comment

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

Question:

This is wrong. The tag_attr comes from the database option. which can be empty, defer or async. wp_print_script_tag() handles booleans correctly: https://github.com/WordPress/wordpress-develop/blob/6.9/src/wp-includes/script-loader.php#L2884-L2888

Answer:

This looks like a classic “wrong assumption about variable provenance” issue: the suggestion assumed $tag_attr is a composite attribute string, but in your codebase it can be "", "defer" or "async", and wp_print_script_tag() handles boolean attributes correctly.

This can be reduced via configuration by tightening the /improve prompt so it:

  • verifies where variables come from (diff + surrounding context) before claiming a “critical bug”
  • avoids proposing broad parsing logic unless the diff clearly shows $tag_attr is a composite string
  • respects WP’s boolean-attribute behavior (async, defer)

Add a repo-level instruction like:

[pr_code_suggestions]
extra_instructions = """\
When reviewing PHP/WordPress code:
- Do NOT assume a variable contains a composite HTML-attribute string unless it is explicitly constructed/validated in the changed code.
- Before flagging a CRITICAL_BUG, confirm the variable’s source (e.g., options/DB values) and allowed values from nearby code/context.
- For wp_print_script_tag()/script attributes, treat 'async' and 'defer' as valid boolean attributes; don't recommend parsing them into key/value pairs unless required by the diff.
- Prefer asking for clarification over asserting breakage when the variable type/value-set is ambiguous.
"""

If you want this to apply only to /improve (and not /review), keep it under [pr_code_suggestions] as above. If you can share which tool produced the original comment (likely /improve) and which platform you’re using (GitHub/GitLab/etc.), we can tailor the wording to reduce this specific false-positive pattern even further.

Relevant Sources:

Comment on lines +11 to +41
$script_attributes = array(
'type' => 'text/javascript',
'id' => 'Cookiebot',
'src' => 'https://consent.cookiebot.com/uc.js',
'data-implementation' => 'wp',
'data-cbid' => $cbid,
);

if ( $tcf ) {
$script_attributes['data-framework'] = $tcf;
}

if ( $data_regions ) {
$georegions = array();
foreach ( $data_regions as $cbid => $regions ) {
$georegions[] = '{\'region\':\'' . esc_attr( $regions ) . '\',\'cbid\':\'' . esc_attr( $cbid ) . '\'}';
}
?>
<?php if ( (bool) get_option( 'cookiebot-gtm' ) !== false && ! empty( get_option( 'cookiebot-data-layer' ) ) ) : ?>
data-layer-name="<?php echo esc_attr( get_option( 'cookiebot-data-layer' ) ); ?>"
<?php endif; ?>
<?php if ( ! empty( $lang ) ) : ?>
data-culture="<?php echo esc_attr( strtoupper( $lang ) ); ?>"
<?php endif; ?>
<?php if ( $cookie_blocking_mode === 'auto' ) : ?>
data-blockingmode="auto"
<?php else : ?>
<?php echo esc_attr( $tag_attr ); ?>
<?php endif; ?>
></script>
$script_attributes['data-georegions'] = implode( ',', $georegions );
}

if ( (bool) get_option( 'cookiebot-gtm' ) !== false && ! empty( get_option( 'cookiebot-data-layer' ) ) ) {
$script_attributes['data-layer-name'] = get_option( 'cookiebot-data-layer' );
}

if ( ! empty( $lang ) ) {
$script_attributes['data-culture'] = strtoupper( $lang );
}

if ( $cookie_blocking_mode === 'auto' ) {
$script_attributes['data-blockingmode'] = 'auto';
} elseif ( ! empty( $tag_attr ) ) {
Copy link

Choose a reason for hiding this comment

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

[VALIDATION] User-supplied or option-derived values are assigned to attributes without explicit escaping (e.g. 'data-cbid', 'data-framework', 'data-layer-name', 'data-culture'). Ensure values are sanitized/escaped before insertion (use esc_attr() or cast to string) or validate them. For example: $script_attributes['data-cbid'] = esc_attr( (string) $cbid ); and $script_attributes['data-layer-name'] = esc_attr( (string) get_option('cookiebot-data-layer') ); This prevents injection or malformed attributes when rendering via wp_print_script_tag.

$script_attributes = array(
	'type'                  => 'text/javascript',
	'id'                    => 'Cookiebot',
	'src'                   => 'https://consent.cookiebot.com/uc.js',
	'data-implementation'   => 'wp',
	'data-cbid'             => esc_attr( (string) $cbid ),
);

if ( $tcf ) {
	$script_attributes['data-framework'] = esc_attr( (string) $tcf );
}

if ( $data_regions ) {
	$georegions = array();
	foreach ( $data_regions as $region_cbid => $regions ) {
		$georegions[] = '{\'region\':\'' . esc_attr( $regions ) . '\',\'cbid\':\'' . esc_attr( $region_cbid ) . '\'}';
	}
	$script_attributes['data-georegions'] = implode( ',', $georegions );
}

$data_layer = get_option( 'cookiebot-data-layer' );

if ( (bool) get_option( 'cookiebot-gtm' ) !== false && ! empty( $data_layer ) ) {
	$script_attributes['data-layer-name'] = esc_attr( (string) $data_layer );
}

if ( ! empty( $lang ) ) {
	$script_attributes['data-culture'] = esc_attr( strtoupper( $lang ) );
}

Comment on lines +25 to +26
foreach ( $data_regions as $cbid => $regions ) {
$georegions[] = '{\'region\':\'' . esc_attr( $regions ) . '\',\'cbid\':\'' . esc_attr( $cbid ) . '\'}';
Copy link

Choose a reason for hiding this comment

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

[REFACTORING] The foreach uses foreach ( $data_regions as $cbid => $regions ) which re-uses the outer $cbid variable name (the Cookiebot ID). Rename the loop key (e.g. $region_cbid) to avoid variable shadowing and confusion: foreach ( $data_regions as $region_cbid => $regions ) { ... }. This improves readability and avoids accidental bugs if the variable usage order changes.

if ( $data_regions ) {
	$georegions = array();
	foreach ( $data_regions as $region_cbid => $regions ) {
		$georegions[] = '{\'region\':\'' . esc_attr( $regions ) . '\',\'cbid\':\'' . esc_attr( $region_cbid ) . '\'}';
	}
	$script_attributes['data-georegions'] = implode( ',', $georegions );
}

Copy link
Author

Choose a reason for hiding this comment

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

While true this case was present before this patch.

Choose a reason for hiding this comment

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

Question:

While true this case was present before this patch.

Answer:

To reduce “this existed before the patch” style refactoring suggestions, configure /improve to focus only on issues introduced by the PR diff and avoid pure readability/naming nits unless they are required for correctness.

Add this to your .pr_agent.toml (or wiki config):

[pr_code_suggestions]
extra_instructions = """\
Only suggest changes that are directly caused by, introduced by, or modified in this PR.
Do not suggest refactors/cleanups for pre-existing code issues unless they create a real bug/security issue in the PR diff.
Prefer correctness/safety issues over readability or naming suggestions.
"""

# Optional: further reduce style-level suggestions
focus_only_on_problems = true
# Optional: filter out low-importance suggestions (tune as needed)
suggestions_score_threshold = 7

If you still want occasional refactoring suggestions, keep the threshold lower (e.g., 5–6) and remove/soften the “prefer correctness” line.

Relevant Sources:

Comment on lines +12 to +14
if ($consent_attribute) {
$script_attributes['data-cookieconsent'] = $consent_attribute;
}
Copy link

Choose a reason for hiding this comment

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

[VALIDATION] When assigning $consent_attribute into the script attributes use escaping/validation: $script_attributes['data-cookieconsent'] = esc_attr( (string) $consent_attribute ); This prevents malformed attribute values from being output in the inline script tag.

$script_attributes = array(
    'type' => $script_type,
);

if ( $consent_attribute ) {
    $script_attributes['data-cookieconsent'] = esc_attr( (string) $consent_attribute );
}

ob_start();
?>
<script>
	window.<?php echo esc_js( $data_layer ); ?> = window.<?php echo esc_js( $data_layer ); ?> || [];
	// ... rest of script body ...
</script>
<?php
wp_print_inline_script_tag( ob_get_clean(), $script_attributes );

Comment on lines 16 to 42
ob_start();
?>
<script type="<?php echo esc_attr( $script_type ); ?>"<?php echo ! $consent_attribute ? '' : ' data-cookieconsent="' . esc_attr( $consent_attribute ) . '"'; ?>>
window.<?php echo esc_js( $data_layer ); ?> = window.<?php echo esc_js( $data_layer ); ?> || [];

function gtag() {
<?php echo esc_js( $data_layer ); ?>.push(arguments);
}

gtag("consent", "default", {
ad_personalization: "denied",
ad_storage: "denied",
ad_user_data: "denied",
analytics_storage: "denied",
functionality_storage: "denied",
personalization_storage: "denied",
security_storage: "granted",
wait_for_update: 500,
});
gtag("set", "ads_data_redaction", true);
<?php
if ( $url_passthrough ) {
echo /** @lang JavaScript */
'gtag("set", "url_passthrough", true);' . PHP_EOL;
}
?>
</script>

<?php wp_print_inline_script_tag( ob_get_clean(), $script_attributes ); ?>
Copy link

Choose a reason for hiding this comment

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

[REFACTORING] You're using ob_start() / ob_get_clean() together with wp_print_inline_script_tag — that's fine, but this relies on WP functions introduced in newer WP versions. Add a fallback for older WP installs (if plugin supports those): if wp_print_inline_script_tag doesn't exist, fallback to echoing a <script> tag with proper escaping. This prevents fatal errors on sites running older WordPress versions.

$script_attributes = array(
    'type' => $script_type,
);

if ( $consent_attribute ) {
    $script_attributes['data-cookieconsent'] = esc_attr( (string) $consent_attribute );
}

ob_start();
?>
<script>
	window.<?php echo esc_js( $data_layer ); ?> = window.<?php echo esc_js( $data_layer ); ?> || [];
	// ... rest of script body ...
</script>
<?php
$script = ob_get_clean();

if ( function_exists( 'wp_print_inline_script_tag' ) ) {
    wp_print_inline_script_tag( $script, $script_attributes );
} else {
    // Fallback for older WP versions.
    $type_attr = isset( $script_attributes['type'] ) ? esc_attr( $script_attributes['type'] ) : 'text/javascript';
    unset( $script_attributes['type'] );

    $attrs = '';
    foreach ( $script_attributes as $key => $value ) {
        $attrs .= ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"';
    }

    echo '<script type="' . $type_attr . '"' . $attrs . '>' . $script . '</script>';
}

Copy link
Author

@oxyc oxyc Jan 20, 2026

Choose a reason for hiding this comment

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

Noting that wp_print_inline_script_tag was added in v5.7.0 and readme.txt does mention the plugin currently supports v4.4

Choose a reason for hiding this comment

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

Code Implementation 🛠️

Implementation: Add a WordPress-version fallback for inline script printing by wrapping the existing buffered JS output with a conditional that uses wp_print_inline_script_tag() when available (WP >= 5.7) and otherwise echoes a properly attributed <script> tag. Also ensure the optional data-cookieconsent attribute value is escaped.

Suggested change
$script_attributes = array(
'type' => $script_type,
);
if ( $consent_attribute ) {
$script_attributes['data-cookieconsent'] = esc_attr( (string) $consent_attribute );
}
ob_start();
?>
window.<?php echo esc_js( $data_layer ); ?> = window.<?php echo esc_js( $data_layer ); ?> || [];
function gtag() {
<?php echo esc_js( $data_layer ); ?>.push(arguments);
}
gtag("consent", "default", {
ad_personalization: "denied",
ad_storage: "denied",
ad_user_data: "denied",
analytics_storage: "denied",
functionality_storage: "denied",
personalization_storage: "denied",
security_storage: "granted",
wait_for_update: 500,
});
gtag("set", "ads_data_redaction", true);
<?php
if ( $url_passthrough ) {
echo /** @lang JavaScript */
'gtag("set", "url_passthrough", true);' . PHP_EOL;
}
?>
<?php
$inline_script = ob_get_clean();
if ( function_exists( 'wp_print_inline_script_tag' ) ) {
wp_print_inline_script_tag( $inline_script, $script_attributes );
} else {
// Fallback for older WordPress versions (< 5.7).
$attrs = '';
foreach ( $script_attributes as $key => $value ) {
if ( null === $value || '' === $value ) {
continue;
}
$attrs .= ' ' . esc_attr( $key ) . '="' . esc_attr( (string) $value ) . '"';
}
echo '<script' . $attrs . '>' . $inline_script . '</script>';
}

See review comment here

@pantoaibot
Copy link

pantoaibot bot commented Jan 20, 2026

Reviewed up to commit:29ee06e8fd5bb519f61d7347d0aa5146aab5f895

Additional Suggestion
src/view/frontend/scripts/cb_frame/cookiebot-js.php, line:31-33 get_option('cookiebot-data-layer') is called twice in the condition. Cache the option in a local variable to avoid duplicate option lookups and make the condition clearer, e.g. $data_layer = get_option('cookiebot-data-layer'); if ( (bool) get_option('cookiebot-gtm') !== false && ! empty( $data_layer ) ) { $script_attributes['data-layer-name'] = esc_attr( $data_layer ); }
$data_layer = get_option( 'cookiebot-data-layer' );

if ( (bool) get_option( 'cookiebot-gtm' ) !== false && ! empty( $data_layer ) ) {
	$script_attributes['data-layer-name'] = esc_attr( (string) $data_layer );
}

Reviewed by Panto AI

@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Review effort 3/5 size:M This PR changes 30-99 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant