From 8f909bcf33e78c3b2cfc645f6c926db1382b1ca5 Mon Sep 17 00:00:00 2001 From: Vitaly Puzrin Date: Sun, 26 Oct 2014 09:13:34 +0300 Subject: [PATCH] Moved HTML escaping to renderer --- lib/common/utils.js | 22 ---------------------- lib/configs/commonmark.js | 1 - lib/configs/default.js | 1 - lib/parser_inline.js | 4 +--- lib/renderer.js | 28 ++++++++++++++++++++++++++-- lib/rules_inline/autolink.js | 5 ++--- lib/rules_inline/entity.js | 7 +++---- lib/rules_inline/escape.js | 16 ++-------------- lib/rules_inline/escape_html_char.js | 20 -------------------- lib/rules_text/linkify.js | 3 +-- lib/rules_text/smartquotes.js | 2 +- 11 files changed, 36 insertions(+), 73 deletions(-) delete mode 100644 lib/rules_inline/escape_html_char.js diff --git a/lib/common/utils.js b/lib/common/utils.js index 4eb7901..98f2498 100644 --- a/lib/common/utils.js +++ b/lib/common/utils.js @@ -30,27 +30,6 @@ function assign(obj /*from1, from2, from3, ...*/) { } -var HTML_ESCAPE_TEST_RE = /[&<>"]/; -var HTML_ESCAPE_REPLACE_RE = /[&<>"]/g; -var HTML_REPLACEMENTS = { - '&': '&', - '<': '<', - '>': '>', - '"': '"' -}; - -function replaceUnsafeChar(ch) { - return HTML_REPLACEMENTS[ch]; -} - -function escapeHtml(str) { - if (HTML_ESCAPE_TEST_RE.test(str)) { - return str.replace(HTML_ESCAPE_REPLACE_RE, replaceUnsafeChar); - } - return str; -} - - var UNESCAPE_MD_RE = /\\([\\!"#$%&'()*+,.\/:;<=>?@[\]^_`{|}~-])/g; function unescapeMd(str) { @@ -104,7 +83,6 @@ function replaceEntities(str) { exports.assign = assign; exports.isString = isString; -exports.escapeHtml = escapeHtml; exports.unescapeMd = unescapeMd; exports.isValidEntityCode = isValidEntityCode; exports.fromCodePoint = fromCodePoint; diff --git a/lib/configs/commonmark.js b/lib/configs/commonmark.js index 832464d..2184cb6 100644 --- a/lib/configs/commonmark.js +++ b/lib/configs/commonmark.js @@ -42,7 +42,6 @@ module.exports = { 'emphasis', 'entity', 'escape', - 'escape_html_char', 'htmltag', 'links', 'newline', diff --git a/lib/configs/default.js b/lib/configs/default.js index 4d34a5d..884c072 100644 --- a/lib/configs/default.js +++ b/lib/configs/default.js @@ -44,7 +44,6 @@ module.exports = { 'emphasis', 'entity', 'escape', - 'escape_html_char', 'htmltag', 'links', 'newline', diff --git a/lib/parser_inline.js b/lib/parser_inline.js index af3246a..e2df761 100644 --- a/lib/parser_inline.js +++ b/lib/parser_inline.js @@ -25,7 +25,6 @@ rules.push(require('./rules_inline/links')); rules.push(require('./rules_inline/autolink')); rules.push(require('./rules_inline/htmltag')); rules.push(require('./rules_inline/entity')); -rules.push(require('./rules_inline/escape_html_char')); var BAD_PROTOCOLS = [ 'vbscript', 'javascript', 'file' ]; @@ -51,8 +50,7 @@ function ParserInline() { // Rule to skip pure text // - '{}$%@+=' reserved for extentions - // - '<>"' added for internal html escaping - this.textMatch = /^[^\n\\`*_\[\]!&{}$%@<>"~+=]+/; + this.textMatch = /^[^\n\\`*_\[\]!&<{}$%@~+=]+/; // By default CommonMark allows too much in links // If you need to restrict it - override this with your validator. diff --git a/lib/renderer.js b/lib/renderer.js index 41d2747..43342ae 100644 --- a/lib/renderer.js +++ b/lib/renderer.js @@ -2,11 +2,13 @@ var assign = require('./common/utils').assign; -var escapeHtml = require('./common/utils').escapeHtml; var unescapeMd = require('./common/utils').unescapeMd; var replaceEntities = require('./common/utils').replaceEntities; +//////////////////////////////////////////////////////////////////////////////// +// Helpers + function escapeUrl(str) { try { return encodeURI(str); @@ -20,6 +22,26 @@ function unescapeUrl(str) { return ''; } +var HTML_ESCAPE_TEST_RE = /[&<>"]/; +var HTML_ESCAPE_REPLACE_RE = /[&<>"]/g; +var HTML_REPLACEMENTS = { + '&': '&', + '<': '<', + '>': '>', + '"': '"' +}; + +function replaceUnsafeChar(ch) { + return HTML_REPLACEMENTS[ch]; +} + +function escapeHtml(str) { + if (HTML_ESCAPE_TEST_RE.test(str)) { + return str.replace(HTML_ESCAPE_REPLACE_RE, replaceUnsafeChar); + } + return str; +} + // check if we need to hide '\n' before next token function getBreak(tokens, idx) { @@ -31,6 +53,8 @@ function getBreak(tokens, idx) { return '\n'; } +//////////////////////////////////////////////////////////////////////////////// + var rules = {}; @@ -225,7 +249,7 @@ rules.softbreak = function (tokens, idx, options) { rules.text = function (tokens, idx /*, options*/) { - return tokens[idx].content; + return escapeHtml(tokens[idx].content); }; diff --git a/lib/rules_inline/autolink.js b/lib/rules_inline/autolink.js index dc695cc..caa6ecd 100644 --- a/lib/rules_inline/autolink.js +++ b/lib/rules_inline/autolink.js @@ -1,7 +1,6 @@ // Process autolinks '' -var escapeHtml = require('../common/utils').escapeHtml; var url_schemas = require('../common/url_schemas'); @@ -36,7 +35,7 @@ module.exports = function autolink(state, silent) { }); state.push({ type: 'text', - content: escapeHtml(url), + content: url, level: state.level + 1 }); state.push({ type: 'link_close', level: state.level }); @@ -62,7 +61,7 @@ module.exports = function autolink(state, silent) { }); state.push({ type: 'text', - content: escapeHtml(url), + content: url, level: state.level + 1 }); state.push({ type: 'link_close', level: state.level }); diff --git a/lib/rules_inline/entity.js b/lib/rules_inline/entity.js index 90f066f..868e283 100644 --- a/lib/rules_inline/entity.js +++ b/lib/rules_inline/entity.js @@ -3,7 +3,6 @@ 'use strict'; var entities = require('../common/entities'); -var escapeHtml = require('../common/utils').escapeHtml; var isValidEntityCode = require('../common/utils').isValidEntityCode; var fromCodePoint = require('../common/utils').fromCodePoint; @@ -25,7 +24,7 @@ module.exports = function entity(state, silent) { if (match) { if (!silent) { code = match[1][0].toLowerCase() === 'x' ? parseInt(match[1].slice(1), 16) : parseInt(match[1], 10); - state.pending += isValidEntityCode(code) ? escapeHtml(fromCodePoint(code)) : fromCodePoint(0xFFFD); + state.pending += isValidEntityCode(code) ? fromCodePoint(code) : fromCodePoint(0xFFFD); } state.pos += match[0].length; return true; @@ -34,7 +33,7 @@ module.exports = function entity(state, silent) { match = state.src.slice(pos).match(NAMED_RE); if (match) { if (entities.hasOwnProperty(match[1])) { - if (!silent) { state.pending += escapeHtml(entities[match[1]]); } + if (!silent) { state.pending += entities[match[1]]; } state.pos += match[0].length; return true; } @@ -42,7 +41,7 @@ module.exports = function entity(state, silent) { } } - if (!silent) { state.pending += '&'; } + if (!silent) { state.pending += '&'; } state.pos++; return true; }; diff --git a/lib/rules_inline/escape.js b/lib/rules_inline/escape.js index a4de4d5..84a55c5 100644 --- a/lib/rules_inline/escape.js +++ b/lib/rules_inline/escape.js @@ -6,7 +6,7 @@ var ESCAPED = {}; .split('').forEach(function(ch) { ESCAPED[ch.charCodeAt(0)] = true; }); module.exports = function escape(state, silent) { - var ch, str, pos = state.pos, max = state.posMax; + var ch, pos = state.pos, max = state.posMax; if (state.src.charCodeAt(pos) !== 0x5C/* \ */) { return false; } @@ -16,19 +16,7 @@ module.exports = function escape(state, silent) { ch = state.src.charCodeAt(pos); if (typeof ESCAPED[ch] !== 'undefined') { - // escape html chars if needed - if (ch === 0x26/* & */) { - str = '&'; - } else if (ch === 0x3C/* < */) { - str = '<'; - } else if (ch === 0x3E/* > */) { - str = '>'; - } else if (ch === 0x22/* " */) { - str = '"'; - } else { - str = state.src[pos]; - } - if (!silent) { state.pending += str; } + if (!silent) { state.pending += state.src[pos]; } state.pos += 2; return true; } diff --git a/lib/rules_inline/escape_html_char.js b/lib/rules_inline/escape_html_char.js deleted file mode 100644 index 20a9765..0000000 --- a/lib/rules_inline/escape_html_char.js +++ /dev/null @@ -1,20 +0,0 @@ -// Process < > " (& was processed in markdown escape) - -module.exports = function escape_html_char(state, silent) { - var ch = state.src.charCodeAt(state.pos), - str; - - if (ch === 0x3C/* < */) { - str = '<'; - } else if (ch === 0x3E/* > */) { - str = '>'; - } else if (ch === 0x22/* " */) { - str = '"'; - } else { - return false; - } - - if (!silent) { state.pending += str; } - state.pos++; - return true; -}; diff --git a/lib/rules_text/linkify.js b/lib/rules_text/linkify.js index cd3880b..784e18b 100644 --- a/lib/rules_text/linkify.js +++ b/lib/rules_text/linkify.js @@ -6,7 +6,6 @@ var Autolinker = require('autolinker'); -var escapeHtml = require('../common/utils').escapeHtml; var links = []; @@ -97,7 +96,7 @@ module.exports = function linkify(t, state) { }); nodes.push({ type: 'text', - content: escapeHtml(links[ln].text), + content: links[ln].text, level: level }); nodes.push({ diff --git a/lib/rules_text/smartquotes.js b/lib/rules_text/smartquotes.js index e262785..5e8ed39 100644 --- a/lib/rules_text/smartquotes.js +++ b/lib/rules_text/smartquotes.js @@ -3,7 +3,7 @@ 'use strict'; -var quoteReg = /"|'/g; +var quoteReg = /['"]/g; var punctReg = /[-\s()\[\]]/; var apostrophe = '’';