diff --git a/app/Http/Controllers/WidgetController.php b/app/Http/Controllers/WidgetController.php index 957a5cf5..7545a407 100644 --- a/app/Http/Controllers/WidgetController.php +++ b/app/Http/Controllers/WidgetController.php @@ -30,25 +30,8 @@ public function config(Request $request): JsonResponse return response()->json(['enabled' => false], 200); } - // Validate origin domain if restrictions are set - if (!empty($settings->allowed_domains)) { - $origin = $request->header('Origin') ?? $request->header('Referer'); - - if ($origin) { - $domain = parse_url($origin, PHP_URL_HOST); - $allowed = false; - - foreach ($settings->allowed_domains as $allowedDomain) { - if ($domain === $allowedDomain || str_ends_with($domain, '.' . $allowedDomain)) { - $allowed = true; - break; - } - } - - if (!$allowed) { - return response()->json(['enabled' => false], 200); - } - } + if (! $this->isOriginAllowed($request, $settings->allowed_domains)) { + return response()->json(['enabled' => false], 200); } return response()->json([ @@ -69,25 +52,8 @@ public function submit(Request $request): JsonResponse return response()->json(['error' => 'Widget is not enabled'], 403); } - // Validate origin domain if restrictions are set - if (!empty($settings->allowed_domains)) { - $origin = $request->header('Origin') ?? $request->header('Referer'); - - if ($origin) { - $domain = parse_url($origin, PHP_URL_HOST); - $allowed = false; - - foreach ($settings->allowed_domains as $allowedDomain) { - if ($domain === $allowedDomain || str_ends_with($domain, '.' . $allowedDomain)) { - $allowed = true; - break; - } - } - - if (!$allowed) { - return response()->json(['error' => 'Domain not allowed'], 403); - } - } + if (! $this->isOriginAllowed($request, $settings->allowed_domains)) { + return response()->json(['error' => 'Domain not allowed'], 403); } // Validate request @@ -155,25 +121,8 @@ public function activityConfig(Request $request): JsonResponse return response()->json(['enabled' => false], 200); } - // Validate origin domain if restrictions are set - if (!empty($settings->allowed_domains)) { - $origin = $request->header('Origin') ?? $request->header('Referer'); - - if ($origin) { - $domain = parse_url($origin, PHP_URL_HOST); - $allowed = false; - - foreach ($settings->allowed_domains as $allowedDomain) { - if ($domain === $allowedDomain || str_ends_with($domain, '.' . $allowedDomain)) { - $allowed = true; - break; - } - } - - if (!$allowed) { - return response()->json(['enabled' => false], 200); - } - } + if (! $this->isOriginAllowed($request, $settings->allowed_domains)) { + return response()->json(['enabled' => false], 200); } return response()->json([ @@ -196,25 +145,8 @@ public function activityList(Request $request): JsonResponse return response()->json(['error' => 'Widget is not enabled'], 403); } - // Validate origin domain if restrictions are set - if (!empty($settings->allowed_domains)) { - $origin = $request->header('Origin') ?? $request->header('Referer'); - - if ($origin) { - $domain = parse_url($origin, PHP_URL_HOST); - $allowed = false; - - foreach ($settings->allowed_domains as $allowedDomain) { - if ($domain === $allowedDomain || str_ends_with($domain, '.' . $allowedDomain)) { - $allowed = true; - break; - } - } - - if (!$allowed) { - return response()->json(['error' => 'Domain not allowed'], 403); - } - } + if (! $this->isOriginAllowed($request, $settings->allowed_domains)) { + return response()->json(['error' => 'Domain not allowed'], 403); } $page = max(1, (int) $request->input('page', 1)); @@ -281,4 +213,34 @@ public function activityList(Request $request): JsonResponse 'has_more' => $paginator->hasMorePages(), ]); } + + /** + * @param array $allowedDomains + */ + private function isOriginAllowed(Request $request, array $allowedDomains): bool + { + if (empty($allowedDomains)) { + return true; + } + + $origin = $request->header('Origin') ?? $request->header('Referer'); + + if (! is_string($origin)) { + return false; + } + + $domain = parse_url($origin, PHP_URL_HOST); + + if (! is_string($domain)) { + return false; + } + + foreach ($allowedDomains as $allowedDomain) { + if ($domain === $allowedDomain || str_ends_with($domain, '.'.$allowedDomain)) { + return true; + } + } + + return false; + } } diff --git a/tests/Feature/Widget/WidgetTest.php b/tests/Feature/Widget/WidgetTest.php index bd3642af..05a11e0f 100644 --- a/tests/Feature/Widget/WidgetTest.php +++ b/tests/Feature/Widget/WidgetTest.php @@ -2,6 +2,7 @@ use App\Models\Item; use App\Settings\WidgetSettings; +use App\Settings\ActivityWidgetSettings; use function Pest\Laravel\assertDatabaseHas; beforeEach(function () { @@ -120,6 +121,16 @@ $response->assertForbidden(); }); +test('widget submission requires an origin when domains are restricted', function () { + $this->settings->allowed_domains = ['example.com']; + $this->settings->save(); + + $this->postJson('/api/widget/submit', [ + 'title' => 'Test Feedback', + 'content' => 'This is a test feedback', + ])->assertForbidden(); +}); + test('widget submission allows configured domains', function () { $this->settings->allowed_domains = ['example.com']; $this->settings->save(); @@ -158,6 +169,29 @@ ]); }); +test('widget config is disabled without an origin when domains are restricted', function () { + $this->settings->allowed_domains = ['example.com']; + $this->settings->save(); + + $this->getJson('/api/widget/config') + ->assertSuccessful() + ->assertJson(['enabled' => false]); +}); + +test('activity widget requires an origin when domains are restricted', function () { + $settings = app(ActivityWidgetSettings::class); + $settings->enabled = true; + $settings->allowed_domains = ['example.com']; + $settings->save(); + + $this->getJson('/api/activity-widget/config') + ->assertSuccessful() + ->assertJson(['enabled' => false]); + + $this->getJson('/api/activity-widget/activities') + ->assertForbidden(); +}); + test('widget javascript is served correctly', function () { $response = $this->get('/widget.js');