authenticate($this->requestWithSignature( 'X-Forgejo-Signature', hash_hmac('sha256', self::PAYLOAD, self::SECRET) ))); } public function testAcceptsGiteaSignatureHeaderForCompatibility(): void { $auth = new WebhookAuthenticator(self::SECRET); self::assertTrue($auth->authenticate($this->requestWithSignature( 'X-Gitea-Signature', hash_hmac('sha256', self::PAYLOAD, self::SECRET) ))); } public function testRejectsSignatureCreatedWithAnotherSecret(): void { $auth = new WebhookAuthenticator(self::SECRET); self::assertFalse($auth->authenticate($this->requestWithSignature( 'X-Forgejo-Signature', hash_hmac('sha256', self::PAYLOAD, 'falsches-secret') ))); } /** * Die Signatur muss ueber den rohen Body gebildet werden: ein Angreifer darf den Payload * nicht veraendern koennen, ohne dass die Pruefsumme bricht. */ public function testRejectsSignatureForDifferentPayload(): void { $auth = new WebhookAuthenticator(self::SECRET); $request = Request::create( 'POST', '/api/v1/webhooks/codeberg', [], [], ['X-Forgejo-Signature' => hash_hmac('sha256', self::PAYLOAD, self::SECRET)], '{"action":"closed","issue":{"number":7}}' ); self::assertFalse($auth->authenticate($request)); } public function testRejectsRequestWithoutSignature(): void { $auth = new WebhookAuthenticator(self::SECRET); $request = Request::create('POST', '/api/v1/webhooks/codeberg', [], [], [], self::PAYLOAD); self::assertFalse($auth->authenticate($request)); } public function testRejectsEverythingWhenNoSecretIsConfigured(): void { foreach ([null, ''] as $secret) { $auth = new WebhookAuthenticator($secret); self::assertFalse($auth->authenticate($this->requestWithSignature( 'X-Forgejo-Signature', hash_hmac('sha256', self::PAYLOAD, (string) $secret) ))); } } private function requestWithSignature(string $header, string $signature): Request { return Request::create( 'POST', '/api/v1/webhooks/codeberg', [], [], [$header => $signature], self::PAYLOAD ); } }