Comparing version 0.0.24 to 1.0.0
14
index.js
@@ -0,6 +1,14 @@ | ||
'use strict'; | ||
const kerberos = require('./lib/kerberos'); | ||
// Get the Kerberos library | ||
module.exports = require('./lib/kerberos'); | ||
module.exports = kerberos; | ||
// Support legacy versions of the mongodb driver which expect this export | ||
module.exports.Kerberos = kerberos; | ||
// Set up the auth processes | ||
module.exports['processes'] = { | ||
module.exports.processes = { | ||
MongoAuthProcess: require('./lib/auth_processes/mongodb').MongoAuthProcess | ||
} | ||
}; |
@@ -1,347 +0,144 @@ | ||
var format = require('util').format, | ||
dns = require('dns'); | ||
'use strict'; | ||
const dns = require('dns'); | ||
const kerberos = require('../kerberos'); | ||
var MongoAuthProcess = function(host, port, service_name, options) { | ||
// Check what system we are on | ||
if(process.platform == 'win32') { | ||
this._processor = new Win32MongoProcessor(host, port, service_name, options); | ||
} else { | ||
this._processor = new UnixMongoProcessor(host, port, service_name, options); | ||
} | ||
} | ||
class MongoAuthProcess { | ||
constructor(host, port, serviceName, options) { | ||
options = options || {}; | ||
this.host = host; | ||
this.port = port; | ||
MongoAuthProcess.prototype.init = function(username, password, callback) { | ||
this._processor.init(username, password, callback); | ||
} | ||
// Set up service name | ||
serviceName = serviceName || options.gssapiServiceName || 'mongodb'; | ||
MongoAuthProcess.prototype.transition = function(payload, callback) { | ||
this._processor.transition(payload, callback); | ||
} | ||
// Options | ||
this.canonicalizeHostName = | ||
typeof options.gssapiCanonicalizeHostName === 'boolean' | ||
? options.gssapiCanonicalizeHostName | ||
: false; | ||
/******************************************************************* | ||
* | ||
* Win32 SSIP Processor for MongoDB | ||
* | ||
*******************************************************************/ | ||
var Win32MongoProcessor = function(host, port, service_name, options) { | ||
options = options || {}; | ||
this.host = host; | ||
this.port = port | ||
this.service = | ||
process.platform === 'win32' ? `${serviceName}/${host}` : `${serviceName}@${host}`; | ||
// Set up service name | ||
service_name = service_name || "mongodb"; | ||
// Options | ||
this.gssapiServiceName = options.gssapiServiceName || service_name; | ||
this.gssapiServiceRealm = options.gssapiServiceRealm; | ||
this.gssapiCanonicalizeHostName = typeof options.gssapiCanonicalizeHostName == 'boolean' ? options.gssapiCanonicalizeHostName : false; | ||
// SSIP classes | ||
this.ssip = require("../kerberos").SSIP; | ||
// Set up first transition | ||
this._transition = Win32MongoProcessor.first_transition(this); | ||
// Number of retries | ||
this.retries = 10; | ||
} | ||
// Set up first transition | ||
this._transition = firstTransition(this); | ||
Win32MongoProcessor.prototype.init = function(username, password, callback) { | ||
var self = this; | ||
// Save the values used later | ||
this.username = username; | ||
this.password = password; | ||
// Canonicialize host name if needed | ||
var performGssapiCanonicalizeHostName = function(gssapiCanonicalizeHostName, host, callback) { | ||
if(!gssapiCanonicalizeHostName) return callback(); | ||
// Attempt to resolve the host name | ||
dns.resolveCname(host, function(err, r) { | ||
if(err) return callback(err); | ||
// Get the first resolve host id | ||
if(Array.isArray(r) && r.length > 0) { | ||
self.host = r[0]; | ||
} | ||
callback(); | ||
}); | ||
// Number of retries | ||
this.retries = 10; | ||
} | ||
// Canonicialize host name if needed | ||
performGssapiCanonicalizeHostName(this.gssapiCanonicalizeHostName, this.host, function(err) { | ||
if(err) return callback(err); | ||
// Acquire credentials | ||
self.ssip.SecurityCredentials.aquire_kerberos(username, password, function(err, security_credentials) { | ||
if(err) return callback(err); | ||
init(username, password, callback) { | ||
const self = this; | ||
this.username = username; | ||
this.password = password; | ||
// Set up target | ||
self.target = format("%s/%s", self.gssapiServiceName, self.host); | ||
// Canonicialize host name if needed | ||
function performGssapiCanonicalizeHostName(canonicalizeHostName, host, callback) { | ||
if (!canonicalizeHostName) return callback(); | ||
// Do we have a service realm | ||
if(self.gssapiServiceRealm) { | ||
self.target = format("%s@%s", self.target, self.gssapiServiceRealm); | ||
} | ||
// Attempt to resolve the host name | ||
dns.resolveCname(host, (err, r) => { | ||
if (err) return callback(err); | ||
// Save credentials | ||
self.security_credentials = security_credentials; | ||
// Callback with success | ||
callback(null); | ||
}); | ||
}); | ||
} | ||
Win32MongoProcessor.prototype.transition = function(payload, callback) { | ||
if(this._transition == null) return callback(new Error("Transition finished")); | ||
this._transition(payload, callback); | ||
} | ||
Win32MongoProcessor.first_transition = function(self) { | ||
return function(payload, callback) { | ||
self.ssip.SecurityContext.initialize( | ||
self.security_credentials, | ||
self.target, | ||
payload, function(err, security_context) { | ||
if(err) return callback(err); | ||
// If no context try again until we have no more retries | ||
if(!security_context.hasContext) { | ||
if(self.retries == 0) return callback(new Error("Failed to initialize security context")); | ||
// Update the number of retries | ||
self.retries = self.retries - 1; | ||
// Set next transition | ||
return self.transition(payload, callback); | ||
// Get the first resolve host id | ||
if (Array.isArray(r) && r.length > 0) { | ||
self.host = r[0]; | ||
} | ||
// Set next transition | ||
self._transition = Win32MongoProcessor.second_transition(self); | ||
self.security_context = security_context; | ||
// Return the payload | ||
callback(null, security_context.payload); | ||
}); | ||
} | ||
} | ||
callback(); | ||
}); | ||
} | ||
Win32MongoProcessor.second_transition = function(self) { | ||
return function(payload, callback) { | ||
// Perform a step | ||
self.security_context.initialize(self.target, payload, function(err, security_context) { | ||
if(err) return callback(err); | ||
// Canonicialize host name if needed | ||
performGssapiCanonicalizeHostName(this.canonicalizeHostName, this.host, err => { | ||
if (err) return callback(err); | ||
// If no context try again until we have no more retries | ||
if(!security_context.hasContext) { | ||
if(self.retries == 0) return callback(new Error("Failed to initialize security context")); | ||
// Update the number of retries | ||
self.retries = self.retries - 1; | ||
// Set next transition | ||
self._transition = Win32MongoProcessor.first_transition(self); | ||
// Retry | ||
return self.transition(payload, callback); | ||
} | ||
kerberos.initializeClient(self.service, { user: username, password }, (err, client) => { | ||
if (err) return callback(err, null); | ||
// Set next transition | ||
self._transition = Win32MongoProcessor.third_transition(self); | ||
// Return the payload | ||
callback(null, security_context.payload); | ||
self.client = client; | ||
callback(null, client); | ||
}); | ||
}); | ||
} | ||
} | ||
Win32MongoProcessor.third_transition = function(self) { | ||
return function(payload, callback) { | ||
var messageLength = 0; | ||
// Get the raw bytes | ||
var encryptedBytes = new Buffer(payload, 'base64'); | ||
var encryptedMessage = new Buffer(messageLength); | ||
// Copy first byte | ||
encryptedBytes.copy(encryptedMessage, 0, 0, messageLength); | ||
// Set up trailer | ||
var securityTrailerLength = encryptedBytes.length - messageLength; | ||
var securityTrailer = new Buffer(securityTrailerLength); | ||
// Copy the bytes | ||
encryptedBytes.copy(securityTrailer, 0, messageLength, securityTrailerLength); | ||
transition(payload, callback) { | ||
if (this._transition == null) { | ||
return callback(new Error('Transition finished')); | ||
} | ||
// Types used | ||
var SecurityBuffer = self.ssip.SecurityBuffer; | ||
var SecurityBufferDescriptor = self.ssip.SecurityBufferDescriptor; | ||
// Set up security buffers | ||
var buffers = [ | ||
new SecurityBuffer(SecurityBuffer.DATA, encryptedBytes) | ||
, new SecurityBuffer(SecurityBuffer.STREAM, securityTrailer) | ||
]; | ||
// Set up the descriptor | ||
var descriptor = new SecurityBufferDescriptor(buffers); | ||
// Decrypt the data | ||
self.security_context.decryptMessage(descriptor, function(err, security_context) { | ||
if(err) return callback(err); | ||
var length = 4; | ||
if(self.username != null) { | ||
length += self.username.length; | ||
} | ||
var bytesReceivedFromServer = new Buffer(length); | ||
bytesReceivedFromServer[0] = 0x01; // NO_PROTECTION | ||
bytesReceivedFromServer[1] = 0x00; // NO_PROTECTION | ||
bytesReceivedFromServer[2] = 0x00; // NO_PROTECTION | ||
bytesReceivedFromServer[3] = 0x00; // NO_PROTECTION | ||
if(self.username != null) { | ||
var authorization_id_bytes = new Buffer(self.username, 'utf8'); | ||
authorization_id_bytes.copy(bytesReceivedFromServer, 4, 0); | ||
} | ||
self.security_context.queryContextAttributes(0x00, function(err, sizes) { | ||
if(err) return callback(err); | ||
var buffers = [ | ||
new SecurityBuffer(SecurityBuffer.TOKEN, new Buffer(sizes.securityTrailer)) | ||
, new SecurityBuffer(SecurityBuffer.DATA, bytesReceivedFromServer) | ||
, new SecurityBuffer(SecurityBuffer.PADDING, new Buffer(sizes.blockSize)) | ||
] | ||
var descriptor = new SecurityBufferDescriptor(buffers); | ||
self.security_context.encryptMessage(descriptor, 0x80000001, function(err, security_context) { | ||
if(err) return callback(err); | ||
callback(null, security_context.payload); | ||
}); | ||
}); | ||
}); | ||
this._transition(payload, callback); | ||
} | ||
} | ||
/******************************************************************* | ||
* | ||
* UNIX MIT Kerberos processor | ||
* | ||
*******************************************************************/ | ||
var UnixMongoProcessor = function(host, port, service_name, options) { | ||
options = options || {}; | ||
this.host = host; | ||
this.port = port | ||
// SSIP classes | ||
this.Kerberos = require("../kerberos").Kerberos; | ||
this.kerberos = new this.Kerberos(); | ||
// Set up service name | ||
service_name = service_name || "mongodb"; | ||
// Options | ||
this.gssapiServiceName = options.gssapiServiceName || service_name; | ||
this.gssapiServiceRealm = options.gssapiServiceRealm; | ||
this.gssapiCanonicalizeHostName = typeof options.gssapiCanonicalizeHostName == 'boolean' ? options.gssapiCanonicalizeHostName : false; | ||
// Set up first transition | ||
this._transition = UnixMongoProcessor.first_transition(this); | ||
// Set up target | ||
this.target = format("%s@%s", service_name, host); | ||
// Number of retries | ||
this.retries = 10; | ||
} | ||
function firstTransition(auth) { | ||
return (payload, callback) => { | ||
auth.client.step('', (err, response) => { | ||
if (err) return callback(err); | ||
UnixMongoProcessor.prototype.init = function(username, password, callback) { | ||
var self = this; | ||
this.username = username; | ||
this.password = password; | ||
// Set up the next step | ||
auth._transition = secondTransition(auth); | ||
// Canonicialize host name if needed | ||
var performGssapiCanonicalizeHostName = function(gssapiCanonicalizeHostName, host, callback) { | ||
if(!gssapiCanonicalizeHostName) return callback(); | ||
// Attempt to resolve the host name | ||
dns.resolveCname(host, function(err, r) { | ||
if(err) return callback(err); | ||
// Get the first resolve host id | ||
if(Array.isArray(r) && r.length > 0) { | ||
self.host = r[0]; | ||
} | ||
callback(); | ||
// Return the payload | ||
callback(null, response); | ||
}); | ||
} | ||
// Canonicialize host name if needed | ||
performGssapiCanonicalizeHostName(this.gssapiCanonicalizeHostName, this.host, function(err) { | ||
if(err) return callback(err); | ||
// Set up target | ||
self.target = format("%s@%s", self.gssapiServiceName, self.host); | ||
// Call client initiate | ||
self.kerberos.authGSSClientInit( | ||
self.target | ||
, self.Kerberos.GSS_C_MUTUAL_FLAG, function(err, context) { | ||
self.context = context; | ||
// Return the context | ||
callback(null, context); | ||
}); | ||
}); | ||
}; | ||
} | ||
UnixMongoProcessor.prototype.transition = function(payload, callback) { | ||
if(this._transition == null) return callback(new Error("Transition finished")); | ||
this._transition(payload, callback); | ||
} | ||
function secondTransition(auth) { | ||
return (payload, callback) => { | ||
auth.client.step(payload, (err, response) => { | ||
if (err && auth.retries === 0) return callback(err); | ||
UnixMongoProcessor.first_transition = function(self) { | ||
return function(payload, callback) { | ||
self.kerberos.authGSSClientStep(self.context, '', function(err, result) { | ||
if(err) return callback(err); | ||
// Set up the next step | ||
self._transition = UnixMongoProcessor.second_transition(self); | ||
// Return the payload | ||
callback(null, self.context.response); | ||
}) | ||
} | ||
} | ||
UnixMongoProcessor.second_transition = function(self) { | ||
return function(payload, callback) { | ||
self.kerberos.authGSSClientStep(self.context, payload, function(err, result) { | ||
if(err && self.retries == 0) return callback(err); | ||
// Attempt to re-establish a context | ||
if(err) { | ||
if (err) { | ||
// Adjust the number of retries | ||
self.retries = self.retries - 1; | ||
auth.retries = auth.retries - 1; | ||
// Call same step again | ||
return self.transition(payload, callback); | ||
return auth.transition(payload, callback); | ||
} | ||
// Set up the next step | ||
self._transition = UnixMongoProcessor.third_transition(self); | ||
auth._transition = thirdTransition(auth); | ||
// Return the payload | ||
callback(null, self.context.response || ''); | ||
callback(null, response || ''); | ||
}); | ||
} | ||
}; | ||
} | ||
UnixMongoProcessor.third_transition = function(self) { | ||
return function(payload, callback) { | ||
function thirdTransition(auth) { | ||
return (payload, callback) => { | ||
// GSS Client Unwrap | ||
self.kerberos.authGSSClientUnwrap(self.context, payload, function(err, result) { | ||
if(err) return callback(err, false); | ||
auth.client.unwrap(payload, (err, response) => { | ||
if (err) return callback(err, false); | ||
// Wrap the response | ||
self.kerberos.authGSSClientWrap(self.context, self.context.response, self.username, function(err, result) { | ||
if(err) return callback(err, false); | ||
auth.client.wrap(response, { user: auth.username }, (err, wrapped) => { | ||
if (err) return callback(err, false); | ||
// Set up the next step | ||
self._transition = UnixMongoProcessor.fourth_transition(self); | ||
auth._transition = fourthTransition(auth); | ||
// Return the payload | ||
callback(null, self.context.response); | ||
callback(null, wrapped); | ||
}); | ||
}); | ||
} | ||
}; | ||
} | ||
UnixMongoProcessor.fourth_transition = function(self) { | ||
return function(payload, callback) { | ||
// Clean up context | ||
self.kerberos.authGSSClientClean(self.context, function(err, result) { | ||
if(err) return callback(err, false); | ||
// Set the transition to null | ||
self._transition = null; | ||
// Callback with valid authentication | ||
callback(null, true); | ||
}); | ||
} | ||
function fourthTransition(auth) { | ||
return (payload, callback) => { | ||
// Set the transition to null | ||
auth._transition = null; | ||
// Callback with valid authentication | ||
callback(null, true); | ||
}; | ||
} | ||
// Set the process | ||
exports.MongoAuthProcess = MongoAuthProcess; | ||
module.exports = { | ||
MongoAuthProcess | ||
}; |
@@ -1,191 +0,245 @@ | ||
var kerberos = require('../build/Release/kerberos') | ||
, KerberosNative = kerberos.Kerberos; | ||
'use strict'; | ||
var Kerberos = function() { | ||
this._native_kerberos = new KerberosNative(); | ||
} | ||
const kerberos = require('bindings')('kerberos'); | ||
const KerberosClient = kerberos.KerberosClient; | ||
const KerberosServer = kerberos.KerberosServer; | ||
// callback takes two arguments, an error string if defined and a new context | ||
// uri should be given as service@host. Services are not always defined | ||
// in a straightforward way. Use 'HTTP' for SPNEGO / Negotiate authentication. | ||
// If credentialsCache is not specified, the default credentials cache from the | ||
// environment will be used (ie. KRB5CCNAME). In the case where multiple | ||
// credentials caches may be in use at once (such as for a server doing | ||
// delegation), specify the cache name here and it will be used for this | ||
// exchange. The credentialsCache is optional. | ||
Kerberos.prototype.authGSSClientInit = function(uri, flags, credentialsCache, callback) { | ||
if (typeof(credentialsCache) == 'function') { | ||
callback = credentialsCache; | ||
credentialsCache = ''; | ||
// GSS Flags | ||
const GSS_C_DELEG_FLAG = 1; | ||
const GSS_C_MUTUAL_FLAG = 2; | ||
const GSS_C_REPLAY_FLAG = 4; | ||
const GSS_C_SEQUENCE_FLAG = 8; | ||
const GSS_C_CONF_FLAG = 16; | ||
const GSS_C_INTEG_FLAG = 32; | ||
const GSS_C_ANON_FLAG = 64; | ||
const GSS_C_PROT_READY_FLAG = 128; | ||
const GSS_C_TRANS_FLAG = 256; | ||
// GSS_OID | ||
const GSS_C_NO_OID = 0; | ||
const GSS_MECH_OID_KRB5 = 9; | ||
const GSS_MECH_OID_SPNEGO = 6; | ||
function validateParameter(parameter, spec) { | ||
if (parameter == null) { | ||
throw new TypeError(`Required parameter \`${spec.name}\` missing`); | ||
} | ||
if (credentialsCache === undefined) { | ||
credentialsCache = ''; | ||
if (spec.type && typeof parameter !== spec.type) { | ||
throw new TypeError( | ||
`Invalid type for parameter \`${spec.name}\`, expected \`${ | ||
spec.type | ||
}\` but found \`${typeof parameter}\`` | ||
); | ||
} | ||
return this._native_kerberos.authGSSClientInit(uri, flags, credentialsCache, callback); | ||
} | ||
// This will obtain credentials using a credentials cache. To override the default | ||
// location (posible /tmp/krb5cc_nnnnnn, where nnnn is your numeric uid) use | ||
// the environment variable KRB5CNAME. | ||
// The credentials (suitable for using in an 'Authenticate: ' header, when prefixed | ||
// with 'Negotiate ') will be available as context.response inside the callback | ||
// if no error is indicated. | ||
// callback takes one argument, an error string if defined | ||
Kerberos.prototype.authGSSClientStep = function(context, challenge, callback) { | ||
if(typeof challenge == 'function') { | ||
callback = challenge; | ||
challenge = ''; | ||
} | ||
/** | ||
* Monkey-patches an existing method to support parameter validation, as well | ||
* as adding support for returning Promises if callbacks are not provided. | ||
* | ||
* @private | ||
* @param {function} fn the function to override | ||
* @param {Array<Object>} paramDefs the definitions of each parameter to the function | ||
*/ | ||
function defineOperation(fn, paramDefs) { | ||
return function() { | ||
const args = Array.prototype.slice.call(arguments); | ||
const params = []; | ||
for (let i = 0; i < paramDefs.length; ++i) { | ||
const def = paramDefs[i]; | ||
let arg = args[i]; | ||
return this._native_kerberos.authGSSClientStep(context, challenge, callback); | ||
} | ||
if (def.default && arg == null) arg = def.default; | ||
if (def.type === 'object' && def.default != null) { | ||
arg = Object.assign({}, def.default, arg); | ||
} | ||
Kerberos.prototype.authGSSClientUnwrap = function(context, challenge, callback) { | ||
if(typeof challenge == 'function') { | ||
callback = challenge; | ||
challenge = ''; | ||
} | ||
// special case to allow `options` to be optional | ||
if (def.name === 'options' && (typeof arg === 'function' || arg == null)) { | ||
arg = {}; | ||
} | ||
return this._native_kerberos.authGSSClientUnwrap(context, challenge, callback); | ||
} | ||
validateParameter(arg, paramDefs[i]); | ||
params.push(arg); | ||
} | ||
Kerberos.prototype.authGSSClientWrap = function(context, challenge, user_name, callback) { | ||
if(typeof user_name == 'function') { | ||
callback = user_name; | ||
user_name = ''; | ||
} | ||
const callback = arguments[arguments.length - 1]; | ||
if (typeof callback !== 'function') { | ||
return new Promise((resolve, reject) => { | ||
params.push((err, response) => { | ||
if (err) return reject(err); | ||
resolve(response); | ||
}); | ||
return this._native_kerberos.authGSSClientWrap(context, challenge, user_name, callback); | ||
} | ||
fn.apply(this, params); | ||
}); | ||
} | ||
// free memory used by a context created using authGSSClientInit. | ||
// callback takes one argument, an error string if defined. | ||
Kerberos.prototype.authGSSClientClean = function(context, callback) { | ||
return this._native_kerberos.authGSSClientClean(context, callback); | ||
params.push(callback); | ||
fn.apply(this, params); | ||
}; | ||
} | ||
// The server will obtain credentials using a keytab. To override the | ||
// default location (probably /etc/krb5.keytab) set the KRB5_KTNAME | ||
// environment variable. | ||
// The service name should be in the form service, or service@host.name | ||
// e.g. for HTTP, use "HTTP" or "HTTP@my.host.name". See gss_import_name | ||
// for GSS_C_NT_HOSTBASED_SERVICE. | ||
// | ||
// a boolean turns on "constrained_delegation". this enables acquisition of S4U2Proxy | ||
// credentials which will be stored in a credentials cache during the authGSSServerStep | ||
// method. this parameter is optional. The credentials will be stored in | ||
// a new cache, the location of which will be made available as the "delegatedCredentialsCache" | ||
// property on the returned context AFTER the authGSSServerStep stage. | ||
// | ||
// when "constrained_delegation" is enabled, a username can (optionally) be provided and | ||
// S4U2Self protocol transition will be initiated. In this case, we will not | ||
// require any "auth" data during the authGSSServerStep. This parameter is optional | ||
// but constrained_delegation MUST be enabled for this to work. When S4U2Self is | ||
// used, the username will be assumed to have been already authenticated, and no | ||
// actual authentication will be performed. This is basically a way to "bootstrap" | ||
// kerberos credentials (which can then be delegated with S4U2Proxy) for a user | ||
// authenticated externally. | ||
// | ||
// callback takes two arguments, an error string if defined and a new context | ||
// | ||
Kerberos.prototype.authGSSServerInit = function(service, constrained_delegation, username, callback) { | ||
if(typeof(constrained_delegation) === 'function') { | ||
callback = constrained_delegation; | ||
constrained_delegation = false; | ||
username = null; | ||
} | ||
/** | ||
* @class KerberosClient | ||
*/ | ||
if (typeof(constrained_delegation) === 'string') { | ||
throw new Error("S4U2Self protocol transation is not possible without enabling constrained delegation"); | ||
} | ||
/** | ||
* Processes a single kerberos client-side step using the supplied server challenge. | ||
* | ||
* @kind function | ||
* @memberof KerberosClient | ||
* @param {string} challenge A string containing the base64-encoded server data (which may be empty for the first step) | ||
* @param {function} [callback] | ||
* @return {Promise} returns Promise if no callback passed | ||
*/ | ||
KerberosClient.prototype.step = defineOperation(KerberosClient.prototype.step, [ | ||
{ name: 'challenge', type: 'string' } | ||
]); | ||
if (typeof(username) === 'function') { | ||
callback = username; | ||
username = null; | ||
} | ||
/** | ||
* Perform the client side kerberos wrap step. | ||
* | ||
* @kind function | ||
* @memberof KerberosClient | ||
* @param {string} challenge The response returned after calling `unwrap` | ||
* @param {object} [options] Optional settings | ||
* @param {string} [options.user] The user to authorize | ||
* @param {function} [callback] | ||
* @return {Promise} returns Promise if no callback passed | ||
*/ | ||
KerberosClient.prototype.wrap = defineOperation(KerberosClient.prototype.wrap, [ | ||
{ name: 'challenge', type: 'string' }, | ||
{ name: 'options', type: 'object' } | ||
]); | ||
constrained_delegation = !!constrained_delegation; | ||
return this._native_kerberos.authGSSServerInit(service, constrained_delegation, username, callback); | ||
}; | ||
/** | ||
* Perform the client side kerberos unwrap step | ||
* | ||
* @kind function | ||
* @memberof KerberosClient | ||
* @param {string} challenge A string containing the base64-encoded server data | ||
* @param {function} [callback] | ||
* @return {Promise} returns Promise if no callback passed | ||
*/ | ||
KerberosClient.prototype.unwrap = defineOperation(KerberosClient.prototype.unwrap, [ | ||
{ name: 'challenge', type: 'string' } | ||
]); | ||
//callback takes one argument, an error string if defined. | ||
Kerberos.prototype.authGSSServerClean = function(context, callback) { | ||
return this._native_kerberos.authGSSServerClean(context, callback); | ||
}; | ||
/** | ||
* @class KerberosServer | ||
*/ | ||
// authData should be the base64 encoded authentication data obtained | ||
// from client, e.g., in the Authorization header (without the leading | ||
// "Negotiate " string) during SPNEGO authentication. The authenticated user | ||
// is available in context.username after successful authentication. | ||
// callback takes one argument, an error string if defined. | ||
// | ||
// Note: when S4U2Self protocol transition was requested in the authGSSServerInit | ||
// no actual authentication will be performed and authData will be ignored. | ||
// | ||
Kerberos.prototype.authGSSServerStep = function(context, authData, callback) { | ||
return this._native_kerberos.authGSSServerStep(context, authData, callback); | ||
}; | ||
/** | ||
* Processes a single kerberos server-side step using the supplied client data. | ||
* | ||
* @kind function | ||
* @memberof KerberosServer | ||
* @param {string} challenge A string containing the base64-encoded client data | ||
* @param {function} callback | ||
* @return {Promise} returns Promise if no callback passed | ||
*/ | ||
KerberosServer.prototype.step = defineOperation(KerberosServer.prototype.step, [ | ||
{ name: 'challenge', type: 'string' } | ||
]); | ||
// authenticate the username and password against the KDC, and verify the KDC using a local | ||
// service key stored in the keytab. See above for details on providing the keytab. | ||
// The service should be the service principal name for a key available in the local keytab, | ||
// e.g. HTTP/somehost.example.com. If service is an empty tring, KDC verification will | ||
// be skipped. DON'T DO THIS - it's a possible security vulnerability if an attacker | ||
// can spoof your KDC (see: https://github.com/qesuto/node-krb5/issues/13) | ||
// callback receives error and boolean | ||
Kerberos.prototype.authUserKrb5Password = function(username, password, service, callback) { | ||
return this._native_kerberos.authUserKrb5Password(username, password, service, callback); | ||
}; | ||
/** | ||
* This function provides a simple way to verify that a user name and password | ||
* match those normally used for Kerberos authentication. | ||
* It does this by checking that the supplied user name and password can be | ||
* used to get a ticket for the supplied service. | ||
* If the user name does not contain a realm, then the default realm supplied | ||
* is used. | ||
* | ||
* For this to work properly the Kerberos must be configured properly on this | ||
* machine. | ||
* That will likely mean ensuring that the edu.mit.Kerberos preference file | ||
* has the correct realms and KDCs listed. | ||
* | ||
* IMPORTANT: This method is vulnerable to KDC spoofing attacks and it should | ||
* only be used for testing. Do not use this in any production system - your | ||
* security could be compromised if you do. | ||
* | ||
* @kind function | ||
* @param {string} username The Kerberos user name. If no realm is supplied, then the `defaultRealm` will be used. | ||
* @param {string} password The password for the user. | ||
* @param {string} service The Kerberos service to check access for. | ||
* @param {string} [defaultRealm] The default realm to use if one is not supplied in the user argument. | ||
* @param {function} [callback] | ||
* @return {Promise} returns Promise if no callback passed | ||
*/ | ||
const checkPassword = defineOperation(kerberos.checkPassword, [ | ||
{ name: 'username', type: 'string' }, | ||
{ name: 'password', type: 'string' }, | ||
{ name: 'service', type: 'string' }, | ||
{ name: 'defaultRealm', type: 'string' } | ||
]); | ||
Kerberos.prototype.acquireAlternateCredentials = function(user_name, password, domain) { | ||
return this._native_kerberos.acquireAlternateCredentials(user_name, password, domain); | ||
} | ||
/** | ||
* This function returns the service principal for the server given a service type and hostname. | ||
* | ||
* Details are looked up via the `/etc/keytab` file. | ||
* | ||
* @kind function | ||
* @param {string} service The Kerberos service type for the server. | ||
* @param {string} hostname The hostname of the server. | ||
* @param {function} [callback] | ||
* @return {Promise} returns Promise if no callback passed | ||
*/ | ||
const principalDetails = defineOperation(kerberos.principalDetails, [ | ||
{ name: 'service', type: 'string' }, | ||
{ name: 'hostname', type: 'string' } | ||
]); | ||
Kerberos.prototype.prepareOutboundPackage = function(principal, inputdata) { | ||
return this._native_kerberos.prepareOutboundPackage(principal, inputdata); | ||
} | ||
/** | ||
* Initializes a context for client-side authentication with the given service principal. | ||
* | ||
* @kind function | ||
* @param {string} service A string containing the service principal in the form 'type@fqdn' (e.g. 'imap@mail.apple.com'). | ||
* @param {object} [options] Optional settings | ||
* @param {string} [options.principal] Optional string containing the client principal in the form 'user@realm' (e.g. 'jdoe@example.com'). | ||
* @param {number} [options.gssFlags] Optional integer used to set GSS flags. (e.g. GSS_C_DELEG_FLAG|GSS_C_MUTUAL_FLAG|GSS_C_SEQUENCE_FLAG will allow for forwarding credentials to the remote host) | ||
* @param {number} [options.mechOID] Optional GSS mech OID. Defaults to None (GSS_C_NO_OID). Other possible values are `GSS_MECH_OID_KRB5`, `GSS_MECH_OID_SPNEGO`. | ||
* @param {function} [callback] | ||
* @return {Promise} returns Promise if no callback passed | ||
*/ | ||
const initializeClient = defineOperation(kerberos.initializeClient, [ | ||
{ name: 'service', type: 'string' }, | ||
{ name: 'options', type: 'object', default: { mechOID: GSS_C_NO_OID } } | ||
]); | ||
Kerberos.prototype.decryptMessage = function(challenge) { | ||
return this._native_kerberos.decryptMessage(challenge); | ||
} | ||
/** | ||
* Initializes a context for server-side authentication with the given service principal. | ||
* | ||
* @kind function | ||
* @param {string} service A string containing the service principal in the form 'type@fqdn' (e.g. 'imap@mail.apple.com'). | ||
* @param {function} [callback] | ||
* @return {Promise} returns Promise if no callback passed | ||
*/ | ||
const initializeServer = defineOperation(kerberos.initializeServer, [ | ||
{ name: 'service', type: 'string' } | ||
]); | ||
Kerberos.prototype.encryptMessage = function(challenge) { | ||
return this._native_kerberos.encryptMessage(challenge); | ||
} | ||
module.exports = { | ||
initializeClient, | ||
initializeServer, | ||
principalDetails, | ||
checkPassword, | ||
Kerberos.prototype.queryContextAttribute = function(attribute) { | ||
if(typeof attribute != 'number' && attribute != 0x00) throw new Error("Attribute not supported"); | ||
return this._native_kerberos.queryContextAttribute(attribute); | ||
} | ||
// gss flags | ||
GSS_C_DELEG_FLAG, | ||
GSS_C_MUTUAL_FLAG, | ||
GSS_C_REPLAY_FLAG, | ||
GSS_C_SEQUENCE_FLAG, | ||
GSS_C_CONF_FLAG, | ||
GSS_C_INTEG_FLAG, | ||
GSS_C_ANON_FLAG, | ||
GSS_C_PROT_READY_FLAG, | ||
GSS_C_TRANS_FLAG, | ||
GSS_C_NO_OID, | ||
// Some useful result codes | ||
Kerberos.AUTH_GSS_CONTINUE = 0; | ||
Kerberos.AUTH_GSS_COMPLETE = 1; | ||
// Some useful gss flags | ||
Kerberos.GSS_C_DELEG_FLAG = 1; | ||
Kerberos.GSS_C_MUTUAL_FLAG = 2; | ||
Kerberos.GSS_C_REPLAY_FLAG = 4; | ||
Kerberos.GSS_C_SEQUENCE_FLAG = 8; | ||
Kerberos.GSS_C_CONF_FLAG = 16; | ||
Kerberos.GSS_C_INTEG_FLAG = 32; | ||
Kerberos.GSS_C_ANON_FLAG = 64; | ||
Kerberos.GSS_C_PROT_READY_FLAG = 128; | ||
Kerberos.GSS_C_TRANS_FLAG = 256; | ||
// Export Kerberos class | ||
exports.Kerberos = Kerberos; | ||
// If we have SSPI (windows) | ||
if(kerberos.SecurityCredentials) { | ||
// Put all SSPI classes in it's own namespace | ||
exports.SSIP = { | ||
SecurityCredentials: require('./win32/wrappers/security_credentials').SecurityCredentials | ||
, SecurityContext: require('./win32/wrappers/security_context').SecurityContext | ||
, SecurityBuffer: require('./win32/wrappers/security_buffer').SecurityBuffer | ||
, SecurityBufferDescriptor: require('./win32/wrappers/security_buffer_descriptor').SecurityBufferDescriptor | ||
} | ||
} | ||
// mechanism OIDs | ||
GSS_MECH_OID_KRB5, | ||
GSS_MECH_OID_SPNEGO | ||
}; |
{ | ||
"name": "kerberos", | ||
"version": "0.0.24", | ||
"version": "1.0.0", | ||
"description": "Kerberos library for Node.js", | ||
@@ -16,10 +16,32 @@ "main": "index.js", | ||
"dependencies": { | ||
"nan": "~2.10.0" | ||
"bindings": "^1.3.0", | ||
"nan": "^2.10.0", | ||
"prebuild-install": "^5.0.0" | ||
}, | ||
"devDependencies": { | ||
"nodeunit": "latest" | ||
"chai": "^4.1.2", | ||
"chai-string": "^1.4.0", | ||
"clang-format": "^1.2.4", | ||
"dmd-clear": "^0.1.2", | ||
"eslint": "^5.3.0", | ||
"eslint-plugin-prettier": "^2.6.2", | ||
"jsdoc-to-markdown": "^4.0.1", | ||
"mocha": "^5.2.0", | ||
"mongodb": "^3.1.3", | ||
"prebuild": "^7.6.2", | ||
"prebuild-ci": "^2.2.3", | ||
"prettier": "^1.14.2", | ||
"request": "^2.88.0", | ||
"segfault-handler": "^1.0.1" | ||
}, | ||
"scripts": { | ||
"install": "(node-gyp rebuild) || (exit 0)", | ||
"test": "nodeunit ./test" | ||
"install": "prebuild-install || node-gyp rebuild", | ||
"format-cxx": "git-clang-format", | ||
"format-js": "prettier --print-width 100 --tab-width 2 --single-quote --write index.js 'test/**/*.js' 'lib/**/*.js'", | ||
"lint": "eslint index.js lib test", | ||
"precommit": "check-clang-format", | ||
"test": "mocha ./test && prebuild-ci", | ||
"docs": "jsdoc2md --template etc/README.hbs --plugin dmd-clear --files lib/kerberos.js > README.md", | ||
"rebuild": "prebuild --compile", | ||
"prebuild": "prebuild --all --strip --verbose" | ||
}, | ||
@@ -26,0 +48,0 @@ "author": "Christian Amor Kvalheim", |
243
README.md
Kerberos | ||
======== | ||
[![Build Status](https://travis-ci.org/mongodb-js/kerberos.svg?branch=master)](https://travis-ci.org/mongodb-js/kerberos) | ||
The `kerberos` package is a C++ extension that requires a build environment to be installed on your system. You must be able to build node.js itself to be able to compile and install the `kerberos` module. Furthermore the `kerberos` module requires the MIT Kerberos package to correctly compile on UNIX operating systems. Consult your UNIX operation system package manager what libraries to install. | ||
The `kerberos` package is a C++ extension for Node.js that provides cross-platform support for kerberos authentication using GSSAPI on linux/osx, and SSPI on windows. Much of the code in this module is adapted from [ccs-kerberos](https://github.com/apple/ccs-pykerberos) and [winkerberos](https://github.com/mongodb-labs/winkerberos). | ||
{{% note class="important" %}} | ||
Windows already contains the SSPI API used for Kerberos authentication. However you will need to install a full compiler tool chain using visual studio C++ to correctly install the kerberos extension. | ||
{{% /note %}} | ||
### Requirements | ||
### Diagnosing on UNIX | ||
**Linux** | ||
- `python` v2.7 | ||
- `make` | ||
- A proper C/C++ compiler toolchain, like [GCC](https://gcc.gnu.org/) | ||
- Distribution-specific kerberos packages (e.g. `krb5-dev` on Ubuntu) | ||
If you don’t have the build essentials it won’t build. In the case of linux you will need gcc and g++, node.js with all the headers and python. The easiest way to figure out what’s missing is by trying to build the kerberos project. You can do this by performing the following steps. | ||
**macOS** | ||
- `Xcode Command Line Tools`: Can be installed with `xcode-select --install` | ||
- Distribution-specific kerberos packages (e.g. `krb5` on Homebrew) | ||
``` | ||
git clone https://github.com/christkv/kerberos.git | ||
cd kerberos | ||
npm install | ||
``` | ||
**Windows** | ||
- **Option 1:** Install all the required tools and configurations using Microsoft's [windows-build-tools](https://github.com/felixrieseberg/windows-build-tools) by running `npm install -g windows-build-tools` from an elevated PowerShell (run as Administrator). | ||
- **Option 2:** Install dependencies and configuration manually | ||
1. Visual C++ Build Environment: | ||
* **Option 1:** Install [Visual C++ Build Tools](http://go.microsoft.com/fwlink/?LinkId=691126) using the *Default Install* option. | ||
* **Option 2:** Install [Visual Studio 2015](https://www.visualstudio.com/products/visual-studio-community-vs) (or modify an existing installation) and select *Common Tools for Visual C++* during setup. | ||
If all the steps complete you have the right toolchain installed. If you get node-gyp not found you need to install it globally by doing. | ||
> :bulb: [Windows Vista / 7 only] requires [.NET Framework 4.5.1](http://www.microsoft.com/en-us/download/details.aspx?id=40773) | ||
``` | ||
npm install -g node-gyp | ||
``` | ||
2. Install [Python 2.7](https://www.python.org/downloads/) or [Miniconda 2.7](http://conda.pydata.org/miniconda.html) (`v3.x.x` is not supported), and run `npm config set python python2.7` | ||
3. Launch cmd, `npm config set msvs_version 2015` | ||
If correctly compiles and runs the tests you are golden. We can now try to install the kerberos module by performing the following command. | ||
### Installation | ||
Now you can install `kerberos` with the following: | ||
```bash | ||
npm install kerberos | ||
``` | ||
cd yourproject | ||
npm install kerberos --save | ||
``` | ||
If it still fails the next step is to examine the npm log. Rerun the command but in this case in verbose mode. | ||
### Testing | ||
Run the test suite using: | ||
```bash | ||
npm test | ||
``` | ||
npm --loglevel verbose install kerberos | ||
``` | ||
This will print out all the steps npm is performing while trying to install the module. | ||
NOTE: The test suite requires an active kerberos deployment, see `test/scripts/travis.sh` to better understand these requirements. | ||
### Diagnosing on Windows | ||
# Documentation | ||
A known compiler tool chain known to work for compiling `kerberos` on windows is the following. | ||
## Classes | ||
* Visual Studio c++ 2010 (do not use higher versions) | ||
* Windows 7 64bit SDK | ||
* Python 2.7 or higher | ||
<dl> | ||
<dt><a href="#KerberosClient">KerberosClient</a></dt> | ||
<dd></dd> | ||
<dt><a href="#KerberosServer">KerberosServer</a></dt> | ||
<dd></dd> | ||
</dl> | ||
Open visual studio command prompt. Ensure node.exe is in your path and install node-gyp. | ||
## Functions | ||
``` | ||
npm install -g node-gyp | ||
``` | ||
<dl> | ||
<dt><a href="#checkPassword">checkPassword(username, password, service, [defaultRealm], [callback])</a> ⇒ <code>Promise</code></dt> | ||
<dd><p>This function provides a simple way to verify that a user name and password | ||
match those normally used for Kerberos authentication. | ||
It does this by checking that the supplied user name and password can be | ||
used to get a ticket for the supplied service. | ||
If the user name does not contain a realm, then the default realm supplied | ||
is used.</p> | ||
<p>For this to work properly the Kerberos must be configured properly on this | ||
machine. | ||
That will likely mean ensuring that the edu.mit.Kerberos preference file | ||
has the correct realms and KDCs listed.</p> | ||
<p>IMPORTANT: This method is vulnerable to KDC spoofing attacks and it should | ||
only be used for testing. Do not use this in any production system - your | ||
security could be compromised if you do.</p> | ||
</dd> | ||
<dt><a href="#principalDetails">principalDetails(service, hostname, [callback])</a> ⇒ <code>Promise</code></dt> | ||
<dd><p>This function returns the service principal for the server given a service type and hostname.</p> | ||
<p>Details are looked up via the <code>/etc/keytab</code> file.</p> | ||
</dd> | ||
<dt><a href="#initializeClient">initializeClient(service, [options], [callback])</a> ⇒ <code>Promise</code></dt> | ||
<dd><p>Initializes a context for client-side authentication with the given service principal.</p> | ||
</dd> | ||
<dt><a href="#initializeServer">initializeServer(service, [callback])</a> ⇒ <code>Promise</code></dt> | ||
<dd><p>Initializes a context for server-side authentication with the given service principal.</p> | ||
</dd> | ||
</dl> | ||
Next you will have to build the project manually to test it. Use any tool you use with git and grab the repo. | ||
<a name="KerberosClient"></a> | ||
``` | ||
git clone https://github.com/christkv/kerberos.git | ||
cd kerberos | ||
npm install | ||
node-gyp rebuild | ||
``` | ||
## KerberosClient | ||
This should rebuild the driver successfully if you have everything set up correctly. | ||
* [KerberosClient](#KerberosClient) | ||
### Other possible issues | ||
* [.step(challenge, [callback])](#KerberosClient+step) | ||
Your python installation might be hosed making gyp break. I always recommend that you test your deployment environment first by trying to build node itself on the server in question as this should unearth any issues with broken packages (and there are a lot of broken packages out there). | ||
* [.wrap(challenge, [options], [callback])](#KerberosClient+wrap) | ||
Another thing is to ensure your user has write permission to wherever the node modules are being installed. | ||
* [.unwrap(challenge, [callback])](#KerberosClient+unwrap) | ||
<a name="KerberosClient+step"></a> | ||
### *kerberosClient*.step(challenge, [callback]) | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| challenge | <code>string</code> | A string containing the base64-encoded server data (which may be empty for the first step) | | ||
| [callback] | <code>function</code> | | | ||
Processes a single kerberos client-side step using the supplied server challenge. | ||
**Returns**: <code>Promise</code> - returns Promise if no callback passed | ||
<a name="KerberosClient+wrap"></a> | ||
### *kerberosClient*.wrap(challenge, [options], [callback]) | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| challenge | <code>string</code> | The response returned after calling `unwrap` | | ||
| [options] | <code>object</code> | Optional settings | | ||
| [options.user] | <code>string</code> | The user to authorize | | ||
| [callback] | <code>function</code> | | | ||
Perform the client side kerberos wrap step. | ||
**Returns**: <code>Promise</code> - returns Promise if no callback passed | ||
<a name="KerberosClient+unwrap"></a> | ||
### *kerberosClient*.unwrap(challenge, [callback]) | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| challenge | <code>string</code> | A string containing the base64-encoded server data | | ||
| [callback] | <code>function</code> | | | ||
Perform the client side kerberos unwrap step | ||
**Returns**: <code>Promise</code> - returns Promise if no callback passed | ||
<a name="KerberosServer"></a> | ||
## KerberosServer | ||
<a name="KerberosServer+step"></a> | ||
### *kerberosServer*.step(challenge, callback) | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| challenge | <code>string</code> | A string containing the base64-encoded client data | | ||
| callback | <code>function</code> | | | ||
Processes a single kerberos server-side step using the supplied client data. | ||
**Returns**: <code>Promise</code> - returns Promise if no callback passed | ||
<a name="checkPassword"></a> | ||
## checkPassword(username, password, service, [defaultRealm], [callback]) | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| username | <code>string</code> | The Kerberos user name. If no realm is supplied, then the `defaultRealm` will be used. | | ||
| password | <code>string</code> | The password for the user. | | ||
| service | <code>string</code> | The Kerberos service to check access for. | | ||
| [defaultRealm] | <code>string</code> | The default realm to use if one is not supplied in the user argument. | | ||
| [callback] | <code>function</code> | | | ||
This function provides a simple way to verify that a user name and password | ||
match those normally used for Kerberos authentication. | ||
It does this by checking that the supplied user name and password can be | ||
used to get a ticket for the supplied service. | ||
If the user name does not contain a realm, then the default realm supplied | ||
is used. | ||
For this to work properly the Kerberos must be configured properly on this | ||
machine. | ||
That will likely mean ensuring that the edu.mit.Kerberos preference file | ||
has the correct realms and KDCs listed. | ||
IMPORTANT: This method is vulnerable to KDC spoofing attacks and it should | ||
only be used for testing. Do not use this in any production system - your | ||
security could be compromised if you do. | ||
**Returns**: <code>Promise</code> - returns Promise if no callback passed | ||
<a name="principalDetails"></a> | ||
## principalDetails(service, hostname, [callback]) | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| service | <code>string</code> | The Kerberos service type for the server. | | ||
| hostname | <code>string</code> | The hostname of the server. | | ||
| [callback] | <code>function</code> | | | ||
This function returns the service principal for the server given a service type and hostname. | ||
Details are looked up via the `/etc/keytab` file. | ||
**Returns**: <code>Promise</code> - returns Promise if no callback passed | ||
<a name="initializeClient"></a> | ||
## initializeClient(service, [options], [callback]) | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| service | <code>string</code> | A string containing the service principal in the form 'type@fqdn' (e.g. 'imap@mail.apple.com'). | | ||
| [options] | <code>object</code> | Optional settings | | ||
| [options.principal] | <code>string</code> | Optional string containing the client principal in the form 'user@realm' (e.g. 'jdoe@example.com'). | | ||
| [options.gssFlags] | <code>number</code> | Optional integer used to set GSS flags. (e.g. GSS_C_DELEG_FLAG|GSS_C_MUTUAL_FLAG|GSS_C_SEQUENCE_FLAG will allow for forwarding credentials to the remote host) | | ||
| [options.mechOID] | <code>number</code> | Optional GSS mech OID. Defaults to None (GSS_C_NO_OID). Other possible values are `GSS_MECH_OID_KRB5`, `GSS_MECH_OID_SPNEGO`. | | ||
| [callback] | <code>function</code> | | | ||
Initializes a context for client-side authentication with the given service principal. | ||
**Returns**: <code>Promise</code> - returns Promise if no callback passed | ||
<a name="initializeServer"></a> | ||
## initializeServer(service, [callback]) | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| service | <code>string</code> | A string containing the service principal in the form 'type@fqdn' (e.g. 'imap@mail.apple.com'). | | ||
| [callback] | <code>function</code> | | | ||
Initializes a context for server-side authentication with the given service principal. | ||
**Returns**: <code>Promise</code> - returns Promise if no callback passed |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 5 instances in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
631550
42
1
228
3
14
343
8
+ Addedbindings@^1.3.0
+ Addedprebuild-install@^5.0.0
+ Addedansi-regex@2.1.1(transitive)
+ Addedaproba@1.2.0(transitive)
+ Addedare-we-there-yet@1.1.7(transitive)
+ Addedbase64-js@1.5.1(transitive)
+ Addedbindings@1.5.0(transitive)
+ Addedbl@4.1.0(transitive)
+ Addedbuffer@5.7.1(transitive)
+ Addedchownr@1.1.4(transitive)
+ Addedcode-point-at@1.1.0(transitive)
+ Addedconsole-control-strings@1.1.0(transitive)
+ Addedcore-util-is@1.0.3(transitive)
+ Addeddecompress-response@4.2.1(transitive)
+ Addeddeep-extend@0.6.0(transitive)
+ Addeddelegates@1.0.0(transitive)
+ Addeddetect-libc@1.0.3(transitive)
+ Addedend-of-stream@1.4.4(transitive)
+ Addedexpand-template@2.0.3(transitive)
+ Addedfile-uri-to-path@1.0.0(transitive)
+ Addedfs-constants@1.0.0(transitive)
+ Addedgauge@2.7.4(transitive)
+ Addedgithub-from-package@0.0.0(transitive)
+ Addedhas-unicode@2.0.1(transitive)
+ Addedieee754@1.2.1(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedini@1.3.8(transitive)
+ Addedis-fullwidth-code-point@1.0.0(transitive)
+ Addedisarray@1.0.0(transitive)
+ Addedmimic-response@2.1.0(transitive)
+ Addedminimist@1.2.8(transitive)
+ Addedmkdirp-classic@0.5.3(transitive)
+ Addednan@2.22.0(transitive)
+ Addednapi-build-utils@1.0.2(transitive)
+ Addednode-abi@2.30.1(transitive)
+ Addednoop-logger@0.1.1(transitive)
+ Addednpmlog@4.1.2(transitive)
+ Addednumber-is-nan@1.0.1(transitive)
+ Addedobject-assign@4.1.1(transitive)
+ Addedonce@1.4.0(transitive)
+ Addedprebuild-install@5.3.6(transitive)
+ Addedprocess-nextick-args@2.0.1(transitive)
+ Addedpump@3.0.2(transitive)
+ Addedrc@1.2.8(transitive)
+ Addedreadable-stream@2.3.83.6.2(transitive)
+ Addedsafe-buffer@5.1.2(transitive)
+ Addedsemver@5.7.2(transitive)
+ Addedset-blocking@2.0.0(transitive)
+ Addedsignal-exit@3.0.7(transitive)
+ Addedsimple-concat@1.0.1(transitive)
+ Addedsimple-get@3.1.1(transitive)
+ Addedstring-width@1.0.2(transitive)
+ Addedstring_decoder@1.1.1(transitive)
+ Addedstrip-ansi@3.0.1(transitive)
+ Addedstrip-json-comments@2.0.1(transitive)
+ Addedtar-fs@2.1.1(transitive)
+ Addedtar-stream@2.2.0(transitive)
+ Addedtunnel-agent@0.6.0(transitive)
+ Addedutil-deprecate@1.0.2(transitive)
+ Addedwhich-pm-runs@1.1.0(transitive)
+ Addedwide-align@1.1.5(transitive)
+ Addedwrappy@1.0.2(transitive)
- Removednan@2.10.0(transitive)
Updatednan@^2.10.0