New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@sap/approuter

Package Overview
Dependencies
Maintainers
3
Versions
195
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sap/approuter - npm Package Compare versions

Comparing version 5.10.1 to 5.11.0

lib/configuration/schemas/url-schema.json

10

CHANGELOG.md

@@ -8,2 +8,12 @@ # Change Log

## 5.11.0 - 2019-01-22
### Added
- Client credentials token support.
## 5.10.2 - 2019-01-08
### Fixed
- Fix proxy issue in Connectivity flow.
## 5.10.1 - 2019-01-03

@@ -10,0 +20,0 @@

45

lib/backend-request/headers.js

@@ -43,17 +43,21 @@ 'use strict';

}
if (destination && destination.proxyType === 'OnPremise' && req.app.services && req.app.services['connectivity']) {
tokens['connectivity'] = req.app.services['connectivity'].token;
}
addCorrleationIdHeader(headers, req);
addCorrelationIdHeader(headers, req);
addXForwardingHeaders(headers, req);
removeSecurityHeaders(headers, req);
headerUtil.updateSapPassport(headers);
tokens['connProxyToken'] = req.app && req.app.connProxyToken && req.app.connProxyToken.accessToken;
tokens['html5AppsRepoToken'] = req.app && req.app.html5AppsRepoToken && req.app.html5AppsRepoToken.accessToken;
if (req.session && req.session.user && req.session.user.destinations
&& req.session.user.destinations[destination.name]
&& req.session.user.destinations[destination.name].authToken){
&& req.session.user.destinations[destination.name]
&& req.session.user.destinations[destination.name].authToken){
tokens['authToken'] = req.session.user.destinations[destination.name].authToken;
}
route = req.internalUrl ? req.internalUrl.route : null;
if (route && route.service && req.app && req.app.services && req.app.services[route.service] && req.app.services[route.service].token){
tokens[route.service] = req.app.services[route.service].token;
}
if (route && route.service === 'html5-apps-repo-rt') {
var appKey = dynamicRoutingUtils.getApplicationKey(req);
let appKey = dynamicRoutingUtils.getApplicationKey(req);
if (appKey && appKey.appPrefix) {

@@ -80,3 +84,3 @@ addAppHostIdHeader(appKey.appPrefix, headers);

headers['SAP-Connectivity-Authentication'] = 'Bearer ' + tokens.accessToken;
headers['Proxy-Authorization'] = 'Bearer ' + tokens.connProxyToken;
headers['Proxy-Authorization'] = 'Bearer ' + tokens['connectivity'].accessToken;
if (destination.cloudConnectorLocationId) {

@@ -90,10 +94,10 @@ headers['SAP-Connectivity-SCC-Location_ID'] = destination.cloudConnectorLocationId;

}
// Common for on premise and cloud destination flows
// Use tokens provided from destination service
if (tokens['authToken']){
// Common for on premise and cloud destination flows
// Use tokens provided from destination service
if (tokens['authToken']) {
headers.authorization = tokens['authToken'].type + ' ' + tokens['authToken'].value;
}
} else if (route && route.service) {// Service flow
if (businessServiceUtils.getGrantType(route.credentials) !== 'user_token') {// html5 apps repo flow
headers['authorization'] = 'Bearer ' + tokens.html5AppsRepoToken;
if (businessServiceUtils.getGrantType(route.credentials) === 'client_credentials') {// html5 apps repo flow
headers.authorization = tokens[route.service].tokenType + ' ' + tokens[route.service].accessToken;
} else {

@@ -104,3 +108,3 @@ headers.authorization = 'Bearer ' + tokens.accessToken;

if (tokens && tokens.accessToken) {
// In case of Logout:
// In case of Logout:
// env destination => we send JWT token in tokens.accessToken as a String

@@ -116,15 +120,10 @@ // destination service destination => we send destination auth token in tokens.accessToken as an Object

function addCorrleationIdHeader(headers, req) {
function addCorrelationIdHeader(headers, req) {
if (req.headers['x-correlationid']) {
return;
}
else if (req.headers['x-request-id']) {
} else if (req.headers['x-request-id']) {
headers['x-correlationid'] = req.headers['x-request-id'];
}
else if (req.headers['x-vcap-request-id']){
} else if (req.headers['x-vcap-request-id']){
headers['x-correlationid'] = req.headers['x-vcap-request-id'];
}
else {
} else {
headers['x-correlationid'] = uuid();

@@ -154,2 +153,2 @@ }

}
}
}

@@ -35,3 +35,3 @@ {

"enum": [
"user_token"
"user_token", "client_credentials"
]

@@ -38,0 +38,0 @@ }

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

var destinationUtils = require('../utils/destination-utils');
var destinationConfigSchema = require('./schemas/destinations-srv-config-schema.json');
var urlSchema = require('./schemas/url-schema.json');
var xsenv = require('@sap/xsenv');

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

if (err.message.includes('No enum match for:')) { //
return 'User credential service is not supported by approuter' + err.message;
return 'User credential service is not supported by approuter ' + err.message;
}

@@ -188,4 +188,3 @@ return err.message;

});
}
,
},

@@ -196,4 +195,3 @@ validateEnvironmentSettings: function (configuration) {

validator.validate(configuration, environmentSchema, 'environment-settings');
}
,
},

@@ -214,4 +212,3 @@ validatePlugins: function (configuration, envDestinations) {

validator.validate(configuration, pluginsSchema, 'plugins');
}
,
},

@@ -235,4 +232,3 @@ validateHeaders: function (configuration) {

validator.validate(configuration, headersSchema, 'http-headers');
}
,
},

@@ -255,4 +251,3 @@ validateWhitelist: function (whitelist) {

validator.validate(whitelist, whitelistSchema, 'clickjack-whitelist');
}
,
},

@@ -274,4 +269,3 @@ validateApprouterStartOptions: function (options) {

validator.validate(options, approuterOptionsSchema, 'options');
}
,
},

@@ -283,4 +277,3 @@ validateUaaOptions: function (options) {

validator.validate(options, uaaOptionsSchema, 'uaa-configuration');
}
,
},

@@ -293,23 +286,21 @@

validator.validate(options, connectivitySchema, 'connectivity-configuration');
},
}
,
validateHTML5AppsRepoCredentials: function (options) {
validateClientCredentials: function (options) {
var validator = new JsonValidator();
validator.addFormat('absolute-uri', validateAbsoluteUri);
if (options.uaa && options.uaa.clientid && options.uaa.clientid.length > 0
&& options.uaa.clientsecret && options.uaa.clientsecret.length > 0
&& options.uaa.url && options.uaa.url.length > 0){
validator.validate(options.uaa.url, urlSchema, 'url-schema');
return;
} else if (options.clientid && options.clientid.length > 0
&& options.clientsecret && options.clientsecret.length > 0
&& options.url && options.url.length > 0){
validator.validate(options.url, urlSchema, 'url-schema');
return;
}
throw new Error('No clientid or clientsercret provided');
},
validator.validate(options, html5RepoCredSchema, 'html5-repo-credentials');
}
,
validateDestinationCredentials: function (options) {
var validator = new JsonValidator();
validator.addFormat('absolute-uri', validateAbsoluteUri);
validator.validate(options, destinationConfigSchema, 'destination-service-credentials-configuration');
}
,
validateCors: function (options) {

@@ -322,4 +313,3 @@ var validator = new JsonValidator();

}
}
;
};

@@ -326,0 +316,0 @@ function addConfigurationDefaults(configuration) {

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

var self = module.exports = {

@@ -20,3 +19,3 @@

var destinationName = options.destinationName;
destinationUtils.findDestination(destinationName, userExchangeToken, function (err, destinationLookUpResult){
destinationUtils.findDestination(destinationName, userExchangeToken, options, function (err, destinationLookUpResult){
if (err){

@@ -46,3 +45,5 @@ return cb(err);

}
result.destination = destinationUtils.normalizeDestination(destinationLookUpResult.destinationConfiguration);
var destination = destinationUtils.normalizeDestination(destinationLookUpResult.destinationConfiguration);
destinationUtils.enhanceDestination (destination);
result.destination = destination;
result.authToken = destinationLookUpResult.authTokens ? destinationLookUpResult.authTokens[0] : null;

@@ -120,3 +121,1 @@ return cb (null, result);

}

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

}
var options = {destinationName: req.internalUrl.route.destination, session: req.session};
var options = {destinationName: req.internalUrl.route.destination, session: req.session, app: req.app};
if (req.routerConfig && req.routerConfig.getToken) {

@@ -37,3 +37,2 @@ req.routerConfig.getToken(req, function (err, jwt) {

}
sessionExt.update(req.session, function(// eslint-disable-next-line

@@ -57,3 +56,2 @@ session) {

}
return next();

@@ -64,3 +62,2 @@ });

function updateInternalUrl (req, destination) {

@@ -67,0 +64,0 @@ var destinationUrl = req.internalUrl.destination.url;

@@ -10,5 +10,6 @@ 'use strict';

module.exports = function (req, res, next) {
if (!isBusinessServiceScenario(req)) {
if (!isTokenExchangeRequired(req)) {
return next();
}
shouldAskBusinessToken(req, function(err, askForToken){

@@ -40,7 +41,6 @@ if (err) {

function isBusinessServiceScenario(req){
if (!req.internalUrl || !req.internalUrl.route || !req.internalUrl.route.service) {
return false;
}
if (req.internalUrl.route.credentials && businessServiceUtils.getGrantType(req.internalUrl.route.credentials) === 'user_token'){
function isTokenExchangeRequired(req){
if (req.internalUrl && req.internalUrl.route
&& req.internalUrl.route.service && req.internalUrl.route.credentials
&& businessServiceUtils.getGrantType(req.internalUrl.route.credentials) === 'user_token'){
return true;

@@ -47,0 +47,0 @@ }

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

exports.normalizeDestination = normalizeDestination;
exports.enhanceDestination = enhanceDestination;

@@ -29,2 +30,12 @@ function adjustDestinationProperties(destinations) {

function enhanceDestination (destination) {
if (destination.proxyType === 'OnPremise') {
var credentials = xsenv.cfServiceCredentials({tag: 'connectivity'});
destination['proxyHost'] = credentials.onpremise_proxy_host;
destination['proxyPort'] = credentials.onpremise_proxy_port;
}
destination.timeout = destination.timeout ? destination.timeout : 30000;
}
function normalizeDestinationProperties(destinations) {

@@ -49,2 +60,20 @@ if (!destinations) {

function getFindDestinationReqOptions (destinationName, token, options, cb) {
var credentials = getDestinationServiceCredentials ();
var headers = {'accept': 'application/json;charset=utf-8'};
if (!credentials) {
return cb ('Destination service is not bound');
}
obtainDestinationServiceToken(token, credentials, options, function(err, token) {
if (err){
return cb(err);
}
headers.Authorization = 'Bearer ' + token;
cb (null, {
url: credentials.uri + '/destination-configuration/v1/destinations/' + destinationName,
headers: headers
});
});
}
function normalizeDestination (destination){

@@ -86,63 +115,13 @@ if (!destination) {

function obtainDestinationServiceToken (token, credentials, options, cb) {
function getUAAurl(serviceCredentials) {
return serviceCredentials.url + '/oauth/token';
}
function getUAARequestFormData(serviceCredentials) {
return {
'client_id': serviceCredentials.clientid,
'client_secret': serviceCredentials.clientsecret,
'grant_type': 'client_credentials',
'response_type': 'token'
};
}
function obtainDestinationServiceToken (token, credentials, cb) {
// this is the case when exchange token performed and we have token of destination service
if (token){
return cb (null, token);
}
} else {
// this is the case when the session is not authenticated, so there were no exchange token.
// in this case we have to get the token of destination using its client credentials
request.post({
url: getUAAurl(credentials),
form: getUAARequestFormData(credentials)
},
function (err, res, body) {
try {
if (err) {
return cb(new Error('Error while obtaining token of destination service. ' + err));
}
if (res.statusCode === 200) {
if (!JSON.parse(body).access_token) {
cb(new Error('Obtaining token of destination service, Error: Bad token'));
}
cb(null, JSON.parse(body).access_token);
} else {
cb(new Error('Error while obtaining token of destination service; Status: ' + res.statusCode +
' Response: ' + JSON.stringify(body)));
}
} catch (e) {
cb(new Error('Obtaining token of destination service, Error: while parsing UAA response; ' + e));
}
});
}
return cb (null, options.app.services['destination'].token);
}
function getFindDestinationReqOptions (destinationName, token, cb) {
var credentials = getDestinationServiceCredentials ();
var headers = {'accept': 'application/json;charset=utf-8'};
if (!credentials) {
return cb ('Destination service is not bound');
}
obtainDestinationServiceToken(token, credentials, function(err, token) {
if (err){
return cb(err);
}
headers.Authorization = 'Bearer ' + token;
cb (null, {
url: credentials.uri + '/destination-configuration/v1/destinations/' + destinationName,
headers: headers
});
});
}

@@ -152,3 +131,3 @@

// Search priority is destination on instance level and after that fallback to the shared destinations on subaccount level.
function findDestination (destinationName, token, cb){
function findDestination (destinationName, token, options, cb){
if (!destinationName) {

@@ -159,4 +138,4 @@ return cb('Cannot find destination, destination name is missing');

var errorMessage;
getFindDestinationReqOptions(destinationName, token, function (error, requestOptions) {
if (error) {
getFindDestinationReqOptions(destinationName, token, options, function(error, requestOptions){
if (error){
return cb(error);

@@ -173,3 +152,2 @@ }

}
try {

@@ -184,3 +162,1 @@ var destinationLookUpResult = JSON.parse(res.body);

}

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

if (!req.app || !req.app.html5AppsRepoToken || !req.app.html5AppsRepoToken.accessToken) {
if (!req.app || !req.app.services || !req.app.services['html5-apps-repo-rt'] || !req.app.services['html5-apps-repo-rt'].token || !req.app.services['html5-apps-repo-rt'].token.accessToken) {
return cb ('Missing html5AppsRepo token');

@@ -202,3 +202,3 @@ }

requestOptions.headers.accept = 'application/json;charset=utf-8';
requestOptions.headers.authorization = 'Bearer ' + req.app.html5AppsRepoToken.accessToken;
requestOptions.headers.authorization = 'Bearer ' + req.app.services['html5-apps-repo-rt'].token.accessToken;

@@ -205,0 +205,0 @@ if (bsCredentials){

'use strict';
var xsenv = require('@sap/xsenv');
var businessServiceUtils = require('./business-service-utils');
var validators = require('../configuration/validators');

@@ -10,58 +9,24 @@ var passportUtils = require('../passport/utils');

var logger = loggerUtils.getLogger('/token-utils');
var VError = require('verror').VError;
exports.getTokens = getBSTokens;
exports.getProxyToken = getProxyToken;
exports.getHTML5AppsRepoToken = getHTML5AppsRepoToken;
exports.getTokens = getTokens;
exports.loadDestinationTokenAsync = loadDestinationTokenAsync;
function getBSTokens(app) {
const DESTINATION = 'destination';
const CONNECTIVITY = 'connectivity';
const CLIENT_CREDENTIALS = 'client_credentials';
function getProxyToken(app) {
try {
var credentials = xsenv.cfServiceCredentials({tag: 'connectivity'});
validators.validateConnectivityCredentials(credentials);
loadClientCredentialsToken(app, credentials, 'connProxyToken');
// eslint-disable-next-line no-empty
} catch (e) {}
}
function getHTML5AppsRepoToken(app) {
try {
var credentials = businessServiceUtils.getCredentials('html5-apps-repo-rt');
} catch (e) {return;}
try {
validators.validateHTML5AppsRepoCredentials(credentials);
loadClientCredentialsToken(app, credentials.uaa, 'html5AppsRepoToken');
} catch (e){
logger.error('can\'t get access token to html5 applications repository runtime service :\n', e);
var services = xsenv.readCFServices();
if (services){
for (var serviceName in services){
var service = services[serviceName];
if (service.label === DESTINATION || service.label === CONNECTIVITY || service.credentials['grant_type'] === CLIENT_CREDENTIALS){
validators.validateClientCredentials(service.credentials);
loadClientCredentialsToken(app, service.credentials, service.credentials['sap.cloud.service'] || service.label);
}
}
}
}
function getTokens(app) {
getProxyToken(app);
getHTML5AppsRepoToken(app);
loadDestinationTokenAsync (app);
}
function loadDestinationTokenAsync (app) {
var credentials;
var message;
try {
credentials = xsenv.cfServiceCredentials({tag: 'destination'});
} catch (e) {
return;
}
try {
validators.validateDestinationCredentials(credentials);
} catch (e) {
message = 'cannot get access token to destination service :\n' + e.message;
logger.error(message);
throw new VError(message);
}
loadClientCredentialsToken(app, credentials, 'destinationToken');
}
function loadClientCredentialsToken(app, credentials, tokenName) {
function loadClientCredentialsToken(app, credentials, serviceName) {
var requestOptions = {
url: credentials.url + '/oauth/token/?grant_type=client_credentials',
url: (credentials.url || credentials.uaa.url) + '/oauth/token/?grant_type=client_credentials',
headers: {

@@ -72,4 +37,4 @@ 'content-type': 'application/x-www-form-urlencoded;charset=utf-8',

auth: {
user: credentials.clientid,
pass: credentials.clientsecret
user: credentials.clientid || credentials.uaa.clientid,
pass: credentials.clientsecret || credentials.uaa.clientsecret
}

@@ -83,3 +48,2 @@ };

}
if (!uaaResponse.access_token || !uaaResponse.expires_in) {

@@ -90,19 +54,13 @@ return tracer.error('Bad response from UAA when getting client credentials token for ' + credentials.label + '- not all fields are present');

accessToken: uaaResponse.access_token,
expiryDate: passportUtils.getExpiresAt(uaaResponse.expires_in).getTime()
tokenType: uaaResponse.token_type
};
app[tokenName] = options;
var tokenRefreshTimestamp = options.expiryDate - toMilliseconds(5);
if (!app.services) {app.services = {};}
app.services[serviceName] = { token: options };
const FIVE_MINUTES = 5 * 60 * 1000;
var tokenRefreshTimestamp = passportUtils.getExpiresAt(uaaResponse.expires_in).getTime() - FIVE_MINUTES;
var msBeforeRetrieval = tokenRefreshTimestamp - Date.now();
executeAfter(function () {
loadClientCredentialsToken(app, credentials, tokenName);
}, msBeforeRetrieval);
setTimeout(function () { loadClientCredentialsToken(app, credentials, serviceName);}, msBeforeRetrieval);
});
}
function executeAfter(fn, timeout) {
setTimeout(fn, timeout);
}
function toMilliseconds(minutes) {
return minutes * 60 * 1000;
}

@@ -1,1 +0,1 @@

{"bundleDependencies":false,"dependencies":{"@sap/audit-logging":"2.2.4","@sap/e2e-trace":"1.3.0","@sap/logging":"4.0.2","@sap/xsenv":"1.2.8","@sap/xssec":"2.1.15","agentkeepalive":"2.0.5","async":"2.0.1","base64-url":"2.0.0","basic-auth":"1.0.3","body-parser":"1.18.3","commander":"2.9.0","compression":"1.7.3","connect":"3.6.5","cookie":"0.2.2","cookie-parser":"1.3.5","cookie-signature":"1.1.0","debug":"3.1.0","deepmerge":"2.1.1","encodeurl":"1.0.2","express-session":"1.15.6","http-proxy-agent":"2.1.0","https-proxy-agent":"2.2.0","jwt-decode":"2.0.1","lodash":"4.17.11","lru-cache":"4.0.0","mime":"1.4.1","moment":"2.19.3","ms":"2.1.1","mustache":"2.2.1","node-cache":"4.1.1","passport":"0.3.2","request":"2.87.0","request-stats":"2.0.1","safe-regex":"1.1.0","scmp":"1.0.0","send":"0.16.2","serve-static":"1.13.2","tough-cookie":"2.3.3","tv4":"1.2.7","uid-safe":"2.1.5","urijs":"1.16.1","uuid":"3.2.1","verror":"1.10.0","ws":"1.1.5"},"deprecated":false,"description":"Node.js based application router","devDependencies":{"chai":"3.5.0","diveSync":"0.3.0","eslint":"3.2.2","express":"4.16.2","filter-node-package":"2.0.0","istanbul":"0.4.4","markdown-toc":"1.1.0","mocha":"3.0.2","mock-require":"3.0.2","node-build":"1.0.0","node-mocks-http":"1.5.2","node-style":"^2.0.0","proxyquire":"1.7.10","rewire":"2.5.2","rimraf":"2.5.4","sinon":"1.17.5","supertest":"3.3.0"},"engines":{"node":"^4.5.0 || ^6.0.0 "},"main":"approuter.js","name":"@sap/approuter","repository":{},"scripts":{"lint":"eslint -c node_modules/node-style/.eslintrc -f stylish lib/ approuter.js","prepareRelease":"node build/delete-extra-packages.js && clean-packages && npm prune --production","start":"node approuter.js","test":"node build/test","toc":"markdown-toc -i README.md && markdown-toc -i doc/extending.md && markdown-toc -i doc/sizingGuide.md"},"version":"5.10.1","warnings":[{"code":"ENOTSUP","required":{"node":"^4.5.0 || ^6.0.0 "},"pkgid":"@sap/approuter@5.10.1"},{"code":"ENOTSUP","required":{"node":"^4.5.0 || ^6.0.0 "},"pkgid":"@sap/approuter@5.10.1"}],"license":"SEE LICENSE IN developer-license-3.1.txt"}
{"bundleDependencies":false,"dependencies":{"@sap/audit-logging":"2.2.4","@sap/e2e-trace":"1.3.0","@sap/logging":"4.0.2","@sap/xsenv":"1.2.8","@sap/xssec":"2.1.15","agentkeepalive":"2.0.5","async":"2.0.1","base64-url":"2.0.0","basic-auth":"1.0.3","body-parser":"1.18.3","commander":"2.9.0","compression":"1.7.3","connect":"3.6.5","cookie":"0.2.2","cookie-parser":"1.3.5","cookie-signature":"1.1.0","debug":"3.1.0","deepmerge":"2.1.1","encodeurl":"1.0.2","express-session":"1.15.6","http-proxy-agent":"2.1.0","https-proxy-agent":"2.2.0","jwt-decode":"2.0.1","lodash":"4.17.11","lru-cache":"4.0.0","mime":"1.4.1","moment":"2.19.3","ms":"2.1.1","mustache":"2.2.1","node-cache":"4.1.1","passport":"0.3.2","request":"2.87.0","request-stats":"2.0.1","safe-regex":"1.1.0","scmp":"1.0.0","send":"0.16.2","serve-static":"1.13.2","tough-cookie":"2.3.3","tv4":"1.2.7","uid-safe":"2.1.5","urijs":"1.16.1","uuid":"3.2.1","verror":"1.10.0","ws":"1.1.5"},"deprecated":false,"description":"Node.js based application router","devDependencies":{"chai":"3.5.0","diveSync":"0.3.0","eslint":"3.2.2","express":"4.16.2","filter-node-package":"2.0.0","istanbul":"0.4.4","markdown-toc":"1.1.0","mocha":"3.0.2","mock-require":"3.0.2","node-build":"1.0.0","node-mocks-http":"1.5.2","node-style":"^2.0.0","proxyquire":"1.7.10","rewire":"2.5.2","rimraf":"2.5.4","sinon":"1.17.5","supertest":"3.3.0"},"engines":{"node":"^4.5.0 || ^6.0.0 "},"main":"approuter.js","name":"@sap/approuter","repository":{},"scripts":{"lint":"eslint -c node_modules/node-style/.eslintrc -f stylish lib/ approuter.js","prepareRelease":"node build/delete-extra-packages.js && clean-packages && npm prune --production","start":"node approuter.js","test":"node build/test","toc":"markdown-toc -i README.md && markdown-toc -i doc/extending.md && markdown-toc -i doc/sizingGuide.md"},"version":"5.11.0","warnings":[{"code":"ENOTSUP","required":{"node":"^4.5.0 || ^6.0.0 "},"pkgid":"@sap/approuter@5.11.0"},{"code":"ENOTSUP","required":{"node":"^4.5.0 || ^6.0.0 "},"pkgid":"@sap/approuter@5.11.0"}],"license":"SEE LICENSE IN developer-license-3.1.txt"}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

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