mirror of
				https://github.com/BookStackApp/BookStack.git
				synced 2025-10-29 16:09:29 +03:00 
			
		
		
		
	Was causing things like emjoi within script content to be somewhat mangled. Instead we force UTF8 only parsing via XML declaration. Added test to cover. For #2923
		
			
				
	
	
		
			97 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			97 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| namespace BookStack\Util;
 | |
| 
 | |
| use Illuminate\Support\Str;
 | |
| use Symfony\Component\HttpFoundation\Response;
 | |
| 
 | |
| class CspService
 | |
| {
 | |
|     /** @var string */
 | |
|     protected $nonce;
 | |
| 
 | |
|     public function __construct(string $nonce = '')
 | |
|     {
 | |
|         $this->nonce = $nonce ?: Str::random(24);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get the nonce value for CSP.
 | |
|      */
 | |
|     public function getNonce(): string
 | |
|     {
 | |
|         return $this->nonce;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Sets CSP 'script-src' headers to restrict the forms of script that can
 | |
|      * run on the page.
 | |
|      */
 | |
|     public function setScriptSrc(Response $response)
 | |
|     {
 | |
|         if (config('app.allow_content_scripts')) {
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         $parts = [
 | |
|             'http:',
 | |
|             'https:',
 | |
|             '\'nonce-' . $this->nonce . '\'',
 | |
|             '\'strict-dynamic\'',
 | |
|         ];
 | |
| 
 | |
|         $value = 'script-src ' . implode(' ', $parts);
 | |
|         $response->headers->set('Content-Security-Policy', $value, false);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Sets CSP "frame-ancestors" headers to restrict the hosts that BookStack can be
 | |
|      * iframed within. Also adjusts the cookie samesite options so that cookies will
 | |
|      * operate in the third-party context.
 | |
|      */
 | |
|     public function setFrameAncestors(Response $response)
 | |
|     {
 | |
|         $iframeHosts = $this->getAllowedIframeHosts();
 | |
|         array_unshift($iframeHosts, "'self'");
 | |
|         $cspValue = 'frame-ancestors ' . implode(' ', $iframeHosts);
 | |
|         $response->headers->set('Content-Security-Policy', $cspValue, false);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Check if the user has configured some allowed iframe hosts.
 | |
|      */
 | |
|     public function allowedIFrameHostsConfigured(): bool
 | |
|     {
 | |
|         return count($this->getAllowedIframeHosts()) > 0;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Sets CSP 'object-src' headers to restrict the types of dynamic content
 | |
|      * that can be embedded on the page.
 | |
|      */
 | |
|     public function setObjectSrc(Response $response)
 | |
|     {
 | |
|         if (config('app.allow_content_scripts')) {
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         $response->headers->set('Content-Security-Policy', 'object-src \'self\'', false);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Sets CSP 'base-uri' headers to restrict what base tags can be set on
 | |
|      * the page to prevent manipulation of relative links.
 | |
|      */
 | |
|     public function setBaseUri(Response $response)
 | |
|     {
 | |
|         $response->headers->set('Content-Security-Policy', 'base-uri \'self\'', false);
 | |
|     }
 | |
| 
 | |
|     protected function getAllowedIframeHosts(): array
 | |
|     {
 | |
|         $hosts = config('app.iframe_hosts', '');
 | |
| 
 | |
|         return array_filter(explode(' ', $hosts));
 | |
|     }
 | |
| }
 |