• I noticed that in cfturnstile_force_render() the Turnstile render code is currently output using a raw <script>…</script>block.

    add_action("cfturnstile_after_field", "cfturnstile_force_render", 10, 1);
    function cfturnstile_force_render($unique_id = '') {
    if(function_exists('cfturnstile_is_block_based_checkout') && cfturnstile_is_block_based_checkout()) {
    return;
    }
    $unique_id = sanitize_text_field($unique_id);
    $key = sanitize_text_field(get_option('cfturnstile_key'));
    if($unique_id) {
    ?>
    <script>document.addEventListener("DOMContentLoaded", function() { setTimeout(function(){ var e=document.getElementById("cf-turnstile<?php echo esc_html($unique_id); ?>"); e&&!e.innerHTML.trim()&&(turnstile.remove("#cf-turnstile<?php echo esc_html($unique_id); ?>"), turnstile.render("#cf-turnstile<?php echo esc_html($unique_id); ?>", {sitekey:"<?php echo esc_html($key); ?>"})); }, 0); });</script>
    <?php
    }
    }

    Would you consider switching that to wp_add_inline_script() tied to a registered/enqueued handle?

    This approach has a few advantages:

    1. Better compatibility with Content Security Policy – inline scripts tied to a handle are easier to identify, hash, or nonce in a CSP-aware environment.
    2. Hook-safe placement – WordPress handles script placement in the head or footer without echoing directly in the middle of output.
    3. Dependency management – you can attach it to a handle that depends on any scripts it needs, ensuring the code runs after them.
    4. Cleaner output – no mixing of PHP and raw HTML script tags, which improves maintainability.

    For example:

    wp_register_script( 'cfturnstile-inline', false, [], null, true );
    wp_enqueue_script( 'cfturnstile-inline' );

    $code = 'document.addEventListener("DOMContentLoaded",function(){ … });';
    wp_add_inline_script( 'cfturnstile-inline', $code, 'after' );

    Functionally it behaves the same, but it makes the snippet part of the normal WordPress script queue.

    Thanks for considering this — it would make it much easier for CSP-hardened installs to use the plugin without having to special-case the inline block.

    I have a filter so that every time an inline script is added via wp_add_inline_script(), I am able to tag the script with ‘data-owner=”my-tag and data-cspid=random session token’. Then in my mu-plugin which generates hashes for inline scripts, I am easily able to pick out all the safe inline scripts by looking for that tag.

    What I need to do now is something like this when I look at the inline scripts in the final html:

    // Allow: your tagged inlines, Rocket placeholders/loader, Rocket IE snippet.
    $tok = preg_quote( DBTN_CSP_TOKEN, '#' );
    $is_ours = preg_match( '#\bdata-owner=["\']dbtn["\']#i', $attrs )
    && preg_match( '#\bdata-cspid=["\']' . $tok . '["\']#i', $attrs );
    $is_rocket_lazy = stripos( $attrs, 'rocketlazyloadscript' ) !== false;
    $is_rocket_loader = stripos( $body, 'RocketLazyLoadScripts' ) !== false;
    $is_rocket_ie = strpos( $body, 'nowprocket=1' ) !== false
    && strpos( $body, 'navigator.userAgent.match(/MSIE' ) !== false;

    $sitekey = '0xmysitekey';

    $looks_like_turnstile = (
    // uses Turnstile API
    preg_match('#\bturnstile\.(?:render|remove)\s*\(#', $body)
    // targets your known element
    && strpos($body, 'cf-turnstile-woo-checkout') !== false
    // carries your exact sitekey
    && preg_match('#"sitekey"\s*:\s*"' . preg_quote($sitekey, '#') . '"#', $body)
    // no obviously dangerous tokens
    && !preg_match('#\b(?:eval|Function|innerHTML\s*=|document\.write)\b#', $body)
    );

    if ( ! ( $is_ours || $is_rocket_lazy || $is_rocket_loader || $is_rocket_ie || $looks_like_turnstile ) ) {
    continue;
    }

    $hashes_a[] = "'sha256-" . base64_encode( hash( 'sha256', $body, true ) ) . "'";

    Thanks for considering this — it would make it much easier for CSP-hardened installs to use the plugin without having to special-case the inline block.

    Best

The topic ‘Using wp_add_inline_script() for inline scripts’ is closed to new replies.