Skip to content

Commit 8bec003

Browse files
authored
Fix escaping of angle brackets <> (#2394)
1 parent ddbe4a2 commit 8bec003

1 file changed

Lines changed: 15 additions & 4 deletions

File tree

ext/js/templates/anki-template-renderer.js

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,13 +149,24 @@ export class AnkiTemplateRenderer {
149149
}
150150

151151
/**
152+
* Marks a string as safe and not necessary to escape by handlebars.
153+
*
154+
* https://handlebarsjs.com/api-reference/utilities.html#handlebars-safestring-string
152155
* @param {string} text
153156
* @returns {string}
154157
*/
155158
_safeString(text) {
156159
return new Handlebars.SafeString(text);
157160
}
158161

162+
/**
163+
* @param {string} text
164+
* @returns {string}
165+
*/
166+
_escapeExpression(text) {
167+
return text.replaceAll('<', '&lt;').replaceAll('>', '&gt;');
168+
}
169+
159170
// Template helpers
160171

161172
/** @type {import('template-renderer').HelperFunction<string>} */
@@ -721,12 +732,12 @@ export class AnkiTemplateRenderer {
721732
_formatGlossary(args, _context, options) {
722733
const [dictionary, content] = /** @type {[dictionary: string, content: import('dictionary-data').TermGlossaryContent]} */ (args);
723734
const data = this._getNoteDataFromOptions(options);
724-
if (typeof content === 'string') { return this._safeString(this._stringToMultiLineHtml(content)); }
735+
if (typeof content === 'string') { return this._safeString(this._stringToMultiLineHtml(this._escapeExpression(content))); }
725736
if (!(typeof content === 'object' && content !== null)) { return ''; }
726737
switch (content.type) {
727738
case 'image': return this._formatGlossaryImage(content, dictionary, data);
728739
case 'structured-content': return this._formatStructuredContent(content, dictionary, data);
729-
case 'text': return this._safeString(this._stringToMultiLineHtml(content.text));
740+
case 'text': return this._safeString(this._stringToMultiLineHtml(this._escapeExpression(content.text)));
730741
}
731742
return '';
732743
}
@@ -761,7 +772,7 @@ export class AnkiTemplateRenderer {
761772
_formatGlossaryPlain(args, _context, options) {
762773
const [dictionary, content] = /** @type {[dictionary: string, content: import('dictionary-data').TermGlossaryContent]} */ (args);
763774
const data = this._getNoteDataFromOptions(options);
764-
if (typeof content === 'string') { return this._safeString(content); }
775+
if (typeof content === 'string') { return this._safeString(this._escapeExpression(content)); }
765776
if (!(typeof content === 'object' && content !== null)) { return ''; }
766777
const structuredContentGenerator = this._createStructuredContentGenerator(data);
767778
switch (content.type) {
@@ -775,7 +786,7 @@ export class AnkiTemplateRenderer {
775786
return node !== null ? this._getStructuredContentText(node) : '';
776787
}
777788
}
778-
case 'text': return this._safeString(content.text);
789+
case 'text': return this._safeString(this._escapeExpression(content.text));
779790
}
780791
return '';
781792
}

0 commit comments

Comments
 (0)