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

url

Package Overview
Dependencies
Maintainers
2
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

url - npm Package Compare versions

Comparing version 0.10.3 to 0.11.0

util.js

4

package.json
{
"name": "url",
"description": "The core `url` packaged standalone for use with Browserify.",
"version": "0.10.3",
"version": "0.11.0",
"dependencies": {

@@ -13,3 +13,3 @@ "punycode": "1.3.2",

"mocha": "1.18.2",
"zuul": "2.0.0"
"zuul": "3.3.0"
},

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

@@ -23,8 +23,5 @@ // Copyright Joyent, Inc. and other Node contributors.

var assert = require('assert');
var util = require('util');
var url = require('./url');
test('god', function() {
// URLs to parse, and expected data

@@ -39,2 +36,48 @@ // { url : parsed }

'http:\\\\evil-phisher\\foo.html#h\\a\\s\\h': {
protocol: 'http:',
slashes: true,
host: 'evil-phisher',
hostname: 'evil-phisher',
pathname: '/foo.html',
path: '/foo.html',
hash: '#h%5Ca%5Cs%5Ch',
href: 'http://evil-phisher/foo.html#h%5Ca%5Cs%5Ch'
},
'http:\\\\evil-phisher\\foo.html?json="\\"foo\\""#h\\a\\s\\h': {
protocol: 'http:',
slashes: true,
host: 'evil-phisher',
hostname: 'evil-phisher',
pathname: '/foo.html',
search: '?json=%22%5C%22foo%5C%22%22',
query: 'json=%22%5C%22foo%5C%22%22',
path: '/foo.html?json=%22%5C%22foo%5C%22%22',
hash: '#h%5Ca%5Cs%5Ch',
href: 'http://evil-phisher/foo.html?json=%22%5C%22foo%5C%22%22#h%5Ca%5Cs%5Ch'
},
'http:\\\\evil-phisher\\foo.html#h\\a\\s\\h?blarg': {
protocol: 'http:',
slashes: true,
host: 'evil-phisher',
hostname: 'evil-phisher',
pathname: '/foo.html',
path: '/foo.html',
hash: '#h%5Ca%5Cs%5Ch?blarg',
href: 'http://evil-phisher/foo.html#h%5Ca%5Cs%5Ch?blarg'
},
'http:\\\\evil-phisher\\foo.html': {
protocol: 'http:',
slashes: true,
host: 'evil-phisher',
hostname: 'evil-phisher',
pathname: '/foo.html',
path: '/foo.html',
href: 'http://evil-phisher/foo.html'
},
'HTTP://www.example.com/' : {

@@ -137,5 +180,17 @@ 'href': 'http://www.example.com/',

// + not an invalid host character
// per https://url.spec.whatwg.org/#host-parsing
'http://x.y.com+a/b/c' : {
'href': 'http://x.y.com+a/b/c',
'protocol': 'http:',
'slashes': true,
'host': 'x.y.com+a',
'hostname': 'x.y.com+a',
'pathname': '/b/c',
'path': '/b/c'
},
// an unexpected invalid char in the hostname.
'HtTp://x.y.cOm*a/b/c?d=e#f g<h>i' : {
'href': 'http://x.y.com/*a/b/c?d=e#f%20g%3Ch%3Ei',
'HtTp://x.y.cOm;a/b/c?d=e#f g<h>i' : {
'href': 'http://x.y.com/;a/b/c?d=e#f%20g%3Ch%3Ei',
'protocol': 'http:',

@@ -145,12 +200,12 @@ 'slashes': true,

'hostname': 'x.y.com',
'pathname': '/*a/b/c',
'pathname': ';a/b/c',
'search': '?d=e',
'query': 'd=e',
'hash': '#f%20g%3Ch%3Ei',
'path': '/*a/b/c?d=e'
'path': ';a/b/c?d=e'
},
// make sure that we don't accidentally lcast the path parts.
'HtTp://x.y.cOm*A/b/c?d=e#f g<h>i' : {
'href': 'http://x.y.com/*A/b/c?d=e#f%20g%3Ch%3Ei',
'HtTp://x.y.cOm;A/b/c?d=e#f g<h>i' : {
'href': 'http://x.y.com/;A/b/c?d=e#f%20g%3Ch%3Ei',
'protocol': 'http:',

@@ -160,7 +215,7 @@ 'slashes': true,

'hostname': 'x.y.com',
'pathname': '/*A/b/c',
'pathname': ';A/b/c',
'search': '?d=e',
'query': 'd=e',
'hash': '#f%20g%3Ch%3Ei',
'path': '/*A/b/c?d=e'
'path': ';A/b/c?d=e'
},

@@ -480,4 +535,4 @@

'http://www.Äffchen.cOm*A/b/c?d=e#f g<h>i' : {
'href': 'http://www.xn--ffchen-9ta.com/*A/b/c?d=e#f%20g%3Ch%3Ei',
'http://www.Äffchen.cOm;A/b/c?d=e#f g<h>i' : {
'href': 'http://www.xn--ffchen-9ta.com/;A/b/c?d=e#f%20g%3Ch%3Ei',
'protocol': 'http:',

@@ -487,7 +542,7 @@ 'slashes': true,

'hostname': 'www.xn--ffchen-9ta.com',
'pathname': '/*A/b/c',
'pathname': ';A/b/c',
'search': '?d=e',
'query': 'd=e',
'hash': '#f%20g%3Ch%3Ei',
'path': '/*A/b/c?d=e'
'path': ';A/b/c?d=e'
},

@@ -527,2 +582,3 @@

protocol: 'http:',
'slashes': true,
slashes: true,

@@ -762,2 +818,13 @@ host: 'bucket_name.s3.amazonaws.com',

'http://x:1/\' <>"`/{}|\\^~`/': {
protocol: 'http:',
slashes: true,
host: 'x:1',
port: '1',
hostname: 'x',
pathname: '/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/',
path: '/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/',
href: 'http://x:1/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/'
},
'http://a@b@c/': {

@@ -800,2 +867,18 @@ protocol: 'http:',

href: 'http://a%0D%22%20%09%0A%3C\'b:b@c/%0D%0Ad/e?f'
},
// git urls used by npm
'git+ssh://git@github.com:npm/npm': {
protocol: 'git+ssh:',
slashes: true,
auth: 'git',
host: 'github.com',
port: null,
hostname: 'github.com',
hash: null,
search: null,
query: null,
pathname: '/:npm/npm',
path: '/:npm/npm',
href: 'git+ssh://git@github.com/:npm/npm'
}

@@ -805,22 +888,24 @@

for (var u in parseTests) {
var actual = url.parse(u),
spaced = url.parse(' \t ' + u + '\n\t');
expected = parseTests[u];
Object.keys(parseTests).forEach(function(u) {
test('parse(' + u + ')', function() {
var actual = url.parse(u),
spaced = url.parse(' \t ' + u + '\n\t');
expected = parseTests[u];
Object.keys(actual).forEach(function (i) {
if (expected[i] === undefined && actual[i] === null) {
expected[i] = null;
}
});
Object.keys(actual).forEach(function (i) {
if (expected[i] === undefined && actual[i] === null) {
expected[i] = null;
}
});
assert.deepEqual(actual, expected);
assert.deepEqual(spaced, expected);
assert.deepEqual(actual, expected);
assert.deepEqual(spaced, expected);
var expected = parseTests[u].href,
actual = url.format(parseTests[u]);
var expected = parseTests[u].href,
actual = url.format(parseTests[u]);
assert.equal(actual, expected,
'format(' + u + ') == ' + u + '\nactual:' + actual);
}
assert.equal(actual, expected,
'format(' + u + ') == ' + u + '\nactual:' + actual);
});
});

@@ -848,15 +933,46 @@ var parseTestsWithQueryString = {

'path': '/'
},
'/example': {
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: '',
query: {},
pathname: '/example',
path: '/example',
href: '/example'
},
'/example?query=value':{
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: '?query=value',
query: { query: 'value' },
pathname: '/example',
path: '/example?query=value',
href: '/example?query=value'
}
};
for (var u in parseTestsWithQueryString) {
var actual = url.parse(u, true);
var expected = parseTestsWithQueryString[u];
for (var i in actual) {
if (actual[i] === null && expected[i] === undefined) {
expected[i] = null;
Object.keys(parseTestsWithQueryString).forEach(function(u) {
test('parse(' + u + ')', function() {
var actual = url.parse(u, true);
var expected = parseTestsWithQueryString[u];
for (var i in actual) {
if (actual[i] === null && expected[i] === undefined) {
expected[i] = null;
}
}
}
assert.deepEqual(actual, expected);
}
assert.deepEqual(actual, expected);
});
});

@@ -1053,16 +1169,19 @@ // some extra formatting tests, just to verify

};
for (var u in formatTests) {
var expect = formatTests[u].href;
delete formatTests[u].href;
var actual = url.format(u);
var actualObj = url.format(formatTests[u]);
assert.equal(actual, expect,
'wonky format(' + u + ') == ' + expect +
'\nactual:' + actual);
assert.equal(actualObj, expect,
'wonky format(' + JSON.stringify(formatTests[u]) +
') == ' + expect +
'\nactual: ' + actualObj);
}
Object.keys(formatTests).forEach(function(u) {
test('format(' + u + ')', function() {
var expect = formatTests[u].href;
delete formatTests[u].href;
var actual = url.format(u);
var actualObj = url.format(formatTests[u]);
assert.equal(actual, expect,
'wonky format(' + u + ') == ' + expect +
'\nactual:' + actual);
assert.equal(actualObj, expect,
'wonky format(' + JSON.stringify(formatTests[u]) +
') == ' + expect +
'\nactual: ' + actualObj);
});
});
/*

@@ -1081,2 +1200,10 @@ [from, path, expected]

['/foo/bar/baz', '../../../../../../../quux/baz', '/quux/baz'],
['/foo', '.', '/'],
['/foo', '..', '/'],
['/foo/', '.', '/foo/'],
['/foo/', '..', '/'],
['/foo/bar', '.', '/foo/'],
['/foo/bar', '..', '/'],
['/foo/bar/', '.', '/foo/bar/'],
['/foo/bar/', '..', '/foo/'],
['foo/bar', '../../../baz', '../../baz'],

@@ -1108,8 +1235,11 @@ ['foo/bar/', '../../../baz', '../baz'],

];
relativeTests.forEach(function(relativeTest) {
var a = url.resolve(relativeTest[0], relativeTest[1]),
e = relativeTest[2];
assert.equal(a, e,
'resolve(' + [relativeTest[0], relativeTest[1]] + ') == ' + e +
'\n actual=' + a);
test('resolve(' + [relativeTest[0], relativeTest[1]] + ')', function() {
var a = url.resolve(relativeTest[0], relativeTest[1]),
e = relativeTest[2];
assert.equal(a, e,
'resolve(' + [relativeTest[0], relativeTest[1]] + ') == ' + e +
'\n actual=' + a);
});
});

@@ -1129,3 +1259,5 @@

].forEach(function(val) {
assert.throws(function() { url.parse(val); }, TypeError);
test('parse(' + val + ')', function() {
assert.throws(function() { url.parse(val); }, TypeError);
});
});

@@ -1186,3 +1318,3 @@

['g;x', bases[0], 'http://a/b/c/g;x'],
['g;x?y#s', bases[0], 'http://a/b/c/g;x?y#s'],
['g;x?y#s' , bases[0], 'http://a/b/c/g;x?y#s'],
// changed with RFC 2396bis

@@ -1198,3 +1330,3 @@ //('', bases[0], CURRENT_DOC_URI],

['../../', bases[0], 'http://a/'],
['../../g', bases[0], 'http://a/g'],
['../../g' , bases[0], 'http://a/g'],
['../../../g', bases[0], ('http://a/../g', 'http://a/g')],

@@ -1238,6 +1370,6 @@ ['../../../../g', bases[0], ('http://a/../../g', 'http://a/g')],

['g?y', bases[1], 'http://a/b/c/g?y'],
['g?y/./x', bases[1], 'http://a/b/c/g?y/./x'],
['g?y/./x' , bases[1], 'http://a/b/c/g?y/./x'],
['g?y/../x', bases[1], 'http://a/b/c/g?y/../x'],
['g#s', bases[1], 'http://a/b/c/g#s'],
['g#s/./x', bases[1], 'http://a/b/c/g#s/./x'],
['g#s/./x' , bases[1], 'http://a/b/c/g#s/./x'],
['g#s/../x', bases[1], 'http://a/b/c/g#s/../x'],

@@ -1248,3 +1380,3 @@ ['./', bases[1], 'http://a/b/c/'],

['../../', bases[1], 'http://a/'],
['../../g', bases[1], 'http://a/g'],
['../../g' , bases[1], 'http://a/g'],

@@ -1266,3 +1398,3 @@ // http://gbiv.com/protocols/uri/test/rel_examples3.html

['../../', bases[2], 'http://a/b/'],
['../../g', bases[2], 'http://a/b/g'],
['../../g' , bases[2], 'http://a/b/g'],

@@ -1284,3 +1416,3 @@ // http://gbiv.com/protocols/uri/test/rel_examples4.html

['../../', bases[3], 'fred:///s//'],
['../../g', bases[3], 'fred:///s//g'],
['../../g' , bases[3], 'fred:///s//g'],
['../../../g', bases[3], 'fred:///s/g'],

@@ -1304,3 +1436,3 @@ // may change to fred:///s//a/../../../g

['../../', bases[4], 'http:///s//'],
['../../g', bases[4], 'http:///s//g'],
['../../g' , bases[4], 'http:///s//g'],
// may change to http:///s//a/../../g

@@ -1427,8 +1559,11 @@ ['../../../g', bases[4], 'http:///s/g'],

];
relativeTests2.forEach(function(relativeTest) {
var a = url.resolve(relativeTest[1], relativeTest[0]),
e = relativeTest[2];
assert.equal(a, e,
'resolve(' + [relativeTest[1], relativeTest[0]] + ') == ' + e +
'\n actual=' + a);
test('resolve(' + [relativeTest[1], relativeTest[0]] + ')', function() {
var a = url.resolve(relativeTest[1], relativeTest[0]),
e = relativeTest[2];
assert.equal(a, e,
'resolve(' + [relativeTest[1], relativeTest[0]] + ') == ' + e +
'\n actual=' + a);
});
});

@@ -1444,23 +1579,25 @@

relativeTests.forEach(function(relativeTest) {
var actual = url.resolveObject(url.parse(relativeTest[0]), relativeTest[1]),
expected = url.parse(relativeTest[2]);
test('resolveObject(' + [relativeTest[0], relativeTest[1]] + ')', function() {
var actual = url.resolveObject(url.parse(relativeTest[0]), relativeTest[1]),
expected = url.parse(relativeTest[2]);
assert.deepEqual(actual, expected);
assert.deepEqual(actual, expected);
expected = relativeTest[2];
actual = url.format(actual);
expected = relativeTest[2];
actual = url.format(actual);
assert.equal(actual, expected,
'format(' + actual + ') == ' + expected + '\nactual:' + actual);
assert.equal(actual, expected,
'format(' + actual + ') == ' + expected + '\nactual:' + actual);
});
});
//format: [to, from, result]
// the test: ['.//g', 'f:/a', 'f://g'] is a fundimental problem
// the test: ['.//g', 'f:/a', 'f://g'] is a fundamental problem
// url.parse('f:/a') does not have a host
// url.resolve('f:/a', './/g') does not have a host becuase you have moved
// url.resolve('f:/a', './/g') does not have a host because you have moved
// down to the g directory. i.e. f: //g, however when this url is parsed
// f:// will indicate that the host is g which is not the case.
// it is unclear to me how to keep this information from being lost
// it may be that a pathname of ////g should colapse to /g but this seems
// it may be that a pathname of ////g should collapse to /g but this seems
// to be a lot of work for an edge case. Right now I remove the test

@@ -1472,16 +1609,17 @@ if (relativeTests2[181][0] === './/g' &&

}
relativeTests2.forEach(function(relativeTest) {
var actual = url.resolveObject(url.parse(relativeTest[1]), relativeTest[0]),
expected = url.parse(relativeTest[2]);
test('resolveObject(' + [relativeTest[1], relativeTest[0]] + ')', function() {
var actual = url.resolveObject(url.parse(relativeTest[1]), relativeTest[0]),
expected = url.parse(relativeTest[2]);
assert.deepEqual(actual, expected);
assert.deepEqual(actual, expected);
var expected = relativeTest[2],
actual = url.format(actual);
var expected = relativeTest[2],
actual = url.format(actual);
assert.equal(actual, expected,
'format(' + relativeTest[1] + ') == ' + expected +
'\nactual:' + actual);
assert.equal(actual, expected,
'format(' + relativeTest[1] + ') == ' + expected +
'\nactual:' + actual);
});
});
});

@@ -22,3 +22,6 @@ // Copyright Joyent, Inc. and other Node contributors.

'use strict';
var punycode = require('punycode');
var util = require('./util');

@@ -54,2 +57,5 @@ exports.parse = urlParse;

// Special case for a simple path URL
simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,
// RFC 2396: characters reserved for delimiting URLs.

@@ -71,4 +77,4 @@ // We actually just auto-escape these.

hostnameMaxLen = 255,
hostnamePartPattern = /^[a-z0-9A-Z_-]{0,63}$/,
hostnamePartStart = /^([a-z0-9A-Z_-]{0,63})(.*)$/,
hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/,
hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/,
// protocols that can allow "unsafe" and "unwise" chars.

@@ -100,3 +106,3 @@ unsafeProtocol = {

function urlParse(url, parseQueryString, slashesDenoteHost) {
if (url && isObject(url) && url instanceof Url) return url;
if (url && util.isObject(url) && url instanceof Url) return url;

@@ -109,6 +115,17 @@ var u = new Url;

Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
if (!isString(url)) {
if (!util.isString(url)) {
throw new TypeError("Parameter 'url' must be a string, not " + typeof url);
}
// Copy chrome, IE, opera backslash-handling behavior.
// Back slashes before the query string get converted to forward slashes
// See: https://code.google.com/p/chromium/issues/detail?id=25916
var queryIndex = url.indexOf('?'),
splitter =
(queryIndex !== -1 && queryIndex < url.indexOf('#')) ? '?' : '#',
uSplit = url.split(splitter),
slashRegex = /\\/g;
uSplit[0] = uSplit[0].replace(slashRegex, '/');
url = uSplit.join(splitter);
var rest = url;

@@ -120,2 +137,24 @@

if (!slashesDenoteHost && url.split('#').length === 1) {
// Try fast path regexp
var simplePath = simplePathPattern.exec(rest);
if (simplePath) {
this.path = rest;
this.href = rest;
this.pathname = simplePath[1];
if (simplePath[2]) {
this.search = simplePath[2];
if (parseQueryString) {
this.query = querystring.parse(this.search.substr(1));
} else {
this.query = this.search.substr(1);
}
} else if (parseQueryString) {
this.search = '';
this.query = {};
}
return this;
}
}
var proto = protocolPattern.exec(rest);

@@ -258,14 +297,7 @@ if (proto) {

if (!ipv6Hostname) {
// IDNA Support: Returns a puny coded representation of "domain".
// It only converts the part of the domain name that
// has non ASCII characters. I.e. it dosent matter if
// you call it with a domain that already is in ASCII.
var domainArray = this.hostname.split('.');
var newOut = [];
for (var i = 0; i < domainArray.length; ++i) {
var s = domainArray[i];
newOut.push(s.match(/[^A-Za-z0-9_-]/) ?
'xn--' + punycode.encode(s) : s);
}
this.hostname = newOut.join('.');
// IDNA Support: Returns a punycoded representation of "domain".
// It only converts parts of the domain name that
// have non-ASCII characters, i.e. it doesn't matter if
// you call it with a domain that already is ASCII-only.
this.hostname = punycode.toASCII(this.hostname);
}

@@ -297,2 +329,4 @@

var ae = autoEscape[i];
if (rest.indexOf(ae) === -1)
continue;
var esc = encodeURIComponent(ae);

@@ -351,3 +385,3 @@ if (esc === ae) {

// to clean up potentially wonky urls.
if (isString(obj)) obj = urlParse(obj);
if (util.isString(obj)) obj = urlParse(obj);
if (!(obj instanceof Url)) return Url.prototype.format.call(obj);

@@ -383,3 +417,3 @@ return obj.format();

if (this.query &&
isObject(this.query) &&
util.isObject(this.query) &&
Object.keys(this.query).length) {

@@ -428,3 +462,3 @@ query = querystring.stringify(this.query);

Url.prototype.resolveObject = function(relative) {
if (isString(relative)) {
if (util.isString(relative)) {
var rel = new Url();

@@ -436,5 +470,7 @@ rel.parse(relative, false, true);

var result = new Url();
Object.keys(this).forEach(function(k) {
result[k] = this[k];
}, this);
var tkeys = Object.keys(this);
for (var tk = 0; tk < tkeys.length; tk++) {
var tkey = tkeys[tk];
result[tkey] = this[tkey];
}

@@ -454,6 +490,8 @@ // hash is always overridden, no matter what.

// take everything except the protocol from relative
Object.keys(relative).forEach(function(k) {
if (k !== 'protocol')
result[k] = relative[k];
});
var rkeys = Object.keys(relative);
for (var rk = 0; rk < rkeys.length; rk++) {
var rkey = rkeys[rk];
if (rkey !== 'protocol')
result[rkey] = relative[rkey];
}

@@ -480,5 +518,7 @@ //urlParse appends trailing / to urls like http://www.example.com

if (!slashedProtocol[relative.protocol]) {
Object.keys(relative).forEach(function(k) {
var keys = Object.keys(relative);
for (var v = 0; v < keys.length; v++) {
var k = keys[v];
result[k] = relative[k];
});
}
result.href = result.format();

@@ -572,3 +612,3 @@ return result;

result.query = relative.query;
} else if (!isNullOrUndefined(relative.search)) {
} else if (!util.isNullOrUndefined(relative.search)) {
// just pull out the search.

@@ -580,3 +620,3 @@ // like href='?foo'.

//occationaly the auth can get stuck only in host
//this especialy happens in cases like
//this especially happens in cases like
//url.resolveObject('mailto:local1@domain1', 'local2@domain2')

@@ -593,3 +633,3 @@ var authInHost = result.host && result.host.indexOf('@') > 0 ?

//to support http.request
if (!isNull(result.pathname) || !isNull(result.search)) {
if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
result.path = (result.pathname ? result.pathname : '') +

@@ -621,4 +661,4 @@ (result.search ? result.search : '');

var hasTrailingSlash = (
(result.host || relative.host) && (last === '.' || last === '..') ||
last === '');
(result.host || relative.host || srcPath.length > 1) &&
(last === '.' || last === '..') || last === '');

@@ -630,3 +670,3 @@ // strip single dots, resolve double dots to parent dir

last = srcPath[i];
if (last == '.') {
if (last === '.') {
srcPath.splice(i, 1);

@@ -666,3 +706,3 @@ } else if (last === '..') {

//occationaly the auth can get stuck only in host
//this especialy happens in cases like
//this especially happens in cases like
//url.resolveObject('mailto:local1@domain1', 'local2@domain2')

@@ -691,3 +731,3 @@ var authInHost = result.host && result.host.indexOf('@') > 0 ?

//to support request.http
if (!isNull(result.pathname) || !isNull(result.search)) {
if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
result.path = (result.pathname ? result.pathname : '') +

@@ -714,16 +754,1 @@ (result.search ? result.search : '');

};
function isString(arg) {
return typeof arg === "string";
}
function isObject(arg) {
return typeof arg === 'object' && arg !== null;
}
function isNull(arg) {
return arg === null;
}
function isNullOrUndefined(arg) {
return arg == null;
}
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