Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
d1da1ab
fix(rich-text-utils): remove HTML escaping from serialized text in se…
ddouglasz Jan 19, 2026
744e9cf
test(rich-text-utils): update HTML serialization test to reflect chan…
ddouglasz Jan 19, 2026
9516d30
feat(rich-text-utils): integrate DOMPurify for HTML sanitization in s…
ddouglasz Jan 19, 2026
e5c0327
Merge branch 'main' into CRAFT-2040-rich-text-input-destroys-hyperlin…
ddouglasz Jan 19, 2026
a6571a4
feat(rich-text-utils): expand allowed HTML tags and normalize output …
ddouglasz Jan 20, 2026
7ffeae1
Merge branch 'CRAFT-2040-rich-text-input-destroys-hyperlink-tag' of h…
ddouglasz Jan 20, 2026
d4f272d
fix(rich-text-utils): remove unnecessary HTML escaping in normalizati…
ddouglasz Jan 20, 2026
a1f7d32
Merge branch 'main' into CRAFT-2040-rich-text-input-destroys-hyperlin…
ddouglasz Jan 21, 2026
46f507d
refactor(rich-text-utils): implement anchor tag handling in HTML esca…
ddouglasz Feb 5, 2026
adf83e3
chore(dependencies): remove dompurify and its type definitions from r…
ddouglasz Feb 5, 2026
471cfd0
Merge branch 'main' into CRAFT-2040-rich-text-input-destroys-hyperlin…
ddouglasz Feb 5, 2026
dd243f6
Merge branch 'main' into CRAFT-2040-rich-text-input-destroys-hyperlin…
ddouglasz Feb 11, 2026
bba6d24
feat(rich-text): use dompurify for improve anchor text security
ddouglasz Feb 23, 2026
6641cac
Merge branch 'main' into CRAFT-2040-rich-text-input-destroys-hyperlin…
ddouglasz Feb 23, 2026
73d2cb1
fix(html.spec.js): correct serialization output for rich text input t…
ddouglasz Feb 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/clever-onions-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@commercetools-uikit/rich-text-utils': minor
---

Updated the Slate HTML serializer to stop escaping text nodes so `</>` stay as-is when the editor value is serialized, which allows tags to pass through.
2 changes: 2 additions & 0 deletions packages/components/inputs/rich-text-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
"@commercetools-uikit/utils": "20.3.1",
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@types/dompurify": "^2.4.0",
"@types/escape-html": "1.0.4",
"dompurify": "3.2.7",
"downshift": "9.0.10",
"escape-html": "1.0.3",
"is-hotkey": "0.2.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ describe('html', () => {
it('should properly serialize and replace with information', () => {
const htmlValue = `<p>\r\n<em>Bitte beachten Sie: Die Lieferung erfolgt nach Verf&uuml;gbarkeit - Jahrgangs- und Designw&uuml;nschen k&ouml;nnen wir leider nicht nachkommen. Wir danken Ihnen f&uuml;r Ihr Verst&auml;ndnis.</em>\r\n</p>\r\n\r\n<h3>Informationen zum Gold K&auml;nguru 1/4</h3>\r\n<p>\r\nDer <strong>Australian Nugget (Gold K&auml;nguru)</strong> ist eine australische Goldm&uuml;nze, die erstmals 1986 von der Perth Mint gepr&auml;gt wurde und sich vor allem dank der j&auml;hrlich wechselnden Motive auf der Vorderseite weltweiter Beliebtheit erfreut. Die M&uuml;nze hat einen Feingehalt von 999,9 und gilt als offizielles Zahlungsmittel.\r\n</p>\r\n\r\n<p >\r\nSeit 1987 wird der <strong>Australian Nugget</strong> in den St&uuml;ckelungen 1/20 Unze, 1/10 Unze, 1/4 Unze, 1/2 Unze und 1 Unze gepr&auml;gt, der Nennwert liegt zwischen 5 und 100 australischen Dollar. Mittlerweile existieren aber auch gr&ouml;&szlig;ere St&uuml;ckelungen mit einem Gewicht von 2 Unzen, 10 Unzen sowie 1 Kilogramm. Im Jahr 2010 wurde eine 2-Dollar-Version der M&uuml;nze mit einem Gewicht von 0,5 Gramm aufgelegt. 2011 stellte die Perth Mint zudem eine Ausgabe der M&uuml;nze mit einem Gewicht von 1 Tonne vor &ndash; damit ist der <strong>Gold K&auml;nguru</strong> zugleich die kleinste und gr&ouml;sste Anlagem&uuml;nze der Welt.\r\n</p>\r\n\r\n<p>\r\nBis 1989 wurden auf der Vorderseite der M&uuml;nze au&szlig;ergew&ouml;hnliche, in Australien gefundene Goldnuggets abgebildet &ndash; daher auch der Name <strong>Australian Nugget</strong>. Seit 1990 zeigt die Goldm&uuml;nze j&auml;hrlich wechselnde Motive von K&auml;ngurus, der offizielle Name lautet seitdem &bdquo;Australian Kangaroo&ldquo;. Gr&ouml;&szlig;ere Einheiten des <strong>Gold K&auml;nguru</strong>, wie die 2-Unzen-, 10-Unzen- und 1-Kilogramm-Version tragen j&auml;hrlich das gleiche Motiv des &bdquo;Red Kangaroo&ldquo;. Auf der R&uuml;ckseite der Goldm&uuml;nze findet sich - wie bei allen Commonwealth-M&uuml;nzen - das Portr&auml;t von K&ouml;nigin Elizabeth II.\r\n</p>\r\n<p>\r\n</p>\r\n<h3>\r\nGold K&auml;nguru 1/4 kaufen bei philoro kaufen\r\n</h3>\r\n<p>\r\n\tNeben der <strong>Gold Känguru 1/4/strong> finden Sie bei uns eine breite Auswahl renommierter <a href="https://philoro.ch/shop/goldmuenzen" target="_blank" title="Goldmünzen kaufen" style="text-decoration: none; color: #86754f;">Goldmünzen</a> aus aller Welt. Gerne bieten wir Ihnen zum Thema Anlagemünzen unsere umfassende Beratung an. Wir garantieren für die von uns vertriebenen Produkte höchste Qualität. Deshalb arbeiten wir ausschliesslich mit international anerkannten und etablierten Herstellern zusammen.\r\n</p>\r\n\r\n<p>\r\nBesuchen Sie uns in einer unserer <a href="https://philoro.ch/filialen" target="_blank" title="Unsere Filialen" style="text-decoration: none; color: #86754f;">Filialen</a> und überzeugen Sie sich selbst, oder bestellen Sie einfach und bequem online.\r\n</p>\r\n`;
expect(html.serialize(html.deserialize(htmlValue))).toEqual(
`<p><br/><em>Bitte beachten Sie: Die Lieferung erfolgt nach Verfügbarkeit - Jahrgangs- und Designwünschen können wir leider nicht nachkommen. Wir danken Ihnen für Ihr Verständnis.</em><br/></p><p><br/><br/></p><h3>Informationen zum Gold Känguru 1/4</h3><p><br/></p><p><br/>Der <strong>Australian Nugget (Gold Känguru)</strong> ist eine australische Goldmünze, die erstmals 1986 von der Perth Mint geprägt wurde und sich vor allem dank der jährlich wechselnden Motive auf der Vorderseite weltweiter Beliebtheit erfreut. Die Münze hat einen Feingehalt von 999,9 und gilt als offizielles Zahlungsmittel.<br/></p><p><br/><br/></p><p><br/>Seit 1987 wird der <strong>Australian Nugget</strong> in den Stückelungen 1/20 Unze, 1/10 Unze, 1/4 Unze, 1/2 Unze und 1 Unze geprägt, der Nennwert liegt zwischen 5 und 100 australischen Dollar. Mittlerweile existieren aber auch größere Stückelungen mit einem Gewicht von 2 Unzen, 10 Unzen sowie 1 Kilogramm. Im Jahr 2010 wurde eine 2-Dollar-Version der Münze mit einem Gewicht von 0,5 Gramm aufgelegt. 2011 stellte die Perth Mint zudem eine Ausgabe der Münze mit einem Gewicht von 1 Tonne vor – damit ist der <strong>Gold Känguru</strong> zugleich die kleinste und grösste Anlagemünze der Welt.<br/></p><p><br/><br/></p><p><br/>Bis 1989 wurden auf der Vorderseite der Münze außergewöhnliche, in Australien gefundene Goldnuggets abgebildet – daher auch der Name <strong>Australian Nugget</strong>. Seit 1990 zeigt die Goldmünze jährlich wechselnde Motive von Kängurus, der offizielle Name lautet seitdem „Australian Kangaroo“. Größere Einheiten des <strong>Gold Känguru</strong>, wie die 2-Unzen-, 10-Unzen- und 1-Kilogramm-Version tragen jährlich das gleiche Motiv des „Red Kangaroo“. Auf der Rückseite der Goldmünze findet sich - wie bei allen Commonwealth-Münzen - das Porträt von Königin Elizabeth II.<br/></p><p><br/></p><p><br/></p><p><br/></p><h3><br/>Gold Känguru 1/4 kaufen bei philoro kaufen<br/></h3><p><br/></p><p><br/>\tNeben der <strong>Gold Känguru 1/4/strong&gt; finden Sie bei uns eine breite Auswahl renommierter </strong><strong>Invalid markup</strong><strong> aus aller Welt. Gerne bieten wir Ihnen zum Thema Anlagemünzen unsere umfassende Beratung an. Wir garantieren für die von uns vertriebenen Produkte höchste Qualität. Deshalb arbeiten wir ausschliesslich mit international anerkannten und etablierten Herstellern zusammen.<br/></strong></p><strong><br/><br/></strong><strong>Invalid markup</strong><strong><br/></strong>`
`<p><br/><em>Bitte beachten Sie: Die Lieferung erfolgt nach Verfügbarkeit - Jahrgangs- und Designwünschen können wir leider nicht nachkommen. Wir danken Ihnen für Ihr Verständnis.</em><br/></p><p><br/><br/></p><h3>Informationen zum Gold Känguru 1/4</h3><p><br/></p><p><br/>Der <strong>Australian Nugget (Gold Känguru)</strong> ist eine australische Goldmünze, die erstmals 1986 von der Perth Mint geprägt wurde und sich vor allem dank der jährlich wechselnden Motive auf der Vorderseite weltweiter Beliebtheit erfreut. Die Münze hat einen Feingehalt von 999,9 und gilt als offizielles Zahlungsmittel.<br/></p><p><br/><br/></p><p><br/>Seit 1987 wird der <strong>Australian Nugget</strong> in den Stückelungen 1/20 Unze, 1/10 Unze, 1/4 Unze, 1/2 Unze und 1 Unze geprägt, der Nennwert liegt zwischen 5 und 100 australischen Dollar. Mittlerweile existieren aber auch größere Stückelungen mit einem Gewicht von 2 Unzen, 10 Unzen sowie 1 Kilogramm. Im Jahr 2010 wurde eine 2-Dollar-Version der Münze mit einem Gewicht von 0,5 Gramm aufgelegt. 2011 stellte die Perth Mint zudem eine Ausgabe der Münze mit einem Gewicht von 1 Tonne vor – damit ist der <strong>Gold Känguru</strong> zugleich die kleinste und grösste Anlagemünze der Welt.<br/></p><p><br/><br/></p><p><br/>Bis 1989 wurden auf der Vorderseite der Münze außergewöhnliche, in Australien gefundene Goldnuggets abgebildet – daher auch der Name <strong>Australian Nugget</strong>. Seit 1990 zeigt die Goldmünze jährlich wechselnde Motive von Kängurus, der offizielle Name lautet seitdem „Australian Kangaroo“. Größere Einheiten des <strong>Gold Känguru</strong>, wie die 2-Unzen-, 10-Unzen- und 1-Kilogramm-Version tragen jährlich das gleiche Motiv des „Red Kangaroo“. Auf der Rückseite der Goldmünze findet sich - wie bei allen Commonwealth-Münzen - das Porträt von Königin Elizabeth II.<br/></p><p><br/></p><p><br/></p><p><br/></p><h3><br/>Gold Känguru 1/4 kaufen bei philoro kaufen<br/></h3><p><br/></p><p><br/> Neben der <strong>Gold Känguru 1/4/strong> finden Sie bei uns eine breite Auswahl renommierter </strong><strong>Invalid markup</strong><strong> aus aller Welt. Gerne bieten wir Ihnen zum Thema Anlagemünzen unsere umfassende Beratung an. Wir garantieren für die von uns vertriebenen Produkte höchste Qualität. Deshalb arbeiten wir ausschliesslich mit international anerkannten und etablierten Herstellern zusammen.<br/></strong></p><strong><br/><br/></strong><strong>Invalid markup</strong><strong><br/></strong>`
);
});
});
Expand Down
30 changes: 29 additions & 1 deletion packages/components/inputs/rich-text-utils/src/html/html.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import escapeHtml from 'escape-html';
import DOMPurify from 'dompurify';
import {
Text,
Element as SlateElement,
Expand Down Expand Up @@ -49,9 +50,35 @@ declare module 'slate' {
}
}

/**
* Escapes HTML but preserves anchor tags with sanitized attributes.
* This allows <a> tags to remain as clickable links while preventing XSS from other tags.
* Uses DOMPurify for robust sanitization against XSS attacks.
*/
const escapeHtmlExceptAnchors = (text: string): string => {
// Use DOMPurify to sanitize the text, allowing only anchor tags
// This is much more secure than regex-based parsing
const sanitized = DOMPurify.sanitize(text, {
ALLOWED_TAGS: ['a'],
ALLOWED_ATTR: ['href', 'title', 'target', 'rel'],
// Block dangerous URL schemes
ALLOWED_URI_REGEXP:
/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i,
// Prevent DOM clobbering
SANITIZE_DOM: true,
// Keep relative URLs
ALLOW_DATA_ATTR: false,
// Return a string, not a DOM node
RETURN_DOM: false,
RETURN_DOM_FRAGMENT: false,
});

return sanitized;
};

const serializeNode = (node: TNode): Html => {
if (Text.isText(node)) {
let string = escapeHtml(node.text);
let string = escapeHtmlExceptAnchors(node.text);
if (node.bold) {
string = `<strong>${string}</strong>`;
}
Expand Down Expand Up @@ -369,6 +396,7 @@ const deserializeElement = (

return children;
};

const deserialize = (html: Html) => {
const document = new DOMParser().parseFromString(
Softbreaker.cleanHtml(html) || '<p></p>',
Expand Down
2 changes: 2 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3679,7 +3679,9 @@ __metadata:
"@commercetools-uikit/utils": 20.3.1
"@emotion/react": ^11.10.5
"@emotion/styled": ^11.10.5
"@types/dompurify": ^2.4.0
"@types/escape-html": 1.0.4
dompurify: 3.2.7
downshift: 9.0.10
escape-html: 1.0.3
is-hotkey: 0.2.0
Expand Down
Loading