Warenwirtschaft: OPT-9 AuthZ/CSRF-Härtung (Audit M-2, M-3, M-6, M-7, M-10)

- M-2: Produktionen buchen Bestand — StoreProductionRequest verlangt
  jetzt admin >= 7 (wie manuelle Bestandsbewegungen); create/edit/copy
  im Controller gegated, UI-Buttons für niedrigere Stufen ausgeblendet.
- M-3: Ausschuss-/Bewegungs-/Produktions-Buttons nur noch für
  Berechtigte sichtbar (keine toten Formulare mehr).
- M-6: Zustandsändernde GET-Routen umgestellt — stock-entries/copy
  jetzt POST mit CSRF; /admin/product/delete jetzt DELETE-Formulare
  (Produktliste + Rezeptur-Zeile im Produktformular).
- M-7: ProductController::imageAttribute whitelistet Spalten
  ('active', 'pos') + numerische Werte, sonst 400 — kein beliebiger
  Spalten-Write per URL mehr.
- M-10: URL-Felder (Lieferant, Verpackungsartikel, INCI-Lieferanten-
  Repeater) validieren url:http,https — keine speicherbaren
  javascript:-Links mehr.

Tests: ProductionHardeningTest +2; drei bestehende Tests an neue
Regeln angepasst (admin-Level 7 für Produktions-Formular, POST-Copy).
Suite: 231 grün.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-07-15 12:55:11 +00:00
parent fa44af0a1f
commit 9ed47c02f3
19 changed files with 104 additions and 41 deletions

View file

@ -637,10 +637,12 @@
<td class="align-middle pi-effective text-right small text-muted">—</td>
<td class="align-middle">
@if ($product->id)
<a class="text-danger"
href="{{ route('admin_product_delete', [$product->id, 'ingredient', $ingredient->id]) }}"
onclick="return confirm('{{ __('Eintrag entfernen?') }}');"><i
class="far fa-trash-alt"></i></a>
<form method="post" action="{{ route('admin_product_delete', [$product->id, 'ingredient', $ingredient->id]) }}"
class="d-inline" onsubmit="return confirm('{{ __('Eintrag entfernen?') }}');">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-link text-danger p-0 border-0 align-baseline"><i class="far fa-trash-alt"></i></button>
</form>
@else
<a class="text-danger ingredient-row-remove" href="#"
title="{{ __('Entfernen') }}"><i class="far fa-trash-alt"></i></a>

View file

@ -97,9 +97,12 @@
<td><a class="text-info" href="{{ route('admin_product_copy', [$value->id]) }}"
onclick="return confirm('{{ __('Eintrag kopieren?') }}');"><i
class="far fa-copy"></i></a> &nbsp;
<a class="text-danger" href="{{ route('admin_product_delete', [$value->id]) }}"
onclick="return confirm('{{ __('Really delete entry?') }}');"><i
class="far fa-trash-alt"></i></a>
<form method="post" action="{{ route('admin_product_delete', [$value->id]) }}" class="d-inline"
onsubmit="return confirm('{{ __('Really delete entry?') }}');">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-link text-danger p-0 border-0 align-baseline"><i class="far fa-trash-alt"></i></button>
</form>
</td>
</tr>
@endforeach