From cd6d3a90d4bbf3793834830b4c77fc8eb0846596 Mon Sep 17 00:00:00 2001 From: Gar Date: Tue, 9 Nov 2021 15:21:05 -0800 Subject: [PATCH] fix: explicitly allow `npm help` to open file:/// man pages PR-URL: https://github.com/npm/cli/pull/4026 Credit: @wraithgar Close: #4026 Reviewed-by: @isaacs --- lib/commands/help.js | 2 +- lib/utils/open-url.js | 16 ++++++++++------ test/lib/utils/open-url.js | 12 ++++++++++++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/lib/commands/help.js b/lib/commands/help.js index 0de047615711a..2986f917f9e0f 100644 --- a/lib/commands/help.js +++ b/lib/commands/help.js @@ -120,7 +120,7 @@ class Help extends BaseCommand { break case 'browser': - await openUrl(this.npm, this.htmlMan(man), 'help available at the following URL') + await openUrl(this.npm, this.htmlMan(man), 'help available at the following URL', true) return default: diff --git a/lib/utils/open-url.js b/lib/utils/open-url.js index ddbbddccf3be4..eed2449dcce51 100644 --- a/lib/utils/open-url.js +++ b/lib/utils/open-url.js @@ -3,7 +3,7 @@ const opener = require('opener') const { URL } = require('url') // attempt to open URL in web-browser, print address otherwise: -const open = async (npm, url, errMsg) => { +const open = async (npm, url, errMsg, isFile) => { url = encodeURI(url) const browser = npm.config.get('browser') @@ -24,12 +24,16 @@ const open = async (npm, url, errMsg) => { return } - try { - if (!/^https?:$/.test(new URL(url).protocol)) { - throw new Error() + // We pass this in as true from the help command so we know we don't have to + // check the protocol + if (!isFile) { + try { + if (!/^https?:$/.test(new URL(url).protocol)) { + throw new Error() + } + } catch (_) { + throw new Error('Invalid URL: ' + url) } - } catch (_) { - throw new Error('Invalid URL: ' + url) } const command = browser === true ? null : browser diff --git a/test/lib/utils/open-url.js b/test/lib/utils/open-url.js index cc63af12943a1..1bf47d8bbaedc 100644 --- a/test/lib/utils/open-url.js +++ b/test/lib/utils/open-url.js @@ -73,6 +73,18 @@ t.test('returns error for file url', async t => { t.same(OUTPUT, [], 'printed no output') }) +t.test('file url allowed if explicitly asked for', async t => { + t.teardown(() => { + openerUrl = null + openerOpts = null + OUTPUT.length = 0 + }) + await openUrl(npm, 'file:///man/page/npm-install', 'npm home', true) + t.equal(openerUrl, 'file:///man/page/npm-install', 'opened the given url') + t.same(openerOpts, { command: null }, 'passed command as null (the default)') + t.same(OUTPUT, [], 'printed no output') +}) + t.test('returns error for non-parseable url', async t => { t.teardown(() => { openerUrl = null