Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

hapi

Package Overview
Dependencies
Maintainers
1
Versions
295
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

hapi - npm Package Compare versions

Comparing version 17.0.0-rc8 to 17.0.0-rc9

275

lib/auth.js

@@ -41,6 +41,6 @@ 'use strict';

_strategy(server, name, scheme, options) {
_strategy(server, name, scheme, options = {}) {
Hoek.assert(name, 'Authentication strategy must have a name');
Hoek.assert(!options || typeof options === 'object', 'options must be an object');
Hoek.assert(typeof options === 'object', 'options must be an object');
Hoek.assert(name !== 'bypass', 'Cannot use reserved strategy name: bypass');

@@ -105,2 +105,15 @@ Hoek.assert(!this._strategies[name], 'Authentication strategy name already exists');

static testAccess(request, route) {
const auth = request._core.auth;
try {
auth._access(request, route);
return true;
}
catch (err) {
return false;
}
}
_setupRoute(options, path) {

@@ -187,2 +200,6 @@

if (type === 'access') {
return !!config.access;
}
for (let i = 0; i < config.strategies.length; ++i) {

@@ -205,22 +222,132 @@ const name = config.strategies[i];

static access(request, route) {
async _authenticate(request) {
const config = this.lookup(request.route);
const errors = [];
request.auth.mode = config.mode;
// Injection bypass
if (request.auth.credentials) {
internals.validate(null, { credentials: request.auth.credentials, artifacts: request.auth.artifacts }, 'bypass', config, request, errors);
return;
}
// Try each strategy
for (let i = 0; i < config.strategies.length; ++i) {
const name = config.strategies[i];
const strategy = this._strategies[name];
const bind = strategy.methods;
const realm = strategy.realm;
const response = await request._core.toolkit.execute(strategy.methods.authenticate, request, { bind, realm, auth: true });
const message = (response.isAuth ? internals.validate(response.error, response.data, name, config, request, errors) : internals.validate(response, null, name, config, request, errors));
if (!message) {
return;
}
if (message !== internals.missing) {
return message;
}
}
// No more strategies
const err = Boom.unauthorized('Missing authentication', errors);
if (config.mode !== 'optional' &&
config.mode !== 'try') {
throw err;
}
request.auth.isAuthenticated = false;
request.auth.credentials = null;
request.auth.error = err;
request._log(['auth', 'unauthenticated']);
}
static access(request) {
const auth = request._core.auth;
const config = auth.lookup(route);
if (!config) {
return true;
try {
auth._access(request);
request.auth.isAuthorized = true;
}
catch (err) {
request.auth.isAuthorized = false;
throw err;
}
}
_access(request, route) {
const config = this.lookup(route || request.route);
if (!config ||
!config.access) {
return;
}
const credentials = request.auth.credentials;
if (!credentials) {
return false;
throw Boom.forbidden('Request is unauthenticated');
}
return !internals.access(request, config, credentials, 'bypass');
}
const requestEntity = (credentials.user ? 'user' : 'app');
_authenticate(request) {
const scopeErrors = [];
for (let i = 0; i < config.access.length; ++i) {
const access = config.access[i];
const config = this.lookup(request.route);
return internals.authenticate(config, request, this);
// Check entity
const entity = access.entity;
if (entity &&
entity !== 'any' &&
entity !== requestEntity) {
continue;
}
// Check scope
let scope = access.scope;
if (scope) {
if (!credentials.scope) {
scopeErrors.push(scope);
continue;
}
scope = internals.expandScope(request, scope);
if (!internals.validateScope(credentials, scope, 'required') ||
!internals.validateScope(credentials, scope, 'selection') ||
!internals.validateScope(credentials, scope, 'forbidden')) {
scopeErrors.push(scope);
continue;
}
}
return;
}
// Scope error
if (scopeErrors.length) {
request._log(['auth', 'scope', 'error']);
throw Boom.forbidden('Insufficient scope', { got: credentials.scope, need: scopeErrors });
}
// Entity error
if (requestEntity === 'app') {
request._log(['auth', 'entity', 'user', 'error']);
throw Boom.forbidden('Application credentials cannot be used on a user endpoint');
}
request._log(['auth', 'entity', 'app', 'error']);
throw Boom.forbidden('User credentials cannot be used on an application endpoint');
}

@@ -313,50 +440,2 @@

internals.authenticate = async function (config, request, manager) {
const errors = [];
request.auth.mode = config.mode;
// Injection bypass
if (request.auth.credentials) {
internals.validate(null, { credentials: request.auth.credentials, artifacts: request.auth.artifacts }, 'bypass', config, request, errors);
return;
}
// Try each strategy
for (let i = 0; i < config.strategies.length; ++i) {
const name = config.strategies[i];
const strategy = manager._strategies[name];
const bind = strategy.methods;
const realm = strategy.realm;
const response = await request._core.toolkit.execute(strategy.methods.authenticate, request, { bind, realm, auth: true });
const message = (response.isAuth ? internals.validate(response.error, response.data, name, config, request, errors) : internals.validate(response, null, name, config, request, errors));
if (!message) {
return;
}
if (message !== internals.missing) {
return message;
}
}
// No more strategies
const err = Boom.unauthorized('Missing authentication', errors);
if (config.mode !== 'optional' &&
config.mode !== 'try') {
throw err;
}
request.auth.isAuthenticated = false;
request.auth.credentials = null;
request.auth.error = err;
request._log(['auth', 'unauthenticated']);
};
internals.validate = function (err, result, name, config, request, errors) { // err can be Boom, Error, or a valid response object

@@ -370,3 +449,3 @@

if (err instanceof Error === false) {
request._log(['auth', 'unauthenticated', 'response', name], err.statusCode);
request._log(['auth', 'unauthenticated', 'response', name], { statusCode: err.statusCode });
return err; // Non-error response

@@ -404,11 +483,2 @@ }

request.auth.artifacts = result.artifacts;
// Check access rules
const error = internals.access(request, config, credentials, name);
if (error) {
request._log(error.tags, error.data);
throw error.err;
}
request.auth.isAuthenticated = true;

@@ -418,63 +488,2 @@ };

internals.access = function (request, config, credentials, name) {
if (!config.access) {
return null;
}
const requestEntity = (credentials.user ? 'user' : 'app');
const scopeErrors = [];
for (let i = 0; i < config.access.length; ++i) {
const access = config.access[i];
// Check entity
const entity = access.entity;
if (entity &&
entity !== 'any' &&
entity !== requestEntity) {
continue;
}
// Check scope
let scope = access.scope;
if (scope) {
if (!credentials.scope) {
scopeErrors.push(scope);
continue;
}
scope = internals.expandScope(request, scope);
if (!internals.validateScope(credentials, scope, 'required') ||
!internals.validateScope(credentials, scope, 'selection') ||
!internals.validateScope(credentials, scope, 'forbidden')) {
scopeErrors.push(scope);
continue;
}
}
return null;
}
// Scope error
if (scopeErrors.length) {
const data = { got: credentials.scope, need: scopeErrors };
return { err: Boom.forbidden('Insufficient scope', data), tags: ['auth', 'scope', 'error', name], data };
}
// Entity error
if (requestEntity === 'app') {
return { err: Boom.forbidden('Application credentials cannot be used on a user endpoint'), tags: ['auth', 'entity', 'user', 'error', name] };
}
return { err: Boom.forbidden('User credentials cannot be used on an application endpoint'), tags: ['auth', 'entity', 'app', 'error', name] };
};
internals.expandScope = function (request, scope) {

@@ -507,3 +516,5 @@

params: request.params,
query: request.query
query: request.query,
payload: request.payload,
credentials: request.auth.credentials
};

@@ -510,0 +521,0 @@

@@ -55,3 +55,4 @@ 'use strict';

catch (err) {
request.log(['accept-encoding', 'error'], { header, error: err });
err.header = header;
request._log(['accept-encoding', 'error'], err);
return 'identity';

@@ -58,0 +59,0 @@ }

@@ -117,2 +117,3 @@ 'use strict';

onPreAuth: Joi.array().items(internals.event).single(),
onCredentials: Joi.array().items(internals.event).single(),
onPostAuth: Joi.array().items(internals.event).single(),

@@ -119,0 +120,0 @@ onPreHandler: Joi.array().items(internals.event).single(),

@@ -31,5 +31,2 @@ 'use strict';

// process.on('unhandledRejection', (reason, p) => console.log('Unhandled Rejection at: Promise', p, 'reason:', reason));
// Declare internals

@@ -43,11 +40,10 @@

events: [
{ name: 'log', tags: true },
{ name: 'log', channels: ['app', 'internal'], tags: true },
{ name: 'request', channels: ['app', 'internal', 'error'], tags: true, spread: true },
'response',
'route',
'start',
'stop',
{ name: 'route', spread: true },
{ name: 'request-internal', spread: true, tags: true },
{ name: 'request', spread: true, tags: true },
{ name: 'request-error', spread: true },
'response'
]
'stop'
],
badRequestResponse: new Buffer('HTTP/1.1 400 Bad Request\r\n\r\n', 'ascii')
};

@@ -104,2 +100,3 @@

onPreAuth: new Ext('onPreAuth', this),
onCredentials: new Ext('onCredentials', this),
onPostAuth: new Ext('onPostAuth', this),

@@ -118,4 +115,2 @@ onPreHandler: new Ext('onPreHandler', this),

this.info = this._info();
this.events.on('route', Cors.options);
}

@@ -130,3 +125,3 @@

const data = event.data;
const data = event.error || event.data;
console.error('Debug:', event.tags.join(', '), (data ? '\n ' + (data.stack || (typeof data === 'object' ? Hoek.stringify(data) : data)) : ''));

@@ -143,3 +138,2 @@ };

this.events.on({ name: 'request', filter }, debug);
this.events.on({ name: 'request-internal', filter }, debug);
}

@@ -346,5 +340,4 @@ }

async _stop(options) {
async _stop(options = {}) {
options = options || {};
options.timeout = options.timeout || 5000; // Default timeout to 5 seconds

@@ -440,6 +433,4 @@

_dispatch(options) {
_dispatch(options = {}) {
options = options || {};
return (req, res) => {

@@ -484,3 +475,9 @@

this._log(['connection', 'client', 'error'], err);
socket.destroy(err);
if (socket.writable) {
socket.end(internals.badRequestResponse);
}
else {
socket.destroy(err);
}
});

@@ -539,19 +536,23 @@

log(tags, data, timestamp, _internal) {
log(tags, data) {
tags = [].concat(tags);
timestamp = (timestamp ? (timestamp instanceof Date ? timestamp.getTime() : timestamp) : Date.now());
const internal = !!_internal;
return this._log(tags, data, 'app');
}
const update = (typeof data !== 'function' ? { timestamp, tags, data, internal } : () => {
_log(tags, data, channel = 'internal') {
return { timestamp, tags, data: data(), internal };
});
if (!Array.isArray(tags)) {
tags = [tags];
}
this.events.emit({ name: 'log', tags }, update);
}
const timestamp = Date.now();
const field = (data instanceof Error ? 'error' : 'data');
_log(tags, data) {
let event = { timestamp, tags, [field]: data, channel };
return this.log(tags, data, null, true);
if (typeof data === 'function') {
event = () => ({ timestamp, tags, data: data(), channel });
}
this.events.emit({ name: 'log', tags, channel }, event);
}

@@ -561,5 +562,5 @@ };

internals.setup = function (options) {
internals.setup = function (options = {}) {
let settings = Hoek.cloneWithShallow(options || {}, ['listener', 'routes.bind']);
let settings = Hoek.cloneWithShallow(options, ['listener', 'routes.bind']);
settings.routes = Config.enable(settings.routes);

@@ -566,0 +567,0 @@ settings = Config.apply('server', settings);

@@ -58,3 +58,3 @@ 'use strict';

if (response.isBoom) {
request._log(['handler', 'error'], { error: response.message, data: response });
request._log(['handler', 'error'], response);
throw response;

@@ -69,3 +69,4 @@ }

if (response.isBoom) {
response = await request._core.toolkit.failAction(request, pre.failAction, response, { tags: ['pre', 'error'], log: { assign: pre.assign, error: response }, retain: true });
response.assign = pre.assign;
response = await request._core.toolkit.failAction(request, pre.failAction, response, { tags: ['pre', 'error'], retain: true });
}

@@ -72,0 +73,0 @@

@@ -48,3 +48,3 @@ 'use strict';

options = Config.apply('method', options || {}, name);
options = Config.apply('method', options, name);

@@ -51,0 +51,0 @@ const settings = Hoek.cloneWithShallow(options, ['bind']);

@@ -19,3 +19,3 @@ 'use strict';

const internals = {
properties: ['server', 'url', 'query', 'path', 'method', 'mime', 'setUrl', 'setMethod', 'headers', 'id', 'app', 'plugins', 'route', 'auth', 'pre', 'preResponses', 'info', 'orig', 'params', 'paramsArray', 'payload', 'state', 'jsonp', 'response', 'raw', 'domain', 'log', 'getLog', 'generateResponse'],
properties: ['server', 'url', 'query', 'path', 'method', 'mime', 'setUrl', 'setMethod', 'headers', 'id', 'app', 'plugins', 'route', 'auth', 'pre', 'preResponses', 'info', 'orig', 'params', 'paramsArray', 'payload', 'state', 'jsonp', 'response', 'raw', 'domain', 'log', 'logs', 'generateResponse'],
events: Podium.validate(['finish', { name: 'peek', spread: true }, 'disconnect'])

@@ -52,5 +52,2 @@ };

options = options || {};
Hoek.assert(!this._decorations || this._decorations[property] === undefined, 'Request interface decoration already defined:', property);
Hoek.assert(internals.properties.indexOf(property) === -1, 'Cannot override built-in request interface decoration:', property);

@@ -76,3 +73,2 @@

this._isReplied = false; // true when response processing started
this._logger = [];
this._route = this._core.router.specials.notFound.route; // Used prior to routing (only settings are used, not the handler)

@@ -86,2 +82,3 @@ this._serverTimeoutId = null;

this.jsonp = null;
this.logs = [];
this.method = req.method.toLowerCase();

@@ -107,2 +104,3 @@ this.mime = null;

isAuthenticated: false,
isAuthorized: false,
credentials: options.credentials || null, // Special keys: 'app', 'user', 'scope'

@@ -394,4 +392,4 @@ artifacts: options.artifacts || null, // Scheme-specific artifacts

this._core.events.emit('request-error', [this, this.response._error]);
this._log(this.response._error.isDeveloperError ? ['internal', 'implementation', 'error'] : ['internal', 'error'], this.response._error);
const tags = this.response._error.isDeveloperError ? ['internal', 'implementation', 'error'] : ['internal', 'error'];
this._log(tags, this.response._error, 'error');
}

@@ -446,7 +444,7 @@

_clearState(name, options) {
_clearState(name, options = {}) {
const state = { name };
state.options = Hoek.clone(options || {});
state.options = Hoek.clone(options);
state.options.ttl = 0;

@@ -466,73 +464,36 @@

generateResponse(source, options) {
log(tags, data) {
return new Response(source, this, options);
return this._log(tags, data, 'app');
}
log(tags, data, timestamp, _internal) {
_log(tags, data, channel = 'internal') {
tags = [].concat(tags);
timestamp = (timestamp ? (timestamp instanceof Date ? timestamp.getTime() : timestamp) : Date.now());
const internal = !!_internal;
if (!Array.isArray(tags)) {
tags = [tags];
}
let update = (typeof data !== 'function' ? [this, { request: this.info.id, timestamp, tags, data, internal }] : () => {
const timestamp = Date.now();
const field = (data instanceof Error ? 'error' : 'data');
return [this, { request: this.info.id, timestamp, tags, data: data(), internal }];
});
let event = { request: this.info.id, timestamp, tags, [field]: data, channel };
if (typeof data === 'function') {
event = () => ({ request: this.info.id, timestamp, tags, data: data(), channel });
}
if (this.route.settings.log.collect) {
if (typeof data === 'function') {
update = update();
event = event();
}
this._logger.push(update[1]); // Add to request array
this.logs.push(event);
}
this._core.events.emit({ name: internal ? 'request-internal' : 'request', tags }, update);
this._core.events.emit({ name: 'request', channel, tags }, [this, event]);
}
_log(tags, data) {
generateResponse(source, options) {
return this.log(tags, data, null, true);
return new Response(source, this, options);
}
getLog(tags, internal) {
Hoek.assert(this.route.settings.log.collect, 'Request logging is disabled');
if (typeof tags === 'boolean') {
internal = tags;
tags = [];
}
tags = [].concat(tags || []);
if (!tags.length &&
internal === undefined) {
return this._logger;
}
const filter = tags.length ? Hoek.mapToObject(tags) : null;
const result = [];
for (let i = 0; i < this._logger.length; ++i) {
const event = this._logger[i];
if (internal === undefined || event.internal === internal) {
if (filter) {
for (let j = 0; j < event.tags.length; ++j) {
const tag = event.tags[j];
if (filter[tag]) {
result.push(event);
break;
}
}
}
else {
result.push(event);
}
}
}
return result;
}
};

@@ -539,0 +500,0 @@

@@ -23,6 +23,4 @@ 'use strict';

constructor(source, request, options) {
constructor(source, request, options = {}) {
options = options || {};
this.app = {};

@@ -141,5 +139,4 @@ this.headers = {}; // Incomplete as some headers are stored in flags

_header(key, value, options) {
_header(key, value, options = {}) {
options = options || {};
const append = options.append || false;

@@ -200,6 +197,4 @@ const separator = options.separator || ',';

static entity(tag, options) {
static entity(tag, options = {}) {
options = options || {};
Hoek.assert(tag !== '*', 'ETag cannot be *');

@@ -206,0 +201,0 @@

@@ -100,3 +100,3 @@ 'use strict';

auth: {
access: (request) => Auth.access(request, this.public)
access: (request) => Auth.testAccess(request, this.public)
}

@@ -200,2 +200,3 @@ };

this._extensions.onPreAuth = Ext.combine(this, 'onPreAuth');
this._extensions.onCredentials = Ext.combine(this, 'onCredentials');
this._extensions.onPostAuth = Ext.combine(this, 'onPostAuth');

@@ -250,2 +251,12 @@ this._extensions.onPreHandler = Ext.combine(this, 'onPreHandler');

if (this._core.auth._enabled(this, 'authenticate') &&
this._extensions.onCredentials.nodes) {
this._cycle.push(this._extensions.onCredentials);
}
if (this._core.auth._enabled(this, 'access')) {
this._cycle.push(Auth.access);
}
if (this._extensions.onPostAuth.nodes) {

@@ -362,3 +373,5 @@ this._cycle.push(this._extensions.onPostAuth);

return request._core.toolkit.failAction(request, request.route.settings.state.failAction, parseError, { tags: ['state', 'error'], log: { header: cookies, errors: parseError.data } });
parseError.header = cookies;
return request._core.toolkit.failAction(request, request.route.settings.state.failAction, parseError, { tags: ['state', 'error'] });
};

@@ -365,0 +378,0 @@

@@ -10,2 +10,3 @@ 'use strict';

const Core = require('./core');
const Cors = require('./cors');
const Ext = require('./ext');

@@ -57,2 +58,3 @@ const Package = require('../package.json');

onPreAuth: new Ext('onPreAuth', core),
onCredentials: new Ext('onCredentials', core),
onPostAuth: new Ext('onPostAuth', core),

@@ -105,3 +107,3 @@ onPreHandler: new Ext('onPreHandler', core),

decorate(type, property, method, options) {
decorate(type, property, method, options = {}) {

@@ -112,8 +114,8 @@ Hoek.assert(this._core.decorations[type], 'Unknown decoration type:', type);

Hoek.assert(property[0] !== '_', 'Property name cannot begin with an underscore:', property);
Hoek.assert(!options || type === 'request', 'Cannot specify options for non-request decoration');
Hoek.assert(this._core.decorations[type].indexOf(property) === -1, `${type[0].toUpperCase() + type.slice(1)} decoration already defined: ${property}`);
// Handler
if (type === 'handler') {
if (type === 'handler') {
Hoek.assert(!this._core.handlers[property], 'Handler name already exists:', property);
// Handler
Hoek.assert(typeof method === 'function', 'Handler must be a function:', property);

@@ -123,32 +125,29 @@ Hoek.assert(!method.defaults || typeof method.defaults === 'object' || typeof method.defaults === 'function', 'Handler defaults property must be an object or function');

this._core.handlers[property] = method;
this._core.decorations.handler.push(property);
}
else if (type === 'request') {
// Request
// Request
if (type === 'request') {
this._core.requestor.decorate(property, method, options);
this._core.decorations.request.push(property);
return;
}
else if (type === 'toolkit') {
// Toolkit
// Toolkit
if (type === 'toolkit') {
this._core.toolkit.decorate(property, method);
this._core.decorations.toolkit.push(property);
return;
}
else {
// Server
// Server
Hoek.assert(!this._core.serverDecorations[property], 'Server decoration already defined:', property);
Hoek.assert(this[property] === undefined && this._core[property] === undefined, 'Cannot override the built-in server interface method:', property);
Hoek.assert(this[property] === undefined && this._core[property] === undefined, 'Cannot override the built-in server interface method:', property);
this._core.serverDecorations[property] = method;
this._core.decorations.server.push(property);
this._core.instances.forEach((server) => {
this._core.serverDecorations[property] = method;
this._core.instances.forEach((server) => {
server[property] = method;
});
server[property] = method;
});
}
this._core.decorations[type].push(property);
}

@@ -283,5 +282,5 @@

log(tags, data, timestamp) {
log(tags, data) {
return this._core.log(tags, data, timestamp);
return this._core.log(tags, data);
}

@@ -316,3 +315,3 @@

method(name, method, options) {
method(name, method, options = {}) {

@@ -453,3 +452,4 @@ return this._core.methods.add(name, method, options, this.realm);

this.events.emit('route', [route.public, server]);
this.events.emit('route', route.public);
Cors.options(route.public, server);
}

@@ -456,0 +456,0 @@

@@ -29,3 +29,2 @@ 'use strict';

Hoek.assert(!this._decorations || !this._decorations[property], 'Reply interface decoration already defined:', property);
Hoek.assert(['abandon', 'authenticated', 'close', 'context', 'continue', 'entity', 'redirect', 'realm', 'request', 'response', 'state', 'unauthenticated', 'unstate'].indexOf(property) === -1, 'Cannot override built-in toolkit decoration:', property);

@@ -47,3 +46,3 @@

catch (err) {
response = err instanceof Error ? Boom.boomify(err) : Boom.badImplementation('Unhandled rejected promise', err);
response = err instanceof Error ? Boom.boomify(err) : Boom.badImplementation('Cannot throw non-error object', err);
}

@@ -93,3 +92,3 @@

if (failAction === 'log') {
request._log(options.tags, options.log || err);
request._log(options.tags, err);
return retain;

@@ -96,0 +95,0 @@ }

@@ -5,3 +5,3 @@ {

"homepage": "http://hapijs.com",
"version": "17.0.0-rc8",
"version": "17.0.0-rc9",
"repository": {

@@ -46,3 +46,3 @@ "type": "git",

"vision": "5.0.0-rc8",
"wreck": "13.x.x"
"wreck": "14.x.x"
},

@@ -49,0 +49,0 @@ "scripts": {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc