Security (R6.2): Haertung nach Audit

- Security-Header-Middleware (nosniff, X-Frame-Options, Referrer-/
  Permissions-Policy) global, proxy-unabhaengig; CSP bewusst separat.
- Firmenlogo-Upload im Create-Formular durch den ImageService-Pfad
  ersetzt (Mime-Whitelist ohne SVG, Re-Encode, UUID-Name) - vorher
  rohe store()-Ablage ohne Server-Validierung (SVG/XSS moeglich).
- Rate-Limits ergaenzt: Passwort-Reset (3/E-Mail + 15/IP pro Std.),
  Registrierung (10/IP pro Std.), throttle auf magic-login- und
  pm-vorschau-Consume-Routen (Defense-in-Depth).
- EnsureUserIsCustomer als persistente Livewire-Middleware
  (symmetrisch zum Admin-Gate, greift auch bei Folge-Requests).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-07-03 13:43:59 +00:00
parent acb2a19aff
commit 89550dac99
9 changed files with 212 additions and 9 deletions

View file

@ -3,6 +3,7 @@
use App\Enums\CompanyType;
use App\Enums\Portal;
use App\Models\Company;
use App\Services\Image\ImageService;
use Illuminate\Support\Str;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
@ -48,7 +49,7 @@ new #[Layout('components.layouts.app'), Title('Neue Firma')] class extends Compo
#[Validate('required|max:2')]
public string $country = 'DE';
#[Validate('nullable|image|max:1024')]
#[Validate('nullable|image|max:4096')]
public $logo;
#[Validate('nullable|max:255')]
@ -61,17 +62,13 @@ new #[Layout('components.layouts.app'), Title('Neue Firma')] class extends Compo
public bool $is_active = true;
public function save(): void
public function save(ImageService $imageService): void
{
$this->validate();
$slug = (new Company)->generateUniqueSlug($this->company_name, ['portal' => $this->portal]);
$logoPath = $this->logo
? $this->logo->store('company-logos', 'public')
: null;
Company::query()->create([
$company = Company::query()->create([
'portal' => $this->portal,
'type' => $this->type,
'name' => $this->company_name,
@ -81,10 +78,24 @@ new #[Layout('components.layouts.app'), Title('Neue Firma')] class extends Compo
'phone' => $this->phone ?: null,
'email' => $this->email ?: null,
'website' => $this->website ?: null,
'logo_path' => $logoPath,
'is_active' => $this->is_active,
]);
// Logo wie im Edit-Pfad über den ImageService (Mime-Whitelist ohne
// SVG, Re-Encode via GD, UUID-Name) — nie roh auf die public-Disk.
if ($this->logo) {
$stored = $imageService->storeCompanyLogo(
$this->logo,
$this->portal === Portal::Both->value ? Portal::Presseecho->value : $this->portal,
$company->id,
);
$company->update([
'logo_path' => $stored['path'],
'logo_variants' => $stored['variants'],
]);
}
session()->flash('success', 'Firma erfolgreich erstellt.');
$this->redirect(route('admin.companies.index'), navigate: true);
}