openvibers/openvibe.work Commits
Work: escaped HTML in a board's description (Arbeitnow) is stripped too, so excerpts never show markup as words
Changes
@@@ -27,15 +27,22 @@ function decode(text) {}/** The description as plain text: no tags, no script or style content, whitespace collapsed. */--function stripHtml(html) {-- let s = String(html == null ? '' : html);++function stripOnce(s) {s = s.replace(/<!--[\s\S]*?-->/g, ' ');// A script or style element's content is code, not prose; drop it with its tags.s = s.replace(/<(script|style|noscript|template|svg)\b[^>]*>[\s\S]*?<\/\1\s*>/gi, ' ');// Block boundaries become a space so words either side do not run together.s = s.replace(/<\/?(br|p|div|li|ul|ol|tr|td|th|h[1-6]|section|article|header|footer|blockquote|pre|figure|hr)\b[^>]*>/gi, ' ');s = s.replace(/<[^>]*>/g, ' ');-- s = decode(s);++ return decode(s);++}++++function stripHtml(html) {++ let s = stripOnce(String(html == null ? '' : html));++ // Some boards send their HTML escaped (Arbeitnow: "<div class="…"><p>…"): one decode brings the++ // markup back, so it is stripped a second time. Two passes at most: a third layer is not a description.++ // Only real HTML tags count as markup coming back; "<ok>" in prose stays the text "ok".++ if (/<\/?(p|div|span|br|ul|ol|li|a|b|i|em|strong|h[1-6]|table|tr|td|section|article|blockquote|pre|code|img|hr|script|style)\b[^>]*>/i.test(s)) s = stripOnce(s);// An unmatched '<' left by broken markup is not something a listing should show.s = s.replace(/<[^>]*$/g, ' ').replace(/[<>]/g, ' ');return s.replace(/\s+/g, ' ').trim();@@@ -80,6 +80,8 @@ const DAY = 86_400_000;assert.strictEqual(stripHtml('<style>p{color:red}</style><p>real</p>'), 'real');assert.strictEqual(stripHtml('<script>alert(1)</script>'), '');assert.strictEqual(stripHtml(null), '');++ // Arbeitnow sends some descriptions as escaped HTML: the markup must not show up as words.++ assert.strictEqual(stripHtml('<div class="content-intro"><p>GitLab is here.</p><script>x()</script>'), 'GitLab is here.');// A doubly-encoded tag decodes to text and is then dropped, never re-formed into markup.assert.ok(!stripHtml('<script>alert(1)</script>').includes('<'));assert.strictEqual(excerpt('<p>short</p>'), 'short');
@@ -27,15 +27,22 @@ function decode(text) {
@@ -27,15 +27,22 @@ function decode(text) {
}
}
/** The description as plain text: no tags, no script or style content, whitespace collapsed. */
/** The description as plain text: no tags, no script or style content, whitespace collapsed. */
-function stripHtml(html) {
- let s = String(html == null ? '' : html);
+function stripOnce(s) {
s = s.replace(/<!--[\s\S]*?-->/g, ' ');
s = s.replace(/<!--[\s\S]*?-->/g, ' ');
// A script or style element's content is code, not prose; drop it with its tags.
// A script or style element's content is code, not prose; drop it with its tags.
s = s.replace(/<(script|style|noscript|template|svg)\b[^>]*>[\s\S]*?<\/\1\s*>/gi, ' ');
s = s.replace(/<(script|style|noscript|template|svg)\b[^>]*>[\s\S]*?<\/\1\s*>/gi, ' ');
// Block boundaries become a space so words either side do not run together.
// Block boundaries become a space so words either side do not run together.
s = s.replace(/<\/?(br|p|div|li|ul|ol|tr|td|th|h[1-6]|section|article|header|footer|blockquote|pre|figure|hr)\b[^>]*>/gi, ' ');
s = s.replace(/<\/?(br|p|div|li|ul|ol|tr|td|th|h[1-6]|section|article|header|footer|blockquote|pre|figure|hr)\b[^>]*>/gi, ' ');
s = s.replace(/<[^>]*>/g, ' ');
s = s.replace(/<[^>]*>/g, ' ');
- s = decode(s);
+ return decode(s);
+}
+
+function stripHtml(html) {
+ let s = stripOnce(String(html == null ? '' : html));
+ // Some boards send their HTML escaped (Arbeitnow: "<div class="…"><p>…"): one decode brings the
+ // markup back, so it is stripped a second time. Two passes at most: a third layer is not a description.
+ // Only real HTML tags count as markup coming back; "<ok>" in prose stays the text "ok".
+ if (/<\/?(p|div|span|br|ul|ol|li|a|b|i|em|strong|h[1-6]|table|tr|td|section|article|blockquote|pre|code|img|hr|script|style)\b[^>]*>/i.test(s)) s = stripOnce(s);
// An unmatched '<' left by broken markup is not something a listing should show.
// An unmatched '<' left by broken markup is not something a listing should show.
s = s.replace(/<[^>]*$/g, ' ').replace(/[<>]/g, ' ');
s = s.replace(/<[^>]*$/g, ' ').replace(/[<>]/g, ' ');
return s.replace(/\s+/g, ' ').trim();
return s.replace(/\s+/g, ' ').trim();
@@ -80,6 +80,8 @@ const DAY = 86_400_000;
@@ -80,6 +80,8 @@ const DAY = 86_400_000;
assert.strictEqual(stripHtml('<style>p{color:red}</style><p>real</p>'), 'real');
assert.strictEqual(stripHtml('<style>p{color:red}</style><p>real</p>'), 'real');
assert.strictEqual(stripHtml('<script>alert(1)</script>'), '');
assert.strictEqual(stripHtml('<script>alert(1)</script>'), '');
assert.strictEqual(stripHtml(null), '');
assert.strictEqual(stripHtml(null), '');
+ // Arbeitnow sends some descriptions as escaped HTML: the markup must not show up as words.
+ assert.strictEqual(stripHtml('<div class="content-intro"><p>GitLab is here.</p><script>x()</script>'), 'GitLab is here.');
// A doubly-encoded tag decodes to text and is then dropped, never re-formed into markup.
// A doubly-encoded tag decodes to text and is then dropped, never re-formed into markup.
assert.ok(!stripHtml('<script>alert(1)</script>').includes('<'));
assert.ok(!stripHtml('<script>alert(1)</script>').includes('<'));
assert.strictEqual(excerpt('<p>short</p>'), 'short');
assert.strictEqual(excerpt('<p>short</p>'), 'short');