diff --git a/app/Http/Controllers/Webhook/Concerns/MatchesManualWebhookApplications.php b/app/Http/Controllers/Webhook/Concerns/MatchesManualWebhookApplications.php index e3a5e9890..f1fd0c40f 100644 --- a/app/Http/Controllers/Webhook/Concerns/MatchesManualWebhookApplications.php +++ b/app/Http/Controllers/Webhook/Concerns/MatchesManualWebhookApplications.php @@ -42,7 +42,13 @@ protected function manualWebhookRepositoryMatches(?string $gitRepository, string { $repositoryPath = $this->canonicalManualWebhookRepository($gitRepository); - return $repositoryPath !== null && hash_equals($fullName, $repositoryPath); + if ($repositoryPath === null) { + return false; + } + + // Git hosts (GitHub, GitLab, Gitea, Bitbucket) treat owner/repo names + // case-insensitively, so compare the canonical paths case-insensitively. + return hash_equals(mb_strtolower($fullName), mb_strtolower($repositoryPath)); } /** diff --git a/tests/Feature/Webhook/WebhookHmacTest.php b/tests/Feature/Webhook/WebhookHmacTest.php index 24d9ed72a..be2417462 100644 --- a/tests/Feature/Webhook/WebhookHmacTest.php +++ b/tests/Feature/Webhook/WebhookHmacTest.php @@ -508,6 +508,29 @@ function createApplicationWithWebhook(string $repo = 'test-org/test-repo', strin $response->assertOk(); expect($response->getContent())->not->toContain('No applications found'); }); + + test('github matches repository case-insensitively', function () { + $app = createApplicationWithWebhook(overrides: [ + 'git_repository' => 'https://github.com/Test-Org/Test-Repo.git', + ]); + $secret = $app->manual_webhook_secret_github; + + $payload = json_encode([ + 'ref' => 'refs/heads/main', + 'repository' => ['full_name' => 'test-org/test-repo'], + 'after' => 'abc123', + 'commits' => [], + ]); + + $response = $this->call('POST', '/webhooks/source/github/events/manual', [], [], [], [ + 'HTTP_X-GitHub-Event' => 'push', + 'HTTP_X-Hub-Signature-256' => 'sha256='.hash_hmac('sha256', $payload, $secret), + 'CONTENT_TYPE' => 'application/json', + ], $payload); + + $response->assertOk(); + expect($response->getContent())->not->toContain('No applications found'); + }); }); describe('Webhook Secret Auto-Generation', function () {