new git integraton code

This commit is contained in:
Mark Gregory
2026-06-24 16:17:07 +01:00
parent 6df0fe52ac
commit 3c3771f744
4 changed files with 60 additions and 15 deletions
@@ -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<string, mixed> $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.
*/
@@ -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
{
+11
View File
@@ -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.
+1 -1
View File
@@ -23,5 +23,5 @@
"Modules\\Helpdesk\\Providers\\HelpdeskServiceProvider"
],
"files": [],
"version": "1.10.18"
"version": "1.10.20"
}