diff --git a/README.md b/README.md index ac0c0fad..8d1d95d8 100644 --- a/README.md +++ b/README.md @@ -147,15 +147,24 @@ Options object is cloned, and mutated along the way to add integrity, resolved, There must be a configured `_keys` entry in the config that is scoped to the registry the manifest is being fetched from. * `tufCache` Where to store metadata/target files when retrieving the package attestation key material via TUF. Defaults to the same cache directory that npm will use by default, based on platform and environment. -* `allowGit` Whether or not to allow data to be fetched from git. +* `allowGit` Whether or not to allow data to be fetched from a git spec. Possible values are `all`, `none`, or `root`. Defaults to `all`. `all` means git is allowed `none` means git is not allowed `root` means that git is only allowed if fetching from a root context. Context for whether or not the package being fetched is `root` is set via the `_isRoot` option. +* `allowRemote` Whether or not to allow data to be fetched from remote specs. + Possible values and defaults are the same as `allowGit` +* `allowFile` Whether or not to allow data to be fetched from file specs. + Possible values and defaults are the same as `allowGit` +* `allowDirectory` Whether or not to allow data to be fetched from directory specs. + Possible values and defaults are the same as `allowGit` * `_isRoot` Whether or not the package being fetched is in a root context. - For `npm` itself this means a package that is defined in the local project or workspace package.json, or a package that is being fetched for another command like `npm view`. + Defaults to `false`, + For `npm` itself this means a package that is defined in the local project or workspace package.json, or a package that is being fetched for another command like `npm view`. This informs the `allowX` options to let them know the context of the current request. + +For more info on spec types (i.e. git, remote) see [npm-package-arg](npm.im/npm-package-arg) ### Advanced API diff --git a/lib/fetcher.js b/lib/fetcher.js index 8d52d28e..0ce7aedb 100644 --- a/lib/fetcher.js +++ b/lib/fetcher.js @@ -470,14 +470,20 @@ const DirFetcher = require('./dir.js') const RemoteFetcher = require('./remote.js') // possible values for allow: 'all', 'root', 'none' -const canUseGit = (allow = 'all', isRoot = false) => { +const canUse = ({ allow = 'all', isRoot = false, allowType, spec }) => { if (allow === 'all') { return true } if (allow !== 'none' && isRoot) { return true } - return false + throw Object.assign( + new Error(`Fetching${allow === 'root' ? ' non-root' : ''} packages of type "${allowType}" have been disabled`), + { + code: `EALLOW${allowType.toUpperCase()}`, + package: spec.toString(), + } + ) } // Get an appropriate fetcher object from a spec and options @@ -485,18 +491,11 @@ FetcherBase.get = (rawSpec, opts = {}) => { const spec = npa(rawSpec, opts.where) switch (spec.type) { case 'git': - if (!canUseGit(opts.allowGit, opts._isRoot)) { - throw Object.assign( - new Error(`Fetching${opts.allowGit === 'root' ? ' non-root' : ''} packages from git has been disabled`), - { - code: 'EALLOWGIT', - package: spec.toString(), - } - ) - } + canUse({ allow: opts.allowGit, isRoot: opts._isRoot, allowType: 'git', spec }) return new GitFetcher(spec, opts) case 'remote': + canUse({ allow: opts.allowRemote, isRoot: opts._isRoot, allowType: 'remote', spec }) return new RemoteFetcher(spec, opts) case 'version': @@ -506,9 +505,11 @@ FetcherBase.get = (rawSpec, opts = {}) => { return new RegistryFetcher(spec.subSpec || spec, opts) case 'file': + canUse({ allow: opts.allowFile, isRoot: opts._isRoot, allowType: 'file', spec }) return new FileFetcher(spec, opts) case 'directory': + canUse({ allow: opts.allowDirectory, isRoot: opts._isRoot, allowType: 'directory', spec }) return new DirFetcher(spec, opts) default: diff --git a/test/fetcher.js b/test/fetcher.js index 3fed72f1..383cb923 100644 --- a/test/fetcher.js +++ b/test/fetcher.js @@ -536,19 +536,30 @@ t.test('fetcher.get', t => { t.end() }) -t.test('allowGit', t => { - t.ok(Fetcher.get('npm/repo'), 'defaults') - t.ok(Fetcher.get('npm/foo', { allowGit: 'all' }), 'allowGit: all') - t.ok(Fetcher.get('npm/foo', { allowGit: 'root', _isRoot: true }), 'allowGit: root') - t.throws(() => { - Fetcher.get('npm/foo', { allowGit: 'none' }) - }, { code: 'EALLOWGIT' }) - t.throws(() => { - Fetcher.get('npm/foo', { allowGit: 'root' }) - }, { code: 'EALLOWGIT' }) - t.throws(() => { - Fetcher.get('npm/foo', { allowGit: 'root', _isRoot: false }) - }, { code: 'EALLOWGIT' }) +t.test('allowX', t => { + const allowTypes = [ + ['allowGit', 'npm/foo'], + ['allowRemote', 'http://npmjs.org/package'], + ['allowFile', './local.tgz'], + ['allowDirectory', './local/dir'], + ] + for (const [allowType, spec] of allowTypes) { + t.test(`${allowType}: ${spec}`, t => { + t.ok(Fetcher.get(spec), 'defaults') + t.ok(Fetcher.get(spec, { [allowType]: 'all' }), `${allowType}: all`) + t.ok(Fetcher.get(spec, { [allowType]: 'root', _isRoot: true }), `${allowType}: root`) + t.throws(() => { + Fetcher.get(spec, { [allowType]: 'none' }) + }, { code: `E${allowType.toUpperCase()}` }) + t.throws(() => { + Fetcher.get(spec, { [allowType]: 'root' }) + }, { code: `E${allowType.toUpperCase()}` }) + t.throws(() => { + Fetcher.get(spec, { [allowType]: 'root', _isRoot: false }) + }, { code: `E${allowType.toUpperCase()}` }) + t.end() + }) + } t.end() })