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

@passport-next/passport

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@passport-next/passport - npm Package Compare versions

Comparing version 1.0.1 to 2.0.0

.eslintignore

8

CHANGELOG.md
This changelog follows Semantic Versioning https://semver.org/
# 2.0.0
### Major
* Added eslint configuration, and fixed a pletora of lint errors @idurotola
# 1.0.1 (2018-08-11)
### Patch
* Fixed premature redirect when using express sessions @zypA13510 @idurotola

@@ -6,0 +14,0 @@

148

lib/authenticator.js
/**
* Module dependencies.
*/
var SessionStrategy = require('./strategies/session')
, SessionManager = require('./sessionmanager');
/* eslint-disable no-underscore-dangle, camelcase, no-proto, no-shadow */
const SessionStrategy = require('./strategies/session');
const SessionManager = require('./sessionmanager');
const connect = require('./framework/connect');
/**

@@ -21,3 +25,3 @@ * `Authenticator` constructor.

this._userProperty = 'user';
this.init();

@@ -31,4 +35,4 @@ }

*/
Authenticator.prototype.init = function() {
this.framework(require('./framework/connect')());
Authenticator.prototype.init = function init() {
this.framework(connect());
this.use(new SessionStrategy(this.deserializeUser.bind(this)));

@@ -53,3 +57,3 @@ this._sm = new SessionManager({ key: this._key }, this.serializeUser.bind(this));

*/
Authenticator.prototype.use = function(name, strategy) {
Authenticator.prototype.use = function use(name, strategy) {
if (!strategy) {

@@ -60,3 +64,3 @@ strategy = name;

if (!name) { throw new Error('Authentication strategies must have a name'); }
this._strategies[name] = strategy;

@@ -85,3 +89,3 @@ return this;

*/
Authenticator.prototype.unuse = function(name) {
Authenticator.prototype.unuse = function unuse(name) {
delete this._strategies[name];

@@ -110,3 +114,3 @@ return this;

*/
Authenticator.prototype.framework = function(fw) {
Authenticator.prototype.framework = function framework(fw) {
this._framework = fw;

@@ -135,6 +139,6 @@ return this;

*/
Authenticator.prototype.initialize = function(options) {
Authenticator.prototype.initialize = function initialize(options) {
options = options || {};
this._userProperty = options.userProperty || 'user';
return this._framework.initialize(this, options);

@@ -149,3 +153,6 @@ };

*
* passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' })(req, res);
* passport.authenticate('local', {
* successRedirect: '/',
* failureRedirect: '/login'
* })(req, res);
*

@@ -172,3 +179,3 @@ * passport.authenticate('local', function(err, user) {

*/
Authenticator.prototype.authenticate = function(strategy, options, callback) {
Authenticator.prototype.authenticate = function authenticate(strategy, options, callback) {
return this._framework.authenticate(this, strategy, options, callback);

@@ -197,7 +204,7 @@ };

*/
Authenticator.prototype.authorize = function(strategy, options, callback) {
Authenticator.prototype.authorize = function authorize(strategy, options, callback) {
options = options || {};
options.assignProperty = 'account';
var fn = this._framework.authorize || this._framework.authenticate;
const fn = this._framework.authorize || this._framework.authenticate;
return fn(this, strategy, options, callback);

@@ -235,3 +242,3 @@ };

*/
Authenticator.prototype.session = function(options) {
Authenticator.prototype.session = function session(options) {
return this.authenticate('session', options);

@@ -259,10 +266,12 @@ };

*/
Authenticator.prototype.serializeUser = function(fn, req, done) {
// eslint-disable-next-line consistent-return
Authenticator.prototype.serializeUser = function serializeUser(fn, req, done) {
if (typeof fn === 'function') {
return this._serializers.push(fn);
}
// private implementation that traverses the chain of serializers, attempting
// to serialize a user
var user = fn;
const user = fn;

@@ -274,7 +283,8 @@ // For backwards compatibility

}
var stack = this._serializers;
const stack = this._serializers;
// eslint-disable-next-line consistent-return
(function pass(i, err, obj) {
// serializers use 'pass' as an error to skip processing
if ('pass' === err) {
if (err === 'pass') {
err = undefined;

@@ -284,16 +294,16 @@ }

if (err || obj || obj === 0) { return done(err, obj); }
var layer = stack[i];
const layer = stack[i];
if (!layer) {
return done(new Error('Failed to serialize user into session'));
}
function serialized(e, o) {
pass(i + 1, e, o);
}
try {
var arity = layer.length;
if (arity == 3) {
const arity = layer.length;
if (arity === 3) {
layer(req, user, serialized);

@@ -303,6 +313,6 @@ } else {

}
} catch(e) {
} catch (e) {
return done(e);
}
})(0);
}(0));
};

@@ -323,10 +333,12 @@

*/
Authenticator.prototype.deserializeUser = function(fn, req, done) {
// eslint-disable-next-line consistent-return
Authenticator.prototype.deserializeUser = function deserializeUser(fn, req, done) {
if (typeof fn === 'function') {
return this._deserializers.push(fn);
}
// private implementation that traverses the chain of deserializers,
// attempting to deserialize a user
var obj = fn;
const obj = fn;

@@ -338,7 +350,8 @@ // For backwards compatibility

}
var stack = this._deserializers;
const stack = this._deserializers;
// eslint-disable-next-line consistent-return
(function pass(i, err, user) {
// deserializers use 'pass' as an error to skip processing
if ('pass' === err) {
if (err === 'pass') {
err = undefined;

@@ -351,16 +364,16 @@ }

if (user === null || user === false) { return done(null, false); }
var layer = stack[i];
const layer = stack[i];
if (!layer) {
return done(new Error('Failed to deserialize user out of session'));
}
function deserialized(e, u) {
pass(i + 1, e, u);
}
try {
var arity = layer.length;
if (arity == 3) {
const arity = layer.length;
if (arity === 3) {
layer(req, obj, deserialized);

@@ -370,6 +383,6 @@ } else {

}
} catch(e) {
} catch (e) {
return done(e);
}
})(0);
}(0));
};

@@ -415,10 +428,12 @@

*/
Authenticator.prototype.transformAuthInfo = function(fn, req, done) {
// eslint-disable-next-line consistent-return
Authenticator.prototype.transformAuthInfo = function transformAuthInfo(fn, req, done) {
if (typeof fn === 'function') {
return this._infoTransformers.push(fn);
}
// private implementation that traverses the chain of transformers,
// attempting to transform auth info
var info = fn;
const info = fn;

@@ -430,7 +445,8 @@ // For backwards compatibility

}
var stack = this._infoTransformers;
const stack = this._infoTransformers;
// eslint-disable-next-line consistent-return
(function pass(i, err, tinfo) {
// transformers use 'pass' as an error to skip processing
if ('pass' === err) {
if (err === 'pass') {
err = undefined;

@@ -440,4 +456,4 @@ }

if (err || tinfo) { return done(err, tinfo); }
var layer = stack[i];
const layer = stack[i];
if (!layer) {

@@ -448,15 +464,15 @@ // if no transformers are registered (or they all pass), the default

}
function transformed(e, t) {
pass(i + 1, e, t);
}
try {
var arity = layer.length;
if (arity == 1) {
const arity = layer.length;
if (arity === 1) {
// sync
var t = layer(info);
const t = layer(info);
transformed(null, t);
} else if (arity == 3) {
} else if (arity === 3) {
layer(req, info, transformed);

@@ -466,10 +482,10 @@ } else {

}
} catch(e) {
} catch (e) {
return done(e);
}
})(0);
}(0));
};
/**
* Return strategy with given `name`.
* Return strategy with given `name`.
*

@@ -480,3 +496,3 @@ * @param {String} name

*/
Authenticator.prototype._strategy = function(name) {
Authenticator.prototype._strategy = function _strategy(name) {
return this._strategies[name];

@@ -483,0 +499,0 @@ };

@@ -7,2 +7,5 @@ /**

*/
/* eslint-disable no-proto, no-proto, no-caller, no-restricted-properties */
function AuthenticationError(message, status) {

@@ -9,0 +12,0 @@ Error.call(this);

/**
* Module dependencies.
*/
var initialize = require('../middleware/initialize')
, authenticate = require('../middleware/authenticate');
/* eslint-disable camelcase, no-proto, no-shadow */
const http = require('http');
const initialize = require('../middleware/initialize');
const authenticate = require('../middleware/authenticate');
const IncomingMessageExt = require('../http/request');
/**

@@ -18,23 +22,23 @@ * Framework support for Connect/Express.

*/
exports = module.exports = function() {
// eslint-disable-next-line no-multi-assign, func-names
exports = module.exports = function () {
// HTTP extensions.
exports.__monkeypatchNode();
return {
initialize: initialize,
authenticate: authenticate
initialize,
authenticate,
};
};
exports.__monkeypatchNode = function() {
var http = require('http');
var IncomingMessageExt = require('../http/request');
http.IncomingMessage.prototype.login =
exports.__monkeypatchNode = function __monkeypatchNode() {
http.IncomingMessage.prototype.logIn = IncomingMessageExt.logIn;
http.IncomingMessage.prototype.logout =
http.IncomingMessage.prototype.login = http.IncomingMessage.prototype.logIn;
http.IncomingMessage.prototype.logOut = IncomingMessageExt.logOut;
http.IncomingMessage.prototype.logout = http.IncomingMessage.prototype.logOut;
http.IncomingMessage.prototype.isAuthenticated = IncomingMessageExt.isAuthenticated;
http.IncomingMessage.prototype.isUnauthenticated = IncomingMessageExt.isUnauthenticated;
};
/**
* Module dependencies.
*/
//var http = require('http')
// var http = require('http')
// , req = http.IncomingMessage.prototype;
/* eslint-disable no-multi-assign, camelcase, no-proto, no-shadow */
var req = exports = module.exports = {};
const req = exports = module.exports = {};

@@ -30,5 +32,4 @@ /**

*/
req.login =
req.logIn = function(user, options, done) {
if (typeof options == 'function') {
req.logIn = function logIn(user, options, done) {
if (typeof options === 'function') {
done = options;

@@ -38,16 +39,17 @@ options = {};

options = options || {};
var property = 'user';
let property = 'user';
if (this._passport && this._passport.instance) {
property = this._passport.instance._userProperty || 'user';
}
var session = (options.session === undefined) ? true : options.session;
const session = (options.session === undefined) ? true : options.session;
this[property] = user;
if (session) {
if (!this._passport) { throw new Error('passport.initialize() middleware not in use'); }
if (typeof done != 'function') { throw new Error('req#login requires a callback function'); }
var self = this;
this._passport.instance._sm.logIn(this, user, function(err) {
if (typeof done !== 'function') { throw new Error('req#login requires a callback function'); }
const self = this;
// eslint-disable-next-line consistent-return
this._passport.instance._sm.logIn(this, user, (err) => {
if (err) { self[property] = null; return done(err); }

@@ -57,2 +59,3 @@ done();

} else {
// eslint-disable-next-line no-unused-expressions
done && done();

@@ -62,2 +65,4 @@ }

req.login = req.logIn;
/**

@@ -68,9 +73,8 @@ * Terminate an existing login session.

*/
req.logout =
req.logOut = function() {
var property = 'user';
req.logOut = function logOut() {
let property = 'user';
if (this._passport && this._passport.instance) {
property = this._passport.instance._userProperty || 'user';
}
this[property] = null;

@@ -82,2 +86,4 @@ if (this._passport) {

req.logout = req.logOut;
/**

@@ -89,9 +95,9 @@ * Test if request is authenticated.

*/
req.isAuthenticated = function() {
var property = 'user';
req.isAuthenticated = function isAuthenticated() {
let property = 'user';
if (this._passport && this._passport.instance) {
property = this._passport.instance._userProperty || 'user';
}
return (this[property]) ? true : false;
return !!(this[property]);
};

@@ -105,4 +111,4 @@

*/
req.isUnauthenticated = function() {
req.isUnauthenticated = function isUnauthenticated() {
return !this.isAuthenticated();
};
/**
* Module dependencies.
*/
var Passport = require('./authenticator')
, SessionStrategy = require('./strategies/session');
/* eslint-disable no-multi-assign */
const Passport = require('./authenticator');
const SessionStrategy = require('./strategies/session');
/**

@@ -18,4 +21,3 @@ * Export default singleton.

*/
exports.Passport =
exports.Authenticator = Passport;
exports.Passport = exports.Authenticator = Passport;
exports.Strategy = require('@passport-next/passport-strategy');

@@ -22,0 +24,0 @@

/**
* Module dependencies.
*/
var http = require('http')
, IncomingMessageExt = require('../http/request')
, AuthenticationError = require('../errors/authenticationerror');
/* eslint-disable camelcase, no-proto, no-shadow */
const http = require('http');
const IncomingMessageExt = require('../http/request');
const AuthenticationError = require('../errors/authenticationerror');
const connect = require('../framework/connect');
/**

@@ -71,3 +75,3 @@ * Authenticates requests.

module.exports = function authenticate(passport, name, options, callback) {
if (typeof options == 'function') {
if (typeof options === 'function') {
callback = options;

@@ -77,5 +81,5 @@ options = {};

options = options || {};
var multi = true;
let multi = true;
// Cast `name` to an array, allowing authentication to pass through a chain of

@@ -92,25 +96,24 @@ // strategies. The first strategy to succeed, redirect, or error will halt

if (!Array.isArray(name)) {
name = [ name ];
name = [name];
multi = false;
}
return function authenticate(req, res, next) {
if (http.IncomingMessage.prototype.logIn
&& http.IncomingMessage.prototype.logIn !== IncomingMessageExt.logIn) {
require('../framework/connect').__monkeypatchNode();
connect.__monkeypatchNode();
}
// accumulator for failures from each strategy in the chain
var failures = [];
const failures = [];
function redirect(url) {
if (req.session && req.session.save && typeof req.session.save == 'function') {
return req.session.save(function() {
return res.redirect(url);
});
if (req.session && req.session.save && typeof req.session.save === 'function') {
return req.session.save(() => res.redirect(url));
}
return res.redirect(url);
}
// eslint-disable-next-line consistent-return
function allFailed() {

@@ -120,25 +123,24 @@ if (callback) {

return callback(null, false, failures[0].challenge, failures[0].status);
} else {
var challenges = failures.map(function(f) { return f.challenge; });
var statuses = failures.map(function(f) { return f.status; });
return callback(null, false, challenges, statuses);
}
const challenges = failures.map(f => f.challenge);
const statuses = failures.map(f => f.status);
return callback(null, false, challenges, statuses);
}
// Strategies are ordered by priority. For the purpose of flashing a
// message, the first failure will be displayed.
var failure = failures[0] || {}
, challenge = failure.challenge || {}
, msg;
let failure = failures[0] || {};
let challenge = failure.challenge || {};
let msg;
if (options.failureFlash) {
var flash = options.failureFlash;
if (typeof flash == 'string') {
let flash = options.failureFlash;
if (typeof flash === 'string') {
flash = { type: 'error', message: flash };
}
flash.type = flash.type || 'error';
var type = flash.type || challenge.type || 'error';
const type = flash.type || challenge.type || 'error';
msg = flash.message || challenge.message || challenge;
if (typeof msg == 'string') {
if (typeof msg === 'string') {
req.flash(type, msg);

@@ -149,6 +151,6 @@ }

msg = options.failureMessage;
if (typeof msg == 'boolean') {
if (typeof msg === 'boolean') {
msg = challenge.message || challenge;
}
if (typeof msg == 'string') {
if (typeof msg === 'string') {
req.session.messages = req.session.messages || [];

@@ -161,3 +163,3 @@ req.session.messages.push(msg);

}
// When failure handling is not delegated to the application, the default

@@ -168,18 +170,19 @@ // is to respond with 401 Unauthorized. Note that the WWW-Authenticate

// will be included in the response.
var rchallenge = []
, rstatus, status;
for (var j = 0, len = failures.length; j < len; j++) {
const rchallenge = [];
let rstatus;
let status;
// eslint-disable-next-line no-plusplus
for (let j = 0, len = failures.length; j < len; j++) {
failure = failures[j];
challenge = failure.challenge;
status = failure.status;
rstatus = rstatus || status;
if (typeof challenge == 'string') {
if (typeof challenge === 'string') {
rchallenge.push(challenge);
}
}
res.statusCode = rstatus || 401;
if (res.statusCode == 401 && rchallenge.length) {
if (res.statusCode === 401 && rchallenge.length) {
res.setHeader('WWW-Authenticate', rchallenge);

@@ -192,17 +195,18 @@ }

}
// eslint-disable-next-line consistent-return
(function attempt(i) {
var layer = name[i];
const layer = name[i];
// If no more strategies exist in the chain, authentication has failed.
if (!layer) { return allFailed(); }
// Get the strategy, which will be used as prototype from which to create
// a new instance. Action functions will then be bound to the strategy
// within the context of the HTTP request/response pair.
var prototype = passport._strategy(layer);
if (!prototype) { return next(new Error('Unknown authentication strategy "' + layer + '"')); }
var strategy = Object.create(prototype);
const prototype = passport._strategy(layer);
if (!prototype) { return next(new Error(`Unknown authentication strategy "${layer}"`)); }
const strategy = Object.create(prototype);
// ----- BEGIN STRATEGY AUGMENTATION -----

@@ -214,3 +218,3 @@ // Augment the new strategy instance with action functions. These action

// third-party identity provider, etc.
/**

@@ -230,20 +234,22 @@ * Authenticate `user`, with optional `info`.

*/
strategy.success = function(user, info) {
// eslint-disable-next-line consistent-return
strategy.success = function success(user, info) {
if (callback) {
return callback(null, user, info);
}
info = info || {};
var msg;
let msg;
if (options.successFlash) {
var flash = options.successFlash;
if (typeof flash == 'string') {
let flash = options.successFlash;
if (typeof flash === 'string') {
flash = { type: 'success', message: flash };
}
flash.type = flash.type || 'success';
var type = flash.type || info.type || 'success';
const type = flash.type || info.type || 'success';
msg = flash.message || info.message || info;
if (typeof msg == 'string') {
if (typeof msg === 'string') {
req.flash(type, msg);

@@ -254,6 +260,6 @@ }

msg = options.successMessage;
if (typeof msg == 'boolean') {
if (typeof msg === 'boolean') {
msg = info.message || info;
}
if (typeof msg == 'string') {
if (typeof msg === 'string') {
req.session.messages = req.session.messages || [];

@@ -267,9 +273,11 @@ req.session.messages.push(msg);

}
req.logIn(user, options, function(err) {
// eslint-disable-next-line consistent-return
req.logIn(user, options, (err) => {
if (err) { return next(err); }
// eslint-disable-next-line consistent-return
function complete() {
if (options.successReturnToOrRedirect) {
var url = options.successReturnToOrRedirect;
let url = options.successReturnToOrRedirect;
if (req.session && req.session.returnTo) {

@@ -286,5 +294,6 @@ url = req.session.returnTo;

}
if (options.authInfo !== false) {
passport.transformAuthInfo(info, req, function(err, tinfo) {
// eslint-disable-next-line consistent-return
passport.transformAuthInfo(info, req, (err, tinfo) => {
if (err) { return next(err); }

@@ -299,3 +308,3 @@ req.authInfo = tinfo;

};
/**

@@ -311,14 +320,14 @@ * Fail authentication, with optional `challenge` and `status`, defaulting

*/
strategy.fail = function(challenge, status) {
if (typeof challenge == 'number') {
strategy.fail = function fail(challenge, status) {
if (typeof challenge === 'number') {
status = challenge;
challenge = undefined;
}
// push this failure into the accumulator and attempt authentication
// using the next strategy
failures.push({ challenge: challenge, status: status });
failures.push({ challenge, status });
attempt(i + 1);
};
/**

@@ -334,3 +343,3 @@ * Redirect to `url` with optional `status`, defaulting to 302.

*/
strategy.redirect = function(url, status) {
strategy.redirect = function redirect(url, status) {
// NOTE: Do not use `res.redirect` from Express, because it can't decide

@@ -346,3 +355,3 @@ // what it wants.

// but issue deprecated versions
res.statusCode = status || 302;

@@ -353,3 +362,3 @@ res.setHeader('Location', url);

};
/**

@@ -364,6 +373,6 @@ * Pass without making a success or fail decision.

*/
strategy.pass = function() {
strategy.pass = function pass() {
next();
};
/**

@@ -379,15 +388,17 @@ * Internal error while performing authentication.

*/
strategy.error = function(err) {
// eslint-disable-next-line consistent-return
strategy.error = function error(err) {
if (callback) {
return callback(err);
}
next(err);
};
// ----- END STRATEGY AUGMENTATION -----
strategy.authenticate(req, options);
})(0); // attempt
}(0)); // attempt
};
};

@@ -42,4 +42,5 @@ /**

*/
/* eslint-disable no-proto, no-shadow */
module.exports = function initialize(passport) {
return function initialize(req, res, next) {

@@ -46,0 +47,0 @@ req._passport = {};

@@ -0,3 +1,5 @@

/* eslint-disable camelcase, no-proto, no-shadow */
function SessionManager(options, serializeUser) {
if (typeof options == 'function') {
if (typeof options === 'function') {
serializeUser = options;

@@ -7,3 +9,3 @@ options = undefined;

options = options || {};
this._key = options.key || 'passport';

@@ -13,5 +15,6 @@ this._serializeUser = serializeUser;

SessionManager.prototype.logIn = function(req, user, cb) {
var self = this;
this._serializeUser(user, req, function(err, obj) {
SessionManager.prototype.logIn = function logIn(req, user, cb) {
const self = this;
// eslint-disable-next-line consistent-return
this._serializeUser(user, req, (err, obj) => {
if (err) {

@@ -30,12 +33,13 @@ return cb(err);

});
}
};
SessionManager.prototype.logOut = function(req, cb) {
SessionManager.prototype.logOut = function logOut(req, cb) {
if (req._passport && req._passport.session) {
delete req._passport.session.user;
}
// eslint-disable-next-line no-unused-expressions
cb && cb();
}
};
module.exports = SessionManager;
/**
* Module dependencies.
*/
var util = require('util')
, Strategy = require('@passport-next/passport-strategy');
/* eslint-disable camelcase, no-proto, no-shadow */
const util = require('util');
const Strategy = require('@passport-next/passport-strategy');
/**

@@ -14,3 +17,3 @@ * `SessionStrategy` constructor.

function SessionStrategy(options, deserializeUser) {
if (typeof options == 'function') {
if (typeof options === 'function') {
deserializeUser = options;

@@ -20,3 +23,3 @@ options = undefined;

options = options || {};
Strategy.call(this);

@@ -45,8 +48,10 @@ this.name = 'session';

*/
SessionStrategy.prototype.authenticate = function(req, options) {
// eslint-disable-next-line consistent-return, no-unused-vars
SessionStrategy.prototype.authenticate = function authenticate(req, options) {
if (!req._passport) { return this.error(new Error('passport.initialize() middleware not in use')); }
options = options || {};
var self = this,
su;
const self = this;
let su;

@@ -58,3 +63,4 @@ if (req._passport.session) {

if (su || su === 0) {
this._deserializeUser(su, req, function(err, user) {
// eslint-disable-next-line consistent-return
this._deserializeUser(su, req, (err, user) => {
if (err) { return self.error(err); }

@@ -65,3 +71,3 @@ if (!user) {

// TODO: Remove instance access
var property = req._passport.instance._userProperty || 'user';
const property = req._passport.instance._userProperty || 'user';
req[property] = user;

@@ -68,0 +74,0 @@ }

{
"name": "@passport-next/passport",
"version": "1.0.1",
"version": "2.0.0",
"description": "Simple, unobtrusive authentication for Node.js.",

@@ -39,2 +39,5 @@ "keywords": [

"chai-connect-middleware": "0.3.x",
"eslint-config-airbnb-base": "13.0.x",
"eslint-plugin-import": "2.12.x",
"eslint": "4.19.x",
"@passport-next/chai-passport-strategy": "1.x.x"

@@ -46,4 +49,7 @@ },

"scripts": {
"test": "node_modules/.bin/mocha --reporter spec --require test/bootstrap/node test/*.test.js test/**/*.test.js"
"spec": "node_modules/.bin/mocha --reporter spec --require test/bootstrap/node test/*.test.js test/**/*.test.js",
"lint": "eslint --ext .js . --max-warnings 0",
"lintfix": "eslint --ext .js . --fix",
"test": "npm run lint && npm run spec"
}
}
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