Skip to content

Commit 46e57c5

Browse files
authored
Validate the security.txt file even when the GPG signature is damaged (#45)
Close #43
2 parents 8c446c1 + 917e57b commit 46e57c5

File tree

9 files changed

+74
-22
lines changed

9 files changed

+74
-22
lines changed

psalm.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
<referencedMethod name="/.*Test::test.*/" />
3333
<referencedMethod name="/.*Test::get.*/" /> <!-- Test data providers -->
3434
<referencedMethod name="/Spaze\\SecurityTxt\\Check\\SecurityTxtCheckHostResult::get.*/" />
35+
<referencedMethod name="/Spaze\\SecurityTxt\\Signature\\SecurityTxtSignatureErrorInfo::get.*/" />
3536
<referencedMethod name="/Spaze\\SecurityTxt\\Violations\\.*::__construct/" />
3637
<referencedMethod name="/Spaze\\SecurityTxt\\Violations\\.*::get.*/" />
3738
<referencedMethod name="/Spaze\\SecurityTxt\\Parser\\SecurityTxtParseResult::hasErrors/" />

src/Check/SecurityTxtCheckHost.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
use Spaze\SecurityTxt\Fetcher\SecurityTxtFetcher;
1818
use Spaze\SecurityTxt\Parser\SecurityTxtParser;
1919
use Spaze\SecurityTxt\Parser\SecurityTxtUrlParser;
20-
use Spaze\SecurityTxt\Signature\Exceptions\SecurityTxtCannotVerifySignatureException;
2120
use Spaze\SecurityTxt\Violations\SecurityTxtSpecViolation;
2221

2322
final class SecurityTxtCheckHost
@@ -86,7 +85,6 @@ public function __construct(
8685
* @throws SecurityTxtOnlyIpv6HostButIpv6DisabledException
8786
* @throws SecurityTxtHostIpAddressInvalidTypeException
8887
* @throws SecurityTxtHostIpAddressNotFoundException
89-
* @throws SecurityTxtCannotVerifySignatureException
9088
*/
9189
public function check(string $url, ?int $expiresWarningThreshold = null, bool $strictMode = false, bool $requireTopLevelLocation = false, bool $noIpv6 = false): SecurityTxtCheckHostResult
9290
{

src/Check/SecurityTxtCheckHostCli.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
use DateTimeImmutable;
77
use Spaze\SecurityTxt\Fetcher\Exceptions\SecurityTxtFetcherException;
8-
use Spaze\SecurityTxt\Signature\Exceptions\SecurityTxtCannotVerifySignatureException;
98

109
final readonly class SecurityTxtCheckHostCli
1110
{
@@ -120,7 +119,7 @@ function (string $keyFingerprint, DateTimeImmutable $signatureDate): void {
120119
} else {
121120
$this->exit(CheckExitStatus::Ok);
122121
}
123-
} catch (SecurityTxtFetcherException | SecurityTxtCannotVerifySignatureException $e) {
122+
} catch (SecurityTxtFetcherException $e) {
124123
$this->consolePrinter->error($e->getMessage());
125124
$this->exit(CheckExitStatus::FileError);
126125
}

src/Parser/SecurityTxtParser.php

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
use Spaze\SecurityTxt\Parser\FieldProcessors\PreferredLanguagesSetFieldValue;
2424
use Spaze\SecurityTxt\SecurityTxt;
2525
use Spaze\SecurityTxt\SecurityTxtValidationLevel;
26-
use Spaze\SecurityTxt\Signature\Exceptions\SecurityTxtCannotVerifySignatureException;
2726
use Spaze\SecurityTxt\Signature\SecurityTxtSignature;
2827
use Spaze\SecurityTxt\Validator\SecurityTxtValidator;
2928
use Spaze\SecurityTxt\Violations\SecurityTxtLineNoEol;
@@ -109,9 +108,6 @@ private function processField(int $lineNumber, string $value, SecurityTxtField $
109108
}
110109

111110

112-
/**
113-
* @throws SecurityTxtCannotVerifySignatureException
114-
*/
115111
public function parseString(string $contents, ?string $fileLocation = null, ?int $expiresWarningThreshold = null, bool $strictMode = false): SecurityTxtParseStringResult
116112
{
117113
$this->expiresWarningThreshold = $expiresWarningThreshold;
@@ -168,9 +164,6 @@ public function parseString(string $contents, ?string $fileLocation = null, ?int
168164
}
169165

170166

171-
/**
172-
* @throws SecurityTxtCannotVerifySignatureException
173-
*/
174167
public function parseFetchResult(SecurityTxtFetchResult $fetchResult, ?int $expiresWarningThreshold = null, bool $strictMode = false): SecurityTxtParseHostResult
175168
{
176169
$parseResult = $this->parseString($fetchResult->getContents(), $fetchResult->getFinalUrl(), $expiresWarningThreshold, $strictMode);
@@ -184,7 +177,6 @@ public function parseFetchResult(SecurityTxtFetchResult $fetchResult, ?int $expi
184177

185178
/**
186179
* @param int<1, max> $lineNumber
187-
* @throws SecurityTxtCannotVerifySignatureException
188180
*/
189181
private function checkSignature(int $lineNumber, string $line, string $contents, SecurityTxt $securityTxt): SecurityTxt
190182
{

src/Signature/Exceptions/SecurityTxtSignatureErrorInfoException.php

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,26 @@
99
abstract class SecurityTxtSignatureErrorInfoException extends SecurityTxtSignatureException
1010
{
1111

12-
public function __construct(string $message, SecurityTxtSignatureErrorInfo $errorInfo, ?Throwable $previous = null)
13-
{
12+
public function __construct(
13+
string $message,
14+
private readonly SecurityTxtSignatureErrorInfo $errorInfo,
15+
?Throwable $previous = null,
16+
) {
1417
$message = sprintf(
1518
'%s: %s; code: %s, source: %s, library message: %s',
1619
$message,
17-
$errorInfo->getMessage() === false ? '<false>' : ($errorInfo->getMessage() === null ? '<null>' : $errorInfo->getMessage()),
18-
$errorInfo->getCode() ?? '<null>',
19-
$errorInfo->getSource() ?? '<null>',
20-
$errorInfo->getLibraryMessage() ?? '<null>',
20+
$errorInfo->getMessageAsString(),
21+
$errorInfo->getCodeAsString(),
22+
$errorInfo->getSourceAsString(),
23+
$errorInfo->getLibraryMessageAsString(),
2124
);
2225
parent::__construct($message, $errorInfo->getCode() ?? 0, previous: $previous);
2326
}
2427

28+
29+
public function getErrorInfo(): SecurityTxtSignatureErrorInfo
30+
{
31+
return $this->errorInfo;
32+
}
33+
2534
}

src/Signature/SecurityTxtSignature.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Spaze\SecurityTxt\Signature\Exceptions\SecurityTxtUnknownSigningKeyException;
1616
use Spaze\SecurityTxt\Signature\Exceptions\SecurityTxtUnusableSigningKeyException;
1717
use Spaze\SecurityTxt\Signature\Providers\SecurityTxtSignatureProvider;
18+
use Spaze\SecurityTxt\Violations\SecurityTxtSignatureCannotVerify;
1819
use Spaze\SecurityTxt\Violations\SecurityTxtSignatureExtensionNotLoaded;
1920
use Spaze\SecurityTxt\Violations\SecurityTxtSignatureInvalid;
2021

@@ -37,14 +38,15 @@ public function __construct(private SecurityTxtSignatureProvider $signatureProvi
3738
/**
3839
* @throws SecurityTxtError
3940
* @throws SecurityTxtWarning
40-
* @throws SecurityTxtCannotVerifySignatureException
4141
*/
4242
public function verify(string $contents): SecurityTxtSignatureVerifyResult
4343
{
4444
try {
4545
$signature = $this->signatureProvider->verify($contents);
4646
} catch (SecurityTxtCannotCreateSignatureExtensionNotLoadedException $e) {
4747
throw new SecurityTxtWarning(new SecurityTxtSignatureExtensionNotLoaded(), $e);
48+
} catch (SecurityTxtCannotVerifySignatureException $e) {
49+
throw new SecurityTxtWarning(new SecurityTxtSignatureCannotVerify($e->getErrorInfo()), $e);
4850
}
4951

5052
if (!$this->isSignatureKindaOkay($signature->getSummary())) {

src/Signature/SecurityTxtSignatureErrorInfo.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,45 @@ public function getMessage(): string|false|null
2121
}
2222

2323

24+
public function getMessageAsString(): string
25+
{
26+
return $this->message === false ? '<false>' : ($this->message === null ? '<null>' : $this->message);
27+
}
28+
29+
2430
public function getCode(): ?int
2531
{
2632
return $this->code;
2733
}
2834

2935

36+
public function getCodeAsString(): string
37+
{
38+
return $this->code !== null ? (string)$this->code : '<null>';
39+
}
40+
41+
3042
public function getSource(): ?string
3143
{
3244
return $this->source;
3345
}
3446

3547

48+
public function getSourceAsString(): string
49+
{
50+
return $this->source ?? '<null>';
51+
}
52+
53+
3654
public function getLibraryMessage(): ?string
3755
{
3856
return $this->libraryMessage;
3957
}
4058

59+
60+
public function getLibraryMessageAsString(): string
61+
{
62+
return $this->libraryMessage ?? '<null>';
63+
}
64+
4165
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
declare(strict_types = 1);
3+
4+
namespace Spaze\SecurityTxt\Violations;
5+
6+
use Spaze\SecurityTxt\Signature\SecurityTxtSignatureErrorInfo;
7+
8+
final class SecurityTxtSignatureCannotVerify extends SecurityTxtSpecViolation
9+
{
10+
11+
public function __construct(SecurityTxtSignatureErrorInfo $errorInfo)
12+
{
13+
parent::__construct(
14+
func_get_args(),
15+
"The file is digitally signed using an OpenPGP cleartext signature but the signature is damaged and cannot be verified (%s, %s, %s, %s)",
16+
[$errorInfo->getMessageAsString(), $errorInfo->getCodeAsString(), $errorInfo->getSourceAsString(), $errorInfo->getLibraryMessageAsString()],
17+
'draft-foudil-securitytxt-01',
18+
null,
19+
'Sign the file again',
20+
[],
21+
'2.3',
22+
);
23+
}
24+
25+
}

tests/Signature/SecurityTxtSignatureTest.phpt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use Spaze\SecurityTxt\Signature\Exceptions\SecurityTxtSigningKeyNoPassphraseSetE
2020
use Spaze\SecurityTxt\Signature\Exceptions\SecurityTxtUnknownSigningKeyException;
2121
use Spaze\SecurityTxt\Signature\Exceptions\SecurityTxtUnusableSigningKeyException;
2222
use Spaze\SecurityTxt\Signature\Providers\SecurityTxtSignatureProvider;
23+
use Spaze\SecurityTxt\Violations\SecurityTxtSignatureCannotVerify;
2324
use Spaze\SecurityTxt\Violations\SecurityTxtSignatureExtensionNotLoaded;
2425
use Spaze\SecurityTxt\Violations\SecurityTxtSignatureInvalid;
2526
use Tester\Assert;
@@ -124,9 +125,10 @@ final class SecurityTxtSignatureTest extends TestCase
124125
$signature = new SecurityTxtSignature($this->getSignatureProvider(verifyThrows: new SecurityTxtCannotVerifySignatureException(null, new SecurityTxtSignatureErrorInfo('msg', 1336, null, null))));
125126
$e = Assert::throws(function () use ($signature): void {
126127
$signature->verify('gnupg::verify returns invalid array');
127-
}, SecurityTxtCannotVerifySignatureException::class);
128-
assert($e instanceof SecurityTxtCannotVerifySignatureException);
129-
Assert::same('Cannot verify signature: msg; code: 1336, source: <null>, library message: <null>', $e->getMessage());
128+
}, SecurityTxtWarning::class);
129+
assert($e instanceof SecurityTxtWarning);
130+
Assert::type(SecurityTxtSignatureCannotVerify::class, $e->getViolation());
131+
Assert::same('The file is digitally signed using an OpenPGP cleartext signature but the signature is damaged and cannot be verified (msg, 1336, <null>, <null>)', $e->getMessage());
130132
}
131133

132134

0 commit comments

Comments
 (0)