Fix R15: no weak admin fallback in prod; validate device room update

- DatabaseSeeder throws outside local/testing when HOMEOS_ADMIN_PASSWORD is
  unset, instead of silently seeding the documented `homeos-dev` password
  (bootstrap always runs migrate --seed, so a prod misconfig must fail loudly).
- Devices\Show::saveRoom validates roomId as nullable|exists:rooms,id, so a
  crafted request can no longer trigger a foreign-key 500. Added a feature test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat/phase-1-bootstrap
HomeOS Bootstrap 2026-07-17 21:55:21 +02:00
parent f00a1ac069
commit dc27b1686c
3 changed files with 28 additions and 3 deletions

View File

@ -37,6 +37,7 @@ class Show extends Component
public function saveRoom(): void
{
$this->validate(['roomId' => ['nullable', 'integer', 'exists:rooms,id']]);
$this->device->update(['room_id' => $this->roomId ?: null]);
$this->flashMessage(__('devices.saved'));
}

View File

@ -15,9 +15,17 @@ class DatabaseSeeder extends Seeder
*/
public function run(): void
{
// `?:` (not env's 2nd arg) so a present-but-empty HOMEOS_ADMIN_PASSWORD can never
// seed a blank-password admin — it falls back to a non-empty dev default.
$adminPassword = env('HOMEOS_ADMIN_PASSWORD') ?: 'homeos-dev';
// The admin password must be explicit outside local/testing — never silently seed a
// known credential in production. The dev fallback only applies to local/testing.
$adminPassword = env('HOMEOS_ADMIN_PASSWORD');
if (blank($adminPassword)) {
if (! app()->environment('local', 'testing')) {
throw new \RuntimeException('HOMEOS_ADMIN_PASSWORD must be set to seed the admin account outside local/testing.');
}
$adminPassword = 'homeos-dev';
}
User::updateOrCreate(
['email' => env('HOMEOS_ADMIN_EMAIL', 'admin@homeos.local')],

View File

@ -2,12 +2,14 @@
namespace Tests\Feature;
use App\Livewire\Devices\Show;
use App\Models\Device;
use App\Models\DeviceState;
use App\Models\Entity;
use App\Models\Room;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Tests\TestCase;
class HomeTest extends TestCase
@ -55,4 +57,18 @@ class HomeTest extends TestCase
->assertSee(__('dashboard.warnings_title'))
->assertSee('Türsensor');
}
public function test_device_room_update_rejects_invalid_room(): void
{
$device = Device::create(['name' => 'Lampe', 'status' => 'active', 'last_seen_at' => now()]);
$this->actingAs(User::factory()->create());
Livewire::test(Show::class, ['device' => $device])
->set('roomId', 999999)
->call('saveRoom')
->assertHasErrors('roomId');
$this->assertNull($device->fresh()->room_id);
}
}