diff --git a/package.json b/package.json index b1a70d21c..42f34cbb1 100644 --- a/package.json +++ b/package.json @@ -123,6 +123,7 @@ "stylelint-webpack-plugin": "0.10.1", "supertest": "^3.0.0", "url-loader": "0.6.2", + "verdaccio-auth-memory": "^0.0.3", "webpack": "3.10.0", "webpack-dev-server": "2.11.1", "webpack-merge": "4.1.1" diff --git a/src/api/endpoint/api/package.js b/src/api/endpoint/api/package.js index 55be61786..ff7203fd0 100644 --- a/src/api/endpoint/api/package.js +++ b/src/api/endpoint/api/package.js @@ -48,6 +48,7 @@ module.exports = function(route, auth, storage, config) { route.get('/:package/-/:filename', can('access'), function(req, res) { const stream = storage.get_tarball(req.params.package, req.params.filename); + stream.on('content-length', function(content) { res.header('Content-Length', content); }); diff --git a/src/api/endpoint/api/user.js b/src/api/endpoint/api/user.js index cacdbce50..4ea752d39 100644 --- a/src/api/endpoint/api/user.js +++ b/src/api/endpoint/api/user.js @@ -30,7 +30,7 @@ module.exports = function(route, auth) { // With npm registering is the same as logging in, // and npm accepts only an 409 error. // So, changing status code here. - return next( createError[409](err.message) ); + return next( createError[err.status || 409](err.message) ); } return next(err); } diff --git a/src/lib/auth.js b/src/lib/auth.js index 21af23002..65229f654 100644 --- a/src/lib/auth.js +++ b/src/lib/auth.js @@ -115,8 +115,12 @@ class Auth { } else { // p.add_user() execution p[n](user, password, function(err, ok) { - if (err) return cb(err); - if (ok) return self.authenticate(user, password, cb); + if (err) { + return cb(err); + } + if (ok) { + return self.authenticate(user, password, cb); + } next(); }); } @@ -142,8 +146,15 @@ class Auth { } p.allow_access(user, pkg, function(err, ok) { - if (err) return callback(err); - if (ok) return callback(null, ok); + + if (err) { + return callback(err); + } + + if (ok) { + return callback(null, ok); + } + next(); // cb(null, false) causes next plugin to roll }); })(); @@ -204,7 +215,9 @@ class Auth { req.remote_user = buildAnonymousUser(); let authorization = req.headers.authorization; - if (authorization == null) return next(); + if (authorization == null) { + return next(); + } let parts = authorization.split(' '); diff --git a/test/functional/adduser/adduser.js b/test/functional/adduser/adduser.js index eed0d7e33..0a139ca73 100644 --- a/test/functional/adduser/adduser.js +++ b/test/functional/adduser/adduser.js @@ -1,10 +1,8 @@ -import fs from 'fs'; -import path from 'path'; - export default function(server) { describe('npm adduser', () => { const user = String(Math.random()); const pass = String(Math.random()); + beforeAll(function() { return server.auth(user, pass) .status(201) @@ -25,22 +23,4 @@ export default function(server) { .body_error(/maximum amount of users reached/); }); }); - - describe('should adduser created with htpasswd', () => { - const user = 'preexisting'; - const pass = 'preexisting'; - - beforeAll(function() { - return fs.appendFileSync( - path.join(__dirname, '../store/test-storage', '.htpasswd'), - 'preexisting:$apr1$4YSboUa9$yVKjE7.PxIOuK3M4D7VjX.' - ); - }); - - test('should log in', () => { - return server.auth(user, pass) - .status(201) - .body_ok(/you are authenticated as/); - }); - }); } diff --git a/test/functional/basic/basic.spec.js b/test/functional/basic/basic.spec.js index 2996d6672..b75f30d40 100644 --- a/test/functional/basic/basic.spec.js +++ b/test/functional/basic/basic.spec.js @@ -16,6 +16,12 @@ function createHash() { export default function(server, server2) { describe('basic test endpoints', () => { + beforeAll(function() { + return server.auth('test', 'test') + .status(201) + .body_ok(/'test'/); + }); + require('./whoIam')(server); require('./ping')(server); @@ -114,31 +120,41 @@ export default function(server, server2) { /* test for before() */ }); - test('downloading newly created package', () => { - return server.getPackage('testpkg') - .status(200) - .then(function (body) { - assert.equal(body.name, 'testpkg'); - assert.equal(body.versions['0.0.1'].name, 'testpkg'); - assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55551/testpkg/-/blahblah'); - assert.deepEqual(body['dist-tags'], { - latest: '0.0.1' + describe('should download a package', () => { + beforeAll(function() { + return server.auth('test', 'test') + .status(201) + .body_ok(/'test'/); + }); + + test('should download a newly created package from server1', () => { + return server.getPackage('testpkg') + .status(200) + .then(function (body) { + assert.equal(body.name, 'testpkg'); + assert.equal(body.versions['0.0.1'].name, 'testpkg'); + assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55551/testpkg/-/blahblah'); + assert.deepEqual(body['dist-tags'], { + latest: '0.0.1' + }); }); - }); + }); + + test('should downloading a package from server2', () => { + return server2.getPackage('testpkg') + .status(200) + .then(function (body) { + assert.equal(body.name, 'testpkg'); + assert.equal(body.versions['0.0.1'].name, 'testpkg'); + assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55552/testpkg/-/blahblah'); + assert.deepEqual(body['dist-tags'], { + latest: '0.0.1' + }); + }); + }); + }); - test('downloading package via server2', () => { - return server2.getPackage('testpkg') - .status(200) - .then(function (body) { - assert.equal(body.name, 'testpkg'); - assert.equal(body.versions['0.0.1'].name, 'testpkg'); - assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55552/testpkg/-/blahblah'); - assert.deepEqual(body['dist-tags'], { - latest: '0.0.1' - }); - }); - }); }); }); }); diff --git a/test/functional/basic/whoIam.js b/test/functional/basic/whoIam.js index 03038c6bd..386c00b3f 100644 --- a/test/functional/basic/whoIam.js +++ b/test/functional/basic/whoIam.js @@ -1,12 +1,8 @@ -'use strict'; - -const assert = require('assert'); - module.exports = function(server) { test('who am I?', () => { return server.whoami().then(function (username) { - assert.equal(username, 'test'); + expect(username).toMatch('test'); }); }); diff --git a/test/functional/fixtures/plugins/authenticate.js b/test/functional/fixtures/plugins/authenticate.js deleted file mode 100644 index 24619db9e..000000000 --- a/test/functional/fixtures/plugins/authenticate.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict'; - -function Plugin(config) { - let self = Object.create(Plugin.prototype); - self._config = config; - return self; -} - -// plugin is expected to be compatible with... -Plugin.prototype.verdaccio_version = '1.1.0'; - -Plugin.prototype.authenticate = function(user, password, cb) { - if (user !== this._config.accept_user) { - // delegate to next plugin - return cb(null, false); - } - if (password !== this._config.with_password) { - const err = Error('i don\'t like your password'); - err.status = 403; - return cb(err); - } - return cb(null, [user]); -}; - -module.exports = Plugin; diff --git a/test/functional/fixtures/plugins/authorize.js b/test/functional/fixtures/plugins/authorize.js deleted file mode 100644 index 5bd017c87..000000000 --- a/test/functional/fixtures/plugins/authorize.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -module.exports = Plugin; - -function Plugin(config) { - let self = Object.create(Plugin.prototype); - self._config = config; - return self; -} - -// plugin is expected to be compatible with... -Plugin.prototype.verdaccio_version = '1.1.0'; - -Plugin.prototype.allow_access = function(user, pkg, cb) { - if (!pkg.handled_by_auth_plugin) { - // delegate to next plugin - return cb(null, false); - } - if (user.name !== this._config.allow_user) { - let err = Error('i don\'t know anything about you'); - err.status = 403; - return cb(err); - } - if (pkg.name !== this._config.to_access) { - let err = Error('you\'re not allowed here'); - err.status = 403; - return cb(err); - } - return cb(null, true); -}; diff --git a/test/functional/gh29.js b/test/functional/gh29.js index 8c6d07aa3..cafa20e44 100644 --- a/test/functional/gh29.js +++ b/test/functional/gh29.js @@ -25,7 +25,7 @@ export default function (server, server2) { test('downloading non-existent tarball #2 / srv2', () => { return server2.getTarball('testpkg-gh29', 'blahblah') .status(404) - .body_error(/no such file/); + .body_error(/no such file available/); }); describe('tarball', () => { @@ -51,10 +51,10 @@ export default function (server, server2) { test('downloading newly created tarball / srv2', () => { return server2.getTarball('testpkg-gh29', 'blahblah') - .status(200) - .then(function(body) { - assert.deepEqual(body, readfile('fixtures/binary')); - }); + .status(200) + .then(function(body) { + assert.deepEqual(body, readfile('fixtures/binary')); + }); }); }); }); diff --git a/test/functional/index.func.js b/test/functional/index.func.js index ad60d86fb..829395d89 100644 --- a/test/functional/index.func.js +++ b/test/functional/index.func.js @@ -34,21 +34,22 @@ import upLinkCache from './uplink.cache.spec'; import upLinkAuth from './uplink.auth.spec'; describe('functional test verdaccio', function() { + jest.setTimeout(20000); const EXPRESS_PORT = 55550; const SILENCE_LOG = !process.env.VERDACCIO_DEBUG; const processRunning = []; const config1 = new VerdaccioConfig( './store/test-storage', './store/config-1.yaml', - 'http://localhost:55551/'); + 'http://localhost:55551/', 55551); const config2 = new VerdaccioConfig( './store/test-storage2', './store/config-2.yaml', - 'http://localhost:55552/'); + 'http://localhost:55552/', 55552); const config3 = new VerdaccioConfig( './store/test-storage3', './store/config-3.yaml', - 'http://localhost:55553/'); + 'http://localhost:55553/', 55553); const server1: IServerBridge = new Server(config1.domainPath); const server2: IServerBridge = new Server(config2.domainPath); const server3: IServerBridge = new Server(config3.domainPath); diff --git a/test/functional/lib/server_process.js b/test/functional/lib/server_process.js index d8a79535d..74b98a960 100644 --- a/test/functional/lib/server_process.js +++ b/test/functional/lib/server_process.js @@ -32,8 +32,9 @@ export default class VerdaccioProcess implements IServerProcess { this.childFork = fork(verdaccioRegisterWrap, ['-c', configPath], { - silent: this.silence - } + silent: this.silence, + execArgv: [`--inspect=${this.config.port + 5}`] + }, ); this.childFork.on('message', (msg) => { @@ -49,16 +50,16 @@ export default class VerdaccioProcess implements IServerProcess { } }); - this.childFork.on('error', function(err) { - reject(err); + this.childFork.on('error', (err) => { + reject([err, this]); }); - this.childFork.on('disconnect', function(err) { - reject(err); + this.childFork.on('disconnect', (err) => { + reject([err, this]); }); - this.childFork.on('exit', function(err) { - reject(err); + this.childFork.on('exit', (err) => { + reject([err, this]); }); }); @@ -70,4 +71,4 @@ export default class VerdaccioProcess implements IServerProcess { return this.childFork.kill('SIGINT'); } -} \ No newline at end of file +} diff --git a/test/functional/lib/types.js b/test/functional/lib/types.js index 6e7beb453..46f025e75 100644 --- a/test/functional/lib/types.js +++ b/test/functional/lib/types.js @@ -4,6 +4,7 @@ export interface IVerdaccioConfig { storagePath: string; configPath: string; domainPath: string; + port: number; } export interface IRequestPromise { @@ -49,4 +50,4 @@ export interface IServerBridge { whoami(): Promise; ping(): Promise; debug(): IRequestPromise; -} \ No newline at end of file +} diff --git a/test/functional/lib/verdaccio-server.js b/test/functional/lib/verdaccio-server.js index bc3ef93a6..93e173426 100644 --- a/test/functional/lib/verdaccio-server.js +++ b/test/functional/lib/verdaccio-server.js @@ -6,10 +6,12 @@ export class VerdaccioConfig implements IVerdaccioConfig { storagePath: string; configPath: string; domainPath: string; + port: number; - constructor(storagePath: string, configPath: string, domainPath: string) { + constructor(storagePath: string, configPath: string, domainPath: string, port: number) { this.storagePath = storagePath; this.configPath = configPath; this.domainPath = domainPath; + this.port = port; } } diff --git a/test/functional/package/access.spec.js b/test/functional/package/access.spec.js index a6cd1563e..9544c1fca 100644 --- a/test/functional/package/access.spec.js +++ b/test/functional/package/access.spec.js @@ -4,15 +4,6 @@ export default function(server) { const buildToken = (auth) => { return `Basic ${(new Buffer(auth).toString('base64'))}`; }; - let oldAuth; - - beforeAll(function() { - oldAuth = server.authstr; - }); - - afterAll(function() { - server.authstr = oldAuth; - }); /** * Check whether the user is allowed to fetch packages @@ -72,7 +63,7 @@ export default function(server) { checkPublish(undefined, testAccessOnly, false); checkPublish(badCredentials, testAccessOnly, false); - // // all are allowed to publish + // all are allowed to publish checkAccess(validCredentials, testPublishOnly, false); checkAccess(undefined, testPublishOnly, false); checkAccess(badCredentials, testPublishOnly, false); diff --git a/test/functional/plugins/auth.spec.js b/test/functional/plugins/auth.spec.js index f71208005..13025f8df 100644 --- a/test/functional/plugins/auth.spec.js +++ b/test/functional/plugins/auth.spec.js @@ -1,9 +1,9 @@ import assert from 'assert'; export default function(server2){ - const requestAuthFail = (user, pass, message) => { + const requestAuthFail = (user, pass, message, statusCode) => { return server2.auth(user, pass) - .status(409) + .status(statusCode) .body_error(message) .then(function() { return server2.whoami(); @@ -12,9 +12,9 @@ export default function(server2){ assert.equal(username, null); }); }; - const requestAuthOk = (user, pass, regex) => { + const requestAuthOk = (user, pass, regex, statusCode) => { return server2.auth(user, pass) - .status(201) + .status(statusCode) .body_ok(regex) .then(function() { return server2.whoami(); @@ -26,39 +26,22 @@ export default function(server2){ }; describe('test default authentication', () => { - let authstr; - - beforeAll(function() { - authstr = server2.authstr; - }); test('should not authenticate with wrong password', () => { - return requestAuthFail('authtest', 'wrongpass', 'this user already exists'); - }); - - test('should be wrong password handled by plugin', () => { - return requestAuthFail('authtest2', 'wrongpass', 'registration is disabled'); + return requestAuthFail('authtest', 'wrongpass1', 'i don\'t like your password', 401); }); test('should right password handled by plugin', () => { - return requestAuthOk('authtest2', 'blahblah', /'authtest2'/); + return requestAuthOk('authtest2', 'blahblah', /'authtest2'/, 201); }); - afterAll(function() { - server2.authstr = authstr; - }); }); describe('test access authorization', () => { - let authstr; - beforeAll(function() { - authstr = server2.authstr; - }); - - describe('access with user authtest', () => { + describe('access with user authtest', () => { beforeAll(function() { - return server2.auth('authtest', 'test') + return server2.auth('authtest', 'blahblah') .status(201) .body_ok(/'authtest'/); }); @@ -69,10 +52,10 @@ export default function(server2){ .body_error('no such package available'); }); - test('access test-auth-deny', () => { - return server2.getPackage('test-auth-deny') + test('access test-deny', () => { + return server2.getPackage('test-deny') .status(403) - .body_error('you\'re not allowed here'); + .body_error('not allowed to access package'); }); test('access test-auth-regular', () => { @@ -92,13 +75,13 @@ export default function(server2){ test('access test-auth-allow', () => { return server2.getPackage('test-auth-allow') .status(403) - .body_error('i don\'t know anything about you'); + .body_error('not allowed to access package'); }); test('access test-auth-deny', () => { return server2.getPackage('test-auth-deny') .status(403) - .body_error('i don\'t know anything about you'); + .body_error('not allowed to access package'); }); test('access test-auth-regular', () => { @@ -108,8 +91,5 @@ export default function(server2){ }); }); - afterAll(function() { - server2.authstr = authstr; - }); }); } diff --git a/test/functional/readme/readme.spec.js b/test/functional/readme/readme.spec.js index 0a8ee76c1..22560b35f 100644 --- a/test/functional/readme/readme.spec.js +++ b/test/functional/readme/readme.spec.js @@ -18,8 +18,8 @@ export default function (server, server2) { test('add pkg', () => {}); describe('should check readme file', () => { - const matchReadme = (server) => { - return server.request({ + const matchReadme = (serverRef) => { + return serverRef.request({ uri: '/-/verdaccio/package/readme/readme-test' }).status(200).then(function(body) { assert.equal(body, '

this is a readme

\n'); diff --git a/test/functional/store/config-1.yaml b/test/functional/store/config-1.yaml index e3e3d4872..b3961ffe8 100644 --- a/test/functional/store/config-1.yaml +++ b/test/functional/store/config-1.yaml @@ -1,14 +1,18 @@ storage: ./test-storage -users: - test: - password: a94a8fe5ccb19ba61c4c0873d391e987982fbbd3 - -users_file: ./test-storage/.htpasswd -max_users: 1 +max_users: 2 web: enable: true + title: verdaccio-server-1 + +auth: + auth-memory: + users: + test: + name: test + password: test + uplinks: express: @@ -26,36 +30,70 @@ logs: packages: '@test/*': - allow_access: all - allow_publish: all + allow_access: $all + allow_publish: $all proxy: server2 'testfwd*': - allow_access: all - allow_publish: all + allow_access: $all + allow_publish: $all proxy_access: server2 proxy_publish: server2 'testloop': - allow_access: all - allow_publish: all + allow_access: $all + allow_publish: $all proxy_access: server2 proxy_publish: server2 - 'testexp*': - allow_access: all - allow_publish: all + 'testexp': + allow_access: $anonymous + + # used by tags.spec.js + 'testexp_tag*': + allow_access: $all + allow_publish: $all + proxy_access: express + + # used by gzip.spec.js + 'testexp_gzi*': + allow_access: $all + allow_publish: $all + proxy_access: express + + # used by gh29.js + 'testpkg-gh29': + allow_access: $all + allow_publish: $all + proxy_access: express + + # used by preserve_tags_spec.js + 'testpkg-preserve': + allow_access: $all + allow_publish: $all + proxy_access: express + + # used by racycrash.js + 'testexp-racycrash': + allow_access: $all + allow_publish: $all + proxy_access: express + + # used by incomplete.js + 'testexp-incomplete': + allow_access: $all + allow_publish: $all proxy_access: express 'test-nullstorage*': - allow_access: all - allow_publish: all + allow_access: $all + allow_publish: $all proxy_access: server2 storage: false 'baduplink': - allow_access: all - allow_publish: all + allow_access: $all + allow_publish: $all proxy_access: baduplink 'test-access-only': @@ -79,12 +117,8 @@ packages: storage: false '*': - allow_access: test undefined - allow_publish: test undefined - - # this should not matter - testpkg: - allow_access: none + allow_access: test $anonymous + allow_publish: test $anonymous listen: 55551 diff --git a/test/functional/store/config-2.yaml b/test/functional/store/config-2.yaml index a49d4fd80..45be27aca 100644 --- a/test/functional/store/config-2.yaml +++ b/test/functional/store/config-2.yaml @@ -1,11 +1,5 @@ storage: ./test-storage2 -users: - test: - password: a94a8fe5ccb19ba61c4c0873d391e987982fbbd3 - authtest: - password: a94a8fe5ccb19ba61c4c0873d391e987982fbbd3 - uplinks: server1: url: http://localhost:55551/ @@ -13,62 +7,84 @@ uplinks: web: enable: true + title: verdaccio-server-2 middlewares: ../fixtures/plugins/middlewares: message: this is a custom route -auth: - ../fixtures/plugins/authenticate: - accept_user: authtest2 - with_password: blahblah +max_users: 3 - ../fixtures/plugins/authorize: - allow_user: authtest - to_access: test-auth-allow +auth: + auth-memory: + users: + test: + name: test + password: test + authtest2: + name: authtest2 + password: blahblah + authtest: + name: authtest + password: blahblah logs: - {type: stdout, format: pretty, level: trace} packages: '@test/*': - allow_access: all - allow_publish: all + allow_access: $all + allow_publish: $all proxy: server1 'testfwd': - allow_access: all - allow_publish: all + allow_access: $all + allow_publish: $all 'testloop': - allow_access: all - allow_publish: all + allow_access: $all + allow_publish: $all proxy_access: server1 proxy_publish: server1 - 'testpkg*': - allow_access: test anonymous - allow_publish: test anonymous + # used by gh29.js + 'testpkg-gh29': + allow_access: test $anonymous + allow_publish: test $anonymous + proxy_access: server1 + + # used by preserve_tags_spec.js + 'testpkg-preserve': + allow_access: test $anonymous + allow_publish: test $anonymous + proxy_access: server1 + + 'testpkg': + allow_access: test $anonymous + allow_publish: test $anonymous proxy_access: server1 'readme-*': - allow_access: test anonymous - allow_publish: test anonymous + allow_access: test $anonymous + allow_publish: test $anonymous proxy_access: server1 'test-nullstorage*': - allow_access: all - allow_publish: all + allow_access: $all + allow_publish: $all 'test-auth-regular': allow_access: $authenticated 'test-auth-*': - handled_by_auth_plugin: true + allow_access: authtest + + 'test-deny': + allow_access: authtest2 '*': - allow_access: test anonymous - allow_publish: test anonymous + allow_access: test $anonymous + allow_publish: test $anonymous listen: 55552 diff --git a/test/functional/store/config-3.yaml b/test/functional/store/config-3.yaml index 54684d907..c2afe82eb 100644 --- a/test/functional/store/config-3.yaml +++ b/test/functional/store/config-3.yaml @@ -1,11 +1,8 @@ storage: ./test-storage3 -users: - test: - password: a94a8fe5ccb19ba61c4c0873d391e987982fbbd3 - web: enable: true + title: verdaccio-server-3 uplinks: server1: @@ -14,6 +11,13 @@ uplinks: url: http://localhost:55552/ cache: false +auth: + auth-memory: + users: + test: + name: test + password: test + logs: - {type: stdout, format: pretty, level: trace} diff --git a/test/unit/api.spec.js b/test/unit/api.spec.js index 7527cf22d..950654339 100644 --- a/test/unit/api.spec.js +++ b/test/unit/api.spec.js @@ -16,7 +16,6 @@ describe('endpoint unit test', () => { let storage; let auth; let app; - jest.setTimeout(500000); beforeAll(function(done) { const store = path.join(__dirname, './partials/store/test-storage'); @@ -131,13 +130,14 @@ describe('endpoint unit test', () => { .put('/-/user/org.couchdb.user:jota') .send(credentialsShort) .expect('Content-Type', /json/) - .expect(409) + .expect(403) .end(function(err, res) { if (err) { return done(err); } expect(res.body.error).toBeDefined(); + //FIXME: message is not 100% accurate expect(res.body.error).toMatch(/this user already exists/); done(); }); @@ -149,13 +149,14 @@ describe('endpoint unit test', () => { .put('/-/user/org.couchdb.user:jota') .send(credentials) .expect('Content-Type', /json/) - .expect(409) + .expect(403) .end(function(err, res) { if (err) { return done(err); } expect(res.body.error).toBeDefined(); + //FIXME: message is not 100% accurate expect(res.body.error).toMatch(/this user already exists/); done(); }); @@ -173,7 +174,7 @@ describe('endpoint unit test', () => { //TODO: this should return 401 and will fail when issue // https://github.com/verdaccio/verdaccio-htpasswd/issues/5 // is being fixed - .expect(409) + .expect(403) .end(function(err, res) { if (err) { return done(err); diff --git a/yarn.lock b/yarn.lock index 78d0a4cf0..7681d3140 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8688,6 +8688,10 @@ vendors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22" +verdaccio-auth-memory@^0.0.3: + version "0.0.3" + resolved "https://registry.npmjs.org/verdaccio-auth-memory/-/verdaccio-auth-memory-0.0.3.tgz#6d175a8959c04a46f1441fa09370ede3517acf5b" + verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"