Changes auto-committed by Conductor

This commit is contained in:
Andras Bacsai 2025-10-20 09:48:37 +02:00
parent 58864b9b20
commit 84559a0e7d
3 changed files with 111 additions and 5 deletions

View file

@ -4,11 +4,36 @@
use App\Models\InstanceSettings;
use Illuminate\Http\Middleware\TrustHosts as Middleware;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Spatie\Url\Url;
class TrustHosts extends Middleware
{
/**
* Handle the incoming request.
*
* Skip host validation for certain routes:
* - Terminal auth routes (called by realtime container)
* - API routes (use token-based authentication, not host validation)
* - Webhook endpoints (use cryptographic signature validation)
*/
public function handle(Request $request, $next)
{
// Skip host validation for these routes
if ($request->is(
'terminal/auth',
'terminal/auth/ips',
'api/*',
'webhooks/*'
)) {
return $next($request);
}
// For all other routes, use parent's host validation
return parent::handle($request, $next);
}
/**
* Get the host patterns that should be trusted.
*

View file

@ -106,7 +106,7 @@ class="flex flex-wrap gap-1.5 max-h-40 overflow-y-auto scrollbar py-1.5 w-full t
wire:dirty.class="dark:border-l-warning border-l-coollabs border-l-4">
{{-- Selected Tags Inside Input --}}
<template x-for="value in selected" :key="value">
<template x-for="value in (selected || [])" :key="value">
<button
type="button"
@click.stop="removeOption(value, $event)"
@ -133,13 +133,13 @@ class="flex-1 min-w-[120px] text-sm border-0 outline-none bg-transparent p-0 foc
<div x-show="open && !{{ $disabled ? 'true' : 'false' }}" x-transition
class="absolute z-50 w-full mt-1 bg-white dark:bg-coolgray-100 border border-neutral-300 dark:border-coolgray-400 rounded shadow-lg max-h-60 overflow-auto scrollbar">
<template x-if="filteredOptions.length === 0">
<template x-if="(filteredOptions || []).length === 0">
<div class="px-3 py-2 text-sm text-neutral-500 dark:text-neutral-400">
No options found
</div>
</template>
<template x-for="option in filteredOptions" :key="option.value">
<template x-for="option in (filteredOptions || [])" :key="option.value">
<div @click="toggleOption(option.value)"
class="px-3 py-2 cursor-pointer hover:bg-neutral-100 dark:hover:bg-coolgray-200 flex items-center gap-3"
:class="{ 'bg-neutral-50 dark:bg-coolgray-300': isSelected(option.value) }">
@ -262,13 +262,13 @@ class="shrink-0 text-neutral-400 px-2 {{ $disabled ? 'cursor-not-allowed' : 'cur
<div x-show="open && !{{ $disabled ? 'true' : 'false' }}" x-transition
class="absolute z-50 w-full mt-1 bg-white dark:bg-coolgray-100 border border-neutral-300 dark:border-coolgray-400 rounded shadow-lg max-h-60 overflow-auto scrollbar">
<template x-if="filteredOptions.length === 0">
<template x-if="(filteredOptions || []).length === 0">
<div class="px-3 py-2 text-sm text-neutral-500 dark:text-neutral-400">
No options found
</div>
</template>
<template x-for="option in filteredOptions" :key="option.value">
<template x-for="option in (filteredOptions || [])" :key="option.value">
<div @click="selectOption(option.value)"
class="px-3 py-2 cursor-pointer hover:bg-neutral-100 dark:hover:bg-coolgray-200"
:class="{ 'bg-neutral-50 dark:bg-coolgray-300': selected == option.value }">

View file

@ -227,3 +227,84 @@
// Should only contain APP_URL pattern, not any FQDN
expect($hosts2)->not->toBeEmpty();
});
it('skips host validation for terminal auth routes', function () {
// These routes should be accessible with any Host header (for internal container communication)
$response = $this->postJson('/terminal/auth', [], [
'Host' => 'coolify:8080', // Internal Docker host
]);
// Should not get 400 Bad Host (might get 401 Unauthorized instead)
expect($response->status())->not->toBe(400);
});
it('skips host validation for terminal auth ips route', function () {
// These routes should be accessible with any Host header (for internal container communication)
$response = $this->postJson('/terminal/auth/ips', [], [
'Host' => 'soketi:6002', // Another internal Docker host
]);
// Should not get 400 Bad Host (might get 401 Unauthorized instead)
expect($response->status())->not->toBe(400);
});
it('still enforces host validation for non-terminal routes', function () {
InstanceSettings::updateOrCreate(
['id' => 0],
['fqdn' => 'https://coolify.example.com']
);
// Regular routes should still validate Host header
$response = $this->get('/', [
'Host' => 'evil.com',
]);
// Should get 400 Bad Host for untrusted host
expect($response->status())->toBe(400);
});
it('skips host validation for API routes', function () {
// All API routes use token-based auth (Sanctum), not host validation
// They should be accessible from any host (mobile apps, CLI tools, scripts)
// Test health check endpoint
$response = $this->get('/api/health', [
'Host' => 'internal-lb.local',
]);
expect($response->status())->not->toBe(400);
// Test v1 health check
$response = $this->get('/api/v1/health', [
'Host' => '10.0.0.5',
]);
expect($response->status())->not->toBe(400);
// Test feedback endpoint
$response = $this->post('/api/feedback', [], [
'Host' => 'mobile-app.local',
]);
expect($response->status())->not->toBe(400);
});
it('skips host validation for webhook endpoints', function () {
// All webhook routes are under /webhooks/* prefix (see RouteServiceProvider)
// and use cryptographic signature validation instead of host validation
// Test GitHub webhook
$response = $this->post('/webhooks/source/github/events', [], [
'Host' => 'github-webhook-proxy.local',
]);
expect($response->status())->not->toBe(400);
// Test GitLab webhook
$response = $this->post('/webhooks/source/gitlab/events/manual', [], [
'Host' => 'gitlab.example.com',
]);
expect($response->status())->not->toBe(400);
// Test Stripe webhook
$response = $this->post('/webhooks/payments/stripe/events', [], [
'Host' => 'stripe-webhook-forwarder.local',
]);
expect($response->status())->not->toBe(400);
});