Index: vendor/magento/module-sales/Helper/Admin.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- vendor/magento/module-sales/Helper/Admin.php	(revision d46f8efde7d6e7c40859e833f462c30edc101476)
+++ vendor/magento/module-sales/Helper/Admin.php	(revision 102b397919b1c82aa13118abb5345ad81c626370)
@@ -6,6 +6,8 @@
 
 namespace Magento\Sales\Helper;
 
+use Magento\Framework\App\ObjectManager;
+
 /**
  * Sales admin helper.
  */
@@ -31,24 +33,33 @@
      */
     protected $escaper;
 
+    /**
+     * @var \DOMDocumentFactory
+     */
+    private $domDocumentFactory;
+
     /**
      * @param \Magento\Framework\App\Helper\Context $context
      * @param \Magento\Store\Model\StoreManagerInterface $storeManager
      * @param \Magento\Sales\Model\Config $salesConfig
      * @param \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency
      * @param \Magento\Framework\Escaper $escaper
+     * @param \DOMDocumentFactory|null $domDocumentFactory
      */
     public function __construct(
         \Magento\Framework\App\Helper\Context $context,
         \Magento\Store\Model\StoreManagerInterface $storeManager,
         \Magento\Sales\Model\Config $salesConfig,
         \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency,
-        \Magento\Framework\Escaper $escaper
+        \Magento\Framework\Escaper $escaper,
+        \DOMDocumentFactory $domDocumentFactory = null
     ) {
         $this->priceCurrency = $priceCurrency;
         $this->_storeManager = $storeManager;
         $this->_salesConfig = $salesConfig;
         $this->escaper = $escaper;
+        $this->domDocumentFactory = $domDocumentFactory
+            ?: ObjectManager::getInstance()->get(\DOMDocumentFactory::class);
         parent::__construct($context);
     }
 
@@ -149,30 +160,41 @@
     public function escapeHtmlWithLinks($data, $allowedTags = null)
     {
         if (!empty($data) && is_array($allowedTags) && in_array('a', $allowedTags)) {
-            $links = [];
-            $i = 1;
-            $data = str_replace('%', '%%', $data);
-            $regexp = "#(?J)<a"
-                ."(?:(?:\s+(?:(?:href\s*=\s*(['\"])(?<link>.*?)\\1\s*)|(?:\S+\s*=\s*(['\"])(.*?)\\3)\s*)*)|>)"
-                .">?(?:(?:(?<text>.*?)(?:<\/a\s*>?|(?=<\w))|(?<text>.*)))#si";
-            while (preg_match($regexp, $data, $matches)) {
-                $text = '';
-                if (!empty($matches['text'])) {
-                    $text = str_replace('%%', '%', $matches['text']);
-                }
-                $url = $this->filterUrl($matches['link'] ?? '');
-                //Recreate a minimalistic secure a tag
-                $links[] = sprintf(
-                    '<a href="%s">%s</a>',
-                    htmlspecialchars($url, ENT_QUOTES, 'UTF-8', false),
-                    $this->escaper->escapeHtml($text)
-                );
-                $data = str_replace($matches[0], '%' . $i . '$s', $data);
-                ++$i;
+            $wrapperElementId = uniqid();
+            $domDocument = $this->domDocumentFactory->create();
+
+            $internalErrors = libxml_use_internal_errors(true);
+
+            $domDocument->loadHTML(
+                '<html><body id="' . $wrapperElementId . '">' . $data . '</body></html>'
+            );
+
+            libxml_use_internal_errors($internalErrors);
+
+            $linkTags = $domDocument->getElementsByTagName('a');
+
+            foreach ($linkTags as $linkNode) {
+                $linkAttributes = [];
+                foreach ($linkNode->attributes as $attribute) {
+                    $linkAttributes[$attribute->name] = $attribute->value;
+                }
+
+                foreach ($linkAttributes as $attributeName => $attributeValue) {
+                    if ($attributeName === 'href') {
+                        $url = $this->filterUrl($attributeValue ?? '');
+                        $url = $this->escaper->escapeUrl($url);
+                        $linkNode->setAttribute('href', $url);
+                    } else {
+                        $linkNode->removeAttribute($attributeName);
+                    }
+                }
             }
-            $data = $this->escaper->escapeHtml($data, $allowedTags);
-            return vsprintf($data, $links);
+
+            $result = mb_convert_encoding($domDocument->saveHTML(), 'UTF-8', 'HTML-ENTITIES');
+            preg_match('/<body id="' . $wrapperElementId . '">(.+)<\/body><\/html>$/si', $result, $matches);
+            $data = !empty($matches) ? $matches[1] : '';
         }
+
         return $this->escaper->escapeHtml($data, $allowedTags);
     }
 
@@ -186,7 +208,6 @@
     {
         if ($url) {
             //Revert the sprintf escaping
-            $url = str_replace('%%', '%', $url);
             $urlScheme = parse_url($url, PHP_URL_SCHEME);
             $urlScheme = $urlScheme ? strtolower($urlScheme) : '';
             if ($urlScheme !== 'http' && $urlScheme !== 'https') {
Index: vendor/magento/module-sales/Test/Unit/Helper/AdminTest.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- vendor/magento/module-sales/Test/Unit/Helper/AdminTest.php	(revision d46f8efde7d6e7c40859e833f462c30edc101476)
+++ vendor/magento/module-sales/Test/Unit/Helper/AdminTest.php	(revision ab7bbf5f3f2ef7827e7f9eb0ea88a06144f36a94)
@@ -71,7 +71,7 @@
             ->disableOriginalConstructor()
             ->getMock();
 
-        $this->adminHelper = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject(
+        $this->adminHelper = (new ObjectManager($this))->getObject(
             \Magento\Sales\Helper\Admin::class,
             [
                 'context' => $this->contextMock,
@@ -330,72 +330,16 @@
     }
 
     /**
-     * @param string $data
-     * @param string $expected
-     * @param null|array $allowedTags
-     * @dataProvider escapeHtmlWithLinksDataProvider
+     * @return void
      */
-    public function testEscapeHtmlWithLinks($data, $expected, $allowedTags = null)
+    public function testEscapeHtmlWithLinks()
     {
+        $expected = '&lt;a&gt;some text in tags&lt;/a&gt;';
         $this->escaperMock
             ->expects($this->any())
             ->method('escapeHtml')
             ->will($this->returnValue($expected));
-        $actual = $this->adminHelper->escapeHtmlWithLinks($data, $allowedTags);
+        $actual = $this->adminHelper->escapeHtmlWithLinks('<a>some text in tags</a>');
         $this->assertEquals($expected, $actual);
     }
-
-    /**
-     * @return array
-     */
-    public function escapeHtmlWithLinksDataProvider()
-    {
-        return [
-            [
-                '<a>some text in tags</a>',
-                '&lt;a&gt;some text in tags&lt;/a&gt;',
-                'allowedTags' => null
-            ],
-            [
-                'Transaction ID: "<a target="_blank" href="https://www.paypal.com/?id=XX123XX">XX123XX</a>"',
-                'Transaction ID: &quot;<a target="_blank" href="https://www.paypal.com/?id=XX123XX">XX123XX</a>&quot;',
-                'allowedTags' => ['b', 'br', 'strong', 'i', 'u', 'a']
-            ],
-            [
-                '<a>some text in tags</a>',
-                '<a>some text in tags</a>',
-                'allowedTags' => ['a']
-            ],
-            'Not replacement with placeholders' => [
-                "<a><script>alert(1)</script></a>",
-                '<a>&lt;script&gt;alert(1)&lt;/script&gt;</a>',
-                'allowedTags' => ['a']
-            ],
-            'Normal usage, url escaped' => [
-                '<a href=\"#\">Foo</a>',
-                '<a href="#">Foo</a>',
-                'allowedTags' => ['a']
-            ],
-            'Normal usage, url not escaped' => [
-                "<a href=http://example.com?foo=1&bar=2&baz[name]=BAZ>Foo</a>",
-                '<a href="http://example.com?foo=1&amp;bar=2&amp;baz[name]=BAZ">Foo</a>',
-                'allowedTags' => ['a']
-            ],
-            'XSS test' => [
-                "<a href=\"javascript&colon;alert(59)\">Foo</a>",
-                '<a href="#">Foo</a>',
-                'allowedTags' => ['a']
-            ],
-            'Additional regex test' => [
-                "<a href=\"http://example1.com\" href=\"http://example2.com\">Foo</a>",
-                '<a href="http://example1.com">Foo</a>',
-                'allowedTags' => ['a']
-            ],
-            'Break of valid urls' => [
-                "<a href=\"http://example.com?foo=text with space\">Foo</a>",
-                '<a href="#">Foo</a>',
-                'allowedTags' => ['a']
-            ],
-        ];
-    }
 }
