Skip to content

Issue #122 (buildora) onderzoeken: vaststellen of RichTextField/EditorField kwetsbaar zijn voor stored XSS doordat WYSIWYG-HTML ongesanitized wordt opgeslagen en unescaped gerenderd, en zo nodig fixen - #189

Merged
zyra-ginkelsoft[bot] merged 1 commit into
mainfrom
zyra/issue-122-buildora-onderzoeken-vaststell-ce181a48
Sep 3, 2026
Merged

zyra-ginkelsoft[bot] merged 1 commit into
mainfrom
zyra/issue-122-buildora-onderzoeken-vaststell-ce181a48

Conversation

@zyra-ginkelsoft

Copy link
Copy Markdown
Contributor

Nu ga ik kijken of htmlpurifier lokaal beschikbaar is via internet, en hoe composer/vendor er nu bij staan.No vendor dir. Ik moet composer install draaien om te testen. Eerst controleren of er netwerktoegang is.Goed, script bestaat al en heeft een fallback voor ext-gd. Ik gebruik die fallback voor composer install hier.Composer install lukt. Nu run ik eerst de bestaande tests als baseline.Baseline: 36 tests slagen. Nu implementeer ik de sanitization. Eerst de trait en de aanpassing van Field/RichTextField/EditorField.Nu voeg ik een default setValue() toe aan de basis Field-klasse, en pas RichTextField/EditorField aan om de trait te gebruiken.Nu de controller aanpassen zodat setValue() vóór save wordt aangeroepen.Nu hetzelfde voor update().Nu de tests draaien.Alle nieuwe tests slagen. Nu de volledige testsuite, lint en typecheck draaien.Nu de trait aanpassen: setValue → prepareForStorage.Nu de controller bijwerken.Nu de RichTextField/EditorField docblocks en de test bijwerken.Nu de resterende docblock-verwijzingen naar setValue() in de test en in RichTextField/EditorField bijwerken.Nu opnieuw de volledige testsuite draaien.Alle 51 tests slagen. Nu lint en phpstan.Lint schoon. Nu phpstan.Precies 59 fouten, zowel vóór als na mijn wijzigingen — allemaal pre-existing en niet in mijn nieuwe code (Field::prepareForStorage, SanitizesHtml-trait niet vermeld). Lint en typecheck zijn dus schoon voor mijn wijzigingen; ik introduceer geen nieuwe phpstan-fouten.Nu controleer ik of .zyra/proef.sh daadwerkelijk werkt.Migraties werken. Nu de server kort starten om te bevestigen dat het proefscript werkt..zyra/proef.sh werkt (200 OK op /buildora/install). Nu een .zyra/issue-122-comment.md toevoegen conform de bestaande conventie, en daarna committen.Goed, composer.lock en vendor/ zijn genegeerd — dat scheelt. Laten we de definitieve diff bekijken en committen.That reordering (sort-packages) is expected and harmless. Nu commit.Laatste check: volledige testsuite nogmaals draaien op de gecommitte staat, ter bevestiging.## Samenvatting

Onderzoek (issue #122): RichTextField/EditorField waren kwetsbaar voor stored XSS.

Bewijs:

  • RichTextField/EditorField deden vóór save geen enkele sanitization van de ingevoerde HTML.
  • resources/views/components/input/richtext.blade.php:4 rendert de waarde als {!! $value !!} (unescaped), en wordt via form.blade.php op zowel create- als edit-pagina's ingeladen.
  • Bonusbevinding (niet gefixt, buiten scope): components/datatable.blade.php gebruikt Alpine's x-html op de ruwe, niet-ge-escapte veldwaarde uit RowFormatter::format() — dat raakt in principe elk fieldtype, niet alleen RichText/Editor.
  • Payload <script>alert(1)</script> opgeslagen via deze velden kwam vóór de fix ongestript terug in beide renderpaden.

Fix:

  • Field::prepareForStorage(mixed $value): mixed toegevoegd (default: identity-functie). Ik heb bewust niet de naam setValue() gebruikt zoals in het plan stond, omdat die methode in dit package al een andere, gevestigde betekenis heeft (waarde vullen vanuit een model voor weergave — gebruikt door o.a. SelectField, BooleanField, ViewField, ...). Hergebruik ervan gaf een fatale signature-conflict met ViewField::setValue(Model $model).
  • BuildoraController::store()/update() roepen nu $field->prepareForStorage($value) aan vlak vóór het opslaan, voor elk veld dat in de request zit.
  • Nieuwe trait Fields\Traits\SanitizesHtml (gebruikt door RichTextField en EditorField) saniteert via ezyang/htmlpurifier:
    • ->sanitize(bool $enabled = true) — standaard aan.
    • ->allowedTags(array $tags) — configureerbare allow-list, met een veilige default (basisopmaak, geen script/event-handlers/javascript:-URI's).

Getest:

  • Nieuwe regressietests tests/Unit/Fields/RichTextAndEditorFieldXssTest.php: <script>-payload, onerror=-attribuut en javascript:-URI worden gestript; gewone opmaak (<p>, <strong>, <li>) blijft behouden; sanitize staat default aan en is uit te zetten; allowedTags() is configureerbaar; gewone fieldtypes (zonder de trait) blijven ongewijzigd.
  • Volledige suite: 51/51 groen (was 36/36 vóór deze wijziging).
  • composer lint (PHPCS/PSR-12): 0 fouten in gewijzigde bestanden.
  • composer analyse (PHPStan level 5): exact dezelfde 59 pre-existing fouten vóór en na mijn wijziging — geen nieuwe fouten in Field.php, SanitizesHtml.php of BuildoraController.php.
  • .zyra/proef.sh bevestigd werkend (composer install met --ignore-platform-req=ext-gd-fallback, migraties, testbench serve → 200 OK op /buildora/install).
  • Geen geheimen/tokens toegevoegd.

Open:

  • De bredere x-html/datatable-bevinding (geldt voor alle fieldtypes, niet alleen RichText/Editor) is gedocumenteerd maar niet meegenomen in deze PR — dat is een apart, groter onderwerp.
  • .zyra/issue-122-comment.md bevat een kant-en-klare GitHub-issuecomment met de audit-conclusie en bewijs (er is geen gh-toegang vanuit deze werkplaats om 'm zelf te plaatsen).

Commit staat klaar op de branch; ik heb niet gepusht en geen PR geopend, conform de afspraken.

…g (stored XSS)

RichTextField en EditorField sloegen ingevoerde HTML ongewijzigd op, terwijl
richtext.blade.php ({!! $value !!}), form.blade.php en de datatable (x-html)
die waarde op meerdere plekken unescaped renderen. Een payload als
<script>alert(1)</script> kwam daardoor ongestript terug: stored XSS.

Voegt Field::prepareForStorage() toe (identity by default) die de
controller vlak vóór create()/update() per veld aanroept, en een
SanitizesHtml-trait (HTMLPurifier) die RichTextField/EditorField gebruiken
om die hook te overschrijven: sanitization staat standaard aan
(->sanitize()) met een configureerbare allow-list (->allowedTags()).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@zyra-ginkelsoft
zyra-ginkelsoft Bot merged commit f8d0281 into main Sep 3, 2026
11 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants