Completed
Push — master ( ac70e1...875e70 )
by Joas
19:29 queued 15s
created
lib/private/legacy/OC_Response.php 1 patch
Indentation   +71 added lines, -71 removed lines patch added patch discarded remove patch
@@ -6,78 +6,78 @@
 block discarded – undo
6 6
  * SPDX-License-Identifier: AGPL-3.0-only
7 7
  */
8 8
 class OC_Response {
9
-	/**
10
-	 * Sets the content disposition header (with possible workarounds)
11
-	 * @param string $filename file name
12
-	 * @param string $type disposition type, either 'attachment' or 'inline'
13
-	 */
14
-	public static function setContentDispositionHeader($filename, $type = 'attachment') {
15
-		if (\OC::$server->getRequest()->isUserAgent(
16
-			[
17
-				\OC\AppFramework\Http\Request::USER_AGENT_IE,
18
-				\OC\AppFramework\Http\Request::USER_AGENT_ANDROID_MOBILE_CHROME,
19
-				\OC\AppFramework\Http\Request::USER_AGENT_FREEBOX,
20
-			])) {
21
-			header('Content-Disposition: ' . rawurlencode($type) . '; filename="' . rawurlencode($filename) . '"');
22
-		} else {
23
-			header('Content-Disposition: ' . rawurlencode($type) . '; filename*=UTF-8\'\'' . rawurlencode($filename)
24
-												 . '; filename="' . rawurlencode($filename) . '"');
25
-		}
26
-	}
9
+    /**
10
+     * Sets the content disposition header (with possible workarounds)
11
+     * @param string $filename file name
12
+     * @param string $type disposition type, either 'attachment' or 'inline'
13
+     */
14
+    public static function setContentDispositionHeader($filename, $type = 'attachment') {
15
+        if (\OC::$server->getRequest()->isUserAgent(
16
+            [
17
+                \OC\AppFramework\Http\Request::USER_AGENT_IE,
18
+                \OC\AppFramework\Http\Request::USER_AGENT_ANDROID_MOBILE_CHROME,
19
+                \OC\AppFramework\Http\Request::USER_AGENT_FREEBOX,
20
+            ])) {
21
+            header('Content-Disposition: ' . rawurlencode($type) . '; filename="' . rawurlencode($filename) . '"');
22
+        } else {
23
+            header('Content-Disposition: ' . rawurlencode($type) . '; filename*=UTF-8\'\'' . rawurlencode($filename)
24
+                                                    . '; filename="' . rawurlencode($filename) . '"');
25
+        }
26
+    }
27 27
 
28
-	/**
29
-	 * Sets the content length header (with possible workarounds)
30
-	 * @param string|int|float $length Length to be sent
31
-	 */
32
-	public static function setContentLengthHeader($length) {
33
-		if (PHP_INT_SIZE === 4) {
34
-			if ($length > PHP_INT_MAX && stripos(PHP_SAPI, 'apache') === 0) {
35
-				// Apache PHP SAPI casts Content-Length headers to PHP integers.
36
-				// This enforces a limit of PHP_INT_MAX (2147483647 on 32-bit
37
-				// platforms). So, if the length is greater than PHP_INT_MAX,
38
-				// we just do not send a Content-Length header to prevent
39
-				// bodies from being received incompletely.
40
-				return;
41
-			}
42
-			// Convert signed integer or float to unsigned base-10 string.
43
-			$lfh = new \OC\LargeFileHelper;
44
-			$length = $lfh->formatUnsignedInteger($length);
45
-		}
46
-		header('Content-Length: ' . $length);
47
-	}
28
+    /**
29
+     * Sets the content length header (with possible workarounds)
30
+     * @param string|int|float $length Length to be sent
31
+     */
32
+    public static function setContentLengthHeader($length) {
33
+        if (PHP_INT_SIZE === 4) {
34
+            if ($length > PHP_INT_MAX && stripos(PHP_SAPI, 'apache') === 0) {
35
+                // Apache PHP SAPI casts Content-Length headers to PHP integers.
36
+                // This enforces a limit of PHP_INT_MAX (2147483647 on 32-bit
37
+                // platforms). So, if the length is greater than PHP_INT_MAX,
38
+                // we just do not send a Content-Length header to prevent
39
+                // bodies from being received incompletely.
40
+                return;
41
+            }
42
+            // Convert signed integer or float to unsigned base-10 string.
43
+            $lfh = new \OC\LargeFileHelper;
44
+            $length = $lfh->formatUnsignedInteger($length);
45
+        }
46
+        header('Content-Length: ' . $length);
47
+    }
48 48
 
49
-	/**
50
-	 * This function adds some security related headers to all requests served via base.php
51
-	 * The implementation of this function has to happen here to ensure that all third-party
52
-	 * components (e.g. SabreDAV) also benefit from this headers.
53
-	 */
54
-	public static function addSecurityHeaders() {
55
-		/**
56
-		 * FIXME: Content Security Policy for legacy ownCloud components. This
57
-		 * can be removed once \OCP\AppFramework\Http\Response from the AppFramework
58
-		 * is used everywhere.
59
-		 * @see \OCP\AppFramework\Http\Response::getHeaders
60
-		 */
61
-		$policy = 'default-src \'self\'; '
62
-			. 'script-src \'self\' \'nonce-' . \OC::$server->getContentSecurityPolicyNonceManager()->getNonce() . '\'; '
63
-			. 'style-src \'self\' \'unsafe-inline\'; '
64
-			. 'frame-src *; '
65
-			. 'img-src * data: blob:; '
66
-			. 'font-src \'self\' data:; '
67
-			. 'media-src *; '
68
-			. 'connect-src *; '
69
-			. 'object-src \'none\'; '
70
-			. 'base-uri \'self\'; ';
71
-		header('Content-Security-Policy:' . $policy);
49
+    /**
50
+     * This function adds some security related headers to all requests served via base.php
51
+     * The implementation of this function has to happen here to ensure that all third-party
52
+     * components (e.g. SabreDAV) also benefit from this headers.
53
+     */
54
+    public static function addSecurityHeaders() {
55
+        /**
56
+         * FIXME: Content Security Policy for legacy ownCloud components. This
57
+         * can be removed once \OCP\AppFramework\Http\Response from the AppFramework
58
+         * is used everywhere.
59
+         * @see \OCP\AppFramework\Http\Response::getHeaders
60
+         */
61
+        $policy = 'default-src \'self\'; '
62
+            . 'script-src \'self\' \'nonce-' . \OC::$server->getContentSecurityPolicyNonceManager()->getNonce() . '\'; '
63
+            . 'style-src \'self\' \'unsafe-inline\'; '
64
+            . 'frame-src *; '
65
+            . 'img-src * data: blob:; '
66
+            . 'font-src \'self\' data:; '
67
+            . 'media-src *; '
68
+            . 'connect-src *; '
69
+            . 'object-src \'none\'; '
70
+            . 'base-uri \'self\'; ';
71
+        header('Content-Security-Policy:' . $policy);
72 72
 
73
-		// Send fallback headers for installations that don't have the possibility to send
74
-		// custom headers on the webserver side
75
-		if (getenv('modHeadersAvailable') !== 'true') {
76
-			header('Referrer-Policy: no-referrer'); // https://www.w3.org/TR/referrer-policy/
77
-			header('X-Content-Type-Options: nosniff'); // Disable sniffing the content type for IE
78
-			header('X-Frame-Options: SAMEORIGIN'); // Disallow iFraming from other domains
79
-			header('X-Permitted-Cross-Domain-Policies: none'); // https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html
80
-			header('X-Robots-Tag: noindex, nofollow'); // https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag
81
-		}
82
-	}
73
+        // Send fallback headers for installations that don't have the possibility to send
74
+        // custom headers on the webserver side
75
+        if (getenv('modHeadersAvailable') !== 'true') {
76
+            header('Referrer-Policy: no-referrer'); // https://www.w3.org/TR/referrer-policy/
77
+            header('X-Content-Type-Options: nosniff'); // Disable sniffing the content type for IE
78
+            header('X-Frame-Options: SAMEORIGIN'); // Disallow iFraming from other domains
79
+            header('X-Permitted-Cross-Domain-Policies: none'); // https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html
80
+            header('X-Robots-Tag: noindex, nofollow'); // https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag
81
+        }
82
+    }
83 83
 }
Please login to merge, or discard this patch.
apps/settings/lib/SetupChecks/SecurityHeaders.php 2 patches
Indentation   +108 added lines, -108 removed lines patch added patch discarded remove patch
@@ -20,120 +20,120 @@
 block discarded – undo
20 20
 
21 21
 class SecurityHeaders implements ISetupCheck {
22 22
 
23
-	use CheckServerResponseTrait;
23
+    use CheckServerResponseTrait;
24 24
 
25
-	public function __construct(
26
-		protected IL10N $l10n,
27
-		protected IConfig $config,
28
-		protected IURLGenerator $urlGenerator,
29
-		protected IClientService $clientService,
30
-		protected LoggerInterface $logger,
31
-	) {
32
-	}
25
+    public function __construct(
26
+        protected IL10N $l10n,
27
+        protected IConfig $config,
28
+        protected IURLGenerator $urlGenerator,
29
+        protected IClientService $clientService,
30
+        protected LoggerInterface $logger,
31
+    ) {
32
+    }
33 33
 
34
-	public function getCategory(): string {
35
-		return 'security';
36
-	}
34
+    public function getCategory(): string {
35
+        return 'security';
36
+    }
37 37
 
38
-	public function getName(): string {
39
-		return $this->l10n->t('HTTP headers');
40
-	}
38
+    public function getName(): string {
39
+        return $this->l10n->t('HTTP headers');
40
+    }
41 41
 
42
-	public function run(): SetupResult {
43
-		$urls = [
44
-			['get', $this->urlGenerator->linkToRoute('heartbeat'), [200]],
45
-		];
46
-		$securityHeaders = [
47
-			'X-Content-Type-Options' => ['nosniff', null],
48
-			'X-Robots-Tag' => ['noindex,nofollow', null],
49
-			'X-Frame-Options' => ['sameorigin', 'deny'],
50
-			'X-Permitted-Cross-Domain-Policies' => ['none', null],
51
-		];
42
+    public function run(): SetupResult {
43
+        $urls = [
44
+            ['get', $this->urlGenerator->linkToRoute('heartbeat'), [200]],
45
+        ];
46
+        $securityHeaders = [
47
+            'X-Content-Type-Options' => ['nosniff', null],
48
+            'X-Robots-Tag' => ['noindex,nofollow', null],
49
+            'X-Frame-Options' => ['sameorigin', 'deny'],
50
+            'X-Permitted-Cross-Domain-Policies' => ['none', null],
51
+        ];
52 52
 
53
-		foreach ($urls as [$verb,$url,$validStatuses]) {
54
-			$works = null;
55
-			foreach ($this->runRequest($verb, $url, ['httpErrors' => false]) as $response) {
56
-				// Check that the response status matches
57
-				if (!in_array($response->getStatusCode(), $validStatuses)) {
58
-					$works = false;
59
-					continue;
60
-				}
61
-				$msg = '';
62
-				$msgParameters = [];
63
-				foreach ($securityHeaders as $header => [$expected, $accepted]) {
64
-					/* Convert to lowercase and remove spaces after comas */
65
-					$value = preg_replace('/,\s+/', ',', strtolower($response->getHeader($header)));
66
-					if ($value !== $expected) {
67
-						if ($accepted !== null && $value === $accepted) {
68
-							$msg .= $this->l10n->t('- The `%1$s` HTTP header is not set to `%2$s`. Some features might not work correctly, as it is recommended to adjust this setting accordingly.', [$header, $expected]) . "\n";
69
-						} else {
70
-							$msg .= $this->l10n->t('- The `%1$s` HTTP header is not set to `%2$s`. This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly.', [$header, $expected]) . "\n";
71
-						}
72
-					}
73
-				}
53
+        foreach ($urls as [$verb,$url,$validStatuses]) {
54
+            $works = null;
55
+            foreach ($this->runRequest($verb, $url, ['httpErrors' => false]) as $response) {
56
+                // Check that the response status matches
57
+                if (!in_array($response->getStatusCode(), $validStatuses)) {
58
+                    $works = false;
59
+                    continue;
60
+                }
61
+                $msg = '';
62
+                $msgParameters = [];
63
+                foreach ($securityHeaders as $header => [$expected, $accepted]) {
64
+                    /* Convert to lowercase and remove spaces after comas */
65
+                    $value = preg_replace('/,\s+/', ',', strtolower($response->getHeader($header)));
66
+                    if ($value !== $expected) {
67
+                        if ($accepted !== null && $value === $accepted) {
68
+                            $msg .= $this->l10n->t('- The `%1$s` HTTP header is not set to `%2$s`. Some features might not work correctly, as it is recommended to adjust this setting accordingly.', [$header, $expected]) . "\n";
69
+                        } else {
70
+                            $msg .= $this->l10n->t('- The `%1$s` HTTP header is not set to `%2$s`. This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly.', [$header, $expected]) . "\n";
71
+                        }
72
+                    }
73
+                }
74 74
 
75
-				$referrerPolicy = $response->getHeader('Referrer-Policy');
76
-				if (!preg_match('/(no-referrer(-when-downgrade)?|strict-origin(-when-cross-origin)?|same-origin)(,|$)/', $referrerPolicy)) {
77
-					$msg .= $this->l10n->t(
78
-						'- The `%1$s` HTTP header is not set to `%2$s`, `%3$s`, `%4$s`, `%5$s` or `%6$s`. This can leak referer information. See the {w3c-recommendation}.',
79
-						[
80
-							'Referrer-Policy',
81
-							'no-referrer',
82
-							'no-referrer-when-downgrade',
83
-							'strict-origin',
84
-							'strict-origin-when-cross-origin',
85
-							'same-origin',
86
-						]
87
-					) . "\n";
88
-					$msgParameters['w3c-recommendation'] = [
89
-						'type' => 'highlight',
90
-						'id' => 'w3c-recommendation',
91
-						'name' => 'W3C Recommendation',
92
-						'link' => 'https://www.w3.org/TR/referrer-policy/',
93
-					];
94
-				}
75
+                $referrerPolicy = $response->getHeader('Referrer-Policy');
76
+                if (!preg_match('/(no-referrer(-when-downgrade)?|strict-origin(-when-cross-origin)?|same-origin)(,|$)/', $referrerPolicy)) {
77
+                    $msg .= $this->l10n->t(
78
+                        '- The `%1$s` HTTP header is not set to `%2$s`, `%3$s`, `%4$s`, `%5$s` or `%6$s`. This can leak referer information. See the {w3c-recommendation}.',
79
+                        [
80
+                            'Referrer-Policy',
81
+                            'no-referrer',
82
+                            'no-referrer-when-downgrade',
83
+                            'strict-origin',
84
+                            'strict-origin-when-cross-origin',
85
+                            'same-origin',
86
+                        ]
87
+                    ) . "\n";
88
+                    $msgParameters['w3c-recommendation'] = [
89
+                        'type' => 'highlight',
90
+                        'id' => 'w3c-recommendation',
91
+                        'name' => 'W3C Recommendation',
92
+                        'link' => 'https://www.w3.org/TR/referrer-policy/',
93
+                    ];
94
+                }
95 95
 
96
-				$transportSecurityValidity = $response->getHeader('Strict-Transport-Security');
97
-				$minimumSeconds = 15552000;
98
-				if (preg_match('/^max-age=(\d+)(;.*)?$/', $transportSecurityValidity, $m)) {
99
-					$transportSecurityValidity = (int)$m[1];
100
-					if ($transportSecurityValidity < $minimumSeconds) {
101
-						$msg .= $this->l10n->t('- The `Strict-Transport-Security` HTTP header is not set to at least `%d` seconds (current value: `%d`). For enhanced security, it is recommended to use a long HSTS policy.', [$minimumSeconds, $transportSecurityValidity]) . "\n";
102
-					}
103
-				} elseif (!empty($transportSecurityValidity)) {
104
-					$msg .= $this->l10n->t('- The `Strict-Transport-Security` HTTP header is malformed: `%s`. For enhanced security, it is recommended to enable HSTS.', [$transportSecurityValidity]) . "\n";
105
-				} else {
106
-					$msg .= $this->l10n->t('- The `Strict-Transport-Security` HTTP header is not set (should be at least `%d` seconds). For enhanced security, it is recommended to enable HSTS.', [$minimumSeconds]) . "\n";
107
-				}
96
+                $transportSecurityValidity = $response->getHeader('Strict-Transport-Security');
97
+                $minimumSeconds = 15552000;
98
+                if (preg_match('/^max-age=(\d+)(;.*)?$/', $transportSecurityValidity, $m)) {
99
+                    $transportSecurityValidity = (int)$m[1];
100
+                    if ($transportSecurityValidity < $minimumSeconds) {
101
+                        $msg .= $this->l10n->t('- The `Strict-Transport-Security` HTTP header is not set to at least `%d` seconds (current value: `%d`). For enhanced security, it is recommended to use a long HSTS policy.', [$minimumSeconds, $transportSecurityValidity]) . "\n";
102
+                    }
103
+                } elseif (!empty($transportSecurityValidity)) {
104
+                    $msg .= $this->l10n->t('- The `Strict-Transport-Security` HTTP header is malformed: `%s`. For enhanced security, it is recommended to enable HSTS.', [$transportSecurityValidity]) . "\n";
105
+                } else {
106
+                    $msg .= $this->l10n->t('- The `Strict-Transport-Security` HTTP header is not set (should be at least `%d` seconds). For enhanced security, it is recommended to enable HSTS.', [$minimumSeconds]) . "\n";
107
+                }
108 108
 
109
-				if (!empty($msg)) {
110
-					return SetupResult::warning(
111
-						$this->l10n->t('Some headers are not set correctly on your instance') . "\n" . $msg,
112
-						$this->urlGenerator->linkToDocs('admin-security'),
113
-						$msgParameters,
114
-					);
115
-				}
116
-				// Skip the other requests if one works
117
-				$works = true;
118
-				break;
119
-			}
120
-			// If 'works' is null then we could not connect to the server
121
-			if ($works === null) {
122
-				return SetupResult::info(
123
-					$this->l10n->t('Could not check that your web server serves security headers correctly. Please check manually.'),
124
-					$this->urlGenerator->linkToDocs('admin-security'),
125
-				);
126
-			}
127
-			// Otherwise if we fail we can abort here
128
-			if ($works === false) {
129
-				return SetupResult::warning(
130
-					$this->l10n->t('Could not check that your web server serves security headers correctly, unable to query `%s`', [$url]),
131
-					$this->urlGenerator->linkToDocs('admin-security'),
132
-				);
133
-			}
134
-		}
135
-		return SetupResult::success(
136
-			$this->l10n->t('Your server is correctly configured to send security headers.')
137
-		);
138
-	}
109
+                if (!empty($msg)) {
110
+                    return SetupResult::warning(
111
+                        $this->l10n->t('Some headers are not set correctly on your instance') . "\n" . $msg,
112
+                        $this->urlGenerator->linkToDocs('admin-security'),
113
+                        $msgParameters,
114
+                    );
115
+                }
116
+                // Skip the other requests if one works
117
+                $works = true;
118
+                break;
119
+            }
120
+            // If 'works' is null then we could not connect to the server
121
+            if ($works === null) {
122
+                return SetupResult::info(
123
+                    $this->l10n->t('Could not check that your web server serves security headers correctly. Please check manually.'),
124
+                    $this->urlGenerator->linkToDocs('admin-security'),
125
+                );
126
+            }
127
+            // Otherwise if we fail we can abort here
128
+            if ($works === false) {
129
+                return SetupResult::warning(
130
+                    $this->l10n->t('Could not check that your web server serves security headers correctly, unable to query `%s`', [$url]),
131
+                    $this->urlGenerator->linkToDocs('admin-security'),
132
+                );
133
+            }
134
+        }
135
+        return SetupResult::success(
136
+            $this->l10n->t('Your server is correctly configured to send security headers.')
137
+        );
138
+    }
139 139
 }
Please login to merge, or discard this patch.
Spacing   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -50,7 +50,7 @@  discard block
 block discarded – undo
50 50
 			'X-Permitted-Cross-Domain-Policies' => ['none', null],
51 51
 		];
52 52
 
53
-		foreach ($urls as [$verb,$url,$validStatuses]) {
53
+		foreach ($urls as [$verb, $url, $validStatuses]) {
54 54
 			$works = null;
55 55
 			foreach ($this->runRequest($verb, $url, ['httpErrors' => false]) as $response) {
56 56
 				// Check that the response status matches
@@ -65,9 +65,9 @@  discard block
 block discarded – undo
65 65
 					$value = preg_replace('/,\s+/', ',', strtolower($response->getHeader($header)));
66 66
 					if ($value !== $expected) {
67 67
 						if ($accepted !== null && $value === $accepted) {
68
-							$msg .= $this->l10n->t('- The `%1$s` HTTP header is not set to `%2$s`. Some features might not work correctly, as it is recommended to adjust this setting accordingly.', [$header, $expected]) . "\n";
68
+							$msg .= $this->l10n->t('- The `%1$s` HTTP header is not set to `%2$s`. Some features might not work correctly, as it is recommended to adjust this setting accordingly.', [$header, $expected])."\n";
69 69
 						} else {
70
-							$msg .= $this->l10n->t('- The `%1$s` HTTP header is not set to `%2$s`. This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly.', [$header, $expected]) . "\n";
70
+							$msg .= $this->l10n->t('- The `%1$s` HTTP header is not set to `%2$s`. This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly.', [$header, $expected])."\n";
71 71
 						}
72 72
 					}
73 73
 				}
@@ -84,7 +84,7 @@  discard block
 block discarded – undo
84 84
 							'strict-origin-when-cross-origin',
85 85
 							'same-origin',
86 86
 						]
87
-					) . "\n";
87
+					)."\n";
88 88
 					$msgParameters['w3c-recommendation'] = [
89 89
 						'type' => 'highlight',
90 90
 						'id' => 'w3c-recommendation',
@@ -96,19 +96,19 @@  discard block
 block discarded – undo
96 96
 				$transportSecurityValidity = $response->getHeader('Strict-Transport-Security');
97 97
 				$minimumSeconds = 15552000;
98 98
 				if (preg_match('/^max-age=(\d+)(;.*)?$/', $transportSecurityValidity, $m)) {
99
-					$transportSecurityValidity = (int)$m[1];
99
+					$transportSecurityValidity = (int) $m[1];
100 100
 					if ($transportSecurityValidity < $minimumSeconds) {
101
-						$msg .= $this->l10n->t('- The `Strict-Transport-Security` HTTP header is not set to at least `%d` seconds (current value: `%d`). For enhanced security, it is recommended to use a long HSTS policy.', [$minimumSeconds, $transportSecurityValidity]) . "\n";
101
+						$msg .= $this->l10n->t('- The `Strict-Transport-Security` HTTP header is not set to at least `%d` seconds (current value: `%d`). For enhanced security, it is recommended to use a long HSTS policy.', [$minimumSeconds, $transportSecurityValidity])."\n";
102 102
 					}
103 103
 				} elseif (!empty($transportSecurityValidity)) {
104
-					$msg .= $this->l10n->t('- The `Strict-Transport-Security` HTTP header is malformed: `%s`. For enhanced security, it is recommended to enable HSTS.', [$transportSecurityValidity]) . "\n";
104
+					$msg .= $this->l10n->t('- The `Strict-Transport-Security` HTTP header is malformed: `%s`. For enhanced security, it is recommended to enable HSTS.', [$transportSecurityValidity])."\n";
105 105
 				} else {
106
-					$msg .= $this->l10n->t('- The `Strict-Transport-Security` HTTP header is not set (should be at least `%d` seconds). For enhanced security, it is recommended to enable HSTS.', [$minimumSeconds]) . "\n";
106
+					$msg .= $this->l10n->t('- The `Strict-Transport-Security` HTTP header is not set (should be at least `%d` seconds). For enhanced security, it is recommended to enable HSTS.', [$minimumSeconds])."\n";
107 107
 				}
108 108
 
109 109
 				if (!empty($msg)) {
110 110
 					return SetupResult::warning(
111
-						$this->l10n->t('Some headers are not set correctly on your instance') . "\n" . $msg,
111
+						$this->l10n->t('Some headers are not set correctly on your instance')."\n".$msg,
112 112
 						$this->urlGenerator->linkToDocs('admin-security'),
113 113
 						$msgParameters,
114 114
 					);
Please login to merge, or discard this patch.
apps/settings/tests/SetupChecks/SecurityHeadersTest.php 1 patch
Indentation   +177 added lines, -177 removed lines patch added patch discarded remove patch
@@ -20,181 +20,181 @@
 block discarded – undo
20 20
 use Test\TestCase;
21 21
 
22 22
 class SecurityHeadersTest extends TestCase {
23
-	private IL10N&MockObject $l10n;
24
-	private IConfig&MockObject $config;
25
-	private IURLGenerator&MockObject $urlGenerator;
26
-	private IClientService&MockObject $clientService;
27
-	private LoggerInterface&MockObject $logger;
28
-	private SecurityHeaders&MockObject $setupcheck;
29
-
30
-	protected function setUp(): void {
31
-		parent::setUp();
32
-
33
-		$this->l10n = $this->createMock(IL10N::class);
34
-		$this->l10n->expects($this->any())
35
-			->method('t')
36
-			->willReturnCallback(function ($message, array $replace) {
37
-				return vsprintf($message, $replace);
38
-			});
39
-
40
-		$this->config = $this->createMock(IConfig::class);
41
-		$this->urlGenerator = $this->createMock(IURLGenerator::class);
42
-		$this->clientService = $this->createMock(IClientService::class);
43
-		$this->logger = $this->createMock(LoggerInterface::class);
44
-
45
-		$this->setupcheck = $this->getMockBuilder(SecurityHeaders::class)
46
-			->onlyMethods(['runRequest'])
47
-			->setConstructorArgs([
48
-				$this->l10n,
49
-				$this->config,
50
-				$this->urlGenerator,
51
-				$this->clientService,
52
-				$this->logger,
53
-			])
54
-			->getMock();
55
-	}
56
-
57
-	public function testInvalidStatusCode(): void {
58
-		$this->setupResponse(500, []);
59
-
60
-		$result = $this->setupcheck->run();
61
-		$this->assertMatchesRegularExpression('/^Could not check that your web server serves security headers correctly/', $result->getDescription());
62
-		$this->assertEquals(SetupResult::WARNING, $result->getSeverity());
63
-	}
64
-
65
-	public function testAllHeadersMissing(): void {
66
-		$this->setupResponse(200, []);
67
-
68
-		$result = $this->setupcheck->run();
69
-		$this->assertMatchesRegularExpression('/^Some headers are not set correctly on your instance/', $result->getDescription());
70
-		$this->assertEquals(SetupResult::WARNING, $result->getSeverity());
71
-	}
72
-
73
-	public function testSomeHeadersMissing(): void {
74
-		$this->setupResponse(
75
-			200,
76
-			[
77
-				'X-Robots-Tag' => 'noindex, nofollow',
78
-				'X-Frame-Options' => 'SAMEORIGIN',
79
-				'Strict-Transport-Security' => 'max-age=15768000;preload',
80
-				'X-Permitted-Cross-Domain-Policies' => 'none',
81
-				'Referrer-Policy' => 'no-referrer',
82
-			]
83
-		);
84
-
85
-		$result = $this->setupcheck->run();
86
-		$this->assertEquals(
87
-			"Some headers are not set correctly on your instance\n- The `X-Content-Type-Options` HTTP header is not set to `nosniff`. This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly.\n",
88
-			$result->getDescription()
89
-		);
90
-		$this->assertEquals(SetupResult::WARNING, $result->getSeverity());
91
-	}
92
-
93
-	public static function dataSuccess(): array {
94
-		return [
95
-			// description => modifiedHeaders
96
-			'basic' => [[]],
97
-			'no-space-in-x-robots' => [['X-Robots-Tag' => 'noindex,nofollow']],
98
-			'strict-origin-when-cross-origin' => [['Referrer-Policy' => 'strict-origin-when-cross-origin']],
99
-			'referrer-no-referrer-when-downgrade' => [['Referrer-Policy' => 'no-referrer-when-downgrade']],
100
-			'referrer-strict-origin' => [['Referrer-Policy' => 'strict-origin']],
101
-			'referrer-strict-origin-when-cross-origin' => [['Referrer-Policy' => 'strict-origin-when-cross-origin']],
102
-			'referrer-same-origin' => [['Referrer-Policy' => 'same-origin']],
103
-			'hsts-minimum' => [['Strict-Transport-Security' => 'max-age=15552000']],
104
-			'hsts-include-subdomains' => [['Strict-Transport-Security' => 'max-age=99999999; includeSubDomains']],
105
-			'hsts-include-subdomains-preload' => [['Strict-Transport-Security' => 'max-age=99999999; preload; includeSubDomains']],
106
-		];
107
-	}
108
-
109
-	/**
110
-	 * @dataProvider dataSuccess
111
-	 */
112
-	public function testSuccess(array $headers): void {
113
-		$headers = array_merge(
114
-			[
115
-				'X-Content-Type-Options' => 'nosniff',
116
-				'X-Robots-Tag' => 'noindex, nofollow',
117
-				'X-Frame-Options' => 'SAMEORIGIN',
118
-				'Strict-Transport-Security' => 'max-age=15768000',
119
-				'X-Permitted-Cross-Domain-Policies' => 'none',
120
-				'Referrer-Policy' => 'no-referrer',
121
-			],
122
-			$headers
123
-		);
124
-		$this->setupResponse(
125
-			200,
126
-			$headers
127
-		);
128
-
129
-		$result = $this->setupcheck->run();
130
-		$this->assertEquals(
131
-			'Your server is correctly configured to send security headers.',
132
-			$result->getDescription()
133
-		);
134
-		$this->assertEquals(SetupResult::SUCCESS, $result->getSeverity());
135
-	}
136
-
137
-	public static function dataFailure(): array {
138
-		return [
139
-			// description => modifiedHeaders
140
-			'x-robots-none' => [['X-Robots-Tag' => 'none'], "- The `X-Robots-Tag` HTTP header is not set to `noindex,nofollow`. This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly.\n"],
141
-			'referrer-origin' => [['Referrer-Policy' => 'origin'], "- The `Referrer-Policy` HTTP header is not set to `no-referrer`, `no-referrer-when-downgrade`, `strict-origin`, `strict-origin-when-cross-origin` or `same-origin`. This can leak referer information. See the {w3c-recommendation}.\n"],
142
-			'referrer-origin-when-cross-origin' => [['Referrer-Policy' => 'origin-when-cross-origin'], "- The `Referrer-Policy` HTTP header is not set to `no-referrer`, `no-referrer-when-downgrade`, `strict-origin`, `strict-origin-when-cross-origin` or `same-origin`. This can leak referer information. See the {w3c-recommendation}.\n"],
143
-			'referrer-unsafe-url' => [['Referrer-Policy' => 'unsafe-url'], "- The `Referrer-Policy` HTTP header is not set to `no-referrer`, `no-referrer-when-downgrade`, `strict-origin`, `strict-origin-when-cross-origin` or `same-origin`. This can leak referer information. See the {w3c-recommendation}.\n"],
144
-			'hsts-missing' => [['Strict-Transport-Security' => ''], "- The `Strict-Transport-Security` HTTP header is not set (should be at least `15552000` seconds). For enhanced security, it is recommended to enable HSTS.\n"],
145
-			'hsts-too-low' => [['Strict-Transport-Security' => 'max-age=15551999'], "- The `Strict-Transport-Security` HTTP header is not set to at least `15552000` seconds (current value: `15551999`). For enhanced security, it is recommended to use a long HSTS policy.\n"],
146
-			'hsts-malformed' => [['Strict-Transport-Security' => 'iAmABogusHeader342'], "- The `Strict-Transport-Security` HTTP header is malformed: `iAmABogusHeader342`. For enhanced security, it is recommended to enable HSTS.\n"],
147
-		];
148
-	}
149
-
150
-	/**
151
-	 * @dataProvider dataFailure
152
-	 */
153
-	public function testFailure(array $headers, string $msg): void {
154
-		$headers = array_merge(
155
-			[
156
-				'X-Content-Type-Options' => 'nosniff',
157
-				'X-Robots-Tag' => 'noindex, nofollow',
158
-				'X-Frame-Options' => 'SAMEORIGIN',
159
-				'Strict-Transport-Security' => 'max-age=15768000',
160
-				'X-Permitted-Cross-Domain-Policies' => 'none',
161
-				'Referrer-Policy' => 'no-referrer',
162
-			],
163
-			$headers
164
-		);
165
-		$this->setupResponse(
166
-			200,
167
-			$headers
168
-		);
169
-
170
-		$result = $this->setupcheck->run();
171
-		$this->assertEquals(
172
-			'Some headers are not set correctly on your instance' . "\n$msg",
173
-			$result->getDescription()
174
-		);
175
-		$this->assertEquals(SetupResult::WARNING, $result->getSeverity());
176
-	}
177
-
178
-	protected function setupResponse(int $statuscode, array $headers): void {
179
-		$response = $this->createMock(IResponse::class);
180
-		$response->expects($this->atLeastOnce())->method('getStatusCode')->willReturn($statuscode);
181
-		$response->expects($this->any())->method('getHeader')
182
-			->willReturnCallback(
183
-				fn (string $header): string => $headers[$header] ?? ''
184
-			);
185
-
186
-		$this->setupcheck
187
-			->expects($this->atLeastOnce())
188
-			->method('runRequest')
189
-			->willReturnOnConsecutiveCalls($this->generate([$response]));
190
-	}
191
-
192
-	/**
193
-	 * Helper function creates a nicer interface for mocking Generator behavior
194
-	 */
195
-	protected function generate(array $yield_values) {
196
-		return $this->returnCallback(function () use ($yield_values) {
197
-			yield from $yield_values;
198
-		});
199
-	}
23
+    private IL10N&MockObject $l10n;
24
+    private IConfig&MockObject $config;
25
+    private IURLGenerator&MockObject $urlGenerator;
26
+    private IClientService&MockObject $clientService;
27
+    private LoggerInterface&MockObject $logger;
28
+    private SecurityHeaders&MockObject $setupcheck;
29
+
30
+    protected function setUp(): void {
31
+        parent::setUp();
32
+
33
+        $this->l10n = $this->createMock(IL10N::class);
34
+        $this->l10n->expects($this->any())
35
+            ->method('t')
36
+            ->willReturnCallback(function ($message, array $replace) {
37
+                return vsprintf($message, $replace);
38
+            });
39
+
40
+        $this->config = $this->createMock(IConfig::class);
41
+        $this->urlGenerator = $this->createMock(IURLGenerator::class);
42
+        $this->clientService = $this->createMock(IClientService::class);
43
+        $this->logger = $this->createMock(LoggerInterface::class);
44
+
45
+        $this->setupcheck = $this->getMockBuilder(SecurityHeaders::class)
46
+            ->onlyMethods(['runRequest'])
47
+            ->setConstructorArgs([
48
+                $this->l10n,
49
+                $this->config,
50
+                $this->urlGenerator,
51
+                $this->clientService,
52
+                $this->logger,
53
+            ])
54
+            ->getMock();
55
+    }
56
+
57
+    public function testInvalidStatusCode(): void {
58
+        $this->setupResponse(500, []);
59
+
60
+        $result = $this->setupcheck->run();
61
+        $this->assertMatchesRegularExpression('/^Could not check that your web server serves security headers correctly/', $result->getDescription());
62
+        $this->assertEquals(SetupResult::WARNING, $result->getSeverity());
63
+    }
64
+
65
+    public function testAllHeadersMissing(): void {
66
+        $this->setupResponse(200, []);
67
+
68
+        $result = $this->setupcheck->run();
69
+        $this->assertMatchesRegularExpression('/^Some headers are not set correctly on your instance/', $result->getDescription());
70
+        $this->assertEquals(SetupResult::WARNING, $result->getSeverity());
71
+    }
72
+
73
+    public function testSomeHeadersMissing(): void {
74
+        $this->setupResponse(
75
+            200,
76
+            [
77
+                'X-Robots-Tag' => 'noindex, nofollow',
78
+                'X-Frame-Options' => 'SAMEORIGIN',
79
+                'Strict-Transport-Security' => 'max-age=15768000;preload',
80
+                'X-Permitted-Cross-Domain-Policies' => 'none',
81
+                'Referrer-Policy' => 'no-referrer',
82
+            ]
83
+        );
84
+
85
+        $result = $this->setupcheck->run();
86
+        $this->assertEquals(
87
+            "Some headers are not set correctly on your instance\n- The `X-Content-Type-Options` HTTP header is not set to `nosniff`. This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly.\n",
88
+            $result->getDescription()
89
+        );
90
+        $this->assertEquals(SetupResult::WARNING, $result->getSeverity());
91
+    }
92
+
93
+    public static function dataSuccess(): array {
94
+        return [
95
+            // description => modifiedHeaders
96
+            'basic' => [[]],
97
+            'no-space-in-x-robots' => [['X-Robots-Tag' => 'noindex,nofollow']],
98
+            'strict-origin-when-cross-origin' => [['Referrer-Policy' => 'strict-origin-when-cross-origin']],
99
+            'referrer-no-referrer-when-downgrade' => [['Referrer-Policy' => 'no-referrer-when-downgrade']],
100
+            'referrer-strict-origin' => [['Referrer-Policy' => 'strict-origin']],
101
+            'referrer-strict-origin-when-cross-origin' => [['Referrer-Policy' => 'strict-origin-when-cross-origin']],
102
+            'referrer-same-origin' => [['Referrer-Policy' => 'same-origin']],
103
+            'hsts-minimum' => [['Strict-Transport-Security' => 'max-age=15552000']],
104
+            'hsts-include-subdomains' => [['Strict-Transport-Security' => 'max-age=99999999; includeSubDomains']],
105
+            'hsts-include-subdomains-preload' => [['Strict-Transport-Security' => 'max-age=99999999; preload; includeSubDomains']],
106
+        ];
107
+    }
108
+
109
+    /**
110
+     * @dataProvider dataSuccess
111
+     */
112
+    public function testSuccess(array $headers): void {
113
+        $headers = array_merge(
114
+            [
115
+                'X-Content-Type-Options' => 'nosniff',
116
+                'X-Robots-Tag' => 'noindex, nofollow',
117
+                'X-Frame-Options' => 'SAMEORIGIN',
118
+                'Strict-Transport-Security' => 'max-age=15768000',
119
+                'X-Permitted-Cross-Domain-Policies' => 'none',
120
+                'Referrer-Policy' => 'no-referrer',
121
+            ],
122
+            $headers
123
+        );
124
+        $this->setupResponse(
125
+            200,
126
+            $headers
127
+        );
128
+
129
+        $result = $this->setupcheck->run();
130
+        $this->assertEquals(
131
+            'Your server is correctly configured to send security headers.',
132
+            $result->getDescription()
133
+        );
134
+        $this->assertEquals(SetupResult::SUCCESS, $result->getSeverity());
135
+    }
136
+
137
+    public static function dataFailure(): array {
138
+        return [
139
+            // description => modifiedHeaders
140
+            'x-robots-none' => [['X-Robots-Tag' => 'none'], "- The `X-Robots-Tag` HTTP header is not set to `noindex,nofollow`. This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly.\n"],
141
+            'referrer-origin' => [['Referrer-Policy' => 'origin'], "- The `Referrer-Policy` HTTP header is not set to `no-referrer`, `no-referrer-when-downgrade`, `strict-origin`, `strict-origin-when-cross-origin` or `same-origin`. This can leak referer information. See the {w3c-recommendation}.\n"],
142
+            'referrer-origin-when-cross-origin' => [['Referrer-Policy' => 'origin-when-cross-origin'], "- The `Referrer-Policy` HTTP header is not set to `no-referrer`, `no-referrer-when-downgrade`, `strict-origin`, `strict-origin-when-cross-origin` or `same-origin`. This can leak referer information. See the {w3c-recommendation}.\n"],
143
+            'referrer-unsafe-url' => [['Referrer-Policy' => 'unsafe-url'], "- The `Referrer-Policy` HTTP header is not set to `no-referrer`, `no-referrer-when-downgrade`, `strict-origin`, `strict-origin-when-cross-origin` or `same-origin`. This can leak referer information. See the {w3c-recommendation}.\n"],
144
+            'hsts-missing' => [['Strict-Transport-Security' => ''], "- The `Strict-Transport-Security` HTTP header is not set (should be at least `15552000` seconds). For enhanced security, it is recommended to enable HSTS.\n"],
145
+            'hsts-too-low' => [['Strict-Transport-Security' => 'max-age=15551999'], "- The `Strict-Transport-Security` HTTP header is not set to at least `15552000` seconds (current value: `15551999`). For enhanced security, it is recommended to use a long HSTS policy.\n"],
146
+            'hsts-malformed' => [['Strict-Transport-Security' => 'iAmABogusHeader342'], "- The `Strict-Transport-Security` HTTP header is malformed: `iAmABogusHeader342`. For enhanced security, it is recommended to enable HSTS.\n"],
147
+        ];
148
+    }
149
+
150
+    /**
151
+     * @dataProvider dataFailure
152
+     */
153
+    public function testFailure(array $headers, string $msg): void {
154
+        $headers = array_merge(
155
+            [
156
+                'X-Content-Type-Options' => 'nosniff',
157
+                'X-Robots-Tag' => 'noindex, nofollow',
158
+                'X-Frame-Options' => 'SAMEORIGIN',
159
+                'Strict-Transport-Security' => 'max-age=15768000',
160
+                'X-Permitted-Cross-Domain-Policies' => 'none',
161
+                'Referrer-Policy' => 'no-referrer',
162
+            ],
163
+            $headers
164
+        );
165
+        $this->setupResponse(
166
+            200,
167
+            $headers
168
+        );
169
+
170
+        $result = $this->setupcheck->run();
171
+        $this->assertEquals(
172
+            'Some headers are not set correctly on your instance' . "\n$msg",
173
+            $result->getDescription()
174
+        );
175
+        $this->assertEquals(SetupResult::WARNING, $result->getSeverity());
176
+    }
177
+
178
+    protected function setupResponse(int $statuscode, array $headers): void {
179
+        $response = $this->createMock(IResponse::class);
180
+        $response->expects($this->atLeastOnce())->method('getStatusCode')->willReturn($statuscode);
181
+        $response->expects($this->any())->method('getHeader')
182
+            ->willReturnCallback(
183
+                fn (string $header): string => $headers[$header] ?? ''
184
+            );
185
+
186
+        $this->setupcheck
187
+            ->expects($this->atLeastOnce())
188
+            ->method('runRequest')
189
+            ->willReturnOnConsecutiveCalls($this->generate([$response]));
190
+    }
191
+
192
+    /**
193
+     * Helper function creates a nicer interface for mocking Generator behavior
194
+     */
195
+    protected function generate(array $yield_values) {
196
+        return $this->returnCallback(function () use ($yield_values) {
197
+            yield from $yield_values;
198
+        });
199
+    }
200 200
 }
Please login to merge, or discard this patch.