[Security][P1] Neutraliseer Markdown- en YAML-injectie uit presentatie-import #876

Closed
opened 2026-07-26 09:06:48 +00:00 by brenno · 3 comments
Owner

Probleem

Tekst uit .pptx, .odp en .key wordt als data in het OciDeck-model gezet, maar wordt later per Markdown-context geserialiseerd. Met name DeckBuilder._freeMarkdownBody schrijft titels, alinea's, koppen, bullets, quotes en links rechtstreeks als Markdown. Ook gestructureerde velden worden door MarkdownService als kop, lijst, tabel of front matter uitgegeven. De importer valideert het uiteindelijke document niet opnieuw en de veiligheidspoort voor het openen van een vreemd .md wordt bij deze modelroute niet doorlopen.

Daardoor kunnen Markdown-metatekens, slide-separators, Marp-directives, afbeeldings-/linksyntax, regeleinden en YAML-controltekens uit een bronpresentatie structurele betekenis krijgen in het opgeslagen deck.

Risico

Een gemanipuleerde presentatie kan extra slides of directives introduceren, ongeldige front matter maken of een externe resource in het gegenereerde document opnemen. Netwerkmedia heeft elders een fail-closed poort, maar de import hoort brondata niet ongemerkt in actieve Markdown-structuur te veranderen.

Voorgestelde aanpak

  • Leg per uitvoercontext vast wat letterlijke brontekst is en wat bewust Markdown mag blijven.
  • Escape of encodeer titel, heading, bullet, tabelcel, quote, linktekst, notitie en vrije tekst contextspecifiek.
  • Maak YAML-scalarserialisatie volledig voor controltekens en gereserveerde waarden.
  • Valideer de definitieve gegenereerde Markdown vóór opslaan/heropenen met dezelfde relevante contract- en veiligheidspoorten.
  • Behoud legitieme hyperlinks/media alleen via expliciete getypeerde velden.

Acceptatiecriteria

  • Brontekst met ![](), ---, Marp-directives, HTML en multiline-invoer verschijnt als tekst en maakt geen extra structuur.
  • YAML-waarden met gereserveerde prefixen en controltekens round-trippen als strings.
  • Getypeerde legitieme hyperlinks en media blijven werken volgens het bestaande netwerkbeleid.
  • Security-regressietests dekken iedere uitvoercontext en de save-open-roundtrip.
  • Een geïmporteerd deck veroorzaakt zonder expliciete toestemming geen onverwachte netwerkrequest.

Verificatie

Draai make check en importeer kwaadaardige synthetische fixtures voor PPTX, ODP en Keynote; genereer, parse en render het resultaat opnieuw.

Herkomst

Verplaatst en tegen de actuele OciDeck-port herijkt vanuit Keiko #6.

## Probleem Tekst uit `.pptx`, `.odp` en `.key` wordt als data in het OciDeck-model gezet, maar wordt later per Markdown-context geserialiseerd. Met name `DeckBuilder._freeMarkdownBody` schrijft titels, alinea's, koppen, bullets, quotes en links rechtstreeks als Markdown. Ook gestructureerde velden worden door `MarkdownService` als kop, lijst, tabel of front matter uitgegeven. De importer valideert het uiteindelijke document niet opnieuw en de veiligheidspoort voor het openen van een vreemd `.md` wordt bij deze modelroute niet doorlopen. Daardoor kunnen Markdown-metatekens, slide-separators, Marp-directives, afbeeldings-/linksyntax, regeleinden en YAML-controltekens uit een bronpresentatie structurele betekenis krijgen in het opgeslagen deck. ## Risico Een gemanipuleerde presentatie kan extra slides of directives introduceren, ongeldige front matter maken of een externe resource in het gegenereerde document opnemen. Netwerkmedia heeft elders een fail-closed poort, maar de import hoort brondata niet ongemerkt in actieve Markdown-structuur te veranderen. ## Voorgestelde aanpak - Leg per uitvoercontext vast wat letterlijke brontekst is en wat bewust Markdown mag blijven. - Escape of encodeer titel, heading, bullet, tabelcel, quote, linktekst, notitie en vrije tekst contextspecifiek. - Maak YAML-scalarserialisatie volledig voor controltekens en gereserveerde waarden. - Valideer de definitieve gegenereerde Markdown vóór opslaan/heropenen met dezelfde relevante contract- en veiligheidspoorten. - Behoud legitieme hyperlinks/media alleen via expliciete getypeerde velden. ## Acceptatiecriteria - Brontekst met `![]()`, `---`, Marp-directives, HTML en multiline-invoer verschijnt als tekst en maakt geen extra structuur. - YAML-waarden met gereserveerde prefixen en controltekens round-trippen als strings. - Getypeerde legitieme hyperlinks en media blijven werken volgens het bestaande netwerkbeleid. - Security-regressietests dekken iedere uitvoercontext en de save-open-roundtrip. - Een geïmporteerd deck veroorzaakt zonder expliciete toestemming geen onverwachte netwerkrequest. ## Verificatie Draai `make check` en importeer kwaadaardige synthetische fixtures voor PPTX, ODP en Keynote; genereer, parse en render het resultaat opnieuw. ## Herkomst Verplaatst en tegen de actuele OciDeck-port herijkt vanuit [Keiko #6](https://pawprint.vigilis.online/brenno/Keiko/issues/6).
Author
Owner

Opgepakt. Tak: fix/876-import-injection. Verwachte reikwijdte: de uitvoercontexten in lib/services/import/deck_builder.dart (_freeMarkdownBody: titel/heading/bullet/quote/link/tekst) en de gestructureerde serialisatie in de MarkdownService/YAML-front-matter; contextspecifieke escaping + validatie van het gegenereerde document vóór opslaan/heropenen. Bouwt voort op het #874-werk dat net op main staat.

Opgepakt. Tak: fix/876-import-injection. Verwachte reikwijdte: de uitvoercontexten in lib/services/import/deck_builder.dart (_freeMarkdownBody: titel/heading/bullet/quote/link/tekst) en de gestructureerde serialisatie in de MarkdownService/YAML-front-matter; contextspecifieke escaping + validatie van het gegenereerde document vóór opslaan/heropenen. Bouwt voort op het #874-werk dat net op main staat.
Author
Owner

Tussenstand. Aanpak afgestemd: neutraliseren aan de importgrens (niet in de serialisatielaag), zodat alleen imports worden geraakt en het .md-formaat voor eigen decks ongewijzigd blijft.

GEDAAN (tak fix/876-import-injection, commit gepusht, nog geen PR): de kern-neutralisator sanitizeImportedText + 26 tests groen. Het contract is getoetst tegen de bestaande fail-closed poort (MarkdownSafetyScanner als orakel): alle scanner-vectoren (script/foreignObject, iframe/object/embed/applet, on...=-handlers, javascript:/vbscript:/data:text/html-links, <!--, plus numerieke-entity- en zero-width-evasie) zijn na sanitizing veilig, en de structurele injectie (koppen, lijsten, thematische breuken, afbeeldings-/linksyntax, regeleinden) is ontmanteld — met behoud van gewone proza (haakjes, mintekens).

Belangrijk inzicht: de scanner decodeert numerieke entities maar geen named, dus &/</> is een veilige, inerte weergave (& eerst escapen).

NOG TE DOEN op deze tak:

  • de neutralisator per uitvoercontext in deck_builder.dart bedraden (titel, heading, paragraph, bullet, quote, quote-auteur, subtitle, linktekst via de volle sanitizer; tabelcel/notities via hun bestaande escaper — géén dubbele escaping);
  • de fail-closed scanner-backstop op het gegenereerde deck vóór loadDeck/saveDeck, als vangnet voor wat een per-veld-escaper mist (nu raakt de import de poort niet, en erger: een opgeslagen import met executable content wordt bij heropenen juist geweigerd);
  • YAML-scalar-hardening voor gereserveerde waarden (true/false/null/~) — de enige, round-trip-neutrale serialisatie-touch;
  • integratie-/save-open-roundtrip-tests per context + een adversariële import→build→scan-test.

Kern bewust apart gehouden en getoetst vóór de bedrading, omdat een fout in een injectie-neutralisator een echt gat is; de rest volgt op deze tak.

Tussenstand. Aanpak afgestemd: neutraliseren aan de importgrens (niet in de serialisatielaag), zodat alleen imports worden geraakt en het .md-formaat voor eigen decks ongewijzigd blijft. GEDAAN (tak fix/876-import-injection, commit gepusht, nog geen PR): de kern-neutralisator sanitizeImportedText + 26 tests groen. Het contract is getoetst tegen de bestaande fail-closed poort (MarkdownSafetyScanner als orakel): alle scanner-vectoren (script/foreignObject, iframe/object/embed/applet, on...=-handlers, javascript:/vbscript:/data:text/html-links, <!--, plus numerieke-entity- en zero-width-evasie) zijn na sanitizing veilig, en de structurele injectie (koppen, lijsten, thematische breuken, afbeeldings-/linksyntax, regeleinden) is ontmanteld — met behoud van gewone proza (haakjes, mintekens). Belangrijk inzicht: de scanner decodeert numerieke entities maar geen named, dus &amp;/&lt;/&gt; is een veilige, inerte weergave (& eerst escapen). NOG TE DOEN op deze tak: - de neutralisator per uitvoercontext in deck_builder.dart bedraden (titel, heading, paragraph, bullet, quote, quote-auteur, subtitle, linktekst via de volle sanitizer; tabelcel/notities via hun bestaande escaper — géén dubbele escaping); - de fail-closed scanner-backstop op het gegenereerde deck vóór loadDeck/saveDeck, als vangnet voor wat een per-veld-escaper mist (nu raakt de import de poort niet, en erger: een opgeslagen import met executable content wordt bij heropenen juist geweigerd); - YAML-scalar-hardening voor gereserveerde waarden (true/false/null/~) — de enige, round-trip-neutrale serialisatie-touch; - integratie-/save-open-roundtrip-tests per context + een adversariële import→build→scan-test. Kern bewust apart gehouden en getoetst vóór de bedrading, omdat een fout in een injectie-neutralisator een echt gat is; de rest volgt op deze tak.
Author
Owner

Gebouwd en gemerged in main (merge 27c172e2, PR #889). Neutralisatie per uitvoercontext aan de importgrens (titel/heading/bullet/quote/section/timeline/vrije-md/chart/linktekst via de volle sanitizer; tabelcel/notitie inline; URL- en video-ref via _safeUrl-breakout-encodering), plus een fail-closed backstop die de definitieve serialisatie nog eens door MarkdownSafetyScanner haalt vóór loadDeck/saveDeck (bulk/service → unsafeContent, enkel bestand → hetzelfde alarm als bij een vreemd .md). YAML-hardening voor gereserveerde woorden + controltekens. 37 tests (26 sanitizer + 11 integratie/regressie), 31 l10n-vertalingen.

Security-architect akkoord na twee gedichte gaten (newline in de hyperlink-URL én de video-ref braken uit tekst, backstop-blind — beide via _safeUrl gedicht, met regressietests die eerst rood stonden), en een uitputtende sweep over alle SourceSlide/SourceDeck-velden. Bewaker akkoord (de YAML-touch is round-trip-neutraal en uitwisselbaarheids-versterkend). make check groen op élke poort die de #876-code raakt, secrets/SAST schoon.

Bewust op het bewijs gemerged: de resterende make check-ruis op main (dartcv4 niet in THIRD_PARTY_NOTICES + een flaky detectietest) is #870's onafgemaakte migratie, niet #876 — apart gemeld. Tijdbudget/annulering blijven bij #875.

Gebouwd en gemerged in main (merge 27c172e2, PR #889). Neutralisatie per uitvoercontext aan de importgrens (titel/heading/bullet/quote/section/timeline/vrije-md/chart/linktekst via de volle sanitizer; tabelcel/notitie inline; URL- en video-ref via _safeUrl-breakout-encodering), plus een fail-closed backstop die de definitieve serialisatie nog eens door MarkdownSafetyScanner haalt vóór loadDeck/saveDeck (bulk/service → unsafeContent, enkel bestand → hetzelfde alarm als bij een vreemd .md). YAML-hardening voor gereserveerde woorden + controltekens. 37 tests (26 sanitizer + 11 integratie/regressie), 31 l10n-vertalingen. Security-architect akkoord na twee gedichte gaten (newline in de hyperlink-URL én de video-ref braken uit [tekst](url), backstop-blind — beide via _safeUrl gedicht, met regressietests die eerst rood stonden), en een uitputtende sweep over alle SourceSlide/SourceDeck-velden. Bewaker akkoord (de YAML-touch is round-trip-neutraal en uitwisselbaarheids-versterkend). make check groen op élke poort die de #876-code raakt, secrets/SAST schoon. Bewust op het bewijs gemerged: de resterende make check-ruis op main (dartcv4 niet in THIRD_PARTY_NOTICES + een flaky detectietest) is #870's onafgemaakte migratie, niet #876 — apart gemeld. Tijdbudget/annulering blijven bij #875.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
LibreKAT/Ocideck#876
No description provided.