diff --git a/README.md b/README.md index 3c9630a..8ab5dc1 100644 --- a/README.md +++ b/README.md @@ -294,6 +294,9 @@ var defaultOptions = showdown.getDefaultOptions(); * **ghMentions**: (boolean) [default false] Enables github @mentions, which link to the username mentioned (since v1.6.0) + * **encodeEmails**: (boolean) [default true] Enables e-mail addresses encoding through the use of Character Entities, transforming ASCII e-mail addresses into its equivalent decimal entities. (since v1.6.1) + + NOTE: Prior to version 1.6.1, emails would always be obfuscated through dec and hex encoding. **NOTE**: Please note that until version 1.6.0, all of these options are ***DISABLED*** by default in the cli tool. diff --git a/dist/showdown.js b/dist/showdown.js index 52fb0da..8b7e44d 100644 Binary files a/dist/showdown.js and b/dist/showdown.js differ diff --git a/dist/showdown.js.map b/dist/showdown.js.map index 1b06051..31a0dc6 100644 Binary files a/dist/showdown.js.map and b/dist/showdown.js.map differ diff --git a/dist/showdown.min.js b/dist/showdown.min.js index 798230d..dde4210 100644 Binary files a/dist/showdown.min.js and b/dist/showdown.min.js differ diff --git a/dist/showdown.min.js.map b/dist/showdown.min.js.map index 5e2077f..70c1a54 100644 Binary files a/dist/showdown.min.js.map and b/dist/showdown.min.js.map differ diff --git a/src/helpers.js b/src/helpers.js index fb21348..d572df4 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -251,6 +251,46 @@ showdown.helper.replaceRecursiveRegExp = function (str, replacement, left, right return finalStr; }; +/** + * Obfuscate an e-mail address through the use of Character Entities, + * transforming ASCII characters into their equivalent decimal or hex entities. + * + * Since it has a random component, subsequent calls to this function produce different results + * + * @param {string} mail + * @returns {string} + */ +showdown.helper.encodeEmailAddress = function (mail) { + 'use strict'; + var encode = [ + function (ch) { + return '&#' + ch.charCodeAt(0) + ';'; + }, + function (ch) { + return '&#x' + ch.charCodeAt(0).toString(16) + ';'; + }, + function (ch) { + return ch; + } + ]; + + mail = mail.replace(/./g, function (ch) { + if (ch === '@') { + // this *must* be encoded. I insist. + ch = encode[Math.floor(Math.random() * 2)](ch); + } else { + var r = Math.random(); + // roughly 10% raw, 45% hex, 45% dec + ch = ( + r > 0.9 ? encode[2](ch) : r > 0.45 ? encode[1](ch) : encode[0](ch) + ); + } + return ch; + }); + + return mail; +}; + /** * POLYFILLS */ diff --git a/src/options.js b/src/options.js index e9d7008..87705f8 100644 --- a/src/options.js +++ b/src/options.js @@ -105,6 +105,11 @@ function getDefaultOpts(simple) { defaultValue: false, description: 'Enables github @mentions', type: 'boolean' + }, + encodeEmails: { + defaultValue: true, + description: 'Encode e-mail addresses through the use of Character Entities, transforming ASCII e-mail addresses into its equivalent decimal entities', + type: 'boolean' } }; if (simple === false) { diff --git a/src/subParsers/autoLinks.js b/src/subParsers/autoLinks.js index 0d5583d..4bf53e9 100644 --- a/src/subParsers/autoLinks.js +++ b/src/subParsers/autoLinks.js @@ -36,8 +36,15 @@ showdown.subParser('autoLinks', function (text, options, globals) { } function replaceMail(wholeMatch, mail) { - var unescapedStr = showdown.subParser('unescapeSpecialChars')(mail); - return showdown.subParser('encodeEmailAddress')(unescapedStr); + var href = 'mailto:'; + mail = showdown.subParser('unescapeSpecialChars')(mail); + if (options.encodeEmails) { + mail = showdown.helper.encodeEmailAddress(mail); + href = showdown.helper.encodeEmailAddress(href + mail); + } else { + href = href + mail; + } + return '' + mail + ''; } text = globals.converter._dispatch('autoLinks.after', text, options, globals); diff --git a/src/subParsers/encodeEmailAddress.js b/src/subParsers/encodeEmailAddress.js deleted file mode 100644 index 925b3cc..0000000 --- a/src/subParsers/encodeEmailAddress.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Input: an email address, e.g. "foo@example.com" - * - * Output: the email address as a mailto link, with each character - * of the address encoded as either a decimal or hex entity, in - * the hopes of foiling most address harvesting spam bots. E.g.: - * - * foo - * @example.com - * - * Based on a filter by Matthew Wickline, posted to the BBEdit-Talk - * mailing list: - * - */ -showdown.subParser('encodeEmailAddress', function (addr) { - 'use strict'; - - var encode = [ - function (ch) { - return '&#' + ch.charCodeAt(0) + ';'; - }, - function (ch) { - return '&#x' + ch.charCodeAt(0).toString(16) + ';'; - }, - function (ch) { - return ch; - } - ]; - - addr = 'mailto:' + addr; - - addr = addr.replace(/./g, function (ch) { - if (ch === '@') { - // this *must* be encoded. I insist. - ch = encode[Math.floor(Math.random() * 2)](ch); - } else if (ch !== ':') { - // leave ':' alone (to spot mailto: later) - var r = Math.random(); - // roughly 10% raw, 45% hex, 45% dec - ch = ( - r > 0.9 ? encode[2](ch) : r > 0.45 ? encode[1](ch) : encode[0](ch) - ); - } - return ch; - }); - - addr = '' + addr + ''; - addr = addr.replace(/">.+:/g, '">'); // strip the mailto: from the visible part - - return addr; -}); diff --git a/test/features/disable-email-encoding.html b/test/features/disable-email-encoding.html new file mode 100644 index 0000000..21e2731 --- /dev/null +++ b/test/features/disable-email-encoding.html @@ -0,0 +1 @@ +

this email foobar@example.com should not be encoded

diff --git a/test/features/disable-email-encoding.md b/test/features/disable-email-encoding.md new file mode 100644 index 0000000..80fb6e8 --- /dev/null +++ b/test/features/disable-email-encoding.md @@ -0,0 +1 @@ +this email should not be encoded diff --git a/test/node/showdown.helpers.js b/test/node/showdown.helpers.js new file mode 100644 index 0000000..ec9fa21 --- /dev/null +++ b/test/node/showdown.helpers.js @@ -0,0 +1,30 @@ +/** + * Created by Estevao on 27/01/2017. + */ + +var bootstrap = require('../bootstrap.js'), + showdown = bootstrap.showdown, + encoder = showdown.helper.encodeEmailAddress; + +describe('encodeEmailAddress', function () { + 'use strict'; + var email = 'foobar@example.com', + encodedEmail = encoder(email); + + it('should encode email', function () { + encodedEmail.should.not.equal(email); + }); + + it('should decode to original email', function () { + var decodedEmail = encodedEmail.replace(/&#(.+?);/g, function (wm, cc) { + if (cc.charAt(0) === 'x') { + //hex + return String.fromCharCode('0' + cc); + } else { + //dec + return String.fromCharCode(cc); + } + }); + decodedEmail.should.equal(email); + }); +}); diff --git a/test/node/testsuite.features.js b/test/node/testsuite.features.js index c25aa6a..f2bccc0 100644 --- a/test/node/testsuite.features.js +++ b/test/node/testsuite.features.js @@ -53,6 +53,8 @@ describe('makeHtml() features testsuite', function () { converter = new showdown.Converter({ghCompatibleHeaderId: true}); } else if (testsuite[i].name === 'ghMentions') { converter = new showdown.Converter({ghMentions: true}); + } else if (testsuite[i].name === 'disable-email-encoding') { + converter = new showdown.Converter({encodeEmails: false}); } else { converter = new showdown.Converter(); }