Skip to content

fix(#173): do not HTML-escape template variables in markdown notes - #186

Open
muditagrawal2007 wants to merge 1 commit into
joplin:masterfrom
muditagrawal2007:fix/html-escaping-leaking-into-markdown-173
Open

muditagrawal2007 wants to merge 1 commit into
joplin:masterfrom
muditagrawal2007:fix/html-escaping-leaking-into-markdown-173

Conversation

@muditagrawal2007

Copy link
Copy Markdown

Summary

Templates render into markdown notes, not HTML. Both Handlebars.compile calls in src/parser.ts were called with default options, which made escapeExpression run on every {{ var }} and encode ' " & < > to HTML entities.

That mangles:

  • The note body — every text, number, boolean, date, time, and dropdown variable.
  • template_title — so the entry in the notebook list reads it&#x27;s a &quot;test&quot;.
  • template_tags — same.

The bug is silent: Joplin's markdown viewer renders the entities correctly, so the rendered note looks fine. The corruption lives in the source. It only surfaces when the user edits, searches, exports, or syncs the note elsewhere.

Fix

Pass { noEscape: true } to both Handlebars.compile calls:

// src/parser.ts:137  (parseSpecialVariables — used for template_title and template_tags)
const compiledText = Handlebars.compile(specialVariables[variable], { noEscape: true });

// src/parser.ts:286  (parseTemplate — used for the note body)
const compiledTemplate = Handlebars.compile(templateBody, { noEscape: true });

Under noEscape, {{ var }} behaves exactly like {{{ var }}}. So:

  • No regression for any existing template, including the workaround users already know ({{{ var }}}) — both forms produce the same output.
  • No input-side fix needed: dropdown (enum) values arrive at the dialog already decoded. Verified — noEscape alone is sufficient.

Reproduction (before the fix)

---
note_text: text
---

{{ note_text }}

Enter it's a "test" in the dialog.

  • Expected body: it's a "test"
  • Actual body: it&#x27;s a &quot;test&quot;

Same with template_title: {{ note_text }} — the title becomes it&#x27;s a &quot;test&quot;.

Risk

None visible:

  • The change is local to two compile calls.
  • The only downstream consumers are the note body, template_title, and the tag list (which already runs through split(",").map(trim).filter(...) in getNoteMetadata).
  • noEscape only turns off escaping that was wrong for this medium.

Tests

Three regression tests added to tests/parser.spec.ts:

  1. should not HTML-escape text, number and dropdown variables in the body (#173) — text_var = "it's a \"test\" with &amp; <markup>" renders verbatim; explicit not.toContain("&#x27;") / &quot; / &lt; / &gt; assertions on the body.
  2. should not HTML-escape template_title and template_tags (#173) — verifies the title and tag list round-trip, with not.toMatch(/&(amp|quot|#x27|lt|gt);/) on all three.
  3. should still support the {{{ var }}} triple-stache under noEscape (#173) — confirms {{ and {{{ produce identical output, so existing templates that already use the workaround are unaffected.

Verification

  • npm test — 104/104 pass (101 existing + 3 new).
  • npm run lint — 0 errors introduced (only pre-existing warnings on unrelated files).
  • npm run dist — plugin archive builds cleanly.

Related

…ariables

Templates render into markdown notes, not HTML. With no options,
Handlebars.compile runs every {{ var }} through escapeExpression and
encodes ' " & < > into HTML entities. That mangles:
  * the note body (template_title / template_tags users hit it everywhere)
  * template_title (so the title in the note list carries the entities)
  * template_tags

Joplin's markdown viewer renders the entities correctly so the bug is
silent. It only surfaces when the user edits the note, searches it,
exports it, or syncs it elsewhere.

Pass { noEscape: true } to both compile calls (parser.ts:137 and
parser.ts:286). Under noEscape, {{ var }} behaves like {{{ var }}},
so existing templates that already use the triple-stache workaround
keep working unchanged.

Three regression tests added in tests/parser.spec.ts:
  * apostrophes, quotes, ampersands, <markup> in text/number/dropdown
  * template_title and template_tags round-trip
  * {{{ var }}} still works identically under noEscape
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant