]> BookStack Code Mirror - bookstack/blob - app/Util/CspService.php
Added testing for our request method overrides
[bookstack] / app / Util / CspService.php
1 <?php
2
3 namespace BookStack\Util;
4
5 use Illuminate\Support\Str;
6
7 class CspService
8 {
9     protected string $nonce;
10
11     public function __construct(string $nonce = '')
12     {
13         $this->nonce = $nonce ?: Str::random(24);
14     }
15
16     /**
17      * Get the nonce value for CSP.
18      */
19     public function getNonce(): string
20     {
21         return $this->nonce;
22     }
23
24     /**
25      * Get the CSP headers for the application.
26      */
27     public function getCspHeader(): string
28     {
29         $headers = [
30             $this->getFrameAncestors(),
31             $this->getFrameSrc(),
32             $this->getScriptSrc(),
33             $this->getObjectSrc(),
34             $this->getBaseUri(),
35         ];
36
37         return implode('; ', array_filter($headers));
38     }
39
40     /**
41      * Get the CSP rules for the application for a HTML meta tag.
42      */
43     public function getCspMetaTagValue(): string
44     {
45         $headers = [
46             $this->getFrameSrc(),
47             $this->getScriptSrc(),
48             $this->getObjectSrc(),
49             $this->getBaseUri(),
50         ];
51
52         return implode('; ', array_filter($headers));
53     }
54
55     /**
56      * Check if the user has configured some allowed iframe hosts.
57      */
58     public function allowedIFrameHostsConfigured(): bool
59     {
60         return count($this->getAllowedIframeHosts()) > 0;
61     }
62
63     /**
64      * Create CSP 'script-src' rule to restrict the forms of script that can run on the page.
65      */
66     protected function getScriptSrc(): string
67     {
68         if (config('app.allow_content_scripts')) {
69             return '';
70         }
71
72         $parts = [
73             'http:',
74             'https:',
75             '\'nonce-' . $this->nonce . '\'',
76             '\'strict-dynamic\'',
77         ];
78
79         return 'script-src ' . implode(' ', $parts);
80     }
81
82     /**
83      * Create CSP "frame-ancestors" rule to restrict the hosts that BookStack can be iframed within.
84      */
85     protected function getFrameAncestors(): string
86     {
87         $iframeHosts = $this->getAllowedIframeHosts();
88         array_unshift($iframeHosts, "'self'");
89
90         return 'frame-ancestors ' . implode(' ', $iframeHosts);
91     }
92
93     /**
94      * Creates CSP "frame-src" rule to restrict what hosts/sources can be loaded
95      * within iframes to provide an allow-list-style approach to iframe content.
96      */
97     protected function getFrameSrc(): string
98     {
99         $iframeHosts = $this->getAllowedIframeSources();
100         array_unshift($iframeHosts, "'self'");
101
102         return 'frame-src ' . implode(' ', $iframeHosts);
103     }
104
105     /**
106      * Creates CSP 'object-src' rule to restrict the types of dynamic content
107      * that can be embedded on the page.
108      */
109     protected function getObjectSrc(): string
110     {
111         if (config('app.allow_content_scripts')) {
112             return '';
113         }
114
115         return "object-src 'self'";
116     }
117
118     /**
119      * Creates CSP 'base-uri' rule to restrict what base tags can be set on
120      * the page to prevent manipulation of relative links.
121      */
122     protected function getBaseUri(): string
123     {
124         return "base-uri 'self'";
125     }
126
127     protected function getAllowedIframeHosts(): array
128     {
129         $hosts = config('app.iframe_hosts', '');
130
131         return array_filter(explode(' ', $hosts));
132     }
133
134     protected function getAllowedIframeSources(): array
135     {
136         $sources = config('app.iframe_sources', '');
137         $hosts = array_filter(explode(' ', $sources));
138
139         // Extract drawing service url to allow embedding if active
140         $drawioConfigValue = config('services.drawio');
141         if ($drawioConfigValue) {
142             $drawioSource = is_string($drawioConfigValue) ? $drawioConfigValue : 'https://embed.diagrams.net/';
143             $drawioSourceParsed = parse_url($drawioSource);
144             $drawioHost = $drawioSourceParsed['scheme'] . '://' . $drawioSourceParsed['host'];
145             $hosts[] = $drawioHost;
146         }
147
148         return $hosts;
149     }
150 }
Morty Proxy This is a proxified and sanitized view of the page, visit original site.