| 
									
										
										
										
											2023-08-26 22:28:29 +08:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace BookStack\Util; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | use BookStack\Exceptions\HttpFetchException; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-17 20:45:37 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Validate the host we're connecting to when making a server-side-request. | 
					
						
							|  |  |  |  * Will use the given hosts config if given during construction otherwise | 
					
						
							|  |  |  |  * will look to the app configured config. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2023-08-26 22:28:29 +08:00
										 |  |  | class SsrUrlValidator | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     protected string $config; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-17 20:45:37 +08:00
										 |  |  |     public function __construct(?string $config = null) | 
					
						
							| 
									
										
										
										
											2023-08-26 22:28:29 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         $this->config = $config ?? config('app.ssr_hosts') ?? ''; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * @throws HttpFetchException | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function ensureAllowed(string $url): void | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!$this->allowed($url)) { | 
					
						
							|  |  |  |             throw new HttpFetchException(trans('errors.http_ssr_url_no_match')); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Check if the given URL is allowed by the configured SSR host values. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function allowed(string $url): bool | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $allowed = $this->getHostPatterns(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         foreach ($allowed as $pattern) { | 
					
						
							|  |  |  |             if ($this->urlMatchesPattern($url, $pattern)) { | 
					
						
							|  |  |  |                 return true; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     protected function urlMatchesPattern($url, $pattern): bool | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-08-27 03:13:37 +08:00
										 |  |  |         $pattern = rtrim(trim($pattern), '/'); | 
					
						
							| 
									
										
										
										
											2023-08-26 22:28:29 +08:00
										 |  |  |         $url = trim($url); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (empty($pattern) || empty($url)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $quoted = preg_quote($pattern, '/'); | 
					
						
							|  |  |  |         $regexPattern = str_replace('\*', '.*', $quoted); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-27 03:13:37 +08:00
										 |  |  |         return preg_match('/^' . $regexPattern . '($|\/.*$|#.*$)/i', $url); | 
					
						
							| 
									
										
										
										
											2023-08-26 22:28:29 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * @return string[] | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     protected function getHostPatterns(): array | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return explode(' ', strtolower($this->config)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |