Forum Replies Created

Viewing 7 replies - 1 through 7 (of 7 total)
  • Thread Starter Kostiantyn Petlia

    (@kostiantynpetlia)

    Thank you for fast implementation of the changes.

    Thread Starter Kostiantyn Petlia

    (@kostiantynpetlia)

    First of all, thank you for your fast support, Pascal.
    I’m going to test the site well, but currently, everything works well.

    I think the next information can be useful for other developers.

    Conflict with the web stories PHP buffer
    The main problem with the PHP buffer was that the web stories’s buffer was opened after my (‘init, 0’) and it is closed by WordPress in the next line:
    /wordpress/wp-includes/default-filters.php line 412: add_action( 'shutdown', 'wp_ob_end_flush_all', 1 );
    As a result, any priority of ‘shutdown’ higher than 1 didn’t work for my closing method. At that moment all buffers are flushed already. Any priority of ‘shutdown’ less than 1 breaks web stories, because my code closes the web stories buffer (not mine). So, I decided to use your approach and set a callback function for my buffering:
    ob_start([self::class, 'preparePageBufferToOutput']);
    On ‘shutdown, 1’ WordPress closes all open buffers: the web stories buffer and mine. No conflicts!

    CSP header, CSP meta, and scripts’s nonces are present, but web stories still throw a CSP blocking error
    It was extraordinary. Everything was present and set well, but there were two CSP errors in the Chrome Console. Despite this, it seemed web stories worked well… Anyway, I figured out where was a problem:
    <link rel="preload" as="script" >
    When you add the CSP nonce to this link those errors disappear. (Nonce to a link tag? Why? It’s still a mystery to me ?? ) So, here is a modified version of your example:

    if (interface_exists(Transformer::class)) {
    class WebStoryNonceTransformer implements Transformer
    {
    /**
    * Applies nonce addition to scripts and preload links in the provided DOM document.
    *
    * @param Document $document DOM document to apply the transformations to.
    * @param ErrorCollection $errors Collection of errors that are collected during transformation.
    *
    * @return void
    */
    public function transform(Document $document, ErrorCollection $errors): void
    {
    $nonce = PolicyBuilder::getTheNonce();
    $xpath = $document->xpath;

    $query = '//script[not(@type) or @type="module"] | '
    . '//link[@rel="preload" and @as="script"] | '
    . '//link[@rel="modulepreload"]';
    $elements = $xpath->query($query);

    if ($elements instanceof DOMNodeList) {
    foreach ($elements as $element) {
    /** @var DOMElement $element */
    $element->setAttribute('nonce', $nonce);
    }
    }
    }
    }
    }

    I hope I won’t have to bother you again.

    Thread Starter Kostiantyn Petlia

    (@kostiantynpetlia)

    Yeap, our CSP plugin breaks Web Stories. When I disable it web stories work. But I need to understand where there is a conflict and why it is. It’s not so complicated plugin with the page buffering from ‘init, 0’ to ‘shutdown, 0’ and changing HTTP headers right before the buffered page outputting.
    Do web stories do some work after the ‘shutdown, 0’ hook? According to not compressed style, it looks possible… The order of execution that I can see:

    • ‘init, 0’: ob_start();
    • your transform() code;
    • ‘shutdown, 0’: $page = ob_get_clean(); … echo $page;

    I’m going to dive deeper into it and happy with any suggestions.

    Thread Starter Kostiantyn Petlia

    (@kostiantynpetlia)

    Here is the shortcode:

    [web_stories title=”true” excerpt=”false” author=”false” date=”false” archive_link=”true” archive_link_label=”View all” circle_size=”150″ sharp_corners=”false” image_alignment=”left” number_of_columns=”1″ number_of_stories=”5″ order=”DESC” orderby=”post_date” view=”carousel” /]

    The shortcode outputs web stories on the https://localhost:8000/insights/ blog page. On this page I can see 4 CSP errors. All them have the same link on the first story in the carousel:
    https://localhost:8000/web-stories/all-you-need-to-knowabout-iomt/#visibilityState=prerender&origin=http%3A%2F%2Flocalhost%3A8000&showStoryUrlInfo=0&storyPlayer=v0&cap=swipe
    Scripts: https://cdn.ampproject.org/v0.js & https://cdn.ampproject.org/v0/amp-story-1.0.js.
    It seems the same two CSP errors were triggered twice: 2+2 = 4 errors.

    When I click (or open in a new tab) on the first story on the carousel I see the color-filled screen and the story excerpt super small text and the same 4 CSP errors, but with URL: https://localhost:8000/web-stories/all-you-need-to-knowabout-iomt/.

    So, the issue is the same for the blog page and the single story page. But the blog page looks normal at least.

    Let me provide a bit more details about the CSP implementation.
    How it currently works:

    • we don’t use CSP on the server level (.htaccess);
    • we add our CSP on the WP/PHP level based on standard WP functions and filters for JS scripts;
    • we use the whole PHP page buffering and build the dynamic SCP (start on the ‘init’ hook), calculate hashes and add the nonce to scripts, update HTTP headers with the CSP, add <meta> with the CSP to <head>, and output the page on the ‘shutdown’ WP action (right after the mentioned our sendHeaders() method that updates headers with the CSP);

    The error message example:
    “Refused to load the script https://cdn.ampproject.org/v0.js because it violates the following Content Security Policy directive: “script-src ‘self’ ‘strict-dynamic’ {domains} ‘nonce-2295cb6ff3′”. Note that ‘strict-dynamic’ is present, so host-based allowlisting is disabled. Note that ‘script-src-elem’ was not explicitly set, so ‘script-src’ is used as a fallback.”

    Important:
    When I use a dirty fix with str_replace() before buffer outputting on the $page everything works well.
    I’m logged and the WP-Rocket cache doesn’t work. I don’t remember that we changed Web Stories functionality somehow; only styles.

    Debug:
    $document in your method has: <$cript async="" src="https://cdn.ampproject.org/v0.mjs" type="module" crossorigin="anonymous" nonce="2295cb6ff3">
    The buffered $page on the echo moment has: <$cript async="" src="https://cdn.ampproject.org/v0.js">

    Thread Starter Kostiantyn Petlia

    (@kostiantynpetlia)

    Could you help me a bit more?

    I’ve implemented this transformer functionality. Firstly I changed it in a more OOP way for my plugin structure, but later I just put your code “as is” in functions.php for debug purposes.

    On the blog page (https://localhost:8000/insights/) I have a Web Stories shortcode and can see 4 stories. It’s the div.web-stories-list. I see CSP errors they referrer to https://localhost:8000/web-stories/all-you-need-to-knowabout-iomt/. It’s iframe.story-player-iframe with src:
    https://localhost:8000/web-stories/all-you-need-to-knowabout-iomt/#visibilityState=prerender&origin=http%3A%2F%2Flocalhost%3A8000&showStoryUrlInfo=0&storyPlayer=v0&cap=swipe.

    The issue: Your code works, $document is updated, but an iframe story page is pretty different. It’s strange…

    1. During debugging, I can see the updated scripts with the nonce (I used $updatedHtml = $document->saveHTML();) in our transform() method. So, your code applies the nonce to scripts in $document.
    2. But on the frontend (the same on the outputting of the page buffer self::sendHeaders($headers); echo $page;) I can see a partially different HTML page: scripts are without the nonce; style tag with the amp-custom attribute is NOT compressed; my <meta> CSP is present (I insert it before page outputting), but it is absent in the #1, etc.

    Could you explain it and advise me on how to fix it? It seems I don’t understand well how web stories are rendered.

    Thread Starter Kostiantyn Petlia

    (@kostiantynpetlia)

    Thank you for fast answer and the code example. I’m going to implement it and back with feedback.

    Thread Starter Kostiantyn Petlia

    (@kostiantynpetlia)

    Update

    Emails are not sent.
    The global $phpmailer; returns null in wp_mail() and creates a new instance $phpmailer = new PHPMailer\PHPMailer\PHPMailer( true );

    In DB in the wp_option table, I can see mailjet_apikey & mailjet_apisecret. The MailjetApi::isValidAPICredentials() returns true during debugging.

    • This reply was modified 1 year, 2 months ago by Kostiantyn Petlia. Reason: Adding a bit more info
Viewing 7 replies - 1 through 7 (of 7 total)