====== std.html ====== Werkzeuge für den Umgang mit HTML als Text: Sonderzeichen maskieren, Tags erkennen und entfernen, Verschachtelung grob prüfen, Leerzeichen für Formularparameter kodieren. Die Unit ist **kein Parser** — sie arbeitet zeichenweise über den Eingabetext und hat kein Dokumentmodell. → [[lyx_-_programmiersprache:units:xml|std.xml]] · [[lyx_-_programmiersprache:units:string|std.string]] · [[lyx_-_programmiersprache:units|Standard Library]] Alle Beispiele dieser Seite sind mit ''lyxc 1.0.21A'' übersetzt und ausgeführt; die gezeigten Ausgaben sind echte Programmausgaben. **Einsatz:** Template-Ausgabe, HTML-Erzeugung, Textextraktion aus Webseiten, SEO- und Feed-Auswertung. > **Nicht für Sicherheitsentscheidungen verwenden.** Frühere Fassungen dieser Seite nannten „HTML-Sanitisierung (XSS-Schutz)" als Einsatzbereich — dafür taugt die Unit nicht: > > * ''StripTags'' entfernt die **Tags**, nicht deren Inhalt: aus ''<script>alert(1)</script>'' wird ''alert(1)''. > * ''Unescape'' löst derzeit fast keine Entity auf (siehe unten), eine darauf gestützte Prüfung sieht also etwas anderes als der Browser. > * Es gibt keine Attributprüfung: ''%%onclick%%'', ''%%javascript:%%''-URLs und Data-URLs bleiben unangetastet, solange sie in einem Tag stehen, das entfernt wird — der Text dahinter aber bleibt. > > Zum Schutz vor XSS ist **''Escape'' beim Ausgeben** das richtige Mittel, nicht das Filtern beim Einlesen. ---- ===== Maskieren ===== ^ Signatur ^ Beschreibung ^ | ''Escape(input: pchar, output: pchar): int64'' | Ersetzt ''&'', ''<'', ''>'', ''"'', ''%%'%%'' und Byte 160 durch Entities. Rückgabe: Länge ohne Nullbyte. | | ''Unescape(input: pchar, output: pchar): int64'' | Soll Entities zurückwandeln — **funktioniert derzeit nur für ''"''**. | | ''NeedsEscape(s: pchar): bool'' | Meldet, ob überhaupt ein zu maskierendes Zeichen vorkommt. | ^ Zeichen ^ Wird zu ^ | ''&'' | ''&'' | | ''<'' | ''<'' | | ''>'' | ''>'' | | ''"'' | ''"'' | | ''%%'%%'' | ''''' (numerisch, nicht ''''') | | Byte 160 | '' '' — **problematisch, siehe Kasten** | Der Ausgabepuffer gehört dem Aufrufer. Sechs Byte je Eingabezeichen sind sicher (''"'' und '' '' sind die längsten Ersetzungen), plus ein Byte für den Abschluss. import std.html; import std.alloc; import std.string; fn main(): int64 { var o: pchar := alloc(512) as pchar; Escape("Tom & Jerry's"c, o); PrintLn(StrConcat("Escape: ", o)); var back: pchar := alloc(512) as pchar; Unescape(o, back); PrintLn(StrConcat("Rueckweg: ", back)); // Einzelne Entities durch den Dekoder Unescape("& < > " ' ' A   &unknown;"c, o); PrintLn(StrConcat("Entities: ", o)); // UTF-8: 'à' ist C3 A0 - das zweite Byte ist 160 Escape("Caf\xc3\xa0 Grand-M\xc3\xa8re"c, o); PrintLn(StrConcat("UTF-8: ", o)); return 0; } Escape: <a href="x">Tom & Jerry's</a> Rueckweg: <a href="x">Tom & Jerry's</a> Entities: & < > " ' ' A   &unknown; UTF-8: CafM-C  Grand-MM-CM-(re Die letzte Zeile ist durch ''cat -v'' sichtbar gemacht: ''M-C'' steht für das Byte 0xC3. Aus ''à'' (C3 A0) wurde ''C3'' + '' '' — ein halbes UTF-8-Zeichen gefolgt von einer Entity. ''è'' (C3 A8) blieb dagegen unversehrt, weil nur 0xA0 geprüft wird. > **''Unescape'' beherrscht seit 1.0.20F den vollen Umfang** ([[https://github.com/SEOLizer/LyX-Compiler/issues/1418|#1418]]): benannte Entities und numerische Angaben. Gemessen wird ''<a> & A  '' zu ''<a> & A'' samt geschütztem Leerzeichen aufgelöst. > **''Escape'' lässt UTF-8 unangetastet** ([[https://github.com/SEOLizer/LyX-Compiler/issues/1419|#1419]] behoben, 1.0.21A). Die frühere Latin-1-Regel „Byte 160 → '' ''" zerlegte Zeichen wie ''à'' (C3 A0); nachgemessen bleibt ''à Grüße'' jetzt unverändert. ---- ===== Tags erkennen ===== ^ Signatur ^ Beschreibung ^ | ''GetTagName(html: pchar, output: pchar): int64'' | Liest den Tag-Namen ab ''<''; funktioniert auch für ''</tag>'' und ''<tag/>''. ''0'', wenn das erste Zeichen kein ''<'' ist. | | ''IsClosingTag(html: pchar): bool'' | ''true'' bei ''</…>''. | | ''IsSelfClosing(html: pchar): bool'' | ''true'' bei ''<…/>'' — **nicht** bei Void-Elementen wie ''<br>''. | | ''HasTags(s: pchar): bool'' | Sucht ein ''<'' mit passendem ''>'' irgendwo im Text. | | ''ValidateBalance(html: pchar): int64'' | Bilanz aus öffnenden und schließenden Tags: ''0'' = ausgeglichen, positiv = offen, negativ = zu viele schließende. | import std.html; import std.alloc; import std.string; fn J(b: bool): pchar { if (b) { return "ja"; } return "nein"; } fn main(): int64 { var o: pchar := alloc(512) as pchar; // Tag-Analyse GetTagName("
"c, o); PrintLn(StrConcat("GetTagName('
'): ", o)); GetTagName(""c, o); PrintLn(StrConcat("GetTagName(''): ", o)); GetTagName("
"c, o); PrintLn(StrConcat("GetTagName('
'): ", o)); PrintLn(StrConcat("IsClosingTag('

'): ", J(IsClosingTag("

"c)))); PrintLn(StrConcat("IsSelfClosing('
'):", J(IsSelfClosing("
"c)))); PrintLn(StrConcat("IsSelfClosing('
'): ", J(IsSelfClosing("
"c)))); PrintLn(StrConcat("HasTags('a < b'): ", J(HasTags("a < b"c)))); PrintLn(StrConcat("NeedsEscape('a & b'): ", J(NeedsEscape("a & b"c)))); // Verschachtelung PrintLn(StrConcat("Balance('

x

'): ", IntToStr(ValidateBalance("

x

"c)))); PrintLn(StrConcat("Balance('

x

'): ", IntToStr(ValidateBalance("

x

"c)))); PrintLn(StrConcat("Balance('

a
b

'): ", IntToStr(ValidateBalance("

a
b

"c)))); PrintLn(StrConcat("Balance(''): ", IntToStr(ValidateBalance(""c)))); PrintLn(StrConcat("Balance('5 < 7 und 9 > 2'): ", IntToStr(ValidateBalance("5 < 7 und 9 > 2"c)))); // StripTags StripTags("

Hallo Welt

"c, o); PrintLn(StrConcat("Strip normal: ", o)); StripTags(""c, o); PrintLn(StrConcat("Strip script: ", o)); StripTags("5 < 7 und 9 > 2"c, o); PrintLn(StrConcat("Strip Text: ", o)); StripTags("

Text

"c, o); PrintLn(StrConcat("Strip Attribut:", o)); // URL-Leerzeichen EncodeSpaces("a b c"c, o); PrintLn(StrConcat("EncodeSpaces('a b c'): ", o)); DecodeSpaces(o, o); PrintLn(StrConcat("DecodeSpaces zurueck: ", o)); return 0; }
GetTagName('
'): div GetTagName(''): span GetTagName('
'): br IsClosingTag('

'): ja IsSelfClosing('
'):ja IsSelfClosing('
'): nein HasTags('a < b'): ja NeedsEscape('a & b'): ja Balance('

x

'): 0 Balance('

x

'): 1 Balance('

a
b

'): 1 Balance(''): 1 Balance('5 < 7 und 9 > 2'): 1 Strip normal: Hallo Welt Strip script: alert(1) Strip Text: 5 2 Strip Attribut:Text EncodeSpaces('a b c'): a+b+c DecodeSpaces zurueck: a b c
> **''ValidateBalance'' rechnet naiv.** Jedes ''<'' zählt als geöffnet, jedes ''</'' als geschlossen — unabhängig davon, ob überhaupt ein Tag vorliegt: > > * **Void-Elemente** (''<br>'', ''<img>'', ''<meta>'', ''<input>'', ''<hr>'') gelten als offen; gültiges HTML bekommt eine Bilanz ungleich null. > * **Selbstschließende Tags** (''<img/>'') ebenfalls, obwohl ''IsSelfClosing'' sie erkennen könnte. > * Ein ''<'' im **Fließtext** (''5 < 7'') zählt mit. > > Brauchbar ist die Funktion damit nur für streng gepaartes, XML-artiges Markup ohne Void-Elemente ([[https://github.com/SEOLizer/LyX-Compiler/issues/1420|#1420]]). ---- ===== Tags entfernen ===== ''StripTags'' kopiert alles, was außerhalb von ''<…>'' steht. Das Verfahren ist bewusst einfach — und hat zwei Folgen, die man kennen muss: * **Der Inhalt bleibt.** Ein ''<script>''-Block verliert seine Tags, der Code darin steht anschließend im Text. * **Ein ''<'' im Text frisst mit.** Aus ''5 < 7 und 9 > 2'' wird ''5 2'' — alles zwischen ''<'' und dem nächsten ''>'' gilt als Tag. Wer Text mit Vergleichszeichen verarbeitet, maskiert ihn vorher mit ''Escape''. Für die Textextraktion aus wohlgeformtem HTML — der eigentliche Zweck — arbeitet die Funktion zuverlässig. ---- ===== Leerzeichen für Formularparameter ===== ^ Signatur ^ Beschreibung ^ | ''EncodeSpaces(input: pchar, output: pchar): int64'' | Ersetzt Leerzeichen durch ''+''. | | ''DecodeSpaces(input: pchar, output: pchar): int64'' | Ersetzt ''+'' durch Leerzeichen. | > **Das ist ''+'', nicht ''%%%20%%''.** Frühere Fassungen dieser Seite sprachen von „''%%%20%%'' für URLs" — tatsächlich erzeugt ''EncodeSpaces'' die Form aus ''application/x-www-form-urlencoded'', wie sie in Formulardaten und Abfrageparametern üblich ist. ''DecodeSpaces'' kennt umgekehrt nur ''+'' und lässt ''%%%20%%'' unverändert stehen. > > Beide Funktionen behandeln **ausschließlich das Leerzeichen**. ''&'', ''='', ''?'', ''#'' und Nicht-ASCII bleiben unangetastet — für vollständige URL-Kodierung ist ''std.net.http'' zuständig. ---- ===== Fallstricke ===== * **Ausgabepuffer selbst anlegen und großzügig bemessen** — keine Funktion prüft die Zielgröße. Für ''Escape'' sind sechs Byte je Eingabezeichen sicher. * **''Unescape'' löst benannte und numerische Entities auf** (#1418 behoben). * **''Escape'' lässt UTF-8 unangetastet** (#1419 behoben). * **Doppelte Maskierung** merkt niemand: ''%%Escape(Escape(x))%%'' ergibt ''&lt;'' statt ''<''. Nur einmal maskieren, unmittelbar vor der Ausgabe. * **Kein Dokumentmodell.** ''GetTagName'' und Verwandte erwarten einen Text, der **am** ''<'' beginnt; einen Tag-Iterator gibt es nicht. Wer Struktur braucht, nimmt [[lyx_-_programmiersprache:units:xml|std.xml]]. * **Kommentare, CDATA und Skriptinhalte** kennt die Unit nicht — ''<!-- a > b -->'' endet für ''StripTags'' beim ersten ''>''. * Die exportierten Entity-Konstanten sind unit-privat; von außen sichtbar sind nur die elf Funktionen. ---- **Weiterführend:** [[lyx_-_programmiersprache:units:xml|std.xml — Parser mit Dokumentmodell]] · [[lyx_-_programmiersprache:units:string|std.string]] · [[lyx_-_programmiersprache:units:net:http|std.net.http]] · [[lyx_-_programmiersprache:units|Standard Library]] **Quelle:** ''std/html.lyx'' (459 Zeilen) · **Autor:** Andreas Röne · **Copyright:** 2024–2025 Andreas Röne Letzte Aktualisierung: 2026-08-13 — Seite gegen ''std/html.lyx'' überarbeitet: leere Konstanten-Tabelle entfernt, zwei lauffähige Beispiele mit echter Ausgabe, Entity-Tabelle, Fallstricke. Die Nennung als Mittel zur „HTML-Sanitisierung (XSS-Schutz)" ist gestrichen — ''StripTags'' entfernt Tags, nicht deren Inhalt. Belegt und als Issue erfasst: ''Unescape'' erkennt nur ''"'' ([[https://github.com/SEOLizer/LyX-Compiler/issues/1418|#1418]]), ''Escape'' zerstört UTF-8-Zeichen mit Folgebyte 0xA0 ([[https://github.com/SEOLizer/LyX-Compiler/issues/1419|#1419]]), ''ValidateBalance'' zählt Void-Elemente und Text-Kleinerzeichen mit ([[https://github.com/SEOLizer/LyX-Compiler/issues/1420|#1420]]). ''EncodeSpaces'' erzeugt ''+'', nicht ''%%%20%%''. Geprüft mit ''lyxc 1.0.21A''.