From a446247a280729643234c47bd197c2b8bec7ec37 Mon Sep 17 00:00:00 2001 From: Josh Date: Sat, 19 Sep 2026 13:51:47 -0400 Subject: [PATCH 1/3] fix(SetupChecks): accept order-independent X-Robots-Tag directives This addresses a portion of the requests in Issue #37409. Signed-off-by: Josh --- apps/settings/lib/SetupChecks/SecurityHeaders.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/settings/lib/SetupChecks/SecurityHeaders.php b/apps/settings/lib/SetupChecks/SecurityHeaders.php index ec4b7a5cc09b0..0be6e4dc0b451 100644 --- a/apps/settings/lib/SetupChecks/SecurityHeaders.php +++ b/apps/settings/lib/SetupChecks/SecurityHeaders.php @@ -53,6 +53,13 @@ public function run(): SetupResult { 'X-Permitted-Cross-Domain-Policies' => ['none', null], ]; + // Normalize header values + $normalize = static function (string $value): string { + $values = array_map('trim', explode(',', strtolower($value))); + sort($values); + return implode(',', $values); + }; + foreach ($urls as [$verb,$url,$validStatuses]) { $works = null; foreach ($this->runRequest($verb, $url, ['httpErrors' => false]) as $response) { @@ -64,8 +71,9 @@ public function run(): SetupResult { $msg = ''; $msgParameters = []; foreach ($securityHeaders as $header => [$expected, $accepted]) { - /* Convert to lowercase and remove spaces after comas */ - $value = preg_replace('/,\s+/', ',', strtolower($response->getHeader($header))); + $value = $normalize($response->getHeader($header)); + $expected = $normalize($expected); + $accepted = $accepted !== null ? $normalize($accepted) : null; if ($value !== $expected) { if ($accepted !== null && $value === $accepted) { $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"; From e0da577f5cadead7ce050674424925a0b394a4e3 Mon Sep 17 00:00:00 2001 From: Josh Date: Sat, 19 Sep 2026 13:58:17 -0400 Subject: [PATCH 2/3] test(settings): cover order-independent X-Robots-Tag directives Signed-off-by: Josh --- apps/settings/tests/SetupChecks/SecurityHeadersTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/settings/tests/SetupChecks/SecurityHeadersTest.php b/apps/settings/tests/SetupChecks/SecurityHeadersTest.php index 7dc4dd970dbb4..9a7a606cff048 100644 --- a/apps/settings/tests/SetupChecks/SecurityHeadersTest.php +++ b/apps/settings/tests/SetupChecks/SecurityHeadersTest.php @@ -96,6 +96,7 @@ public static function dataSuccess(): array { // description => modifiedHeaders 'basic' => [[]], 'no-space-in-x-robots' => [['X-Robots-Tag' => 'noindex,nofollow']], + 'reordered-x-robots' => [['X-Robots-Tag' => 'nofollow, noindex']], 'strict-origin-when-cross-origin' => [['Referrer-Policy' => 'strict-origin-when-cross-origin']], 'referrer-no-referrer-when-downgrade' => [['Referrer-Policy' => 'no-referrer-when-downgrade']], 'referrer-strict-origin' => [['Referrer-Policy' => 'strict-origin']], @@ -137,6 +138,7 @@ public static function dataFailure(): array { return [ // description => modifiedHeaders '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"], + 'x-robots-additional-directive' => [['X-Robots-Tag' => 'noindex,nofollow,noarchive'], "- 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"], '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"], '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"], '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"], From 08849978f6b3a87d297fc415994b9393ff204282 Mon Sep 17 00:00:00 2001 From: Josh Date: Sat, 19 Sep 2026 14:12:45 -0400 Subject: [PATCH 3/3] chore(SetupChecks): preserve the original expected value for display Signed-off-by: Josh --- .../lib/SetupChecks/SecurityHeaders.php | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/apps/settings/lib/SetupChecks/SecurityHeaders.php b/apps/settings/lib/SetupChecks/SecurityHeaders.php index 0be6e4dc0b451..09f8b09b331aa 100644 --- a/apps/settings/lib/SetupChecks/SecurityHeaders.php +++ b/apps/settings/lib/SetupChecks/SecurityHeaders.php @@ -71,14 +71,21 @@ public function run(): SetupResult { $msg = ''; $msgParameters = []; foreach ($securityHeaders as $header => [$expected, $accepted]) { - $value = $normalize($response->getHeader($header)); - $expected = $normalize($expected); - $accepted = $accepted !== null ? $normalize($accepted) : null; - if ($value !== $expected) { - if ($accepted !== null && $value === $accepted) { - $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"; + $normalizedValue = $normalize($response->getHeader($header)); + $normalizedExpected = $normalize($expected); + $normalizedAccepted = $accepted !== null ? $normalize($accepted) : null; + + if ($normalizedValue !== $normalizedExpected) { + if ($normalizedAccepted !== null && $normalizedValue === $normalizedAccepted) { + $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"; } else { - $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"; + $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"; } } }