• Resolved patternmb

    (@patternmb)


    Hello!

    This is a great plugin, however I’ve unfortunately found some concerning major issues when updating from version 3.3.7 to 3.3.9.

    When updating posts with JSON stored in the post_content column escaping stashes are removed. For example I am using a plugin called TablePress which stores content that may look like this:

    [["test cell 1","test cell 2"],["test cell 3","<a href=\"/uploads/2020/02/example.png\">Link</a>"]]

    After doing a replace and update all links the data looks like this:

    [["test cell 1","test cell 2"],["test cell 3","<a href="/uploads/2020/02/new-example.png">Link</a>"]]

    Where the back slashes for the href quotes are removed.

    The problem lies in classes/replacer.php.

    First the function isJSON (line 567) will ALWAYS return false. This is what it looks like:

    private function isJSON($content)
      {
        if (is_array($content) || is_object($content))
          return false; // can never be.
    
        $json = json_decode($content);
        $json = is_string($content) && json_decode($content);
    
        return $json && $content != $json;
     }

    So the $json variable is being overwritten as a boolean and will always be not equal to $content. I think it should look like this:

      private function isJSON($content)
      {
        if (is_array($content) || is_object($content))
          return false; // can never be.
    
        $json = json_decode($content);
        $isjson = is_string($content) && json_decode($content);
    
        return $isjson && $content != $json;
      }

    Next in the replaceContent function (line 503) at the bottom there is a check that does $content = json_encode($content);. However since every if statement before it has a return it will never reach that part of the function if the content is JSON.

    I think this:

    private function replaceContent($content, $search, $replace)
      {
        //$is_serial = false;
        $content = maybe_unserialize($content);
        $isJson = $this->isJSON($content);
    
        if ($isJson)
        {
          $content = json_decode($content);
        }
    
        if (is_string($content))  // let's check the normal one first.
        {
          return str_replace($search, $replace, $content);
        }
        elseif (is_wp_error($content)) // seen this.
        {
           return $content;  // do nothing.
        }
        elseif (is_array($content) ) // array metadata and such.
        {
          foreach($content as $index => $value)
          {
            $content[$index] = $this->replaceContent($value, $search, $replace); //str_replace($value, $search, $replace);
          }
          return $content;
        }
        elseif(is_object($content)) // metadata objects, they exist.
        {
          foreach($content as $key => $value)
          {
            $content->{$key} = $this->replaceContent($value, $search, $replace); //str_replace($value, $search, $replace);
          }
          return $content;
        }
    
        if ($isJson) // convert back to JSON, if this was JSON. Different than serialize which does WP automatically.
        {
          Log::addDebug('Value was found to be JSON, encoding');
          $content = json_encode($content);
        }
    
        return $content;
    }

    Should look like this:

    private function replaceContent($content, $search, $replace)
      {
        //$is_serial = false;
        $content = maybe_unserialize($content);
        $isJson = $this->isJSON($content);
    
        if ($isJson)
        {
          $content = json_decode($content);
        }
    
        if (is_string($content))  // let's check the normal one first.
        {
          $content = str_replace($search, $replace, $content);
        }
        elseif (is_wp_error($content)) // seen this.
        {
          // do nothing.
        }
        elseif (is_array($content) ) // array metadata and such.
        {
          foreach($content as $index => $value)
          {
            $content[$index] = $this->replaceContent($value, $search, $replace); //str_replace($value, $search, $replace);
          }
        }
        elseif(is_object($content)) // metadata objects, they exist.
        {
          foreach($content as $key => $value)
          {
            $content->{$key} = $this->replaceContent($value, $search, $replace); //str_replace($value, $search, $replace);
          }
        }
    
        if ($isJson) // convert back to JSON, if this was JSON. Different than serialize which does WP automatically.
        {
          Log::addDebug('Value was found to be JSON, encoding');
          $content = json_encode($content);
          $content = wp_slash($content);
        }
    
        return $content;
    }

    Finally you removed your custom insert SQL around line 474 and replaced $wpdb->query(...) with wp_update_post(...). It seems that the wp_update_post function uses stripslashes or something of that sort. This brings us back to our main problem of slashes being removed from our JSON content.

    To fix this I added this as a workaround on line 541 in the replaceContent function after the json_encode is called:

    $content = wp_slash($content);

    Note this also happens for other WordPress functions such as update_metadata() if you happen to be using those anywhere – didn’t have time to check myself. (See this WordPress Stackexchange question as reference: WordPress is stripping escape backslashes from JSON strings in post_meta)

    Anyway I am not sure if this is the correct solution or how it may affect other use cases. I doubt I am anywhere as familiar with the plugin as you the Authors are but it seems to have resolved the issue for me.

    I hope this helps and that there is a patch in the next update. Again thanks for the awesome work you’ve put into this plugin ??

    • This topic was modified 4 years, 9 months ago by patternmb. Reason: Used wrong function "wp_update_meta". Changed to "wp_update_post"
Viewing 2 replies - 1 through 2 (of 2 total)
Viewing 2 replies - 1 through 2 (of 2 total)
  • The topic ‘Major flaws breaking JSON data, suggested fix.’ is closed to new replies.