0

eMy wordpress site has a custom post type called tool, which has three custom fields called html, css, and js. This tool post type is displayed on my site with a custom template, and a shortcode that renders the contents of the html, css, and js fields in an iframe. This shortcode is created by a function in my functions.php file, and a simplified version of this function looks like this:

function renderTool() {
return '
<html>

  <head>

    <script

      src="https://kit.fontawesome.com/c48c422dea.js"

      crossorigin="anonymous"

    ></script>

    <style>

      .container {background:blue;}

    </style>

  </head>

  <body>

    <div id="container" class="container">

      <div id="tool" class="tool cssreset">

      <iframe src="data:text/html;charset=utf-8,' . 
  '<html>
    <head>
      <style>' . htmlspecialchars(get_field("css")) . '</style>
      <script>' . htmlspecialchars(get_field("js")) . '</script>
    </head>
    <body>' . htmlspecialchars(get_field("html")) . '</body>
  </html>'
        . '"></iframe>

      </div>

      <div class="bar" id="bar">

         <i class="toggle fa fa-solid fa-expand"></i>

      </div>

    </div>

    <script>

      function myfunction() {

console.log("hello world");

         }

           </script>

  </body>

  </html>';
}

The problem with this code is that when i use the shortcode which runs this function, it only renders the css, and the html and js are rendered in plain text.

I chatted with openai's chatgpt about it and it recommended me to store the iframe contents in a seperate html file and then to set the iframe src to this html file as such:


$css = htmlspecialchars(get_field("css"));

$js = htmlspecialchars(get_field("js"));

$html = htmlspecialchars(get_field("html"));

// Create the inner document and save it to a separate file

$inner_document = '

  <html>

    <head>

      <style>

        '. $css .' 

      </style>

      <script>

        '. $js .' 

      </script>

    </head>

    <body>

      '. $html .' 

    </body>

  </html>

';

file_put_contents('inner-document.html', $inner_document);

// Use the inner document as the src for the iframe

return '<iframe src="inner-document.html"></iframe>';

This soultion i already dislike because it just seems like it is complicating things even more. When i did try to use this code, the iframe instead rendered the page i was on and an equally broken iframe in it. iframeception.

Another thing i tried was passing the html that should be in the iframe inside a urlencode as such: (this is a snippet, this code would replace the iframe in my current approach)

<iframe src="data:text/html;charset=utf-8,' . urlencode(
  '<html>
    <head>
      <style>' . htmlspecialchars(get_field("css")) . '</style>
      <script>' . htmlspecialchars(get_field("js")) . '</script>
    </head>
    <body>' . htmlspecialchars(get_field("html")) . '</body>
  </html>'
        ) . '"></iframe>

This just rendered it as raw text into the correct html elements at least (view https://prnt.sc/4I24tFgxQ56m)

To clarify further, these get_field() functions are from a wordpress plugin called acf, view the documentation here: https://www.advancedcustomfields.com/resources/get_field/

From all of this information i concluded that the error must stem from the string literals not properly being escaped and therefore conflict with/ cause the rest of the values to be returned in string format. Yet i was hoping that the htmlspecialchars was gonna solve it, but it only helped with rendering CSS. In my current solution, I put all the code I want to render inside the iframe's source using a data URL, the iframe closing tag comes after the content of the iframe therefore.

So you can further visualize what i am trying to convey, here is the link to the page that is suffering this bug: https://tropical.team/tools/calculator. It is posible i still have some testing code on here so you will see something a bit diffrent than i described but i will try to change it to this when i get back.

I chatted with chatGPT for over 3 hours yesterday but concluded that when i realized that it started going in circles. I doubt there is a replacement for the get_field function, or an argument that would help with this, but if there is do tell me, i also have yet to integrate validation for those html, css, and js fields, so for now, just expect proper code being passed.

treepek
  • 37
  • 6
  • In your original code, there's an `echo` inside the string you're returning. Also, the opening tag of your `iframe` is not properly set - it's outside of the string you're returning. – FiddlingAway Jan 01 '23 at 15:08
  • @FiddlingAway those were both just typos as this code is a remade copy of the actual code, these are not causing the issue as these bugs don't exist in the actual code, i have fixed this now – treepek Jan 01 '23 at 15:56
  • So the problem is resolved? I visited your link, and the calculator renders properly (there's a JS error in the console, though, but I'm guessing you're working on it at this time). – FiddlingAway Jan 01 '23 at 16:02
  • The html gets rendered but the css and js do not, inspect the tool element and view its contents, you can see that some code is rendered as text, the calculated is considered rendered properly when it has a yellow background and works, so now its only 1/3 – treepek Jan 01 '23 at 16:15
  • Your `iframe` opening tag is still unclosed. You're missing `>` after declaring the charset. Also, instead of this `htmlspecialchars(...)` go with this `htmlspecialchars_decode(...)`, if the HTML content stored in your DB has gone through `htmlspecialchars` before being inserted into the DB. – FiddlingAway Jan 01 '23 at 16:22
  • It is closed, look again, but im not actually putting all the code inside the iframe, I'm putting it inside the src value as a data URL because putting it inside the iframe (between the start and closing tags) doesn't do anything. – treepek Jan 01 '23 at 18:22
  • Right, I missed that part about HTML being inside the `src`, sorry about that. Since that's the case, how about using `urlencode(...)`? You need this because you're setting it as the content of the `src`. Please see [this image](https://i.imgur.com/WAQ3xhq.png) to see what I mean. The `urlencode`d version of HTML is also used in [this SO answer](https://stackoverflow.com/a/6102829/6133426). – FiddlingAway Jan 01 '23 at 18:35
  • I tried that but then it gets broken aswell: https://prnt.sc/4I24tFgxQ56m – treepek Jan 02 '23 at 11:28
  • Not sure why that is happening. Let's abandon that route for the moment, try one more thing, and then I'll give up. While looking at your generated code, I noticed that the `script` element inside the `iframe` is using the quotes for its `src` attribute. I think this might be breaking your code - please see [this image](https://i.imgur.com/Iwe5HGI.png) in case if it sounds like I'm not making any sense. Solving this would only be a temporary solution, as it would work only in this case - `urlencode` should have worked originally, and I honestly don't know why it didn't. – FiddlingAway Jan 02 '23 at 11:52
  • I have jsut fixed that part, it was because my caching plugin was making my life hell as usual, I excluded js file modifications for these pages and now the javascript is properly added to the header. The calculator still doesn't work and I'm currently attempting to find out if that is an error with the PHP or just the actual calculator code – treepek Jan 02 '23 at 11:54
  • Nice, glad you solved that part. After posting my previous comment, I realized what the issue with `urlencode` was - the spaces are encoded as `+`, and that's why you were seeing them all over the place. What I should've recommended is `rawurlencode` - this would encode spaces as `%20`. Regarding script execution - please take a look at [this SO question and related answers](https://stackoverflow.com/questions/4619668/). – FiddlingAway Jan 02 '23 at 12:02
  • I just attempted to use rawurlencode and its better but still broken, view https://tropical.team/tool/calculator-2-0/ and https://prnt.sc/tynlQTNv9JAh – treepek Jan 02 '23 at 12:19
  • The rendered body of your iframe seems to contain an HTML document within, with content that looks like it's been through `htmlcharacters()`. Can you check your background code - specifically this: `get_field("html")`, and make sure it doesn't contain anything like `.......`? – FiddlingAway Jan 02 '23 at 12:35
  • it does, every time it will, i guess using srcdoc would be more appropiate – treepek Jan 02 '23 at 12:39
  • @FiddlingAway any idea how i could tackle the fact that the js doesn't work, I'm guessing it's because the document.somrthing references don't apply to iframes, but is that also the case with srcdoc? – treepek Jan 02 '23 at 18:04
  • Because it's script injection - [try this](https://stackoverflow.com/questions/4619668/). – FiddlingAway Jan 03 '23 at 09:26

1 Answers1

0

Instead of passing the html as a data url, a better approach is to use the srcdoc attribute of the iframe. This allows you to pass a string (the html) to it and render it properly. You will have to keep the htmlspecialchars function because otherwise the double quotes that may be returned by get_field as it is user submitted would end the srcdoc value string.

NodeX4
  • 150
  • 1
  • 10