| 
									
										
										
										
											2021-09-04 06:32:42 +08:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace BookStack\Util; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | use DOMDocument; | 
					
						
							|  |  |  | use DOMElement; | 
					
						
							|  |  |  | use DOMNodeList; | 
					
						
							|  |  |  | use DOMXPath; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class HtmlNonceApplicator | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2021-09-04 20:57:04 +08:00
										 |  |  |     protected static $placeholder = '[CSP_NONCE_VALUE]'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-04 06:32:42 +08:00
										 |  |  |     /** | 
					
						
							| 
									
										
										
										
											2021-09-04 20:57:04 +08:00
										 |  |  |      * Prepare the given HTML content with nonce attributes including a placeholder | 
					
						
							|  |  |  |      * value which we can target later. | 
					
						
							| 
									
										
										
										
											2021-09-04 06:32:42 +08:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-09-04 20:57:04 +08:00
										 |  |  |     public static function prepare(string $html): string | 
					
						
							| 
									
										
										
										
											2021-09-04 06:32:42 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         if (empty($html)) { | 
					
						
							|  |  |  |             return $html; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-12 23:19:17 +08:00
										 |  |  |         $html = '<?xml encoding="utf-8" ?><body>' . $html . '</body>'; | 
					
						
							| 
									
										
										
										
											2021-09-04 06:32:42 +08:00
										 |  |  |         libxml_use_internal_errors(true); | 
					
						
							|  |  |  |         $doc = new DOMDocument(); | 
					
						
							| 
									
										
										
										
											2021-09-12 23:19:17 +08:00
										 |  |  |         $doc->loadHTML($html, LIBXML_SCHEMA_CREATE); | 
					
						
							| 
									
										
										
										
											2021-09-04 06:32:42 +08:00
										 |  |  |         $xPath = new DOMXPath($doc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Apply to scripts
 | 
					
						
							|  |  |  |         $scriptElems = $xPath->query('//script'); | 
					
						
							| 
									
										
										
										
											2021-09-04 20:57:04 +08:00
										 |  |  |         static::addNonceAttributes($scriptElems, static::$placeholder); | 
					
						
							| 
									
										
										
										
											2021-09-04 06:32:42 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // Apply to styles
 | 
					
						
							|  |  |  |         $styleElems = $xPath->query('//style'); | 
					
						
							| 
									
										
										
										
											2021-09-04 20:57:04 +08:00
										 |  |  |         static::addNonceAttributes($styleElems, static::$placeholder); | 
					
						
							| 
									
										
										
										
											2021-09-04 06:32:42 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         $returnHtml = ''; | 
					
						
							|  |  |  |         $topElems = $doc->documentElement->childNodes->item(0)->childNodes; | 
					
						
							|  |  |  |         foreach ($topElems as $child) { | 
					
						
							| 
									
										
										
										
											2021-09-07 05:19:06 +08:00
										 |  |  |             $content = $doc->saveHTML($child); | 
					
						
							| 
									
										
										
										
											2021-09-06 06:52:39 +08:00
										 |  |  |             $returnHtml .= $content; | 
					
						
							| 
									
										
										
										
											2021-09-04 06:32:42 +08:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $returnHtml; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-04 20:57:04 +08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Apply the give nonce value to the given prepared HTML. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public static function apply(string $html, string $nonce): string | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return str_replace(static::$placeholder, $nonce, $html); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     protected static function addNonceAttributes(DOMNodeList $nodes, string $attrValue): void | 
					
						
							| 
									
										
										
										
											2021-09-04 06:32:42 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         /** @var DOMElement $node */ | 
					
						
							|  |  |  |         foreach ($nodes as $node) { | 
					
						
							| 
									
										
										
										
											2021-09-04 20:57:04 +08:00
										 |  |  |             $node->setAttribute('nonce', $attrValue); | 
					
						
							| 
									
										
										
										
											2021-09-04 06:32:42 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |