From 0ca80a24c84742cd0392fe9d4264d38f24c81407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C5=A0pa=C4=8Dek?= Date: Tue, 9 Dec 2025 02:58:03 +0100 Subject: [PATCH 1/2] Move Content Type header values to a separate class This will make it easier to use those in projects where `SecurityTxt` as a name is already a thing, to avoid at least some conflicts. --- README.md | 8 +++++--- ...ype.php => SecurityTxtFetchHostContentType.php} | 2 +- src/Fetcher/SecurityTxtFetcher.php | 6 +++--- src/Fetcher/SecurityTxtFetcherFetchHostResult.php | 6 +++--- src/SecurityTxt.php | 4 ---- src/SecurityTxtContentType.php | 14 ++++++++++++++ src/Violations/SecurityTxtContentTypeInvalid.php | 10 +++++----- .../SecurityTxtContentTypeWrongCharset.php | 8 ++++---- tests/Fetcher/SecurityTxtFetcherTest.phpt | 4 ++-- 9 files changed, 37 insertions(+), 25 deletions(-) rename src/Fetcher/{SecurityTxtContentType.php => SecurityTxtFetchHostContentType.php} (94%) create mode 100644 src/SecurityTxtContentType.php diff --git a/README.md b/README.md index 1f98d36..850b71e 100644 --- a/README.md +++ b/README.md @@ -111,10 +111,12 @@ By default, values are validated when set, and an exception is thrown when they' - `AllowInvalidValues` (an exception will be thrown but the value will still be set) - `AllowInvalidValuesSilently` (an exception will not be thrown, and the value will be set) +## Content type You can use the following `SecurityTxt` constants to serve the file with correct HTTP content type: -- `SecurityTxt::CONTENT_TYPE_HEADER`, the value to be sent as `Content-Type` header value (`text/plain; charset=utf-8`); -- `SecurityTxt::CONTENT_TYPE`, the correct content type `text/plain` -- `SecurityTxt::CHARSET`, the correct charset as `charset=utf-8` +- `SecurityTxtContentType::MEDIA_TYPE`, the value to be sent as `Content-Type` header value (`text/plain; charset=utf-8`); +- `SecurityTxtContentType::CONTENT_TYPE`, the correct content type `text/plain` +- `SecurityTxtContentType::CHARSET`, the charset `utf-8` +- `SecurityTxtContentType::CHARSET_PARAMETER`, the correct charset parameter name and value as `charset=utf-8` ## Example ```php diff --git a/src/Fetcher/SecurityTxtContentType.php b/src/Fetcher/SecurityTxtFetchHostContentType.php similarity index 94% rename from src/Fetcher/SecurityTxtContentType.php rename to src/Fetcher/SecurityTxtFetchHostContentType.php index 8d527e6..5cfef93 100644 --- a/src/Fetcher/SecurityTxtContentType.php +++ b/src/Fetcher/SecurityTxtFetchHostContentType.php @@ -3,7 +3,7 @@ namespace Spaze\SecurityTxt\Fetcher; -final readonly class SecurityTxtContentType +final readonly class SecurityTxtFetchHostContentType { /** diff --git a/src/Fetcher/SecurityTxtFetcher.php b/src/Fetcher/SecurityTxtFetcher.php index 6d7e341..3698c19 100644 --- a/src/Fetcher/SecurityTxtFetcher.php +++ b/src/Fetcher/SecurityTxtFetcher.php @@ -17,7 +17,7 @@ use Spaze\SecurityTxt\Fetcher\HttpClients\SecurityTxtFetcherHttpClient; use Spaze\SecurityTxt\Parser\SecurityTxtSplitLines; use Spaze\SecurityTxt\Parser\SecurityTxtUrlParser; -use Spaze\SecurityTxt\SecurityTxt; +use Spaze\SecurityTxt\SecurityTxtContentType; use Spaze\SecurityTxt\Violations\SecurityTxtContentTypeInvalid; use Spaze\SecurityTxt\Violations\SecurityTxtContentTypeWrongCharset; use Spaze\SecurityTxt\Violations\SecurityTxtTopLevelDiffers; @@ -213,9 +213,9 @@ private function getResult(SecurityTxtFetcherFetchHostResult $wellKnown, Securit $this->callOnCallback($this->onFinalUrl, $result->getFinalUrl()); $contentTypeHeader = $result->getContentType(); - if ($contentTypeHeader === null || $contentTypeHeader->getLowercaseContentType() !== SecurityTxt::CONTENT_TYPE) { + if ($contentTypeHeader === null || $contentTypeHeader->getLowercaseContentType() !== SecurityTxtContentType::CONTENT_TYPE) { $errors[] = new SecurityTxtContentTypeInvalid($result->getUrl(), $contentTypeHeader?->getContentType()); - } elseif ($contentTypeHeader->getLowercaseCharset() !== SecurityTxt::CHARSET) { + } elseif ($contentTypeHeader->getLowercaseCharset() !== SecurityTxtContentType::CHARSET_PARAMETER) { $errors[] = new SecurityTxtContentTypeWrongCharset($result->getUrl(), $contentTypeHeader->getContentType(), $contentTypeHeader->getCharset()); } return new SecurityTxtFetchResult( diff --git a/src/Fetcher/SecurityTxtFetcherFetchHostResult.php b/src/Fetcher/SecurityTxtFetcherFetchHostResult.php index 77f96f1..6cf4faa 100644 --- a/src/Fetcher/SecurityTxtFetcherFetchHostResult.php +++ b/src/Fetcher/SecurityTxtFetcherFetchHostResult.php @@ -9,7 +9,7 @@ final readonly class SecurityTxtFetcherFetchHostResult { - private ?SecurityTxtContentType $contentType; + private ?SecurityTxtFetchHostContentType $contentType; private ?string $contents; @@ -32,7 +32,7 @@ public function __construct( $this->contentType = null; } else { $parts = explode(';', $header, 2); - $this->contentType = new SecurityTxtContentType($parts[0], $parts[1] ?? null); + $this->contentType = new SecurityTxtFetchHostContentType($parts[0], $parts[1] ?? null); } $this->contents = $response?->getContents(); $this->isTruncated = $response !== null && $response->isTruncated(); @@ -78,7 +78,7 @@ public function isTruncated(): bool } - public function getContentType(): ?SecurityTxtContentType + public function getContentType(): ?SecurityTxtFetchHostContentType { return $this->contentType; } diff --git a/src/SecurityTxt.php b/src/SecurityTxt.php index 4c7f8be..5208137 100644 --- a/src/SecurityTxt.php +++ b/src/SecurityTxt.php @@ -40,10 +40,6 @@ final class SecurityTxt implements JsonSerializable { - public const string CONTENT_TYPE = 'text/plain'; - public const string CHARSET = 'charset=utf-8'; - public const string CONTENT_TYPE_HEADER = self::CONTENT_TYPE . '; ' . self::CHARSET; - private ?string $fileLocation = null; private ?SecurityTxtExpires $expires = null; private ?SecurityTxtSignatureVerifyResult $signatureVerifyResult = null; diff --git a/src/SecurityTxtContentType.php b/src/SecurityTxtContentType.php new file mode 100644 index 0000000..c782fee --- /dev/null +++ b/src/SecurityTxtContentType.php @@ -0,0 +1,14 @@ +getHttpClient(new SecurityTxtFetcherResponse(123, [], 'random', false)); $fetcher = new SecurityTxtFetcher($httpClient, $this->urlParser, $this->splitLines); - $fetcherResponseWellKnown = new SecurityTxtFetcherResponse(200, ['content-type' => SecurityTxt::CONTENT_TYPE_HEADER], $contentsWellKnown, false); + $fetcherResponseWellKnown = new SecurityTxtFetcherResponse(200, ['content-type' => SecurityTxtContentType::MEDIA_TYPE], $contentsWellKnown, false); $fetcherResponseTopLevel = new SecurityTxtFetcherResponse(200, [], $contentsTopLevel, false); $wellKnown = new SecurityTxtFetcherFetchHostResult('https://url1.example/', $finalUrlWellKnown, '192.0.2.1', DNS_A, 200, $fetcherResponseWellKnown); $topLevel = new SecurityTxtFetcherFetchHostResult('https://url2.example/', $finalUrlTopLevel, '198.51.100.1', DNS_A, 200, $fetcherResponseTopLevel); From 71f7a16f34e6d4ed28fd6d213473d9a143df3b6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C5=A0pa=C4=8Dek?= Date: Tue, 9 Dec 2025 03:01:44 +0100 Subject: [PATCH 2/2] Add "parameter" suffix to "charset" where it makes sense "Charset" is just `utf-8`, while "charset parameter" is the whole string `charset=utf-8`. --- README.md | 2 +- src/Fetcher/SecurityTxtFetchHostContentType.php | 14 +++++++------- src/Fetcher/SecurityTxtFetcher.php | 4 ++-- .../SecurityTxtContentTypeWrongCharset.php | 8 ++++---- .../SecurityTxtFetcherFetchHostResultTest.phpt | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 850b71e..c9b10e0 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ By default, values are validated when set, and an exception is thrown when they' - `AllowInvalidValuesSilently` (an exception will not be thrown, and the value will be set) ## Content type -You can use the following `SecurityTxt` constants to serve the file with correct HTTP content type: +You can use the following `SecurityTxtContentType` constants to serve the file with correct HTTP content type: - `SecurityTxtContentType::MEDIA_TYPE`, the value to be sent as `Content-Type` header value (`text/plain; charset=utf-8`); - `SecurityTxtContentType::CONTENT_TYPE`, the correct content type `text/plain` - `SecurityTxtContentType::CHARSET`, the charset `utf-8` diff --git a/src/Fetcher/SecurityTxtFetchHostContentType.php b/src/Fetcher/SecurityTxtFetchHostContentType.php index 5cfef93..452cca0 100644 --- a/src/Fetcher/SecurityTxtFetchHostContentType.php +++ b/src/Fetcher/SecurityTxtFetchHostContentType.php @@ -14,15 +14,15 @@ /** * @var lowercase-string|null */ - private ?string $lowercaseCharset; + private ?string $lowercaseCharsetParameter; public function __construct( private string $contentType, - private ?string $charset, + private ?string $charsetParameter, ) { $this->lowercaseContentType = strtolower(trim($this->contentType)); - $this->lowercaseCharset = $this->charset !== null ? strtolower(trim($this->charset)) : null; + $this->lowercaseCharsetParameter = $this->charsetParameter !== null ? strtolower(trim($this->charsetParameter)) : null; } @@ -32,9 +32,9 @@ public function getContentType(): string } - public function getCharset(): ?string + public function getCharsetParameter(): ?string { - return $this->charset; + return $this->charsetParameter; } @@ -50,9 +50,9 @@ public function getLowercaseContentType(): string /** * @return lowercase-string|null */ - public function getLowercaseCharset(): ?string + public function getLowercaseCharsetParameter(): ?string { - return $this->lowercaseCharset; + return $this->lowercaseCharsetParameter; } } diff --git a/src/Fetcher/SecurityTxtFetcher.php b/src/Fetcher/SecurityTxtFetcher.php index 3698c19..0737f52 100644 --- a/src/Fetcher/SecurityTxtFetcher.php +++ b/src/Fetcher/SecurityTxtFetcher.php @@ -215,8 +215,8 @@ private function getResult(SecurityTxtFetcherFetchHostResult $wellKnown, Securit $contentTypeHeader = $result->getContentType(); if ($contentTypeHeader === null || $contentTypeHeader->getLowercaseContentType() !== SecurityTxtContentType::CONTENT_TYPE) { $errors[] = new SecurityTxtContentTypeInvalid($result->getUrl(), $contentTypeHeader?->getContentType()); - } elseif ($contentTypeHeader->getLowercaseCharset() !== SecurityTxtContentType::CHARSET_PARAMETER) { - $errors[] = new SecurityTxtContentTypeWrongCharset($result->getUrl(), $contentTypeHeader->getContentType(), $contentTypeHeader->getCharset()); + } elseif ($contentTypeHeader->getLowercaseCharsetParameter() !== SecurityTxtContentType::CHARSET_PARAMETER) { + $errors[] = new SecurityTxtContentTypeWrongCharset($result->getUrl(), $contentTypeHeader->getContentType(), $contentTypeHeader->getCharsetParameter()); } return new SecurityTxtFetchResult( $result->getUrl(), diff --git a/src/Violations/SecurityTxtContentTypeWrongCharset.php b/src/Violations/SecurityTxtContentTypeWrongCharset.php index 3e594fb..64626b3 100644 --- a/src/Violations/SecurityTxtContentTypeWrongCharset.php +++ b/src/Violations/SecurityTxtContentTypeWrongCharset.php @@ -8,18 +8,18 @@ final class SecurityTxtContentTypeWrongCharset extends SecurityTxtSpecViolation { - public function __construct(string $uri, string $contentType, ?string $charset) + public function __construct(string $uri, string $contentType, ?string $charsetParameter) { - $format = $charset !== null + $format = $charsetParameter !== null ? 'The file at %s has a correct %s of %s but the %s parameter should be changed to %s' : 'The file at %s has a correct %s of %s but the %s parameter is missing'; parent::__construct( func_get_args(), $format, - $charset !== null ? [$uri, 'Content-Type', $contentType, $charset, SecurityTxtContentType::CHARSET_PARAMETER] : [$uri, 'Content-Type', $contentType, SecurityTxtContentType::CHARSET_PARAMETER], + $charsetParameter !== null ? [$uri, 'Content-Type', $contentType, $charsetParameter, SecurityTxtContentType::CHARSET_PARAMETER] : [$uri, 'Content-Type', $contentType, SecurityTxtContentType::CHARSET_PARAMETER], 'draft-foudil-securitytxt-03', SecurityTxtContentType::MEDIA_TYPE, - $charset !== null ? 'Change the parameter to %s' : 'Add a %s parameter', + $charsetParameter !== null ? 'Change the parameter to %s' : 'Add a %s parameter', [SecurityTxtContentType::CHARSET_PARAMETER], '3', ); diff --git a/tests/Fetcher/SecurityTxtFetcherFetchHostResultTest.phpt b/tests/Fetcher/SecurityTxtFetcherFetchHostResultTest.phpt index f2c52f9..878f30f 100644 --- a/tests/Fetcher/SecurityTxtFetcherFetchHostResultTest.phpt +++ b/tests/Fetcher/SecurityTxtFetcherFetchHostResultTest.phpt @@ -21,7 +21,7 @@ final class SecurityTxtFetcherFetchHostResultTest extends TestCase $contentType = $wellKnown->getContentType(); assert($contentType !== null); Assert::same('text/html', $contentType->getLowercaseContentType()); - Assert::same('charset=win-1337', $contentType->getLowercaseCharset()); + Assert::same('charset=win-1337', $contentType->getLowercaseCharsetParameter()); } }