From 9ff66edda755d2091b012e9ca6e7a1d1c1135e4a Mon Sep 17 00:00:00 2001 From: SukkaW Date: Sun, 27 Oct 2019 21:25:46 +0800 Subject: [PATCH 1/4] fix: external link ignore mailto: & javascript: Close #3796 --- .../filter/after_post_render/external_link.js | 2 +- test/scripts/filters/external_link.js | 32 ++++++++++++------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/lib/plugins/filter/after_post_render/external_link.js b/lib/plugins/filter/after_post_render/external_link.js index a056a5a525..d102f054b2 100644 --- a/lib/plugins/filter/after_post_render/external_link.js +++ b/lib/plugins/filter/after_post_render/external_link.js @@ -37,7 +37,7 @@ function externalLinkFilter(data) { config.external_link.field !== 'post') return; data.content = data.content.replace(//gi, (str, hrefStr, href) => { - if (/target=/gi.test(str) || !isExternal(href, config)) return str; + if (/target=/gi.test(str) || /mailto:|javascript:/gi.test(href) || !isExternal(href, config)) return str; if (/rel=/gi.test(str)) { str = str.replace(/rel="(.*?)"/gi, (relStr, rel) => { diff --git a/test/scripts/filters/external_link.js b/test/scripts/filters/external_link.js index b0d7d64c40..5eec7074fe 100644 --- a/test/scripts/filters/external_link.js +++ b/test/scripts/filters/external_link.js @@ -208,23 +208,27 @@ describe('External link - post', () => { '# External link test', '1. External link', 'Hexo', - '2. External link with "rel" Attribute', + '2. Link with hash (#), mailto: , javascript: shouldn\'t be processed', + 'Hexo', + 'Hexo', + 'Hexo', + '3. External link with "rel" Attribute', 'Hexo', 'Hexo', 'Hexo', 'Hexo', 'Hexo', 'Hexo', - '3. External link with Other Attributes', + '4. External link with Other Attributes', 'Hexo', 'Hexo', - '4. Internal link', + '5. Internal link', 'Link', - '5. Ignore links have "target" attribute', + '6. Ignore links have "target" attribute', 'Hexo', - '6. Ignore links don\'t have "href" attribute', + '7. Ignore links don\'t have "href" attribute', 'Anchor', - '7. Ignore links whose hostname is same as config', + '8. Ignore links whose hostname is same as config', 'Example Domain' ].join('\n'); @@ -235,23 +239,27 @@ describe('External link - post', () => { '# External link test', '1. External link', 'Hexo', - '2. External link with "rel" Attribute', + '2. Link with hash (#), mailto: , javascript: shouldn\'t be processed', + 'Hexo', + 'Hexo', + 'Hexo', + '3. External link with "rel" Attribute', 'Hexo', 'Hexo', 'Hexo', 'Hexo', 'Hexo', 'Hexo', - '3. External link with Other Attributes', + '4. External link with Other Attributes', 'Hexo', 'Hexo', - '4. Internal link', + '5. Internal link', 'Link', - '5. Ignore links have "target" attribute', + '6. Ignore links have "target" attribute', 'Hexo', - '6. Ignore links don\'t have "href" attribute', + '7. Ignore links don\'t have "href" attribute', 'Anchor', - '7. Ignore links whose hostname is same as config', + '8. Ignore links whose hostname is same as config', 'Example Domain' ].join('\n')); }); From 322251361950463e16c1fbeac6e9bc5c995dbc6e Mon Sep 17 00:00:00 2001 From: SukkaW Date: Sun, 27 Oct 2019 21:32:10 +0800 Subject: [PATCH 2/4] refactor: use startsWith instead of regex --- lib/plugins/filter/after_post_render/external_link.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/filter/after_post_render/external_link.js b/lib/plugins/filter/after_post_render/external_link.js index d102f054b2..83ab95c22e 100644 --- a/lib/plugins/filter/after_post_render/external_link.js +++ b/lib/plugins/filter/after_post_render/external_link.js @@ -37,7 +37,7 @@ function externalLinkFilter(data) { config.external_link.field !== 'post') return; data.content = data.content.replace(//gi, (str, hrefStr, href) => { - if (/target=/gi.test(str) || /mailto:|javascript:/gi.test(href) || !isExternal(href, config)) return str; + if (/target=/gi.test(str) || href.startsWith('mailto:') || href.startsWith('javascript:') || !isExternal(href, config)) return str; if (/rel=/gi.test(str)) { str = str.replace(/rel="(.*?)"/gi, (relStr, rel) => { From ab23935be17f7b2bc6bd79488c1a065f164e5398 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Mon, 28 Oct 2019 13:09:45 +0800 Subject: [PATCH 3/4] refactor(external_link): use whatwg url --- .../filter/after_post_render/external_link.js | 20 +++++++++++++------ .../filter/after_render/external_link.js | 16 +++++++++++---- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/lib/plugins/filter/after_post_render/external_link.js b/lib/plugins/filter/after_post_render/external_link.js index 83ab95c22e..4d1fbc75b2 100644 --- a/lib/plugins/filter/after_post_render/external_link.js +++ b/lib/plugins/filter/after_post_render/external_link.js @@ -1,15 +1,23 @@ 'use strict'; -const { parse } = require('url'); +const { URL } = require('url'); + +const urlObj = (str) => { + try { + return new URL(str); + } catch (err) { + return str; + } +}; const isExternal = (url, config) => { const exclude = Array.isArray(config.external_link.exclude) ? config.external_link.exclude : [config.external_link.exclude]; - const data = parse(url); + const data = urlObj(url); const host = data.hostname; - const sitehost = parse(config.url).hostname || config.url; + const sitehost = urlObj(config.url).hostname || config.url; - if (!data.protocol || !sitehost) return false; + if (!data.protocol || !sitehost || !data.origin) return false; if (exclude && exclude.length) { for (const i of exclude) { @@ -34,10 +42,10 @@ function externalLinkFilter(data) { }, config.external_link); } if (config.external_link === false || config.external_link.enable === false || - config.external_link.field !== 'post') return; + config.external_link.field !== 'post') return; data.content = data.content.replace(//gi, (str, hrefStr, href) => { - if (/target=/gi.test(str) || href.startsWith('mailto:') || href.startsWith('javascript:') || !isExternal(href, config)) return str; + if (/target=/gi.test(str) || !isExternal(href, config)) return str; if (/rel=/gi.test(str)) { str = str.replace(/rel="(.*?)"/gi, (relStr, rel) => { diff --git a/lib/plugins/filter/after_render/external_link.js b/lib/plugins/filter/after_render/external_link.js index fd3d7b4ea8..21dd1318d7 100644 --- a/lib/plugins/filter/after_render/external_link.js +++ b/lib/plugins/filter/after_render/external_link.js @@ -1,6 +1,14 @@ 'use strict'; -const { parse } = require('url'); +const { URL } = require('url'); + +const urlObj = (str) => { + try { + return new URL(str); + } catch (err) { + return str; + } +}; /** * Check whether the link is external @@ -11,11 +19,11 @@ const { parse } = require('url'); const isExternal = (url, config) => { const exclude = Array.isArray(config.external_link.exclude) ? config.external_link.exclude : [config.external_link.exclude]; - const data = parse(url); + const data = urlObj(url); const host = data.hostname; - const sitehost = parse(config.url).hostname || config.url; + const sitehost = urlObj(config.url).hostname || config.url; - if (!data.protocol || !sitehost) return false; + if (!data.protocol || !sitehost || !data.origin) return false; if (exclude && exclude.length) { for (const i of exclude) { From ec465a561eb4cd1f370716163880f4c1624132ab Mon Sep 17 00:00:00 2001 From: SukkaW Date: Mon, 28 Oct 2019 14:28:41 +0800 Subject: [PATCH 4/4] fix(external_link): handle whatwg url api Apply code suggestions from code review by @curbengh --- lib/plugins/filter/after_post_render/external_link.js | 5 +++-- lib/plugins/filter/after_render/external_link.js | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/plugins/filter/after_post_render/external_link.js b/lib/plugins/filter/after_post_render/external_link.js index 4d1fbc75b2..43c8dd348e 100644 --- a/lib/plugins/filter/after_post_render/external_link.js +++ b/lib/plugins/filter/after_post_render/external_link.js @@ -15,9 +15,10 @@ const isExternal = (url, config) => { [config.external_link.exclude]; const data = urlObj(url); const host = data.hostname; - const sitehost = urlObj(config.url).hostname || config.url; + const sitehost = typeof urlObj(config.url) === 'object' ? urlObj(config.url).hostname : config.url; - if (!data.protocol || !sitehost || !data.origin) return false; + if (!sitehost || typeof data === 'string') return false; + if (data.origin === 'null') return false; if (exclude && exclude.length) { for (const i of exclude) { diff --git a/lib/plugins/filter/after_render/external_link.js b/lib/plugins/filter/after_render/external_link.js index 21dd1318d7..d064bf13d5 100644 --- a/lib/plugins/filter/after_render/external_link.js +++ b/lib/plugins/filter/after_render/external_link.js @@ -21,9 +21,10 @@ const isExternal = (url, config) => { [config.external_link.exclude]; const data = urlObj(url); const host = data.hostname; - const sitehost = urlObj(config.url).hostname || config.url; + const sitehost = typeof urlObj(config.url) === 'object' ? urlObj(config.url).hostname : config.url; - if (!data.protocol || !sitehost || !data.origin) return false; + if (!sitehost || typeof data === 'string') return false; + if (data.origin === 'null') return false; if (exclude && exclude.length) { for (const i of exclude) {