@@ -26,8 +26,9 @@ use Spaze\SecurityTxt\Violations\SecurityTxtNoExpires;
2626use Spaze \SecurityTxt \Violations \SecurityTxtPossibelFieldTypo ;
2727use Spaze \SecurityTxt \Violations \SecurityTxtPreferredLanguagesCommonMistake ;
2828use Spaze \SecurityTxt \Violations \SecurityTxtPreferredLanguagesSeparatorNotComma ;
29- use Spaze \SecurityTxt \Violations \SecurityTxtSpecViolation ;
29+ use Spaze \SecurityTxt \Violations \SecurityTxtSignatureCannotVerify ;
3030use Spaze \SecurityTxt \Violations \SecurityTxtTopLevelPathOnly ;
31+ use Spaze \SecurityTxt \Violations \SecurityTxtUnknownField ;
3132use Tester \Assert ;
3233use Tester \TestCase ;
3334
@@ -69,10 +70,10 @@ final class SecurityTxtParserTest extends TestCase
6970 */
7071 public function testParseStringExpiresField (string $ fieldValue , bool $ isExpired , array $ errors ): void
7172 {
72- $ contents = "Foo: bar \nExpires: " . (new DateTime ($ fieldValue ))->format (SecurityTxtExpires::FORMAT ) . "\n Bar: foo \n" ;
73+ $ contents = "Contact: https://example.com/ \nExpires: " . (new DateTime ($ fieldValue ))->format (SecurityTxtExpires::FORMAT ) . "\n Hiring: https://com.example \n" ;
7374 $ parseResult = $ this ->securityTxtParser ->parseString ($ contents );
7475 Assert::same ($ isExpired , $ parseResult ->getSecurityTxt ()->getExpires ()?->isExpired());
75- Assert::true ( $ parseResult ->hasErrors ());
76+ Assert::same ( $ isExpired , $ parseResult ->hasErrors ());
7677 Assert::false ($ parseResult ->hasWarnings ());
7778 foreach ($ parseResult ->getLineErrors () as $ lineNumber => $ lineErrors ) {
7879 foreach ($ lineErrors as $ key => $ lineError ) {
@@ -145,19 +146,19 @@ final class SecurityTxtParserTest extends TestCase
145146
146147 public function testParseStringMissingExpires (): void
147148 {
148- $ contents = "Foo: bar \n Bar: foo \n#Expires: 2020-10-05T10:20:30+00:00 \nExpires \n" ;
149+ $ contents = "Contact: https://example.com/ \n Hiring: https://com.example \n#Expires: 2020-10-05T10:20:30+00:00 \nExpires \n" ;
149150 $ parseResult = $ this ->securityTxtParser ->parseString ($ contents );
150- Assert::contains (SecurityTxtNoExpires::class, array_map ( function ( SecurityTxtSpecViolation $ throwable ): string {
151- return $ throwable ::class ;
152- } , $ parseResult ->getFileErrors ()) );
151+ Assert::count ( 0 , $ parseResult -> getLineErrors ());
152+ Assert:: count ( 1 , $ parseResult -> getFileErrors ()) ;
153+ Assert:: type (SecurityTxtNoExpires::class , $ parseResult ->getFileErrors ()[ 0 ] );
153154 Assert::true ($ parseResult ->hasErrors ());
154155 Assert::false ($ parseResult ->hasWarnings ());
155156 }
156157
157158
158159 public function testParseStringMultipleExpires (): void
159160 {
160- $ contents = "Foo: bar \nExpires: " . (new DateTime ('+2 months ' ))->format (SecurityTxtExpires::FORMAT ) . "\nExpires: " . (new DateTime ('+3 months ' ))->format (SecurityTxtExpires::FORMAT ) . "\n Bar: foo \n" ;
161+ $ contents = "Contact: https://example.com/ \nExpires: " . (new DateTime ('+2 months ' ))->format (SecurityTxtExpires::FORMAT ) . "\nExpires: " . (new DateTime ('+3 months ' ))->format (SecurityTxtExpires::FORMAT ) . "\n Hiring: https://com.example/ \n" ;
161162 $ parseResult = $ this ->securityTxtParser ->parseString ($ contents );
162163 $ expiresError = $ parseResult ->getLineErrors ()[3 ][0 ];
163164 Assert::type (SecurityTxtMultipleExpires::class, $ expiresError );
@@ -168,7 +169,7 @@ final class SecurityTxtParserTest extends TestCase
168169
169170 public function testParseStringMultipleExpiresAllWrong (): void
170171 {
171- $ contents = "Foo: bar \nExpires: Mon, 15 Aug 2005 15:52:01 +0000 \nExpires: Mon, 15 Aug 2015 15:52:01 +0000 \n Bar: foo \n" ;
172+ $ contents = "Contact: https://example.com/ \nExpires: Mon, 15 Aug 2005 15:52:01 +0000 \nExpires: Mon, 15 Aug 2015 15:52:01 +0000 \n Hiring: https://com.example/ \n" ;
172173 $ parseResult = $ this ->securityTxtParser ->parseString ($ contents );
173174 Assert::type (SecurityTxtExpiresOldFormat::class, $ parseResult ->getLineErrors ()[2 ][0 ]);
174175 Assert::type (SecurityTxtMultipleExpires::class, $ parseResult ->getLineErrors ()[3 ][0 ]);
@@ -180,7 +181,7 @@ final class SecurityTxtParserTest extends TestCase
180181
181182 public function testParseStringMultipleExpiresFirstWrong (): void
182183 {
183- $ contents = "Foo: bar \nExpires: Mon, 15 Aug 2005 15:52:01 +0000 \nExpires: " . (new DateTime ('+2 months ' ))->format (SecurityTxtExpires::FORMAT ) . "\n Bar: foo \n" ;
184+ $ contents = "Contact: https://example.com/ \nExpires: Mon, 15 Aug 2005 15:52:01 +0000 \nExpires: " . (new DateTime ('+2 months ' ))->format (SecurityTxtExpires::FORMAT ) . "\n Hiring: https://com.example/ \n" ;
184185 $ parseResult = $ this ->securityTxtParser ->parseString ($ contents );
185186 Assert::type (SecurityTxtExpiresOldFormat::class, $ parseResult ->getLineErrors ()[2 ][0 ]);
186187 Assert::true ($ parseResult ->hasErrors ());
@@ -190,7 +191,7 @@ final class SecurityTxtParserTest extends TestCase
190191
191192 public function testParseStringMultipleExpiresFirstCorrect (): void
192193 {
193- $ contents = "Foo: bar \nExpires: " . (new DateTime ('+2 months ' ))->format (SecurityTxtExpires::FORMAT ) . "\nExpires: Mon, 15 Aug 2005 15:52:01 +0000 \n Bar: foo \n" ;
194+ $ contents = "Contact: https://example.com/ \nExpires: " . (new DateTime ('+2 months ' ))->format (SecurityTxtExpires::FORMAT ) . "\nExpires: Mon, 15 Aug 2005 15:52:01 +0000 \n Hiring: https://com.example/ \n" ;
194195 $ parseResult = $ this ->securityTxtParser ->parseString ($ contents );
195196 Assert::count (3 , $ parseResult ->getLineErrors ()[3 ]);
196197 Assert::type (SecurityTxtMultipleExpires::class, $ parseResult ->getLineErrors ()[3 ][0 ]);
@@ -220,7 +221,7 @@ final class SecurityTxtParserTest extends TestCase
220221
221222 public function testParseMultipleBadFiles (): void
222223 {
223- $ contents = "Foo: bar \nExpires: " . (new DateTime ('+2 months ' ))->format (SecurityTxtExpires::FORMAT ) . "\nExpires: " . (new DateTime ('+3 months ' ))->format (SecurityTxtExpires::FORMAT ) . "\n Bar: foo \n" ;
224+ $ contents = "Contact: https://example.com/ \nExpires: " . (new DateTime ('+2 months ' ))->format (SecurityTxtExpires::FORMAT ) . "\nExpires: " . (new DateTime ('+3 months ' ))->format (SecurityTxtExpires::FORMAT ) . "\n Hiring: https://com.example/ \n" ;
224225 $ parseResult = $ this ->securityTxtParser ->parseString ($ contents );
225226 Assert::type (SecurityTxtMultipleExpires::class, $ parseResult ->getLineErrors ()[3 ][0 ]);
226227 Assert::true ($ parseResult ->hasErrors ());
@@ -249,7 +250,7 @@ final class SecurityTxtParserTest extends TestCase
249250
250251 public function testParseStringMissingContact (): void
251252 {
252- $ contents = "Foo: bar \n Bar: foo \n" ;
253+ $ contents = "Acknowledgments: https://example.com/ \n Hiring: https://com.example/ \n" ;
253254 $ parseResult = $ this ->securityTxtParser ->parseString ($ contents );
254255 $ contactError = $ parseResult ->getFileErrors ()[0 ];
255256 Assert::type (SecurityTxtNoContact::class, $ contactError );
@@ -393,6 +394,68 @@ final class SecurityTxtParserTest extends TestCase
393394 }
394395
395396
397+ public function testParseStringUnknownField (): void
398+ {
399+ $ contents = "Foo: bar \nHash: file-not-signed-0123 \nContact: https://example.com/ \nExpires: " . (new DateTime ('+3 weeks ' ))->format (SecurityTxtExpires::FORMAT ) . "\n" ;
400+ $ parseResult = $ this ->securityTxtParser ->parseString ($ contents );
401+ Assert::count (0 , $ parseResult ->getLineErrors ());
402+ Assert::false ($ parseResult ->hasErrors ());
403+ Assert::true ($ parseResult ->hasWarnings ());
404+ Assert::count (2 , $ parseResult ->getLineWarnings ());
405+ Assert::count (0 , $ parseResult ->getFileWarnings ());
406+ Assert::type (SecurityTxtUnknownField::class, $ parseResult ->getLineWarnings ()[1 ][0 ]);
407+ Assert::type (SecurityTxtUnknownField::class, $ parseResult ->getLineWarnings ()[2 ][0 ]);
408+ }
409+
410+
411+ public function testParseStringSignedFile (): void
412+ {
413+ $ expires = (new DateTime ('+2 weeks ' ))->format (SecurityTxtExpires::FORMAT );
414+ $ contents = <<< EOT
415+ -----BEGIN PGP SIGNED MESSAGE-----
416+ Hash: SHA512
417+
418+ Contact: https://example.com/
419+ Expires: {$ expires }
420+ Canonical: https://foo.bar.example/
421+ -----BEGIN PGP SIGNATURE-----
422+
423+ iJIEARYKADoWIQSvbhd14xH/eOkR59x/h5ABqcj1CgUCaH7y/xwcc3RpbGwudGVz
424+ dHNAbGlicmFyeS5leGFtcGxlAAoJEH+HkAGpyPUKRvEA/2cVGZs54ieQ7s1nSTla
425+ 6O+JHJNaLOf3llvGRi55gW+BAQCDVLTj2q7cbHPS78lD/uvsgFI3NVWwZx8m72sx
426+ SmjCCQ==
427+ =bZYA
428+ -----END PGP SIGNATURE-----
429+ EOT . "\n" ;
430+ $ parseResult = $ this ->securityTxtParser ->parseString ($ contents );
431+ Assert::false ($ parseResult ->hasErrors ());
432+ Assert::false ($ parseResult ->hasWarnings ());
433+ Assert::same ('AF6E1775E311FF78E911E7DC7F879001A9C8F50A ' , $ parseResult ->getSecurityTxt ()->getSignatureVerifyResult ()?->getKeyFingerprint());
434+ }
435+
436+
437+ public function testParseStringSignedFileDamaged (): void
438+ {
439+ $ expires = (new DateTime ('+2 weeks ' ))->format (SecurityTxtExpires::FORMAT );
440+ $ contents = <<< EOT
441+ -----BEGIN PGP SIGNED MESSAGE-----
442+ Hash: SHA512
443+
444+ Contact: https://example.com/
445+ Expires: {$ expires }
446+ Canonical: https://foo.bar.example/
447+ -----BEGIN PGP SIGNATURE-----
448+ yes, but
449+ -----END PGP SIGNATURE-----
450+ EOT . "\n" ;
451+ $ parseResult = $ this ->securityTxtParser ->parseString ($ contents );
452+ Assert::false ($ parseResult ->hasErrors ());
453+ Assert::true ($ parseResult ->hasWarnings ());
454+ Assert::count (1 , $ parseResult ->getLineWarnings ());
455+ Assert::type (SecurityTxtSignatureCannotVerify::class, $ parseResult ->getLineWarnings ()[1 ][0 ]);
456+ }
457+
458+
396459 public function testParseFetchResult (): void
397460 {
398461 $ lines = ["Contact: mailto:example@example.com \r\n" , "Expires: 2020-12-31T23:59:59.000Z " ];
0 commit comments