Repository: npm/cli (JavaScript)

Issue:
[BUG] npm install "" installs package "undefined"
<!--
Note: Please search to see if an issue already exists for your problem: https://github.com/npm/cli/issues
-->

### Current Behavior:
<!-- ex. a clear & concise description of what you're experiencing. -->

```
$ npm install --global ""
npm verb cli [
npm verb cli   '/usr/local/bin/node',
npm verb cli   '/usr/local/bin/npm',
npm verb cli   'install',
npm verb cli   '--global',
npm verb cli   '',
npm verb cli ]
npm info using npm@7.0.8
npm info using node@v15.1.0
npm timing config:load:defaults Completed in 1ms
npm timing config:load:file:/usr/local/lib/node_modules/npm/npmrc Completed in 0ms
npm timing config:load:builtin Completed in 0ms
npm timing config:load:cli Completed in 2ms
npm timing config:load:env Completed in 0ms
npm timing config:load:file:/.npmrc Completed in 0ms
npm timing config:load:project Completed in 1ms
npm timing config:load:file:/root/.npmrc Completed in 0ms
npm timing config:load:user Completed in 0ms
npm timing config:load:file:/usr/local/etc/npmrc Completed in 0ms
npm timing config:load:global Completed in 0ms
npm timing config:load:cafile Completed in 1ms
npm timing config:load:validate Completed in 0ms
npm timing config:load:setUserAgent Completed in 0ms
npm timing config:load:setEnvs Completed in 1ms
npm timing config:load Completed in 6ms
npm verb npm-session b77bf6dc929f4ac7
npm timing npm:load Completed in 18ms
npm http fetch GET 200 https://registry.npmjs.org/npm 999ms
npm timing arborist:ctor Completed in 0ms
npm timing idealTree:init Completed in 2ms
npm http fetch GET 200 https://registry.npmjs.org/undefined 33ms
npm timing idealTree:userRequests Completed in 35ms
npm http fetch GET 304 https://registry.npmjs.org/undefined 42ms (from cache)
npm timing idealTree:#root Completed in 47ms
npm timing idealTree:node_modules/undefined Completed in 0ms
npm timing idealTree:buildDeps Completed in 48ms
npm timing idealTree:fixDepFlags Completed in 1ms
npm timing idealTree Completed in 88ms
npm timing reify:loadTrees Completed in 91ms
npm timing reify:diffTrees Completed in 0ms
npm timing reify:retireShallow Completed in 1ms
npm timing reify:createSparse Completed in 1ms
npm timing reify:loadBundles Completed in 0ms
npm http fetch GET 200 https://registry.npmjs.org/undefined/-/undefined-0.1.0.tgz 67ms
npm WARN deprecated undefined@0.1.0: this package has been deprecated
npm timing reifyNode:node_modules/undefined Completed in 75ms
npm timing reify:unpack Completed in 76ms
npm timing reify:unretire Completed in 0ms
npm timing build:queue Completed in 0ms
npm timing build:deps Completed in 0ms
npm timing build Completed in 1ms
npm timing reify:build Completed in 2ms
npm timing reify:trash Completed in 0ms
npm http fetch POST 200 https://registry.npmjs.org/-/npm/v1/security/advisories/bulk 83ms
npm timing auditReport:getReport Completed in 85ms
npm timing auditReport:init Completed in 0ms
npm timing reify:audit Completed in 85ms
npm timing reify Completed in 181ms

added 1 package, and audited 1 package in 1s

found 0 vulnerabilities
npm timing command:install Completed in 186ms
npm notice 
npm notice New minor version of npm available! 7.0.8 -> 7.8.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v7.8.0
npm notice Run npm install -g npm@7.8.0 to update!
npm notice 
npm verb exit 0
npm timing npm Completed in 1428ms
npm info ok 
```

### Expected Behavior:
Probably an error

### Steps To Reproduce:
`npm install --global ""`

### Environment:
Docker's [`node:latest`](https://hub.docker.com/layers/node/library/node/latest/images/sha256-0850e1871e459d6d8c84375dd1dc9e151c8ec814c1d07508cfb84d31d437bb36) at time of writing

```
$ node --version
v15.13.0
$ npm --version
7.7.6
```



Discussion:

Patch:
diff --git a/lib/commands/install.js b/lib/commands/install.js
index 0a5c827bcc97b..d1f6d1481dddc 100644
--- a/lib/commands/install.js
+++ b/lib/commands/install.js
@@ -139,6 +139,12 @@ class Install extends ArboristWorkspaceCmd {
       args = ['.']
     }
 
+    // throw usage error if trying to install empty package
+    // name to global space, e.g: `npm i -g ""`
+    if (where === globalTop && !args.every(Boolean)) {
+      throw this.usageError()
+    }
+
     const opts = {
       ...this.npm.flatOptions,
       auditLevel: null,


Test patch:
diff --git a/test/lib/commands/install.js b/test/lib/commands/install.js
index afb6adb4fb0a5..9b2d52f6edd21 100644
--- a/test/lib/commands/install.js
+++ b/test/lib/commands/install.js
@@ -139,6 +139,23 @@ t.test('should install globally using Arborist', async t => {
   t.strictSame(SCRIPTS, [], 'no scripts when installing globally')
 })
 
+t.test('should not install invalid global package name', async t => {
+  const { npm } = await loadMockNpm(t, {
+    '@npmcli/run-script': () => {},
+    '../../lib/utils/reify-finish.js': async () => {},
+    '@npmcli/arborist': function (args) {
+      throw new Error('should not reify')
+    },
+  })
+  npm.config.set('global', true)
+  npm.globalPrefix = path.resolve(t.testdir({}))
+  await t.rejects(
+    npm.exec('install', ['']),
+    /Usage:/,
+    'should not install invalid package name'
+  )
+})
+
 t.test('npm i -g npm engines check success', async t => {
   const { npm } = await loadMockNpm(t, {
     '../../lib/utils/reify-finish.js': async () => {},