]> BookStack Code Mirror - bookstack/blob - app/Util/HtmlNonceApplicator.php
Notifications: Switched testing from string to reference levels
[bookstack] / app / Util / HtmlNonceApplicator.php
1 <?php
2
3 namespace BookStack\Util;
4
5 use DOMDocument;
6 use DOMElement;
7 use DOMNodeList;
8 use DOMXPath;
9
10 class HtmlNonceApplicator
11 {
12     protected static $placeholder = '[CSP_NONCE_VALUE]';
13
14     /**
15      * Prepare the given HTML content with nonce attributes including a placeholder
16      * value which we can target later.
17      */
18     public static function prepare(string $html): string
19     {
20         if (empty($html)) {
21             return $html;
22         }
23
24         $html = '<?xml encoding="utf-8" ?><body>' . $html . '</body>';
25         libxml_use_internal_errors(true);
26         $doc = new DOMDocument();
27         $doc->loadHTML($html, LIBXML_SCHEMA_CREATE);
28         $xPath = new DOMXPath($doc);
29
30         // Apply to scripts
31         $scriptElems = $xPath->query('//script');
32         static::addNonceAttributes($scriptElems, static::$placeholder);
33
34         // Apply to styles
35         $styleElems = $xPath->query('//style');
36         static::addNonceAttributes($styleElems, static::$placeholder);
37
38         $returnHtml = '';
39         $topElems = $doc->documentElement->childNodes->item(0)->childNodes;
40         foreach ($topElems as $child) {
41             $content = $doc->saveHTML($child);
42             $returnHtml .= $content;
43         }
44
45         return $returnHtml;
46     }
47
48     /**
49      * Apply the give nonce value to the given prepared HTML.
50      */
51     public static function apply(string $html, string $nonce): string
52     {
53         return str_replace(static::$placeholder, $nonce, $html);
54     }
55
56     protected static function addNonceAttributes(DOMNodeList $nodes, string $attrValue): void
57     {
58         /** @var DOMElement $node */
59         foreach ($nodes as $node) {
60             $node->setAttribute('nonce', $attrValue);
61         }
62     }
63 }