openvibers/openvibe.watch Commits
The update log's lists are escaped once (#183)
Changes
@@@ -25,7 +25,9 @@ const inline = (s) => raw(String(s)function renderMarkdown(md) {const out = [];let list = null;-- const flush = () => { if (list) { out.push(`<ul>${list.join('')}</ul>`); list = null; } };++ // The list is markup already (each item escaped by html``): raw, or the page escapes it a second time (/updates showed++ // "<ul><li>… watch&#39;s", 2026-11-02).++ const flush = () => { if (list) { out.push(raw(`<ul>${list.join('')}</ul>`)); list = null; } };for (const line of String(md).split(/\r?\n/)) {const t = line.trim();if (!t) { flush(); continue; }nnew file mode 100644@@@ -0,0 +1,23 @@++'use strict';++/**++ * The update log (/updates, server/render/changelog.js): a bullet list is markup once, never escaped a second time++ * (2026-11-02 the page showed "<ul><li>… watch&#39;s"), and what the changelog says is still escaped.++ */++const assert = require('assert');++const { renderMarkdown } = require('../server/render/changelog');++++const out = String(renderMarkdown([++ '## 2026-11-01 — the condition in words',++ '',++ "- The watch's condition in one sentence, with `code` and **bold**.",++ '- A second item <script>alert(1)</script>',++ '',++ 'A paragraph after the list.',++].join('\n')));++++assert.match(out, /<ul><li>The watch's condition in one sentence, with <code>code<\/code> and <strong>bold<\/strong>\.<\/li><li>/, out);++assert.ok(!out.includes('<ul>') && !out.includes('&#39;'), `a list is escaped once: ${out}`);++assert.ok(out.includes('<script>') && !out.includes('<script>'), 'what the changelog says is still text');++assert.match(out, /<h2>2026-11-01 — the condition in words<\/h2>/);++assert.match(out, /<p>A paragraph after the list\.<\/p>/);++console.log('changelog: all checks passed');
@@ -25,7 +25,9 @@ const inline = (s) => raw(String(s)
@@ -25,7 +25,9 @@ const inline = (s) => raw(String(s)
function renderMarkdown(md) {
function renderMarkdown(md) {
const out = [];
const out = [];
let list = null;
let list = null;
- const flush = () => { if (list) { out.push(`<ul>${list.join('')}</ul>`); list = null; } };
+ // The list is markup already (each item escaped by html``): raw, or the page escapes it a second time (/updates showed
+ // "<ul><li>… watch&#39;s", 2026-11-02).
+ const flush = () => { if (list) { out.push(raw(`<ul>${list.join('')}</ul>`)); list = null; } };
for (const line of String(md).split(/\r?\n/)) {
for (const line of String(md).split(/\r?\n/)) {
const t = line.trim();
const t = line.trim();
if (!t) { flush(); continue; }
if (!t) { flush(); continue; }
new file mode 100644
new file mode 100644
@@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
+'use strict';
+/**
+ * The update log (/updates, server/render/changelog.js): a bullet list is markup once, never escaped a second time
+ * (2026-11-02 the page showed "<ul><li>… watch&#39;s"), and what the changelog says is still escaped.
+ */
+const assert = require('assert');
+const { renderMarkdown } = require('../server/render/changelog');
+
+const out = String(renderMarkdown([
+ '## 2026-11-01 — the condition in words',
+ '',
+ "- The watch's condition in one sentence, with `code` and **bold**.",
+ '- A second item <script>alert(1)</script>',
+ '',
+ 'A paragraph after the list.',
+].join('\n')));
+
+assert.match(out, /<ul><li>The watch's condition in one sentence, with <code>code<\/code> and <strong>bold<\/strong>\.<\/li><li>/, out);
+assert.ok(!out.includes('<ul>') && !out.includes('&#39;'), `a list is escaped once: ${out}`);
+assert.ok(out.includes('<script>') && !out.includes('<script>'), 'what the changelog says is still text');
+assert.match(out, /<h2>2026-11-01 — the condition in words<\/h2>/);
+assert.match(out, /<p>A paragraph after the list\.<\/p>/);
+console.log('changelog: all checks passed');