]> BookStack Code Mirror - bookstack/blobdiff - app/Util/HtmlContentFilter.php
Updated test for perms. changes and fixed static issues
[bookstack] / app / Util / HtmlContentFilter.php
index 729b80474757c9acd8384ca24af05447c490756e..5e3c4822c2e16a43c48da4c6f8a0edcbcf618746 100644 (file)
@@ -2,14 +2,16 @@
 
 namespace BookStack\Util;
 
+use DOMAttr;
 use DOMDocument;
+use DOMElement;
 use DOMNodeList;
 use DOMXPath;
 
 class HtmlContentFilter
 {
     /**
-     * Remove all of the script elements from the given HTML.
+     * Remove all the script elements from the given HTML.
      */
     public static function removeScripts(string $html): string
     {
@@ -43,13 +45,20 @@ class HtmlContentFilter
         $badIframes = $xPath->query('//*[' . static::xpathContains('@src', 'data:') . '] | //*[' . static::xpathContains('@src', 'javascript:') . '] | //*[@srcdoc]');
         static::removeNodes($badIframes);
 
+        // Remove attributes, within svg children, hiding JavaScript or data uris.
+        // A bunch of svg element and attribute combinations expose xss possibilities.
+        // For example, SVG animate tag can exploit javascript in values.
+        $badValuesAttrs = $xPath->query('//svg//@*[' . static::xpathContains('.', 'data:') . '] | //svg//@*[' . static::xpathContains('.', 'javascript:') . ']');
+        static::removeAttributes($badValuesAttrs);
+
+        // Remove elements with a xlink:href attribute
+        // Used in SVG but deprecated anyway, so we'll be a bit more heavy-handed here.
+        $xlinkHrefAttributes = $xPath->query('//@*[contains(name(), \'xlink:href\')]');
+        static::removeAttributes($xlinkHrefAttributes);
+
         // Remove 'on*' attributes
         $onAttributes = $xPath->query('//@*[starts-with(name(), \'on\')]');
-        foreach ($onAttributes as $attr) {
-            /** @var \DOMAttr $attr */
-            $attrName = $attr->nodeName;
-            $attr->parentNode->removeAttribute($attrName);
-        }
+        static::removeAttributes($onAttributes);
 
         $html = '';
         $topElems = $doc->documentElement->childNodes->item(0)->childNodes;
@@ -68,11 +77,12 @@ class HtmlContentFilter
     {
         $value = strtolower($value);
         $upperVal = strtoupper($value);
+
         return 'contains(translate(' . $property . ', \'' . $upperVal . '\', \'' . $value . '\'), \'' . $value . '\')';
     }
 
     /**
-     * Removed all of the given DOMNodes.
+     * Remove all the given DOMNodes.
      */
     protected static function removeNodes(DOMNodeList $nodes): void
     {
@@ -80,4 +90,18 @@ class HtmlContentFilter
             $node->parentNode->removeChild($node);
         }
     }
+
+    /**
+     * Remove all the given attribute nodes.
+     */
+    protected static function removeAttributes(DOMNodeList $attrs): void
+    {
+        /** @var DOMAttr $attr */
+        foreach ($attrs as $attr) {
+            $attrName = $attr->nodeName;
+            /** @var DOMElement $parentNode */
+            $parentNode = $attr->parentNode;
+            $parentNode->removeAttribute($attrName);
+        }
+    }
 }