provider ?: 'git')); $event = $this->eventName($request, $provider); $deliveryId = $this->deliveryId($request, $provider); $rawPayload = $request->getContent(); if (! $repository->is_active) { $result = ['status' => 'rejected', 'message' => 'Repository is inactive.', 'commit_count' => 0, 'linked_count' => 0, 'ignored_count' => 1]; $service->logWebhook($repository, $provider, $deliveryId, $event, $result); return response()->json($result, 403); } if (! $this->hasValidSignature($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); return response()->json($result, 403); } try { /** @var array $payload */ $payload = json_decode($rawPayload, true, 512, JSON_THROW_ON_ERROR); } catch (JsonException) { $result = ['status' => 'rejected', 'message' => 'Invalid JSON webhook payload.', 'commit_count' => 0, 'linked_count' => 0, 'ignored_count' => 1]; $service->logWebhook($repository, $provider, $deliveryId, $event, $result); return response()->json($result, 400); } $result = $service->ingest($repository, $payload, $provider, $deliveryId, $event); return response()->json($result, 202); } /** * Validate provider-specific webhook signatures or tokens. */ private function hasValidSignature(Request $request, GitRepository $repository, string $provider, string $rawPayload): bool { $secret = trim((string) $repository->webhook_secret); if ($secret === '') { return false; } return match ($provider) { 'github' => $this->hasValidGitHubSignature($request, $secret, $rawPayload), 'gitlab' => hash_equals($secret, (string) $request->header('X-Gitlab-Token', '')), default => hash_equals($secret, (string) $request->header('X-Helpdesk-Token', '')), }; } /** * Validate GitHub's X-Hub-Signature-256 HMAC header. */ private function hasValidGitHubSignature(Request $request, string $secret, string $rawPayload): bool { $signature = (string) $request->header('X-Hub-Signature-256', ''); $expected = 'sha256='.hash_hmac('sha256', $rawPayload, $secret); return hash_equals($expected, $signature); } /** * Read the event name from provider-specific headers. */ private function eventName(Request $request, string $provider): ?string { return match ($provider) { 'github' => $request->header('X-GitHub-Event'), 'gitlab' => $request->header('X-Gitlab-Event'), default => $request->header('X-Helpdesk-Event', 'push'), }; } /** * Read the provider delivery/request id used for idempotent audit tracing. */ private function deliveryId(Request $request, string $provider): ?string { return match ($provider) { 'github' => $request->header('X-GitHub-Delivery'), 'gitlab' => $request->header('X-Gitlab-Event-UUID') ?: $request->header('X-Request-Id'), default => $request->header('X-Helpdesk-Delivery'), }; } }