CVE-2022-4647: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Cross-site Scripting (XSS) - Stored in GitHub repository microweber/microweber prior to 1.3.2.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 6.1
Published: 2022-12-22T02:15Z
Repository: https://github.com/microweber/microweber
Language: PHP
Files changed: src/MicroweberPackages/App/Utils/Parser.php, src/MicroweberPackages/App/Utils/lib/phpQuery.php, src/MicroweberPackages/Content/ContentManagerCrud.php, src/MicroweberPackages/Content/ContentManagerHelpers.php, src/MicroweberPackages/Content/tests/ContentApiControllerLiveEditSaveTest.php, src/MicroweberPackages/Database/DatabaseManager.php, src/MicroweberPackages/Database/Utils.php, src/MicroweberPackages/Helper/Format.php, src/MicroweberPackages/Helper/XSSClean.php
Commit date: 2022-11-29 14:22:23 +0200
Commit message: update

Diff: diff --git a/src/MicroweberPackages/App/Utils/Parser.php b/src/MicroweberPackages/App/Utils/Parser.php
index f4219142dca..6e64c490297 100644
--- a/src/MicroweberPackages/App/Utils/Parser.php
+++ b/src/MicroweberPackages/App/Utils/Parser.php
@@ -133,14 +133,18 @@ class Parser
         return $layout;
     }
 
     public function make_tags($layout, $options = array())
     {
-
-        if ($layout == '') {
+         if ($layout == '') {
             return $layout;
         }
+
+        $layout = str_ireplace('{SITE_URL}','___mw-site-url-temp-replace-on-make-tags___', $layout);
+
+
+
         require_once __DIR__ . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'phpQuery.php';
 
         $pq = \phpQuery::newDocument($layout);
 
 
@@ -259,10 +263,14 @@ class Parser
                     }
                 }
             }
         }
 
+        $layout = str_ireplace('___mw-site-url-temp-replace-on-make-tags___','{SITE_URL}', $layout);
+
+
+
         return $layout;
     }
 
     public function modify_html_preg($layout, $preg_match_all, $content = '', $action = 'append')
     {
diff --git a/src/MicroweberPackages/App/Utils/lib/phpQuery.php b/src/MicroweberPackages/App/Utils/lib/phpQuery.php
index 84b76ba16cf..a6cac5e1a77 100644
--- a/src/MicroweberPackages/App/Utils/lib/phpQuery.php
+++ b/src/MicroweberPackages/App/Utils/lib/phpQuery.php
@@ -219,11 +219,18 @@ class DOMDocumentWrapper
         } else {
             $loaded = $this->loadMarkup($markup);
         }
         if ($loaded) {
             //			$this->document->formatOutput = true;
+            $this->document->formatOutput = false;
             $this->document->preserveWhiteSpace = true;
+
+            $this->document->validateOnParse=false;
+            $this->document->strictErrorChecking=false;
+            $this->document->recover=false;
+
+
             $this->xpath = new DOMXPath($this->document);
             $this->afterMarkupLoad();
 
             return true;
             // remember last loaded document
@@ -292,15 +299,25 @@ class DOMDocumentWrapper
     protected function documentCreate($charset, $version = '1.0')
     {
         if (!$version) {
             $version = '1.0';
         }
+
+        libxml_use_internal_errors(true);
+
         $this->document = new DOMDocument($version, $charset);
         $this->charset = $this->document->encoding;
         //		$this->document->encoding = $charset;
-        $this->document->formatOutput = true;
+       // $this->document->formatOutput = true;
+        $this->document->formatOutput = false;
+       // $this->document->standalone = true;
         $this->document->preserveWhiteSpace = true;
+
+
+        $this->document->validateOnParse=false;
+        $this->document->strictErrorChecking=false;
+        $this->document->recover=false;
     }
 
     protected function loadMarkupHTML($markup, $requestedCharset = null)
     {
         if (phpQuery::$debug) {
@@ -376,11 +393,19 @@ class DOMDocumentWrapper
             $this->documentCreate($charset);
             /* 		$return = phpQuery::$debug === 2
               ? $this->document->loadHTML($markup)
               : @$this->document->loadHTML($markup); */
 
-            $return = @$this->document->loadHTML($markup);
+         // $return = @$this->document->loadHTML($markup);
+            $return = @$this->document->loadHTML($markup,
+                LIBXML_SCHEMA_CREATE |
+                LIBXML_HTML_NOIMPLIED |
+                LIBXML_HTML_NODEFDTD |
+                LIBXML_NOERROR |
+                LIBXML_NONET |
+                LIBXML_NOWARNING
+            );
 
             if ($return) {
                 $this->root = $this->document;
             }
         }
@@ -761,10 +786,14 @@ class DOMDocumentWrapper
                 // add FAKE element to set default namespace
                 $fragment->loadMarkupXML('<?xml version="1.0" encoding="'.$charset.'"?>'
                         .'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" '
                         .'"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
                         .'<fake xmlns="http://www.w3.org/1999/xhtml">'.$markup.'</fake>');
+
+/*                $fragment->loadMarkupXML('<?xml version="1.0" encoding="'.$charset.'"?>'*/
+//                       . phpQuery::$defaultDoctype.''
+//                        .'<fake xmlns="http://www.w3.org/1999/xhtml">'.$markup.'</fake>');
                 $fragment->root = $fragment->document->firstChild->nextSibling;
             } else {
                 $fragment->loadMarkupXML('<?xml version="1.0" encoding="'.$charset.'"?><fake>'.$markup.'</fake>');
                 $fragment->root = $fragment->document->firstChild;
             }
@@ -5077,12 +5106,14 @@ abstract class phpQuery
     /**
      * Applies only to HTML.
      *
      * @var unknown_type
      */
-    public static $defaultDoctype = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+    public static $defaultDoctype_old = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd">';
+
+    public static $defaultDoctype = '<!doctype html>';
     public static $defaultCharset = 'UTF-8';
 
     /**
      * Static namespace for plugins.
      *
diff --git a/src/MicroweberPackages/Content/ContentManagerCrud.php b/src/MicroweberPackages/Content/ContentManagerCrud.php
index 116da8075a4..b7499f551d6 100644
--- a/src/MicroweberPackages/Content/ContentManagerCrud.php
+++ b/src/MicroweberPackages/Content/ContentManagerCrud.php
@@ -1,9 +1,10 @@
 <?php
 namespace MicroweberPackages\Content;
 
 use MicroweberPackages\Content\Repositories\ContentRepository;
+use MicroweberPackages\Content\Models\Content;
 use MicroweberPackages\Database\Crud;
 use Illuminate\Support\Facades\DB;
 use function Opis\Closure\serialize as serializeClosure;
 
 class ContentManagerCrud extends Crud
diff --git a/src/MicroweberPackages/Content/ContentManagerHelpers.php b/src/MicroweberPackages/Content/ContentManagerHelpers.php
index 01fb10eddfb..dea893e4eed 100644
--- a/src/MicroweberPackages/Content/ContentManagerHelpers.php
+++ b/src/MicroweberPackages/Content/ContentManagerHelpers.php
@@ -1063,19 +1063,18 @@ class ContentManagerHelpers extends ContentManagerCrud
                         }
                         $html_to_save = $the_field_data['html'];
 
                         $html_to_save = $content =  $this->app->parser->make_tags($html_to_save);
 
-//  AntiXSS makes bug on save convertind comments to htmlentities
-//                        $antixss = new AntiXSS();
-//                        $html_to_save = $content = $antixss->xss_clean($html_to_save);
+                        //\Log::info($html_to_save);
+
 
                         $xssClean = new XSSClean();
                         $html_to_save = $content = $xssClean->clean($html_to_save);
 
 
-
+                      //  \Log::info($html_to_save);
 
                         if ($save_module == false and $save_global == false and $save_layout == false) {
                             if ($content_id) {
 
                                 $for_histroy = get_content_by_id($content_id);
@@ -1133,10 +1132,12 @@ class ContentManagerHelpers extends ContentManagerCrud
                                     } else {
                                         $cont_table_save = array();
                                         $cont_table_save[$field]=$html_to_save;
                                     }
                                 }
+
+
                                 $this->app->event_manager->trigger('mw.content.save_edit', $cont_field);
 
                                 $to_save = array();
                                 $to_save['id'] = $content_id;
                                 if(isset($cont_table_save)  and $cont_table_save){
@@ -1369,10 +1370,11 @@ class ContentManagerHelpers extends ContentManagerCrud
 
         if (isset($data['is_draft']) and $data['is_draft']) {
             $table = $table_drafts;
         }
 
+
         $data = $this->app->format->strip_unsafe($data);
 
         if (isset($data['is_draft']) and $data['is_draft'] and isset($data['url'])) {
             $draft_url = $this->app->database_manager->escape_string($data['url']);
             $last_saved_date = date('Y-m-d H:i:s', strtotime('-1 week'));
@@ -1504,10 +1506,12 @@ class ContentManagerHelpers extends ContentManagerCrud
 
         if ($find and isset($find['id'])) {
             $data['id'] = $find['id'];
         }
 
+
+
         $save = $this->app->database_manager->save($data);
 
         $this->app->cache_manager->delete('content_fields');
 
         return $save;
diff --git a/src/MicroweberPackages/Content/tests/ContentApiControllerLiveEditSaveTest.php b/src/MicroweberPackages/Content/tests/ContentApiControllerLiveEditSaveTest.php
index 24ee83a2746..34afdb1c546 100644
--- a/src/MicroweberPackages/Content/tests/ContentApiControllerLiveEditSaveTest.php
+++ b/src/MicroweberPackages/Content/tests/ContentApiControllerLiveEditSaveTest.php
@@ -1,48 +1,46 @@
 <?php
+
 namespace MicroweberPackages\Content\tests;
 
 use Illuminate\Database\Eloquent\Model;
 use Illuminate\Support\Facades\Auth;
 use MicroweberPackages\Category\Models\Category;
 use MicroweberPackages\Core\tests\TestCase;
+use MicroweberPackages\Helper\XSSClean;
 use MicroweberPackages\Multilanguage\MultilanguageHelpers;
 use MicroweberPackages\Page\Models\Page;
 use MicroweberPackages\User\Models\User;
 
 class ContentApiControllerLiveEditSaveTest extends TestCase
 {
-    public function testSaveContentOnPageLiveEdit()
+    public function testSaveContentOnPageLiveEditSingle()
     {
-        $user = User::where('is_admin', '=', '1')->first();
-        Auth::login($user);
-
-
-        MultilanguageHelpers::setMultilanguageEnabled(0);
+        $this->cleanupAndPrepare();
 
         $unique = uniqid('testSaveContentOnPage');
         $newCleanMlPage = save_content([
             'subtype' => 'static',
             'content_type' => 'page',
             'layout_file' => 'clean.php',
-            'title' => 'pagecontent222'.$unique,
-            'url' => 'pagecontent222'.$unique,
+            'title' => 'pagecontent222' . $unique,
+            'url' => 'pagecontent222' . $unique,
             'preview_layout_file' => 'clean.php',
-            'active_site_template'=> 'new-world',
+            'active_site_template' => 'new-world',
             'is_active' => 1,
         ]);
 
         $findPage = Page::whereId($newCleanMlPage)->first();
 
         $pageId = $findPage->id;
         $this->assertEquals($findPage->id, $newCleanMlPage);
 
         $pageLink = content_link($pageId);
-        $pageLink = '/' . str_replace(site_url(),'', $pageLink);
+        $pageLink = '/' . str_replace(site_url(), '', $pageLink);
 
         $_SERVER['PHP_SELF'] = '/index.php';
-        $_SERVER['REQUEST_URI'] = $pageLink;
+        $_SERVER['REQ`UEST_URI'] = $pageLink;
         $_SERVER['REDIRECT_URL'] = $pageLink;
         $_SERVER['HTTP_REFERER'] = content_link($pageId);
 
 
         // Save on default lang
@@ -50,30 +48,34 @@ class ContentApiControllerLiveEditSaveTest extends TestCase
 
         $contentFieldHtml = <<<HTML
 <div class="feature-icon bg-primary bg-gradient">
 <svg class="bi" width="1em" height="1em"><use xlink:href="#collection"></use></svg>
 </div>
+<div class="feature-icon bg-primary bg-gradient">
 <h2>Featured title</h2>
 <p>Paragraph of text beneath the heading to explain the heading. We'll add onto it with another sentence and probably just keep going until we run out of words.</p>
 <a href="#" class="icon-link">
 Call to action
-</a><a class="mb-2" href=""><i class="mdi mdi-arrow-right"></i></a>
+</a>
+<a class="mb-2" href=""><i class="mdi mdi-arrow-right"></i></a>
+<a class="mb-2" href="https://example.com"><i class="mdi mdi-arrow-up"></i>example link</a>
+<a class="mb-3" target="_blank" href="https://example.com/2"><i class="mdi mdi-arrow-left-bold-box"></i>link 2</a>
+</div>
 HTML;
 
 
         $fieldsData = [
-            'field_data_0'=>[
-                'attributes'=>[
-                    'class'=>'container edit',
-                    'rel'=>'content',
-                    'rel_id'=>$findPage->id,
-                    'field'=>'content',
+            'field_data_0' => [
+                'attributes' => [
+                    'class' => 'container edit',
+                    'rel' => 'content',
+                    'rel_id' => $findPage->id,
+                    'field' => 'content',
                 ],
-                'html'=>$contentFieldHtml
+                'html' => $contentFieldHtml
             ]
         ];
-
         $encoded = base64_encode(json_encode($fieldsData));
 
         $response = $this->call(
             'POST',
             route('api.content.save_edit'),
@@ -90,16 +92,405 @@ HTML;
         $fieldSaved = $response->decodeResponseJson();
 
         $this->assertEquals(trim($fieldSaved[0]['content']), trim($contentFieldHtml));
         $this->assertEquals($fieldSaved[0]['rel_type'], 'content');
         $this->assertEquals($fieldSaved[0]['field'], 'content');
+        $findPage = Page::whereId($findPage->id)->first();
 
-
-        $findPage = Page::whereId($fieldSaved[0]['id'])->first();
         $this->assertTrue(str_contains($findPage->content, 'svg class="bi" width="1em" height="1em"'));
         $this->assertTrue(str_contains($findPage->content, '<h2>Featured title</h2>'));
+        $this->assertTrue(str_contains($findPage->content, '<a class="mb-2" href="https://example.com"><i class="mdi mdi-arrow-up"></i>example link</a>'));
+        $this->assertTrue(str_contains($findPage->content, '<a class="mb-3" target="_blank" href="https://example.com/2"><i class="mdi mdi-arrow-left-bold-box"></i>link 2</a>'));
+
+
+    }
+
+
+    public function testSaveContentOnPageLiveEditFromBootstrapExamples()
+    {
+        $this->cleanupAndPrepare();
+        $unique = uniqid('testSaveContentOnPage');
+        $newCleanMlPage = save_content([
+            'subtype' => 'static',
+            'content_type' => 'page',
+            'layout_file' => 'clean.php',
+            'title' => 'pagecontent-bs-examples-save' . $unique,
+            'preview_layout_file' => 'clean.php',
+            'is_active' => 1,
+        ]);
+
+
+
+        $findPage = Page::whereId($newCleanMlPage)->first();
+
+        $pageId = $findPage->id;
+        $this->assertEquals($findPage->id, $newCleanMlPage);
+
+        $pageLink = content_link($pageId);
+        $pageLink = '/' . str_replace(site_url(), '', $pageLink);
+
+        $_SERVER['PHP_SELF'] = '/index.php';
+        $_SERVER['REQ`UEST_URI'] = $pageLink;
+        $_SERVER['REDIRECT_URL'] = $pageLink;
+        $_SERVER['HTTP_REFERER'] = content_link($pageId);
+
+        $zipname = __DIR__ . '/../../Helper/tests/misc/bootstrap-5.0.2-examples.zip';
+
+
+        //
+
+        $zip = new \ZipArchive();
+
+        $htmls = [];
+        if ($zip->open($zipname)) {
+            for ($i = 0; $i < $zip->numFiles; $i++) {
+                $fn = $zip->getNameIndex($i);
+                $ext = get_file_extension($fn);
+                if ($ext == 'html') {
+                    $string = $zip->getFromName($fn);
+                    $htmls[$fn] = $string;
+                }
+            }
+        }
+        $zip->close();
+
+
+        $this->assertTrue(!empty($htmls));
+
+
+        foreach ($htmls as $k => $html) {
+
+
+            //remove empty tags
+           $html =  preg_replace('/<[^\/>]*>([\s]?)*<\/[^>]*>/', '', $html);
+
+            // remove script tags
+            $html = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', "", $html);
+
+             // remove code tags
+            $html = preg_replace('/<code\b[^>]*>(.*?)<\/code>/is', "", $html);
+
+
+            $l = $html;
+            $pq = \phpQuery::newDocument($l);
+
+            // $isolated_html = pq('main')->eq(0)->htmlOuter();
+            $contentFieldHtml = pq('main')->eq(0)->htmlOuter();
+
+
+            $fieldsData = [
+                'field_data_0' => [
+                    'attributes' => [
+                        'class' => 'container edit',
+                        'rel' => 'content',
+                        'rel_id' => $findPage->id,
+                        'field' => 'content',
+                    ],
+                    'html' => $contentFieldHtml
+                ]
+            ];
+
+
+
+
+            $encoded = base64_encode(json_encode($fieldsData));
+
+            $response = $this->call(
+                'POST',
+                route('api.content.save_edit'),
+                [
+                    'data_base64' => $encoded,
+                ],
+                [],//params
+                $_COOKIE,//cookie
+                [],//files
+                $_SERVER //server
+            );
+
+
+            $fieldSaved = $response->decodeResponseJson();
+
+            $this->assertEquals(trim($fieldSaved[0]['content']), trim($contentFieldHtml));
+            $this->assertEquals($fieldSaved[0]['rel_type'], 'content');
+            $this->assertEquals($fieldSaved[0]['field'], 'content');
+
+
+
+            $pq2 = \phpQuery::newDocument($contentFieldHtml);
+            $this->assertEquals($contentFieldHtml, $pq2->htmlOuter());
+
+
+            $findPage = Page::whereId($fieldSaved[0]['id'])->first();
+            $contentFieldHtml1 = trim($contentFieldHtml);
+            $contentFieldHtml2 = trim($findPage->content);
+
+            $this->assertEquals($contentFieldHtml1, $contentFieldHtml2);
+            $this->assertEquals($contentFieldHtml, $findPage->content);
+
+
+        }
+
+
+    }
+
+
+    public function testSaveContentOnPageLiveEditXssList()
+    {
+
+        $this->cleanupAndPrepare();
+
+        $unique = uniqid('testSaveContentOnPageLiveEditXssList');
+        $newCleanMlPage = save_content([
+            'subtype' => 'static',
+            'content_type' => 'page',
+            'layout_file' => 'clean.php',
+            'title' => 'xss-test-' . $unique,
+            'preview_layout_file' => 'clean.php',
+            'is_active' => 1,
+        ]);
+
+        $findPage = Page::whereId($newCleanMlPage)->first();
+
+        $pageId = $findPage->id;
+        $this->assertEquals($findPage->id, $newCleanMlPage);
+
+        $pageLink = content_link($pageId);
+        $pageLink = '/' . str_replace(site_url(), '', $pageLink);
+
+        $_SERVER['PHP_SELF'] = '/index.php';
+        $_SERVER['REQ`UEST_URI'] = $pageLink;
+        $_SERVER['REDIRECT_URL'] = $pageLink;
+        $_SERVER['HTTP_REFERER'] = content_link($pageId);
+
+
+
+        $zip = new \ZipArchive();
+        $zip->open(__DIR__ . '/../../Helper/tests/misc/xss-test-files.zip');
+        $xssList = $zip->getFromName('xss-payload-list.txt');
+        $zip->close();
+
+        $xssList = preg_replace('~\R~u', "\r\n", $xssList);
+        $xssList = explode(PHP_EOL, $xssList);
+
+
+        $xssListChunks = array_chunk($xssList, 100);
+
+
+        foreach ($xssListChunks as $stringChunk) {
+
+            $string = implode(PHP_EOL, $stringChunk);
+             if (empty(trim($string))) {
+                continue;
+            }
+
+            $contentFieldHtml = $string;
+
+            $fieldsData = [
+                'field_data_0' => [
+                    'attributes' => [
+                        'class' => 'container edit',
+                        'rel' => 'content',
+                        'rel_id' => $findPage->id,
+                        'field' => 'content',
+                    ],
+                    'html' => $contentFieldHtml
+                ]
+            ];
+
+            $encoded = base64_encode(json_encode($fieldsData));
+
+            $response = $this->call(
+                'POST',
+                route('api.content.save_edit'),
+                [
+                    'data_base64' => $encoded,
+                ],
+                [],//params
+                $_COOKIE,//cookie
+                [],//files
+                $_SERVER //server
+            );
+
+            $fieldSaved = $response->decodeResponseJson();
+
+            $this->assertNotEquals(trim($fieldSaved[0]['content']), trim($contentFieldHtml));
+            $this->assertEquals($fieldSaved[0]['rel_type'], 'content');
+            $this->assertEquals($fieldSaved[0]['field'], 'content');
+
+            $findPage = Page::whereId($fieldSaved[0]['id'])->first();
+            $contentFieldHtml1 = trim($contentFieldHtml);
+            $contentFieldHtml2 = trim($findPage->content);
+
+           $this->assertNotEquals($contentFieldHtml1, $contentFieldHtml2);
+
+
+            foreach ($stringChunk as $stringItem) {
+                if (trim($stringItem) == '') {
+                    continue;
+                }
+                $this->assertStringNotContainsString($stringItem, $findPage->content);
+            }
+
+
+
+        }
+    }
+
+
+
+    public function testSaveContentOnPageLiveEditFromOtherExamples()
+    {
+        $this->cleanupAndPrepare();
+
+
+        $unique = uniqid('testSaveContentOnPageLiveEditFromOtherExamples');
+        $newCleanMlPage = save_content([
+            'subtype' => 'static',
+            'content_type' => 'page',
+            'layout_file' => 'clean.php',
+            'title' => 'pagecontent-other-examples-save' . $unique,
+            'preview_layout_file' => 'clean.php',
+            'is_active' => 1,
+        ]);
+
+        $findPage = Page::whereId($newCleanMlPage)->first();
+
+        $pageId = $findPage->id;
+        $this->assertEquals($findPage->id, $newCleanMlPage);
+
+        $pageLink = content_link($pageId);
+        $pageLink = '/' . str_replace(site_url(), '', $pageLink);
+
+        $_SERVER['PHP_SELF'] = '/index.php';
+        $_SERVER['REQ`UEST_URI'] = $pageLink;
+        $_SERVER['REDIRECT_URL'] = $pageLink;
+        $_SERVER['HTTP_REFERER'] = content_link($pageId);
+
+        $zipname = __DIR__ . '/../../Helper/tests/misc/edit-fields-other-html.zip';
+
+
+        $zip = new \ZipArchive();
+
+        $htmls = [];
+        if ($zip->open($zipname)) {
+            for ($i = 0; $i < $zip->numFiles; $i++) {
+                $fn = $zip->getNameIndex($i);
+                $ext = get_file_extension($fn);
+                if ($ext == 'html') {
+                    $string = $zip->getFromName($fn);
+                    if(trim($string) == ''){
+                        continue;
+                    }
+                    $htmls[$fn] = $string;
+                }
+            }
+        }
+        $zip->close();
+
+
+        $this->assertTrue(!empty($htmls));
+
+        foreach ($htmls as $k => $html) {
+
+            $contentFieldHtml = $html;
+            $contentFieldHtml = self::fixLinksPrecentAttributes($contentFieldHtml);
+
+            $contentFieldHtmlTest1 = app()->parser->make_tags($contentFieldHtml);
+            $this->assertEquals($contentFieldHtmlTest1, $contentFieldHtml);
+
+
+            $xssClean = new XSSClean();
+            $contentFieldHtmlTest1 = $xssClean->clean($contentFieldHtml);
+            $this->assertEquals($contentFieldHtmlTest1, $contentFieldHtml);
+
+
+        }
+
+        $htmlsChunks = array_chunk($htmls, 100);
+
+
+
+      foreach ($htmlsChunks as $k => $htmlChunk) {
+
+          $html = implode('--------chunk-------', $htmlChunk);
+         $contentFieldHtml = $html;
+
+
+           // $contentFieldHtml = str_replace('{SITE_URL}', site_url(), $contentFieldHtml);
+            $fieldsData = [
+                'field_data_0' => [
+                    'attributes' => [
+                        'class' => 'container edit',
+                        'rel' => 'content',
+                        'rel_id' => $findPage->id,
+                        'field' => 'content',
+                    ],
+                    'html' => $contentFieldHtml
+                ]
+            ];
+
+            $contentFieldHtml = self::fixLinksPrecentAttributes($contentFieldHtml);
+
 
+            $encoded = base64_encode(json_encode($fieldsData));
 
+            $response = $this->call(
+                'POST',
+                route('api.content.save_edit'),
+                [
+                    'data_base64' => $encoded,
+                ],
+                [],//params
+                $_COOKIE,//cookie
+                [],//files
+                $_SERVER //server
+            );
+
+
+            $fieldSaved = $response->decodeResponseJson();
+
+
+
+            $findPage = Page::whereId($fieldSaved[0]['id'])->first();
+            $contentFieldHtml1 = trim($contentFieldHtml);
+            $contentFieldHtml2 = trim($findPage->content);
+
+            $this->assertEquals($contentFieldHtml1, $contentFieldHtml2);
+            $this->assertEquals($contentFieldHtml, $findPage->content);
+
+
+       }
+
+
+    }
 
+
+    private function cleanupAndPrepare(){
+        $user = User::where('is_admin', '=', '1')->first();
+        Auth::login($user);
+        \Config::set('microweber.disable_model_cache', 1);
+
+
+        MultilanguageHelpers::setMultilanguageEnabled(0);
+        Page::truncate();
     }
 
+    public static function fixLinksPrecentAttributes($text)
+    {
+        $text = str_ireplace('{SITE_URL}','___mw-site-url-temp-replace-on-clean___', $text);
+        $pq = \phpQuery::newDocument($text);
+
+        foreach($pq->find('a') as $stuffs)
+        {
+            $href = pq($stuffs)->attr('href');
+            if($href){
+            pq($stuffs)->attr('href', str_replace(' ', '%20', $href));
+            }
+        }
+        $text = $pq->htmlOuter();
+        $text = str_ireplace('___mw-site-url-temp-replace-on-clean___','{SITE_URL}',     $text);
+        return $text;
+
+    }
+
+
 }
diff --git a/src/MicroweberPackages/Database/DatabaseManager.php b/src/MicroweberPackages/Database/DatabaseManager.php
index 3e3798fb8d0..7f4bc13af75 100644
--- a/src/MicroweberPackages/Database/DatabaseManager.php
+++ b/src/MicroweberPackages/Database/DatabaseManager.php
@@ -616,11 +616,10 @@ class DatabaseManager extends DbUtils
 
         $table_assoc_name = $this->assoc_table_name($table);
 
         $criteria_orig = $data;
         $criteria = $this->map_array_to_table($table, $data);
-
         if ($allow_html == false) {
             $criteria = $this->app->format->clean_html($criteria);
         } else {
             if ($allow_scripts == false) {
                $criteria = $this->clean_input($criteria);
diff --git a/src/MicroweberPackages/Database/Utils.php b/src/MicroweberPackages/Database/Utils.php
index 3b15670c030..5df40edd998 100644
--- a/src/MicroweberPackages/Database/Utils.php
+++ b/src/MicroweberPackages/Database/Utils.php
@@ -604,11 +604,11 @@ class Utils
             }
         } elseif (is_string($input)) {
             $search = array(
                 '@<script[^>]*?>.*?</script>@si', // Strip out javascript
 
-                '@<![\s\S]*?--[ \t\n\r]*>@', // Strip multi-line comments
+          //      '@<![\s\S]*?--[ \t\n\r]*>@', // Strip multi-line comments
             );
             if (is_string($input)) {
                 $output = preg_replace($search, '', $input);
             } else {
                 $output = $input;
diff --git a/src/MicroweberPackages/Helper/Format.php b/src/MicroweberPackages/Helper/Format.php
index adfac69184b..7d221c39d67 100644
--- a/src/MicroweberPackages/Helper/Format.php
+++ b/src/MicroweberPackages/Helper/Format.php
@@ -628,10 +628,11 @@ class Format
         }
     }
 
     public function strip_unsafe($string, $img = false)
     {
+
         if (is_array($string)) {
             foreach ($string as $key => $val) {
                 $string[$key] = $this->strip_unsafe($val, $img);
             }
 
@@ -639,14 +640,14 @@ class Format
         } else {
 
             // Unsafe HTML tags that members may abuse
             $unsafe = array(
                 '/<iframe(.*?)<\/iframe>/is',
-                '/<title(.*?)<\/title>/is',
+               // '/<title(.*?)<\/title>/is',
                 //'/<pre(.*?)<\/pre>/is',
-                '/<audio(.*?)<\/audio>/is',
-                '/<video(.*?)<\/video>/is',
+              //  '/<audio(.*?)<\/audio>/is',
+             //   '/<video(.*?)<\/video>/is',
                 '/<frame(.*?)<\/frame>/is',
                 '/<frameset(.*?)<\/frameset>/is',
                 '/<object(.*?)<\/object>/is',
                 '/<script(.*?)<\/script>/is',
                 '/<embed(.*?)<\/embed>/is',
@@ -655,11 +656,11 @@ class Format
                 '/<!doctype(.*?)>/is',
                 '/<link(.*?)>/is',
                 '/<style(.*?)<\/style>/is',
                 '/<body(.*?)>/is',
                 '/<\/body>/is',
-                '/<head(.*?)>/is',
+               // '/<head(.*?)>/is',
                 '/<\/head>/is',
                 '/onload="(.*?)"/is',
                 '/onunload="(.*?)"/is',
                 '/onafterprint="(.*?)"/is',
                 '/onbeforeprint="(.*?)"/is',
diff --git a/src/MicroweberPackages/Helper/XSSClean.php b/src/MicroweberPackages/Helper/XSSClean.php
index e76ed78042f..6cac2b9ebff 100644
--- a/src/MicroweberPackages/Helper/XSSClean.php
+++ b/src/MicroweberPackages/Helper/XSSClean.php
@@ -32,10 +32,46 @@ class XSSClean
     public function clean($html)
     {
         if(is_array($html)){
             return $this->cleanArray($html);
         }
+
+
+        $_preserve_replaced_tags = [];
+        $html = str_ireplace('{SITE_URL}','___mw-site-url-temp-replace-on-clean___', $html);
+//        $tags = [ 'textarea', 'pre','code', 'svg', 'kbd'];
+//
+//        foreach ($tags as $tag) {
+//
+//            //  $script_pattern = "/<".$tag."[^>]*>(.*)<\/.$tag.>/Uis";
+//            $script_pattern = "/\<" . $tag . "(.*?)?\>(.|\s)*?\<\/" . $tag . "\>/i";
+//
+//            preg_match_all($script_pattern, $html, $mw_script_matches);
+//
+//            if (!empty($mw_script_matches)) {
+//                foreach ($mw_script_matches [0] as $key => $value) {
+//                    if ($value != '') {
+//                        $v1 = crc32($value);
+//                        $v1 = 'mw_xss_clean_repeserve_tags_tag_' . $tag . $v1 . '';
+//                        $html = str_replace($value, $v1, $html);
+//                        $_preserve_replaced_tags[$v1] = $value;
+//
+//                    }
+//                }
+//            }
+//
+//        }
+
+
+
+
+
+
+
+
+
+
          // from https://portswigger.net/web-security/cross-site-scripting/cheat-sheet#ontransitionend
         $cleanStrings = [
             'ontransitionstart',
             'onwebkitanimationend',
             'onwebkitanimationiteration',
@@ -152,25 +188,76 @@ class XSSClean
         $antiXss = new AntiXSS();
         $antiXss->addEvilHtmlTags($cleanStrings);
         $antiXss->addEvilAttributes($cleanStrings);
         $antiXss->addNeverAllowedOnEventsAfterwards($cleanStrings);
 
-        $allow_attibutes = [
+        $allowAttibutes = [
             'style',
+
+            'href',
+            'alt',
+            'target',
+            'srcset',
+            'sizes',
+            'title',
             'xlink:href',
         ];
-        $antiXss->removeEvilAttributes($allow_attibutes);
+        $antiXss->removeEvilAttributes($allowAttibutes);
 
-        $allow_tags = [
+        $allowTags = [
+            'head',
+            'header',
+            'main',
+            'aside',
             'img',
+            'form',
             'svg',
+            'title',
+            'input',
+            'button',
+            'select',
+            'option',
+            'textarea',
+            'picture',
+            'source',
+         ];
+
+        $antiXss->removeEvilHtmlTags($allowTags);
+        $allowRegex = [
+//            '<!--(.*)-->' => '<!--(.*)-->',
+//            '&lt;!--',
+//            '&lt;!--$1--&gt;'
+            '<!--(.*)-->' => '&lt;!--$1--&gt;',
+            '&lt;!--', '&lt;!--$1--&gt;'
         ];
 
-        $antiXss->removeEvilHtmlTags($allow_tags);
+
+
+        $antiXss->removeNeverAllowedRegex($allowRegex);
+
+        $allowNotClosed= [
+            'li',
+            'ul',
+            'textarea',
+        ];
+        $antiXss->removeDoNotCloseHtmlTags($allowNotClosed);
+
 
         $html = $antiXss->xss_clean($html);
+        $html_to_return = $html;
+        if ($_preserve_replaced_tags) {
+            foreach ($_preserve_replaced_tags as $key => $value) {
+
+                $html_to_return = str_replace($key, $value, $html_to_return);
+            }
+        }
+
+
+        $html_to_return = str_ireplace('___mw-site-url-temp-replace-on-clean___','{SITE_URL}', $html_to_return);
+
+
 
 
-        return $html;
+        return $html_to_return;
     }
 
 }
diff --git a/src/MicroweberPackages/Helper/tests/misc/bootstrap-5.0.2-examples.zip b/src/MicroweberPackages/Helper/tests/misc/bootstrap-5.0.2-examples.zip
new file mode 100644
index 00000000000..4e8fa1c0444
Binary files /dev/null and b/src/MicroweberPackages/Helper/tests/misc/bootstrap-5.0.2-examples.zip differ
diff --git a/src/MicroweberPackages/Helper/tests/misc/edit-fields-other-html.zip b/src/MicroweberPackages/Helper/tests/misc/edit-fields-other-html.zip
new file mode 100644
index 00000000000..08499d47ae1
Binary files /dev/null and b/src/MicroweberPackages/Helper/tests/misc/edit-fields-other-html.zip differ

Vulnerable code: 
        if ($layout == '') {
        $this->document->formatOutput = true;
            $return = @$this->document->loadHTML($markup);
    public static $defaultDoctype = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
//  AntiXSS makes bug on save convertind comments to htmlentities
//                        $antixss = new AntiXSS();
//                        $html_to_save = $content = $antixss->xss_clean($html_to_save);

    public function testSaveContentOnPageLiveEdit()
        $user = User::where('is_admin', '=', '1')->first();
        Auth::login($user);


        MultilanguageHelpers::setMultilanguageEnabled(0);
            'title' => 'pagecontent222'.$unique,
            'url' => 'pagecontent222'.$unique,
            'active_site_template'=> 'new-world',
        $pageLink = '/' . str_replace(site_url(),'', $pageLink);
        $_SERVER['REQUEST_URI'] = $pageLink;
</a><a class="mb-2" href=""><i class="mdi mdi-arrow-right"></i></a>
            'field_data_0'=>[
                'attributes'=>[
                    'class'=>'container edit',
                    'rel'=>'content',
                    'rel_id'=>$findPage->id,
                    'field'=>'content',
                'html'=>$contentFieldHtml


        $findPage = Page::whereId($fieldSaved[0]['id'])->first();

                '@<![\s\S]*?--[ \t\n\r]*>@', // Strip multi-line comments
                '/<title(.*?)<\/title>/is',
                '/<audio(.*?)<\/audio>/is',
                '/<video(.*?)<\/video>/is',
                '/<head(.*?)>/is',
        $allow_attibutes = [
        $antiXss->removeEvilAttributes($allow_attibutes);
        $allow_tags = [
        $antiXss->removeEvilHtmlTags($allow_tags);
        return $html;
Fixed code:          if ($layout == '') {

        $layout = str_ireplace('{SITE_URL}','___mw-site-url-temp-replace-on-make-tags___', $layout);



        $layout = str_ireplace('___mw-site-url-temp-replace-on-make-tags___','{SITE_URL}', $layout);



            $this->document->formatOutput = false;

            $this->document->validateOnParse=false;
            $this->document->strictErrorChecking=false;
            $this->document->recover=false;



        libxml_use_internal_errors(true);

       // $this->document->formatOutput = true;
        $this->document->formatOutput = false;
       // $this->document->standalone = true;


        $this->document->validateOnParse=false;
        $this->document->strictErrorChecking=false;
        $this->document->recover=false;
         // $return = @$this->document->loadHTML($markup);
            $return = @$this->document->loadHTML($markup,
                LIBXML_SCHEMA_CREATE |
                LIBXML_HTML_NOIMPLIED |
                LIBXML_HTML_NODEFDTD |
                LIBXML_NOERROR |
                LIBXML_NONET |
                LIBXML_NOWARNING
            );

/*                $fragment->loadMarkupXML('<?xml version="1.0" encoding="'.$charset.'"?>'*/
//                       . phpQuery::$defaultDoctype.''
//                        .'<fake xmlns="http://www.w3.org/1999/xhtml">'.$markup.'</fake>');
    public static $defaultDoctype_old = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

    public static $defaultDoctype = '<!doctype html>';
use MicroweberPackages\Content\Models\Content;
                        //\Log::info($html_to_save);

                      //  \Log::info($html_to_save);






use MicroweberPackages\Helper\XSSClean;
    public function testSaveContentOnPageLiveEditSingle()
        $this->cleanupAndPrepare();
            'title' => 'pagecontent222' . $unique,
            'url' => 'pagecontent222' . $unique,
            'active_site_template' => 'new-world',
        $pageLink = '/' . str_replace(site_url(), '', $pageLink);
        $_SERVER['REQ`UEST_URI'] = $pageLink;
<div class="feature-icon bg-primary bg-gradient">
</a>
<a class="mb-2" href=""><i class="mdi mdi-arrow-right"></i></a>
<a class="mb-2" href="https://example.com"><i class="mdi mdi-arrow-up"></i>example link</a>
<a class="mb-3" target="_blank" href="https://example.com/2"><i class="mdi mdi-arrow-left-bold-box"></i>link 2</a>
</div>
            'field_data_0' => [
                'attributes' => [
                    'class' => 'container edit',
                    'rel' => 'content',
                    'rel_id' => $findPage->id,
                    'field' => 'content',
                'html' => $contentFieldHtml
        $findPage = Page::whereId($findPage->id)->first();
        $this->assertTrue(str_contains($findPage->content, '<a class="mb-2" href="https://example.com"><i class="mdi mdi-arrow-up"></i>example link</a>'));
        $this->assertTrue(str_contains($findPage->content, '<a class="mb-3" target="_blank" href="https://example.com/2"><i class="mdi mdi-arrow-left-bold-box"></i>link 2</a>'));


    }


    public function testSaveContentOnPageLiveEditFromBootstrapExamples()
    {
        $this->cleanupAndPrepare();
        $unique = uniqid('testSaveContentOnPage');
        $newCleanMlPage = save_content([
            'subtype' => 'static',
            'content_type' => 'page',
            'layout_file' => 'clean.php',
            'title' => 'pagecontent-bs-examples-save' . $unique,
            'preview_layout_file' => 'clean.php',
            'is_active' => 1,
        ]);



        $findPage = Page::whereId($newCleanMlPage)->first();

        $pageId = $findPage->id;
        $this->assertEquals($findPage->id, $newCleanMlPage);

        $pageLink = content_link($pageId);
        $pageLink = '/' . str_replace(site_url(), '', $pageLink);

        $_SERVER['PHP_SELF'] = '/index.php';
        $_SERVER['REQ`UEST_URI'] = $pageLink;
        $_SERVER['REDIRECT_URL'] = $pageLink;
        $_SERVER['HTTP_REFERER'] = content_link($pageId);

        $zipname = __DIR__ . '/../../Helper/tests/misc/bootstrap-5.0.2-examples.zip';


        //

        $zip = new \ZipArchive();

        $htmls = [];
        if ($zip->open($zipname)) {
            for ($i = 0; $i < $zip->numFiles; $i++) {
                $fn = $zip->getNameIndex($i);
                $ext = get_file_extension($fn);
                if ($ext == 'html') {
                    $string = $zip->getFromName($fn);
                    $htmls[$fn] = $string;
                }
            }
        }
        $zip->close();


        $this->assertTrue(!empty($htmls));


        foreach ($htmls as $k => $html) {


            //remove empty tags
           $html =  preg_replace('/<[^\/>]*>([\s]?)*<\/[^>]*>/', '', $html);

            // remove script tags
            $html = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', "", $html);

             // remove code tags
            $html = preg_replace('/<code\b[^>]*>(.*?)<\/code>/is', "", $html);


            $l = $html;
            $pq = \phpQuery::newDocument($l);

            // $isolated_html = pq('main')->eq(0)->htmlOuter();
            $contentFieldHtml = pq('main')->eq(0)->htmlOuter();


            $fieldsData = [
                'field_data_0' => [
                    'attributes' => [
                        'class' => 'container edit',
                        'rel' => 'content',
                        'rel_id' => $findPage->id,
                        'field' => 'content',
                    ],
                    'html' => $contentFieldHtml
                ]
            ];




            $encoded = base64_encode(json_encode($fieldsData));

            $response = $this->call(
                'POST',
                route('api.content.save_edit'),
                [
                    'data_base64' => $encoded,
                ],
                [],//params
                $_COOKIE,//cookie
                [],//files
                $_SERVER //server
            );


            $fieldSaved = $response->decodeResponseJson();

            $this->assertEquals(trim($fieldSaved[0]['content']), trim($contentFieldHtml));
            $this->assertEquals($fieldSaved[0]['rel_type'], 'content');
            $this->assertEquals($fieldSaved[0]['field'], 'content');



            $pq2 = \phpQuery::newDocument($contentFieldHtml);
            $this->assertEquals($contentFieldHtml, $pq2->htmlOuter());


            $findPage = Page::whereId($fieldSaved[0]['id'])->first();
            $contentFieldHtml1 = trim($contentFieldHtml);
            $contentFieldHtml2 = trim($findPage->content);

            $this->assertEquals($contentFieldHtml1, $contentFieldHtml2);
            $this->assertEquals($contentFieldHtml, $findPage->content);


        }


    }


    public function testSaveContentOnPageLiveEditXssList()
    {

        $this->cleanupAndPrepare();

        $unique = uniqid('testSaveContentOnPageLiveEditXssList');
        $newCleanMlPage = save_content([
            'subtype' => 'static',
            'content_type' => 'page',
            'layout_file' => 'clean.php',
            'title' => 'xss-test-' . $unique,
            'preview_layout_file' => 'clean.php',
            'is_active' => 1,
        ]);

        $findPage = Page::whereId($newCleanMlPage)->first();

        $pageId = $findPage->id;
        $this->assertEquals($findPage->id, $newCleanMlPage);

        $pageLink = content_link($pageId);
        $pageLink = '/' . str_replace(site_url(), '', $pageLink);

        $_SERVER['PHP_SELF'] = '/index.php';
        $_SERVER['REQ`UEST_URI'] = $pageLink;
        $_SERVER['REDIRECT_URL'] = $pageLink;
        $_SERVER['HTTP_REFERER'] = content_link($pageId);



        $zip = new \ZipArchive();
        $zip->open(__DIR__ . '/../../Helper/tests/misc/xss-test-files.zip');
        $xssList = $zip->getFromName('xss-payload-list.txt');
        $zip->close();

        $xssList = preg_replace('~\R~u', "\r\n", $xssList);
        $xssList = explode(PHP_EOL, $xssList);


        $xssListChunks = array_chunk($xssList, 100);


        foreach ($xssListChunks as $stringChunk) {

            $string = implode(PHP_EOL, $stringChunk);
             if (empty(trim($string))) {
                continue;
            }

            $contentFieldHtml = $string;

            $fieldsData = [
                'field_data_0' => [
                    'attributes' => [
                        'class' => 'container edit',
                        'rel' => 'content',
                        'rel_id' => $findPage->id,
                        'field' => 'content',
                    ],
                    'html' => $contentFieldHtml
                ]
            ];

            $encoded = base64_encode(json_encode($fieldsData));

            $response = $this->call(
                'POST',
                route('api.content.save_edit'),
                [
                    'data_base64' => $encoded,
                ],
                [],//params
                $_COOKIE,//cookie
                [],//files
                $_SERVER //server
            );

            $fieldSaved = $response->decodeResponseJson();

            $this->assertNotEquals(trim($fieldSaved[0]['content']), trim($contentFieldHtml));
            $this->assertEquals($fieldSaved[0]['rel_type'], 'content');
            $this->assertEquals($fieldSaved[0]['field'], 'content');

            $findPage = Page::whereId($fieldSaved[0]['id'])->first();
            $contentFieldHtml1 = trim($contentFieldHtml);
            $contentFieldHtml2 = trim($findPage->content);

           $this->assertNotEquals($contentFieldHtml1, $contentFieldHtml2);


            foreach ($stringChunk as $stringItem) {
                if (trim($stringItem) == '') {
                    continue;
                }
                $this->assertStringNotContainsString($stringItem, $findPage->content);
            }



        }
    }



    public function testSaveContentOnPageLiveEditFromOtherExamples()
    {
        $this->cleanupAndPrepare();


        $unique = uniqid('testSaveContentOnPageLiveEditFromOtherExamples');
        $newCleanMlPage = save_content([
            'subtype' => 'static',
            'content_type' => 'page',
            'layout_file' => 'clean.php',
            'title' => 'pagecontent-other-examples-save' . $unique,
            'preview_layout_file' => 'clean.php',
            'is_active' => 1,
        ]);

        $findPage = Page::whereId($newCleanMlPage)->first();

        $pageId = $findPage->id;
        $this->assertEquals($findPage->id, $newCleanMlPage);

        $pageLink = content_link($pageId);
        $pageLink = '/' . str_replace(site_url(), '', $pageLink);

        $_SERVER['PHP_SELF'] = '/index.php';
        $_SERVER['REQ`UEST_URI'] = $pageLink;
        $_SERVER['REDIRECT_URL'] = $pageLink;
        $_SERVER['HTTP_REFERER'] = content_link($pageId);

        $zipname = __DIR__ . '/../../Helper/tests/misc/edit-fields-other-html.zip';


        $zip = new \ZipArchive();

        $htmls = [];
        if ($zip->open($zipname)) {
            for ($i = 0; $i < $zip->numFiles; $i++) {
                $fn = $zip->getNameIndex($i);
                $ext = get_file_extension($fn);
                if ($ext == 'html') {
                    $string = $zip->getFromName($fn);
                    if(trim($string) == ''){
                        continue;
                    }
                    $htmls[$fn] = $string;
                }
            }
        }
        $zip->close();


        $this->assertTrue(!empty($htmls));

        foreach ($htmls as $k => $html) {

            $contentFieldHtml = $html;
            $contentFieldHtml = self::fixLinksPrecentAttributes($contentFieldHtml);

            $contentFieldHtmlTest1 = app()->parser->make_tags($contentFieldHtml);
            $this->assertEquals($contentFieldHtmlTest1, $contentFieldHtml);


            $xssClean = new XSSClean();
            $contentFieldHtmlTest1 = $xssClean->clean($contentFieldHtml);
            $this->assertEquals($contentFieldHtmlTest1, $contentFieldHtml);


        }

        $htmlsChunks = array_chunk($htmls, 100);



      foreach ($htmlsChunks as $k => $htmlChunk) {

          $html = implode('--------chunk-------', $htmlChunk);
         $contentFieldHtml = $html;


           // $contentFieldHtml = str_replace('{SITE_URL}', site_url(), $contentFieldHtml);
            $fieldsData = [
                'field_data_0' => [
                    'attributes' => [
                        'class' => 'container edit',
                        'rel' => 'content',
                        'rel_id' => $findPage->id,
                        'field' => 'content',
                    ],
                    'html' => $contentFieldHtml
                ]
            ];

            $contentFieldHtml = self::fixLinksPrecentAttributes($contentFieldHtml);

            $encoded = base64_encode(json_encode($fieldsData));
            $response = $this->call(
                'POST',
                route('api.content.save_edit'),
                [
                    'data_base64' => $encoded,
                ],
                [],//params
                $_COOKIE,//cookie
                [],//files
                $_SERVER //server
            );


            $fieldSaved = $response->decodeResponseJson();



            $findPage = Page::whereId($fieldSaved[0]['id'])->first();
            $contentFieldHtml1 = trim($contentFieldHtml);
            $contentFieldHtml2 = trim($findPage->content);

            $this->assertEquals($contentFieldHtml1, $contentFieldHtml2);
            $this->assertEquals($contentFieldHtml, $findPage->content);


       }


    }

    private function cleanupAndPrepare(){
        $user = User::where('is_admin', '=', '1')->first();
        Auth::login($user);
        \Config::set('microweber.disable_model_cache', 1);


        MultilanguageHelpers::setMultilanguageEnabled(0);
        Page::truncate();
    public static function fixLinksPrecentAttributes($text)
    {
        $text = str_ireplace('{SITE_URL}','___mw-site-url-temp-replace-on-clean___', $text);
        $pq = \phpQuery::newDocument($text);

        foreach($pq->find('a') as $stuffs)
        {
            $href = pq($stuffs)->attr('href');
            if($href){
            pq($stuffs)->attr('href', str_replace(' ', '%20', $href));
            }
        }
        $text = $pq->htmlOuter();
        $text = str_ireplace('___mw-site-url-temp-replace-on-clean___','{SITE_URL}',     $text);
        return $text;

    }


          //      '@<![\s\S]*?--[ \t\n\r]*>@', // Strip multi-line comments

               // '/<title(.*?)<\/title>/is',
              //  '/<audio(.*?)<\/audio>/is',
             //   '/<video(.*?)<\/video>/is',
               // '/<head(.*?)>/is',


        $_preserve_replaced_tags = [];
        $html = str_ireplace('{SITE_URL}','___mw-site-url-temp-replace-on-clean___', $html);
//        $tags = [ 'textarea', 'pre','code', 'svg', 'kbd'];
//
//        foreach ($tags as $tag) {
//
//            //  $script_pattern = "/<".$tag."[^>]*>(.*)<\/.$tag.>/Uis";
//            $script_pattern = "/\<" . $tag . "(.*?)?\>(.|\s)*?\<\/" . $tag . "\>/i";
//
//            preg_match_all($script_pattern, $html, $mw_script_matches);
//
//            if (!empty($mw_script_matches)) {
//                foreach ($mw_script_matches [0] as $key => $value) {
//                    if ($value != '') {
//                        $v1 = crc32($value);
//                        $v1 = 'mw_xss_clean_repeserve_tags_tag_' . $tag . $v1 . '';
//                        $html = str_replace($value, $v1, $html);
//                        $_preserve_replaced_tags[$v1] = $value;
//
//                    }
//                }
//            }
//
//        }










        $allowAttibutes = [

            'href',
            'alt',
            'target',
            'srcset',
            'sizes',
            'title',
        $antiXss->removeEvilAttributes($allowAttibutes);
        $allowTags = [
            'head',
            'header',
            'main',
            'aside',
            'form',
            'title',
            'input',
            'button',
            'select',
            'option',
            'textarea',
            'picture',
            'source',
         ];

        $antiXss->removeEvilHtmlTags($allowTags);
        $allowRegex = [
//            '<!--(.*)-->' => '<!--(.*)-->',
//            '&lt;!--',
//            '&lt;!--$1--&gt;'
            '<!--(.*)-->' => '&lt;!--$1--&gt;',
            '&lt;!--', '&lt;!--$1--&gt;'


        $antiXss->removeNeverAllowedRegex($allowRegex);

        $allowNotClosed= [
            'li',
            'ul',
            'textarea',
        ];
        $antiXss->removeDoNotCloseHtmlTags($allowNotClosed);

        $html_to_return = $html;
        if ($_preserve_replaced_tags) {
            foreach ($_preserve_replaced_tags as $key => $value) {

                $html_to_return = str_replace($key, $value, $html_to_return);
            }
        }


        $html_to_return = str_ireplace('___mw-site-url-temp-replace-on-clean___','{SITE_URL}', $html_to_return);


        return $html_to_return;