]> BookStack Code Mirror - bookstack/blob - app/Util/HtmlContentFilter.php
Apply fixes from StyleCI
[bookstack] / app / Util / HtmlContentFilter.php
1 <?php
2
3 namespace BookStack\Util;
4
5 use DOMDocument;
6 use DOMNodeList;
7 use DOMXPath;
8
9 class HtmlContentFilter
10 {
11     /**
12      * Remove all of the script elements from the given HTML.
13      */
14     public static function removeScripts(string $html): string
15     {
16         if (empty($html)) {
17             return $html;
18         }
19
20         $html = '<body>' . $html . '</body>';
21         libxml_use_internal_errors(true);
22         $doc = new DOMDocument();
23         $doc->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
24         $xPath = new DOMXPath($doc);
25
26         // Remove standard script tags
27         $scriptElems = $xPath->query('//script');
28         static::removeNodes($scriptElems);
29
30         // Remove clickable links to JavaScript URI
31         $badLinks = $xPath->query('//*[contains(@href, \'javascript:\')]');
32         static::removeNodes($badLinks);
33
34         // Remove forms with calls to JavaScript URI
35         $badForms = $xPath->query('//*[contains(@action, \'javascript:\')] | //*[contains(@formaction, \'javascript:\')]');
36         static::removeNodes($badForms);
37
38         // Remove meta tag to prevent external redirects
39         $metaTags = $xPath->query('//meta[contains(@content, \'url\')]');
40         static::removeNodes($metaTags);
41
42         // Remove data or JavaScript iFrames
43         $badIframes = $xPath->query('//*[contains(@src, \'data:\')] | //*[contains(@src, \'javascript:\')] | //*[@srcdoc]');
44         static::removeNodes($badIframes);
45
46         // Remove 'on*' attributes
47         $onAttributes = $xPath->query('//@*[starts-with(name(), \'on\')]');
48         foreach ($onAttributes as $attr) {
49             /** @var \DOMAttr $attr */
50             $attrName = $attr->nodeName;
51             $attr->parentNode->removeAttribute($attrName);
52         }
53
54         $html = '';
55         $topElems = $doc->documentElement->childNodes->item(0)->childNodes;
56         foreach ($topElems as $child) {
57             $html .= $doc->saveHTML($child);
58         }
59
60         return $html;
61     }
62
63     /**
64      * Removed all of the given DOMNodes.
65      */
66     protected static function removeNodes(DOMNodeList $nodes): void
67     {
68         foreach ($nodes as $node) {
69             $node->parentNode->removeChild($node);
70         }
71     }
72 }