From f75622b63276212c86b8a26a62da93b48b5497c4 Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Fri, 10 Apr 2026 12:00:27 +0100 Subject: [PATCH] Allow GitHub webhook ping events for unapproved plugins Move the X-GitHub-Event header check before the plugin approval gate so that GitHub's test ping returns 200 regardless of plugin status. This lets developers verify webhook setup before their plugin is approved. Co-Authored-By: Claude Opus 4.6 --- .../Controllers/PluginWebhookController.php | 8 ++- tests/Feature/PluginWebhookTest.php | 70 +++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 tests/Feature/PluginWebhookTest.php diff --git a/app/Http/Controllers/PluginWebhookController.php b/app/Http/Controllers/PluginWebhookController.php index 73ace98b..a6360637 100644 --- a/app/Http/Controllers/PluginWebhookController.php +++ b/app/Http/Controllers/PluginWebhookController.php @@ -18,12 +18,16 @@ public function __invoke(Request $request, string $secret, PluginSyncService $sy return response()->json(['error' => 'Invalid webhook secret'], 404); } + $event = $request->header('X-GitHub-Event'); + + if ($event === 'ping') { + return response()->json(['success' => true, 'message' => 'pong']); + } + if (! $plugin->isApproved()) { return response()->json(['error' => 'Plugin is not approved'], 403); } - $event = $request->header('X-GitHub-Event'); - if ($event === 'release') { // Sync plugin metadata to update latest_version $syncService->sync($plugin); diff --git a/tests/Feature/PluginWebhookTest.php b/tests/Feature/PluginWebhookTest.php new file mode 100644 index 00000000..ac4776df --- /dev/null +++ b/tests/Feature/PluginWebhookTest.php @@ -0,0 +1,70 @@ +create(); + + $response = $this->postJson( + route('webhooks.plugins', $plugin->webhook_secret), + [], + ['X-GitHub-Event' => 'ping'] + ); + + $response->assertOk() + ->assertJson(['success' => true, 'message' => 'pong']); + } + + #[Test] + public function ping_event_succeeds_for_approved_plugin(): void + { + $plugin = Plugin::factory()->approved()->create(); + + $response = $this->postJson( + route('webhooks.plugins', $plugin->webhook_secret), + [], + ['X-GitHub-Event' => 'ping'] + ); + + $response->assertOk() + ->assertJson(['success' => true, 'message' => 'pong']); + } + + #[Test] + public function non_ping_event_returns_403_for_unapproved_plugin(): void + { + $plugin = Plugin::factory()->create(); + + $response = $this->postJson( + route('webhooks.plugins', $plugin->webhook_secret), + [], + ['X-GitHub-Event' => 'push'] + ); + + $response->assertForbidden() + ->assertJson(['error' => 'Plugin is not approved']); + } + + #[Test] + public function invalid_secret_returns_404(): void + { + $response = $this->postJson( + route('webhooks.plugins', 'invalid-secret'), + [], + ['X-GitHub-Event' => 'ping'] + ); + + $response->assertNotFound(); + } +}