Socket
Socket
Sign inDemoInstall

micromatch

Package Overview
Dependencies
93
Maintainers
1
Versions
66
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.2.2 to 1.3.0

lib/glob.js

266

index.js

@@ -11,3 +11,5 @@ /*!

var diff = require('arr-diff');
var fileRe = require('filename-regex');
var typeOf = require('kind-of');
var cache = require('regex-cache');
var isGlob = require('is-glob');
var expand = require('./lib/expand');

@@ -80,4 +82,5 @@ var utils = require('./lib/utils');

var negate = opts.negate || false;
var orig = pattern;
if (opts.nonegate !== true) {
if (typeof pattern === 'string' && opts.nonegate !== true) {
negate = pattern.charAt(0) === '!';

@@ -89,6 +92,3 @@ if (negate) {

if (!(pattern instanceof RegExp)) {
pattern = makeRe(pattern, opts);
}
var isMatch = matcher(pattern, opts);
var len = files.length;

@@ -102,11 +102,25 @@ var res = [];

if (!isMatch(fp, pattern, opts)) { continue; }
if (!isMatch(fp)) { continue; }
res.push(fp);
}
if (opts.nonull && !res.length) {
return pattern;
if (res.length === 0) {
if (opts.failglob === true) {
throw new Error('micromatch found no matches for: "' + orig + '".');
}
if (opts.nonull || opts.nullglob) {
res.push(utils.unescapeGlob(orig));
}
}
if (negate) { return diff(files, res); }
// if `negate` was diffined, diff negated files
if (negate) { res = diff(files, res); }
// if `ignore` was defined, diff ignored filed
if (opts.ignore && opts.ignore.length) {
pattern = opts.ignore;
delete opts.ignore;
return diff(res, micromatch(res, pattern, opts));
}
return res;

@@ -116,21 +130,74 @@ }

/**
* Returns true if the filepath matches the given
* pattern.
* Return a function for matching based on the
* given `pattern` and `options`.
*
* @param {String} `pattern`
* @param {Object} `options`
* @return {Function}
*/
function isMatch(fp, pattern, opts) {
function matcher(pattern, opts) {
// pattern is a function
if (typeof pattern === 'function') {
return pattern;
}
// pattern is a string
if (!(pattern instanceof RegExp)) {
pattern = makeRe(pattern, opts);
if (!isGlob(pattern)) {
return utils.matchPath(pattern, opts);
}
var re = makeRe(pattern, opts);
if (opts && opts.matchBase) {
return utils.hasFilename(re, opts);
}
return function (fp) {
return re.test(fp);
};
}
// pattern is already a regex
return function (fp) {
return pattern.test(fp);
};
}
if (opts && opts.matchBase) {
var matches = fileRe().exec(fp);
if (pattern.test(matches[0])) {
return true;
}
/**
* Returns true if the filepath contains the given
* pattern. Can also return a function for matching.
*
* ```js
* isMatch('foo.md', '*.md', {});
* //=> true
*
* isMatch('*.md', {})('foo.md')
* //=> true
* ```
*
* @param {String} `fp`
* @param {String} `pattern`
* @param {Object} `opts`
* @return {Boolean}
*/
function isMatch(fp, pattern, opts) {
if (typeOf(pattern) === 'object') {
return matcher(fp, pattern);
}
return pattern.test(fp);
return matcher(pattern, opts)(fp);
}
/**
* Returns true if the filepath matches the
* given pattern.
*/
function contains(fp, pattern, opts) {
opts = opts || {};
opts.contains = (pattern !== '');
if (opts.contains && !isGlob(pattern)) {
return fp.indexOf(pattern) !== -1;
}
return matcher(pattern, opts)(fp);
}
/**
* Filter the keys in an object.

@@ -147,10 +214,9 @@ *

var keys = Object.keys(obj);
var len = keys.length;
var res = {};
while (len--) {
var key = keys[len];
if (re.test(key)) {
res[key] = obj[key];
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (re.test(key)) {
res[key] = obj[key];
}
}

@@ -192,3 +258,2 @@ }

}
return res;

@@ -210,47 +275,33 @@ };

function makeRe(glob, options) {
var opts = options || {};
function toRegex(glob, options) {
// clone options to prevent mutating upstream variables
var opts = Object.create(options || {});
var flags = opts.flags || '';
// reset cache, recompile regex if options change
optsCache = typeof optsCache !== 'undefined'
? optsCache
: opts;
if (!equal(optsCache, opts)) {
cache = glob;
globRe = null;
if (opts.nocase && !/i/.test(flags)) {
flags += 'i';
}
// reset cache, recompile regex if glob changes
cache = typeof cache !== 'undefined'
? cache
: glob;
if (cache !== glob) {
glob = utils.unixify(glob, opts);
cache = glob;
globRe = null;
}
// if `true`, then we can just return
// the regex that was previously cached
if (globRe instanceof RegExp) {
return globRe;
}
if (opts.nocase) { flags += 'i'; }
// pass in tokens to avoid parsing more than once
// var parsed = !tokens ? expand(glob, opts) : tokens;
var parsed = expand(glob, opts);
opts.negated = opts.negated || parsed.negated || false;
glob = wrapGlob(parsed.glob, opts.negated);
opts.negated = opts.negated || parsed.negated;
opts.negate = opts.negated;
glob = wrapGlob(parsed.pattern, opts);
// cache regex
globRe = new RegExp(glob, flags);
return globRe;
try {
return new RegExp(glob, flags);
} catch (err) {}
return /^$/;
}
/**
* Wrap `toRegex` to memoize the generated regex
* the string and options don't change
*/
function makeRe(glob, opts) {
return cache(toRegex, glob, opts);
}
/**
* Create the regex to do the matching. If

@@ -264,40 +315,25 @@ * the leading character in the `glob` is `!`

function wrapGlob(glob, negate) {
glob = ('(?:' + glob + ')$');
return '^' + (negate ? ('(?!^' + glob + ').*$') : glob);
}
/**
* Return true if object A is equal (enough)
* to object B. Used for options caching. All
* we need to know is if the object has changed
* in any way.
*
* @param {Object} a
* @param {Object} b
* @return {Boolean}
*/
function equal(a, b) {
if (!b) return false;
for (var prop in b) {
if (!a.hasOwnProperty(prop) || a[prop] !== b[prop]) {
return false;
}
function wrapGlob(glob, opts) {
var prefix = (opts && !opts.contains) ? '^' : '';
var after = (opts && !opts.contains) ? '$' : '';
glob = ('(?:' + glob + ')' + after);
if (opts && opts.negate) {
return prefix + ('(?!^' + glob + ').*$');
}
return true;
return prefix + glob;
}
/**
* Results cache
* Public methods
*/
var globRe;
var cache;
var optsCache;
micromatch.braces = micromatch.braceExpand = require('braces');
micromatch.expand = expand;
micromatch.filter = filter;
micromatch.isMatch = isMatch;
micromatch.contains = contains;
micromatch.makeRe = makeRe;
micromatch.match = match;
micromatch.matchKeys = matchKeys;
// no, this isn't permanent. I will organize
// the following when the API is locked.
/**

@@ -308,43 +344,1 @@ * Expose `micromatch`

module.exports = micromatch;
/**
* Expose `micromatch.match`
*/
module.exports.match = match;
/**
* Expose `micromatch.isMatch`
*/
module.exports.isMatch = isMatch;
/**
* Expose `micromatch.matchKeys`
*/
module.exports.matchKeys = matchKeys;
/**
* Expose `micromatch.makeRe`
*/
module.exports.makeRe = makeRe;
/**
* Expose `micromatch.braces`
*/
module.exports.braces = require('braces');
/**
* Expose `micromatch.filter`
*/
module.exports.filter = filter;
/**
* Expose `micromatch.expand`
*/
module.exports.expand = expand;

@@ -10,7 +10,4 @@ /*!

var isGlob = require('is-glob');
var braces = require('braces');
var parse = require('./parse');
var utils = require('./utils');
var chars = require('./chars');
var Glob = require('./glob');

@@ -33,53 +30,60 @@ /**

function expand(glob, opts) {
opts = opts || {};
function expand(pattern, options) {
var opts = options || {};
var glob = new Glob(pattern, opts);
function replace(re, str) {
glob = glob.replace(re, esc(str));
glob.repl(re, esc(str));
pattern = glob.pattern;
}
if (specialCase(glob) && opts.safemode) {
return new RegExp(utils.escapeRe(glob));
// return early if the glob pattern tests `true`
if (specialCase(pattern) && opts.safemode) {
return new RegExp(utils.escapeRe(pattern), 'g');
}
if (opts.nonegate !== true) {
var negate = glob.charCodeAt(0) === 33; /* '!' */
if (negate) {
opts.negated = true;
glob = glob.slice(1);
}
opts.negated = glob.negated;
}
// expand braces, e.g `{1..5}`
if (glob.indexOf('{') !== -1 && opts.nobraces !== true) {
glob = expandBraces(glob, opts);
}
glob.track('before brackets');
glob.brackets();
glob.track('before braces');
glob.braces();
glob.track('after braces');
glob.parse();
glob.repl('[]', '\\[\\]');
glob.repl('(?', '__QMARK_GROUP__');
// parse the glob pattern into tokens
var tok = parse.glob(glob, opts);
if (tok.isDotGlob) {
var tok = glob.tokens;
if (tok.dotfiles) {
opts.dot = true;
}
if (!isGlob(glob)) {
return {glob: utils.escapePath(glob), tokens: tok, options: opts};
if (!tok.isGlob) {
return {
pattern: utils.escapePath(glob.pattern),
tokens: tok,
options: opts
};
}
if (glob === '**' && opts.globstar !== false) {
glob = doublestar(opts);
if (glob.pattern === '**' && opts.globstar !== false) {
glob.pattern = globstar(opts);
} else {
if (/^\*\.\w*$/.test(glob)) {
glob = glob.replace(/\*/, star(opts.dot) + '\\');
return {glob: glob, tokens: tok, options: opts};
if (/^\*\.\w*$/.test(glob.pattern)) {
glob.repl('*', star(opts.dot) + '\\');
glob.repl('__QMARK_GROUP__', '(?');
return glob;
}
// fix imbalanced brackets and parens
if (/[[\]()]/.test(glob)) {
glob = brackets(glob);
glob = parens(glob);
}
glob.pattern = balance(glob.pattern, '[', ']');
// use heuristics to replace common escape patterns
glob = escape(glob);
glob.escape(glob.pattern);

@@ -89,26 +93,34 @@ // if the glob is for one directory deep, we can

if (tok.dirname === '') {
return expandFilename(glob, tok, opts);
return expandFilename(glob, opts);
}
// windows drives
replace(/^(\w):([\\\/]+?)/gi, lookahead + '$1:$2');
// has '**/'
replace(/\*\*\//g, '.*\\/?');
// has '**'
replace(/\*\*/g, doublestar(opts));
// ends with '/*'
// if the pattern has `**`
if (tok.globstar) {
glob.repl(/(^|[^\\])\*{2,}([^\\]|$)/g, '$1**$2');
// foo/**
replace(/(\w+)\*\*(?!\/)/g, '(?=.)$1[^/]*?');
// **/
replace('**/', '(.*\\/|^)');
// **
replace('**', globstar(opts));
}
// *.*
replace('*.*', '([^/]*?.[^/]*?)');
// ends with /*
replace(/\/\*$/g, '\\/' + stardot(opts));
// ends with '*', no slashes
// ends with *, no slashes
replace(/(?!\/)\*$/g, boxQ);
// has '*'
replace(/\*/g, stardot(opts));
replace('*', stardot(opts));
// has '?.'
replace(/\?\./g, '?\\.');
// has '?:'
replace(/\?:/g, '?:');
replace('?.', '?\\.');
replace('?:', '?:');
// first of '????'
replace(/[^?]\?/g, '\\/'+ dotstarbase(opts.dot) + box);
replace(/(?!\?)\?/g, '\\/'+ dotstarbase(opts.dot) + box);
// rest of '????'
replace(/\?/g, box);
replace(/(?!\()\?/g, box);

@@ -123,7 +135,10 @@ // escape '.abc' => '\\.abc'

replace(/\\+\//g, '\\/');
glob = unesc(glob);
}
glob = unescape(glob);
return {glob: glob, tokens: tok, options: opts};
glob.repl('__QMARK_GROUP__', '(?');
glob.unescape(glob.pattern);
glob.repl('__UNESC_STAR__', '*');
glob.repl('%~', '?');
glob.repl('%%', '*');
return glob;
}

@@ -141,34 +156,41 @@

function expandFilename(glob, tok, opts) {
switch (glob) {
function expandFilename(glob, opts) {
var tok = glob.tokens;
switch (glob.pattern) {
case '.':
glob = '\\.';
glob.pattern = '\\.';
break;
case '.*':
glob = '\\..*';
glob.pattern = '\\..*';
break;
case '*.*':
glob = star(opts.dot) + '\\.[^/]*?';
glob.pattern = star(opts.dot) + '\\.[^/]*?';
break;
case '*':
glob = star(opts.dot);
glob.pattern = star(opts.dot);
break;
default:
if (tok.basename === '*') {
glob = star(opts.dot) + '\\' + tok.extname;
glob.pattern = star(opts.dot) + '\\' + tok.extname;
} else {
glob = glob.replace(/\?/g, '[^/]');
if (/^[^.]/.test(tok.filename)) {
glob.repl(/(?!\()\?/g, '[^/]');
if (tok.filename.charAt(0) !== '.') {
opts.dot = true;
}
glob = glob.replace(/\*/g, star(opts.dot));
glob.repl('*', star(opts.dot));
}
}
glob = unescape(glob);
return {glob: glob, options: opts};
glob.repl('__QMARK_GROUP__', '(?');
glob.unescape(glob.pattern);
glob.repl('__UNESC_STAR__', '*');
return glob;
}
/**
* Special cases
*/
function specialCase(glob) {
if (/^\\$/.test(glob)) {
if (glob === '\\') {
return true;

@@ -180,34 +202,15 @@ }

/**
* Naive approach to fixing brackets and parens
* in character classes etc.
* Escape imbalanced braces/bracket
*/
function brackets(glob) {
var lt = glob.match(/\[/g);
var rt = glob.match(/\]/g);
if (lt && rt && lt.length !== rt.length) {
if (lt.length > rt.length) {
return glob.replace(/\[/, '\\[');
}
return glob.replace(/\]/, '\\]');
}
if (lt && !rt) {
return glob.replace(/\[/, '\\[');
}
if (rt && !lt) {
return glob.replace(/\]/, '\\]');
}
return glob;
}
function balance(str, a, b) {
var aarr = str.split(a);
var alen = aarr.join('').length;
var blen = str.split(b).join('').length;
function parens(glob) {
var lt = glob.match(/\(/g);
var rt = glob.match(/\)/g);
if (lt && rt && lt.length !== rt.length) {
if (lt.length > rt.length) {
return glob.replace(/\(/, '\\(');
}
return glob.replace(/\)/, '\\)');
if (alen !== blen) {
str = aarr.join('\\' + a);
return str.split(b).join('\\' + b);
}
return glob;
return str;
}

@@ -220,24 +223,7 @@

function esc(str) {
return str.replace(/\?/g, '%~')
.replace(/\*/g, '%%');
str = str.split('?').join('%~');
str = str.split('*').join('%%');
return str;
}
function unesc(str) {
return str.replace(/%%/g, '*')
.replace(/%~/g, '?');
}
function escape(str, ch) {
var re = ch ? chars.escapeRegex[ch] : /\\([^\\])/g;
return str.replace(re, function($0, $1) {
return chars[ch ? 'ESC_TEMP' : 'ESC'][ch ? $0 : $1];
});
}
function unescape(str) {
return str.replace(/__([A-Z]+)_([A-Z]+)__/g, function($0, $1) {
return chars[$1][$0];
});
}
/**

@@ -249,3 +235,2 @@ * Special patterns to be converted to regex.

var box = '[^/]';

@@ -257,4 +242,3 @@ var boxQ = '[^/]*?';

var ex = {};
ex.dotfileGlob = '(?:^|\\\/)(?:\\.{1,2})(?:$|\\\/)';
ex.dotfileGlob = '(?:^|\\/)(?:\\.{1,2})(?:$|\\/)';
ex.stardot = '(?!' + ex.dotfileGlob + ')(?=.)[^/]*?';

@@ -278,5 +262,5 @@ ex.twoStarDot = '(?:(?!' + ex.dotfileGlob + ').)*?';

function doublestar(opts) {
function globstar(opts) {
if (opts.dot) { return ex.twoStarDot; }
return '(?:(?!(?:^|\\\/)\\.).)*?';
return '(?:(?!(?:^|\\/)\\.).)*?';
}

@@ -287,27 +271,1 @@

}
/**
* Expand braces in the given glob pattern.
*
* We only need to use the [braces] lib when
* patterns are nested.
*
* @param {String} `glob`
* @return {String}
*/
function expandBraces(glob, options) {
options = options || {};
options.makeRe = options.makeRe || true;
var a = glob.match(/[\{\(\[]/g);
var b = glob.match(/[\}\)\]]/g);
if (a && b && (a.length !== b.length)) {
options.makeRe = false;
}
var res = braces(glob, options);
return res.join('|');
}
'use strict';
var path = require('path');
var fileRe = require('filename-regex');
var win32 = process.platform === 'win32';
var win;
var utils = module.exports;
utils.filename = function filename(fp) {
var seg = fp.match(fileRe());
return seg && seg[0];
};
utils.isPath = function isPath(pattern) {
return function (fp) {
return fp === pattern;
};
};
utils.hasPath = function hasPath(pattern) {
return function (fp) {
return fp.indexOf(pattern) !== -1;
};
};
utils.matchPath = function matchPath(pattern, opts) {
var fn = (opts && opts.contains)
? utils.hasPath(pattern)
: utils.isPath(pattern);
return fn;
};
utils.hasFilename = function hasFilename(re) {
return function (fp) {
var name = utils.filename(fp);
return name && re.test(name);
};
};
/**

@@ -14,3 +48,3 @@ * Coerce `val` to an array

exports.arrayify = function arrayify(val) {
utils.arrayify = function arrayify(val) {
return !Array.isArray(val)

@@ -25,4 +59,4 @@ ? [val]

exports.unixify = function unixify(fp, opts) {
if (opts && opts.normalize) {
utils.unixify = function unixify(fp, opts) {
if (opts && opts.unixify === true) {
win = true;

@@ -38,27 +72,16 @@ } else if (opts && opts.cache && typeof win === 'undefined') {

exports.escapePath = function escapePath(fp) {
/**
* Escape/unescape utils
*/
utils.escapePath = function escapePath(fp) {
return fp.replace(/[\\.]/g, '\\$&');
};
exports.isDrive = function isDrive(fp) {
return /^\w:/.test(fp);
utils.unescapeGlob = function unescapeGlob(fp) {
return fp.replace(/[\\"']/g, '');
};
exports.isCWD = function isCWD(fp) {
return fp.charAt(0) === '.';
utils.escapeRe = function escapeRe(str) {
return str.replace(/[-[\\$*+?.#^\s{}(|)\]]/g, '\\$&');
};
exports.isLongPath = function isLongPath(fp, len) {
if (typeof len === 'number') {
return len > 248;
}
return fp.length > 248;
};
exports.isUNC = function isUNC(fp) {
return /^\\\\/.test(fp);
};
exports.escapeRe = function escapeRe(str) {
return str.replace(/[$*+?.#\\^\s[-\]{}(|)]/g, '\\$&');
};
{
"name": "micromatch",
"description": "Glob matching for javascript/node.js. A faster alternative to minimatch (10-20x faster on avg), with all the features you're used to using in your Grunt and gulp tasks.",
"version": "1.2.2",
"description": "Glob matching for javascript/node.js. A faster alternative to minimatch (10-45x faster on avg), with all the features you're used to using in your Grunt and gulp tasks.",
"version": "1.3.0",
"homepage": "https://github.com/jonschlinkert/micromatch",

@@ -19,3 +19,3 @@ "author": {

"type": "MIT",
"url": "https://github.com/jonschlinkert/micromatch/blob/master/LICENSE-MIT"
"url": "https://github.com/jonschlinkert/micromatch/blob/master/LICENSE"
},

@@ -27,3 +27,3 @@ "main": "index.js",

"scripts": {
"test": "mocha -R spec",
"test": "mocha",
"benchmark": "node benchmark"

@@ -38,5 +38,10 @@ },

"braces": "^1.6.0",
"expand-brackets": "^0.1.0",
"extglob": "^0.2.0",
"filename-regex": "^2.0.0",
"glob-path-regex": "^1.0.0",
"is-glob": "^1.1.0"
"is-glob": "^1.1.0",
"kind-of": "^1.1.0",
"parse-glob": "^1.2.0",
"regex-cache": "^0.2.0"
},

@@ -43,0 +48,0 @@ "devDependencies": {

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

# micromatch [![NPM version](https://badge.fury.io/js/micromatch.svg)](http://badge.fury.io/js/micromatch)
# micromatch [![NPM version](https://badge.fury.io/js/micromatch.svg)](http://badge.fury.io/js/micromatch) [![Build Status](https://travis-ci.org/jonschlinkert/micromatch.svg)](https://travis-ci.org/jonschlinkert/micromatch)
> Glob matching for javascript/node.js. A faster alternative to minimatch (10-20x faster on avg), with all the features you're used to using in your Grunt and gulp tasks.
> Glob matching for javascript/node.js. A faster alternative to minimatch (10-45x faster on avg), with all the features you're used to using in your Grunt and gulp tasks.
- 10-20x faster than [minimatch] on average ([see benchmarks](#benchmarks))
- 10-45x faster than [minimatch] on average ([see benchmarks](#benchmarks))
- Focus on core Bash 4.3 specification features that are actually used (or can be used) in node.js

@@ -20,2 +20,4 @@ - Supports passing glob patterns as a string or array

+ Regex character classes (`foo/bar/baz-[1-5].js`)
+ POSIX bracket expressions (`**/[:alpha:][:digit:]/`)
+ extglobs (`**/+(x|y)`, `!(a|b)`, etc)

@@ -118,2 +120,16 @@ You can combine these to create whatever matching patterns you need.

### .contains
Returns true if a file path contains a match for the given glob pattern.
**Example**
```js
mm.contains('a/b/c', 'a/b');
//=> true
mm.contains('a/b/c', 'a/*');
//=> true
```
### .filter

@@ -136,3 +152,3 @@

```js
mm.makeRe('*.js');
mm.expand('*.js');
// results in:

@@ -234,3 +250,3 @@ // { glob: '(?!\\.)(?=.)[^/]*?\\.js',

As of January 30, 2015:
As of February 11, 2015:

@@ -318,3 +334,3 @@ ```bash

_This file was generated by [verb](https://github.com/assemble/verb) on January 30, 2015._
_This file was generated by [verb](https://github.com/assemble/verb) on February 11, 2015._

@@ -326,10 +342,13 @@ [extended]: http://mywiki.wooledge.org/BashGuide/Patterns#Extended_Globs

[arr-diff]: https://github.com/jonschlinkert/arr-diff
[arr-filter]: https://github.com/jonschlinkert/arr-filter
[arr-filter]: null
[arr-map]: https://github.com/jonschlinkert/arr-map
[array-slice]: https://github.com/jonschlinkert/array-slice
[benchmarked]: null
[braces]: https://github.com/jonschlinkert/braces
[chalk]: null
[expand-brackets]: https://github.com/jonschlinkert/expand-brackets
[expand-range]: https://github.com/jonschlinkert/expand-range
[extglob]: https://github.com/jonschlinkert/extglob
[filename-regex]: https://github.com/regexps/filename-regex
[fill-range]: https://github.com/jonschlinkert/fill-range
[for-in]: https://github.com/jonschlinkert/for-in
[for-own]: https://github.com/jonschlinkert/for-own
[glob-path-regex]: https://github.com/regexps/glob-path-regex

@@ -340,5 +359,7 @@ [is-glob]: https://github.com/jonschlinkert/is-glob

[kind-of]: https://github.com/jonschlinkert/kind-of
[make-iterator]: https://github.com/jonschlinkert/make-iterator
[micromatch]: https://github.com/jonschlinkert/micromatch
[parse-glob]: https://github.com/jonschlinkert/parse-glob
[preserve]: https://github.com/jonschlinkert/preserve
[randomatic]: https://github.com/jonschlinkert/randomatic
[regex-cache]: https://github.com/jonschlinkert/regex-cache
[repeat-element]: https://github.com/jonschlinkert/repeat-element

@@ -345,0 +366,0 @@ [repeat-string]: https://github.com/jonschlinkert/repeat-string

SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc