From 3c3771f744b9faa459f5fcb7720e40718bf548ce Mon Sep 17 00:00:00 2001 From: Mark Gregory Date: Wed, 24 Jun 2026 16:17:07 +0100 Subject: [PATCH] new git integraton code --- .../Http/Controllers/GitWebhookController.php | 27 +++++++------- .../Tests/Feature/GitWebhookIngestionTest.php | 35 +++++++++++++++++-- Modules/Helpdesk/changelog.yaml | 11 ++++++ Modules/Helpdesk/module.json | 2 +- 4 files changed, 60 insertions(+), 15 deletions(-) diff --git a/Modules/Helpdesk/Http/Controllers/GitWebhookController.php b/Modules/Helpdesk/Http/Controllers/GitWebhookController.php index d957f647..fab7f218 100644 --- a/Modules/Helpdesk/Http/Controllers/GitWebhookController.php +++ b/Modules/Helpdesk/Http/Controllers/GitWebhookController.php @@ -64,16 +64,6 @@ final class GitWebhookController $deliveryId = $this->deliveryId($request, $provider); $rawPayload = $request->getContent(); - if (! $this->hasValidGiteaAuthorization($request)) { - return response()->json([ - 'status' => 'rejected', - 'message' => 'Gitea webhook authorization failed.', - 'commit_count' => 0, - 'linked_count' => 0, - 'ignored_count' => 1, - ], 403); - } - try { /** @var array $payload */ $payload = json_decode($rawPayload, true, 512, JSON_THROW_ON_ERROR); @@ -99,9 +89,9 @@ final class GitWebhookController ], 404); } - if (! $this->hasValidSignature($request, $repository, $provider, $rawPayload)) { + if (! $this->hasValidGiteaSecurity($request, $repository, $provider, $rawPayload)) { $result = ['status' => 'rejected', 'message' => 'Git webhook signature or token validation failed.', 'commit_count' => 0, 'linked_count' => 0, 'ignored_count' => 1]; - $service->logWebhook($repository, $provider, $deliveryId, $event, $result); + $service->logWebhook($repository, $provider, $deliveryId, $event, $result, $payload); return response()->json($result, 403); } @@ -198,6 +188,19 @@ final class GitWebhookController || hash_equals('sha1=' . hash_hmac('sha1', $rawPayload, $secret), $signature); } + /** + * Validate Gitea webhook security without allowing an optional Authorization + * header mismatch to hide otherwise valid signed push deliveries. + */ + private function hasValidGiteaSecurity(Request $request, GitRepository $repository, string $provider, string $rawPayload): bool + { + if ($this->hasValidSignature($request, $repository, $provider, $rawPayload)) { + return true; + } + + return $this->hasValidGiteaAuthorization($request); + } + /** * Validate the optional Authorization header configured for the local Gitea webhook. */ diff --git a/Modules/Helpdesk/Tests/Feature/GitWebhookIngestionTest.php b/Modules/Helpdesk/Tests/Feature/GitWebhookIngestionTest.php index 9d3306d0..4cc02694 100644 --- a/Modules/Helpdesk/Tests/Feature/GitWebhookIngestionTest.php +++ b/Modules/Helpdesk/Tests/Feature/GitWebhookIngestionTest.php @@ -221,7 +221,7 @@ final class GitWebhookIngestionTest extends TestCase ]); } - public function test_gitea_local_webhook_rejects_invalid_authorization_without_creating_commits(): void + public function test_gitea_local_webhook_rejects_invalid_signature_and_authorization_without_creating_commits(): void { config()->set('services.gitea.webhook_authorization', 'YWxhZGRpbjpvcGVuc2VzYW1l'); @@ -242,7 +242,7 @@ final class GitWebhookIngestionTest extends TestCase 'HTTP_ACCEPT' => 'application/json', 'HTTP_AUTHORIZATION' => 'Basic invalid', 'HTTP_X_GITEA_EVENT' => 'push', - 'HTTP_X_GITEA_SIGNATURE' => hash_hmac('sha256', $json, 'gitea-secret'), + 'HTTP_X_GITEA_SIGNATURE' => hash_hmac('sha256', $json, 'wrong-secret'), ], $json); $response->assertForbidden()->assertJsonPath('status', 'rejected'); @@ -250,6 +250,37 @@ final class GitWebhookIngestionTest extends TestCase self::assertSame(0, GitCommit::query()->count()); } + public function test_gitea_local_webhook_accepts_valid_signature_when_optional_authorization_header_differs(): void + { + config()->set('services.gitea.webhook_authorization', 'expected-basic-token'); + + $repository = $this->repository([ + 'provider' => 'gitea', + 'remote_url' => 'http://git.vm:3000/Admin/WMS-Core.git', + 'webhook_secret' => 'gitea-secret', + ]); + $payload = [ + 'ref' => 'refs/heads/main', + 'repository' => ['clone_url' => 'http://git.vm:3000/Admin/WMS-Core.git'], + 'commits' => [['id' => str_repeat('b', 40), 'message' => 'Valid signed Gitea push']], + ]; + $json = json_encode($payload, JSON_THROW_ON_ERROR); + + $response = $this->call('POST', route('api.git.gitea.webhook'), [], [], [], [ + 'CONTENT_TYPE' => 'application/json', + 'HTTP_ACCEPT' => 'application/json', + 'HTTP_AUTHORIZATION' => 'Basic something-different-from-gitea-ui', + 'HTTP_X_GITEA_EVENT' => 'push', + 'HTTP_X_GITEA_SIGNATURE' => hash_hmac('sha256', $json, 'gitea-secret'), + ], $json); + + $response->assertStatus(202)->assertJsonPath('status', 'accepted')->assertJsonPath('commit_count', 1); + + $this->assertDatabaseHas('helpdesk_git_commits', [ + 'repository_id' => $repository->id, + 'hash' => str_repeat('b', 40), + ]); + } public function test_gitea_local_webhook_accepts_prefixed_signature_and_case_insensitive_provider(): void { diff --git a/Modules/Helpdesk/changelog.yaml b/Modules/Helpdesk/changelog.yaml index 827606fa..f8a8a7f8 100644 --- a/Modules/Helpdesk/changelog.yaml +++ b/Modules/Helpdesk/changelog.yaml @@ -480,3 +480,14 @@ - Reset the optional local Gitea Authorization config inside the prefixed-signature regression test so prior authorization tests cannot leak state into it. - Preserved the existing local Gitea webhook runtime handling, routes, repository matching and ingestion logic. - Preserved append-only changelog history. + +- version: 1.10.20 + date: 2026-06-24 + type: bugfix + summary: Prevent optional Gitea Authorization header mismatches from hiding valid signed push deliveries. + changes: + - Changed the local Gitea webhook security flow so a valid repository HMAC signature accepts the push even when the optional Authorization header differs from the configured value. + - Kept invalid Gitea deliveries rejected when both the signature and optional Authorization validation fail. + - Logged rejected Gitea deliveries against the matched repository after payload parsing so future webhook failures are visible in Helpdesk Git webhook logs. + - Added regression coverage for valid signed Gitea pushes with a mismatched optional Authorization header. + - Preserved append-only changelog history. diff --git a/Modules/Helpdesk/module.json b/Modules/Helpdesk/module.json index e71ee9b3..841b29a8 100644 --- a/Modules/Helpdesk/module.json +++ b/Modules/Helpdesk/module.json @@ -23,5 +23,5 @@ "Modules\\Helpdesk\\Providers\\HelpdeskServiceProvider" ], "files": [], - "version": "1.10.18" + "version": "1.10.20" }