Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

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`
## 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`
- `SecurityTxtContentType::CHARSET_PARAMETER`, the correct charset parameter name and value as `charset=utf-8`

## Example
```php
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Spaze\SecurityTxt\Fetcher;

final readonly class SecurityTxtContentType
final readonly class SecurityTxtFetchHostContentType
{

/**
Expand All @@ -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;
}


Expand All @@ -32,9 +32,9 @@ public function getContentType(): string
}


public function getCharset(): ?string
public function getCharsetParameter(): ?string
{
return $this->charset;
return $this->charsetParameter;
}


Expand All @@ -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;
}

}
8 changes: 4 additions & 4 deletions src/Fetcher/SecurityTxtFetcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -213,10 +213,10 @@ 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) {
$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(),
Expand Down
6 changes: 3 additions & 3 deletions src/Fetcher/SecurityTxtFetcherFetchHostResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
final readonly class SecurityTxtFetcherFetchHostResult
{

private ?SecurityTxtContentType $contentType;
private ?SecurityTxtFetchHostContentType $contentType;

private ?string $contents;

Expand All @@ -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();
Expand Down Expand Up @@ -78,7 +78,7 @@ public function isTruncated(): bool
}


public function getContentType(): ?SecurityTxtContentType
public function getContentType(): ?SecurityTxtFetchHostContentType
{
return $this->contentType;
}
Expand Down
4 changes: 0 additions & 4 deletions src/SecurityTxt.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
14 changes: 14 additions & 0 deletions src/SecurityTxtContentType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php
declare(strict_types = 1);

namespace Spaze\SecurityTxt;

final readonly class SecurityTxtContentType
{

public const string CONTENT_TYPE = 'text/plain';
public const string CHARSET = 'utf-8';
public const string CHARSET_PARAMETER = 'charset=' . self::CHARSET;
public const string MEDIA_TYPE = self::CONTENT_TYPE . '; ' . self::CHARSET_PARAMETER;

}
10 changes: 5 additions & 5 deletions src/Violations/SecurityTxtContentTypeInvalid.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Spaze\SecurityTxt\Violations;

use Spaze\SecurityTxt\SecurityTxt;
use Spaze\SecurityTxt\SecurityTxtContentType;

final class SecurityTxtContentTypeInvalid extends SecurityTxtSpecViolation
{
Expand All @@ -12,19 +12,19 @@ public function __construct(string $uri, ?string $contentType)
{
if ($contentType !== null) {
$format = 'The file at %s has a %s of %s but it should be a %s of %s with the %s parameter set to %s';
$values = [$uri, 'Content-Type', $contentType, 'Content-Type', SecurityTxt::CONTENT_TYPE, 'charset', SecurityTxt::CHARSET];
$values = [$uri, 'Content-Type', $contentType, 'Content-Type', SecurityTxtContentType::CONTENT_TYPE, 'charset', SecurityTxtContentType::CHARSET_PARAMETER];
} else {
$format = 'The file at %s has no %s but it should be a %s of %s with the %s parameter set to %s';
$values = [$uri, 'Content-Type', 'Content-Type', SecurityTxt::CONTENT_TYPE, 'charset', SecurityTxt::CHARSET];
$values = [$uri, 'Content-Type', 'Content-Type', SecurityTxtContentType::CONTENT_TYPE, 'charset', SecurityTxtContentType::CHARSET_PARAMETER];
}
parent::__construct(
func_get_args(),
$format,
$values,
'draft-foudil-securitytxt-03',
SecurityTxt::CONTENT_TYPE_HEADER,
SecurityTxtContentType::MEDIA_TYPE,
'Send a correct %s header value of %s with the %s parameter set to %s',
['Content-Type', SecurityTxt::CONTENT_TYPE, 'charset', SecurityTxt::CHARSET],
['Content-Type', SecurityTxtContentType::CONTENT_TYPE, 'charset', SecurityTxtContentType::CHARSET_PARAMETER],
'3',
);
}
Expand Down
14 changes: 7 additions & 7 deletions src/Violations/SecurityTxtContentTypeWrongCharset.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@

namespace Spaze\SecurityTxt\Violations;

use Spaze\SecurityTxt\SecurityTxt;
use Spaze\SecurityTxt\SecurityTxtContentType;

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, SecurityTxt::CHARSET] : [$uri, 'Content-Type', $contentType, SecurityTxt::CHARSET],
$charsetParameter !== null ? [$uri, 'Content-Type', $contentType, $charsetParameter, SecurityTxtContentType::CHARSET_PARAMETER] : [$uri, 'Content-Type', $contentType, SecurityTxtContentType::CHARSET_PARAMETER],
'draft-foudil-securitytxt-03',
SecurityTxt::CONTENT_TYPE_HEADER,
$charset !== null ? 'Change the parameter to %s' : 'Add a %s parameter',
[SecurityTxt::CHARSET],
SecurityTxtContentType::MEDIA_TYPE,
$charsetParameter !== null ? 'Change the parameter to %s' : 'Add a %s parameter',
[SecurityTxtContentType::CHARSET_PARAMETER],
'3',
);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Fetcher/SecurityTxtFetcherFetchHostResultTest.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

}
Expand Down
4 changes: 2 additions & 2 deletions tests/Fetcher/SecurityTxtFetcherTest.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use Spaze\SecurityTxt\Fetcher\Exceptions\SecurityTxtTooManyRedirectsException;
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\SecurityTxtTopLevelDiffers;
use Tester\Assert;
use Tester\TestCase;
Expand Down Expand Up @@ -232,7 +232,7 @@ final class SecurityTxtFetcherTest extends TestCase
{
$httpClient = $this->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);
Expand Down