Socket
Socket
Sign inDemoInstall

zxcvbn

Package Overview
Dependencies
0
Maintainers
1
Versions
23
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 4.2.0 to 4.3.0

2

lib/feedback.js

@@ -90,3 +90,3 @@ // Generated by CoffeeScript 1.10.0

suggestions.push("Capitalization doesn't help very much");
} else if (word.match(scoring.ALL_UPPER)) {
} else if (word.match(scoring.ALL_UPPER) && word.toLowerCase() !== word) {
suggestions.push("All-uppercase is almost as easy to guess as all-lowercase");

@@ -93,0 +93,0 @@ }

// Generated by CoffeeScript 1.10.0
var DATE_MAX_YEAR, DATE_MIN_YEAR, DATE_SPLITS, GRAPHS, L33T_TABLE, RANKED_DICTIONARIES, REGEXEN, SEQUENCES, adjacency_graphs, build_ranked_dict, frequency_lists, lst, matching, name, scoring,
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
var DATE_MAX_YEAR, DATE_MIN_YEAR, DATE_SPLITS, GRAPHS, L33T_TABLE, RANKED_DICTIONARIES, REGEXEN, adjacency_graphs, build_ranked_dict, frequency_lists, lst, matching, name, scoring;

@@ -37,8 +36,2 @@ frequency_lists = require('./frequency_lists');

SEQUENCES = {
lower: 'abcdefghijklmnopqrstuvwxyz',
upper: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
digits: '0123456789'
};
L33T_TABLE = {

@@ -450,29 +443,28 @@ a: ['4', '@'],

},
MAX_DELTA: 5,
sequence_match: function(password) {
var direction, i, j, len1, matches, next_sequence_position, o, ref, ref1, sequence, sequence_name, sequence_position;
matches = [];
for (sequence_name in SEQUENCES) {
sequence = SEQUENCES[sequence_name];
ref = [1, -1];
for (o = 0, len1 = ref.length; o < len1; o++) {
direction = ref[o];
i = 0;
while (i < password.length) {
if (ref1 = password.charAt(i), indexOf.call(sequence, ref1) < 0) {
i += 1;
continue;
}
j = i + 1;
sequence_position = sequence.indexOf(password.charAt(i));
while (j < password.length) {
next_sequence_position = this.mod(sequence_position + direction, sequence.length);
if (sequence.indexOf(password.charAt(j)) !== next_sequence_position) {
break;
var delta, i, j, k, last_delta, o, ref, result, update;
if (password.length === 1) {
return [];
}
update = (function(_this) {
return function(i, j, delta) {
var ref, sequence_name, sequence_space, token;
if (j - i > 1 || Math.abs(delta) === 1) {
if ((0 < (ref = Math.abs(delta)) && ref <= _this.MAX_DELTA)) {
token = password.slice(i, +j + 1 || 9e9);
if (/^[a-z]+$/.test(token)) {
sequence_name = 'lower';
sequence_space = 26;
} else if (/^[A-Z]+$/.test(token)) {
sequence_name = 'upper';
sequence_space = 26;
} else if (/^\d+$/.test(token)) {
sequence_name = 'digits';
sequence_space = 10;
} else {
sequence_name = 'unicode';
sequence_space = 26;
}
j += 1;
sequence_position = next_sequence_position;
}
j -= 1;
if (j - i + 1 > 1) {
matches.push({
return result.push({
pattern: 'sequence',

@@ -483,11 +475,27 @@ i: i,

sequence_name: sequence_name,
sequence_space: sequence.length,
ascending: direction === 1
sequence_space: sequence_space,
ascending: delta > 0
});
}
i = j + 1;
}
};
})(this);
result = [];
i = 0;
last_delta = null;
for (k = o = 1, ref = password.length; 1 <= ref ? o < ref : o > ref; k = 1 <= ref ? ++o : --o) {
delta = password.charCodeAt(k) - password.charCodeAt(k - 1);
if (last_delta == null) {
last_delta = delta;
}
if (delta === last_delta) {
continue;
}
j = k - 1;
update(i, j, last_delta);
i = j;
last_delta = delta;
}
return this.sorted(matches);
update(i, password.length - 1, last_delta);
return result;
},

@@ -682,5 +690,5 @@ regex_match: function(password, _regexen) {

} else if (year > 50) {
return year + scoring.REFERENCE_YEAR - 100;
return year + 1900;
} else {
return year + scoring.REFERENCE_YEAR;
return year + 2000;
}

@@ -687,0 +695,0 @@ }

@@ -12,6 +12,6 @@ // Generated by CoffeeScript 1.10.0

average += ((function() {
var len, m, results;
var len, o, results;
results = [];
for (m = 0, len = neighbors.length; m < len; m++) {
n = neighbors[m];
for (o = 0, len = neighbors.length; o < len; o++) {
n = neighbors[o];
if (n) {

@@ -46,3 +46,3 @@ results.push(n);

nCk: function(n, k) {
var d, m, r, ref;
var d, o, r, ref;
if (k > n) {

@@ -55,3 +55,3 @@ return 0;

r = 1;
for (d = m = 1, ref = k; 1 <= ref ? m <= ref : m >= ref; d = 1 <= ref ? ++m : --m) {
for (d = o = 1, ref = k; 1 <= ref ? o <= ref : o >= ref; d = 1 <= ref ? ++o : --o) {
r *= n;

@@ -70,3 +70,3 @@ r /= d;

factorial: function(n) {
var f, i, m, ref;
var f, i, o, ref;
if (n < 2) {

@@ -76,3 +76,3 @@ return 1;

f = 1;
for (i = m = 2, ref = n; 2 <= ref ? m <= ref : m >= ref; i = 2 <= ref ? ++m : --m) {
for (i = o = 2, ref = n; 2 <= ref ? o <= ref : o >= ref; i = 2 <= ref ? ++o : --o) {
f *= i;

@@ -83,14 +83,100 @@ }

most_guessable_match_sequence: function(password, matches, _exclude_additive) {
var backpointers, bf_i, bf_j, bf_match, candidate_product, candidate_score, consider_bruteforce, guesses, i, j, k, l, len, m, make_bruteforce_match, match, match_sequence, max_l, new_l, o, optimal_l, optimal_product, optimal_score, prev_j, prev_l, q, ref, ref1, ref2, ref3, ref4, ref5, ref6, score;
var _, bruteforce_update, guesses, k, l, len, len1, m, make_bruteforce_match, matches_by_j, n, o, optimal, optimal_match_sequence, q, ref, ref1, u, unwind, update;
if (_exclude_additive == null) {
_exclude_additive = false;
}
optimal_product = [];
backpointers = [];
max_l = 0;
optimal_l = null;
n = password.length;
matches_by_j = (function() {
var o, ref, results;
results = [];
for (_ = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; _ = 0 <= ref ? ++o : --o) {
results.push([]);
}
return results;
})();
for (o = 0, len = matches.length; o < len; o++) {
m = matches[o];
matches_by_j[m.j].push(m);
}
optimal = {
m: (function() {
var q, ref, results;
results = [];
for (_ = q = 0, ref = n; 0 <= ref ? q < ref : q > ref; _ = 0 <= ref ? ++q : --q) {
results.push({});
}
return results;
})(),
pi: (function() {
var q, ref, results;
results = [];
for (_ = q = 0, ref = n; 0 <= ref ? q < ref : q > ref; _ = 0 <= ref ? ++q : --q) {
results.push({});
}
return results;
})(),
g: (function() {
var q, ref, results;
results = [];
for (_ = q = 0, ref = n; 0 <= ref ? q < ref : q > ref; _ = 0 <= ref ? ++q : --q) {
results.push(Infinity);
}
return results;
})(),
l: (function() {
var q, ref, results;
results = [];
for (_ = q = 0, ref = n; 0 <= ref ? q < ref : q > ref; _ = 0 <= ref ? ++q : --q) {
results.push(0);
}
return results;
})()
};
update = (function(_this) {
return function(m, l) {
var g, k, pi;
k = m.j;
pi = _this.estimate_guesses(m, password);
if (l > 1) {
pi *= optimal.pi[m.i - 1][l - 1];
}
g = _this.factorial(l) * pi;
if (!_exclude_additive) {
g += Math.pow(MIN_GUESSES_BEFORE_GROWING_SEQUENCE, l - 1);
}
if (g < optimal.g[k]) {
optimal.g[k] = g;
optimal.l[k] = l;
optimal.m[k][l] = m;
return optimal.pi[k][l] = pi;
}
};
})(this);
bruteforce_update = (function(_this) {
return function(k) {
var l, last_m, ref, results;
m = make_bruteforce_match(0, k);
update(m, 1);
if (k === 0) {
return;
}
ref = optimal.m[k - 1];
results = [];
for (l in ref) {
last_m = ref[l];
l = parseInt(l);
if (last_m.pattern === 'bruteforce') {
m = make_bruteforce_match(last_m.i, k);
results.push(update(m, l));
} else {
m = make_bruteforce_match(k, k);
results.push(update(m, l + 1));
}
}
return results;
};
})(this);
make_bruteforce_match = (function(_this) {
return function(i, j) {
var match;
match = {
return {
pattern: 'bruteforce',

@@ -101,94 +187,39 @@ token: password.slice(i, +j + 1 || 9e9),

};
return match;
};
})(this);
score = (function(_this) {
return function(guess_product, sequence_length) {
var result;
result = _this.factorial(sequence_length) * guess_product;
if (!_exclude_additive) {
result += Math.pow(MIN_GUESSES_BEFORE_GROWING_SEQUENCE, sequence_length - 1);
unwind = (function(_this) {
return function(n) {
var k, l, optimal_match_sequence;
optimal_match_sequence = [];
k = n - 1;
l = optimal.l[k];
while (k >= 0) {
m = optimal.m[k][l];
optimal_match_sequence.unshift(m);
k = m.i - 1;
l--;
}
return result;
return optimal_match_sequence;
};
})(this);
for (k = m = 0, ref = password.length; 0 <= ref ? m < ref : m > ref; k = 0 <= ref ? ++m : --m) {
backpointers[k] = [];
optimal_product[k] = [];
optimal_score = Infinity;
for (prev_l = o = 0, ref1 = max_l; 0 <= ref1 ? o <= ref1 : o >= ref1; prev_l = 0 <= ref1 ? ++o : --o) {
consider_bruteforce = true;
bf_j = k;
if (prev_l === 0) {
bf_i = 0;
new_l = 1;
} else if (((ref2 = backpointers[k - 1]) != null ? (ref3 = ref2[prev_l]) != null ? ref3.pattern : void 0 : void 0) === 'bruteforce') {
bf_i = backpointers[k - 1][prev_l].i;
new_l = prev_l;
} else if (((ref4 = backpointers[k - 1]) != null ? ref4[prev_l] : void 0) != null) {
bf_i = k;
new_l = prev_l + 1;
for (k = q = 0, ref = n; 0 <= ref ? q < ref : q > ref; k = 0 <= ref ? ++q : --q) {
ref1 = matches_by_j[k];
for (u = 0, len1 = ref1.length; u < len1; u++) {
m = ref1[u];
if (m.i > 0) {
for (l in optimal.m[m.i - 1]) {
l = parseInt(l);
update(m, l + 1);
}
} else {
consider_bruteforce = false;
update(m, 1);
}
if (consider_bruteforce) {
bf_match = make_bruteforce_match(bf_i, bf_j);
prev_j = k - bf_match.token.length;
candidate_product = this.estimate_guesses(bf_match, password);
if (new_l > 1) {
candidate_product *= optimal_product[prev_j][new_l - 1];
}
candidate_score = score(candidate_product, new_l);
if (candidate_score < optimal_score) {
optimal_score = candidate_score;
optimal_product[k][new_l] = candidate_product;
optimal_l = new_l;
max_l = Math.max(max_l, new_l);
backpointers[k][new_l] = bf_match;
}
}
for (q = 0, len = matches.length; q < len; q++) {
match = matches[q];
if (!(match.j === k)) {
continue;
}
ref5 = [match.i, match.j], i = ref5[0], j = ref5[1];
if (prev_l === 0) {
if (i !== 0) {
continue;
}
} else {
if (((ref6 = optimal_product[i - 1]) != null ? ref6[prev_l] : void 0) == null) {
continue;
}
}
candidate_product = this.estimate_guesses(match, password);
if (prev_l > 0) {
candidate_product *= optimal_product[i - 1][prev_l];
}
candidate_score = score(candidate_product, prev_l + 1);
if (candidate_score < optimal_score) {
optimal_score = candidate_score;
optimal_product[k][prev_l + 1] = candidate_product;
optimal_l = prev_l + 1;
max_l = Math.max(max_l, prev_l + 1);
backpointers[k][prev_l + 1] = match;
}
}
}
bruteforce_update(k);
}
match_sequence = [];
l = optimal_l;
k = password.length - 1;
while (k >= 0) {
match = backpointers[k][l];
match_sequence.push(match);
k = match.i - 1;
l -= 1;
}
match_sequence.reverse();
optimal_match_sequence = unwind(n);
if (password.length === 0) {
guesses = 1;
} else {
guesses = optimal_score;
guesses = optimal.g[n - 1];
}

@@ -199,3 +230,3 @@ return {

guesses_log10: this.log10(guesses),
sequence: match_sequence
sequence: optimal_match_sequence
};

@@ -253,3 +284,3 @@ },

MIN_YEAR_SPACE: 20,
REFERENCE_YEAR: 2000,
REFERENCE_YEAR: 2016,
regex_guesses: function(match) {

@@ -279,3 +310,3 @@ var char_class_bases, year_space;

year_space = Math.max(Math.abs(match.year - this.REFERENCE_YEAR), this.MIN_YEAR_SPACE);
guesses = year_space * 31 * 12;
guesses = year_space * 365;
if (match.has_full_year) {

@@ -312,3 +343,3 @@ guesses *= 2;

spatial_guesses: function(match) {
var L, S, U, d, guesses, i, j, m, o, possible_turns, q, ref, ref1, ref2, ref3, s, shifted_variations, t;
var L, S, U, d, guesses, i, j, o, possible_turns, q, ref, ref1, ref2, ref3, s, shifted_variations, t, u;
if ((ref = match.graph) === 'qwerty' || ref === 'dvorak') {

@@ -324,5 +355,5 @@ s = this.KEYBOARD_STARTING_POSITIONS;

t = match.turns;
for (i = m = 2, ref1 = L; 2 <= ref1 ? m <= ref1 : m >= ref1; i = 2 <= ref1 ? ++m : --m) {
for (i = o = 2, ref1 = L; 2 <= ref1 ? o <= ref1 : o >= ref1; i = 2 <= ref1 ? ++o : --o) {
possible_turns = Math.min(t, i - 1);
for (j = o = 1, ref2 = possible_turns; 1 <= ref2 ? o <= ref2 : o >= ref2; j = 1 <= ref2 ? ++o : --o) {
for (j = q = 1, ref2 = possible_turns; 1 <= ref2 ? q <= ref2 : q >= ref2; j = 1 <= ref2 ? ++q : --q) {
guesses += this.nCk(i - 1, j - 1) * s * Math.pow(d, j);

@@ -338,3 +369,3 @@ }

shifted_variations = 0;
for (i = q = 1, ref3 = Math.min(S, U); 1 <= ref3 ? q <= ref3 : q >= ref3; i = 1 <= ref3 ? ++q : --q) {
for (i = u = 1, ref3 = Math.min(S, U); 1 <= ref3 ? u <= ref3 : u >= ref3; i = 1 <= ref3 ? ++u : --u) {
shifted_variations += this.nCk(S + U, i);

@@ -360,10 +391,10 @@ }

uppercase_variations: function(match) {
var L, U, chr, i, len, m, o, ref, ref1, regex, variations, word;
var L, U, chr, i, len, o, q, ref, ref1, regex, variations, word;
word = match.token;
if (word.match(this.ALL_LOWER)) {
if (word.match(this.ALL_LOWER) || word.toLowerCase() === word) {
return 1;
}
ref = [this.START_UPPER, this.END_UPPER, this.ALL_UPPER];
for (m = 0, len = ref.length; m < len; m++) {
regex = ref[m];
for (o = 0, len = ref.length; o < len; o++) {
regex = ref[o];
if (word.match(regex)) {

@@ -374,7 +405,7 @@ return 2;

U = ((function() {
var len1, o, ref1, results;
var len1, q, ref1, results;
ref1 = word.split('');
results = [];
for (o = 0, len1 = ref1.length; o < len1; o++) {
chr = ref1[o];
for (q = 0, len1 = ref1.length; q < len1; q++) {
chr = ref1[q];
if (chr.match(/[A-Z]/)) {

@@ -387,7 +418,7 @@ results.push(chr);

L = ((function() {
var len1, o, ref1, results;
var len1, q, ref1, results;
ref1 = word.split('');
results = [];
for (o = 0, len1 = ref1.length; o < len1; o++) {
chr = ref1[o];
for (q = 0, len1 = ref1.length; q < len1; q++) {
chr = ref1[q];
if (chr.match(/[a-z]/)) {

@@ -400,3 +431,3 @@ results.push(chr);

variations = 0;
for (i = o = 1, ref1 = Math.min(U, L); 1 <= ref1 ? o <= ref1 : o >= ref1; i = 1 <= ref1 ? ++o : --o) {
for (i = q = 1, ref1 = Math.min(U, L); 1 <= ref1 ? q <= ref1 : q >= ref1; i = 1 <= ref1 ? ++q : --q) {
variations += this.nCk(U + L, i);

@@ -407,3 +438,3 @@ }

l33t_variations: function(match) {
var S, U, chr, chrs, i, m, p, possibilities, ref, ref1, subbed, unsubbed, variations;
var S, U, chr, chrs, i, o, p, possibilities, ref, ref1, subbed, unsubbed, variations;
if (!match.l33t) {

@@ -418,6 +449,6 @@ return 1;

S = ((function() {
var len, m, results;
var len, o, results;
results = [];
for (m = 0, len = chrs.length; m < len; m++) {
chr = chrs[m];
for (o = 0, len = chrs.length; o < len; o++) {
chr = chrs[o];
if (chr === subbed) {

@@ -430,6 +461,6 @@ results.push(chr);

U = ((function() {
var len, m, results;
var len, o, results;
results = [];
for (m = 0, len = chrs.length; m < len; m++) {
chr = chrs[m];
for (o = 0, len = chrs.length; o < len; o++) {
chr = chrs[o];
if (chr === unsubbed) {

@@ -446,3 +477,3 @@ results.push(chr);

possibilities = 0;
for (i = m = 1, ref1 = p; 1 <= ref1 ? m <= ref1 : m >= ref1; i = 1 <= ref1 ? ++m : --m) {
for (i = o = 1, ref1 = p; 1 <= ref1 ? o <= ref1 : o >= ref1; i = 1 <= ref1 ? ++o : --o) {
possibilities += this.nCk(U + S, i);

@@ -449,0 +480,0 @@ }

@@ -9,3 +9,3 @@ // Generated by CoffeeScript 1.10.0

online_throttling_100_per_hour: guesses / (100 / 3600),
online_no_throttling_10_per_second: guesses / 1e2,
online_no_throttling_10_per_second: guesses / 10,
offline_slow_hashing_1e4_per_second: guesses / 1e4,

@@ -12,0 +12,0 @@ offline_fast_hashing_1e10_per_second: guesses / 1e10

{
"name": "zxcvbn",
"version": "4.2.0",
"version": "4.3.0",
"description": "realistic password strength estimation",

@@ -5,0 +5,0 @@ "author": "Dan Wheeler",

@@ -13,3 +13,3 @@ ```

`zxcvbn` is a password strength estimator inspired by password crackers. Through pattern matching and conservative entropy calculations, it recognizes and weighs 30k common passwords, common names and surnames according to US census data, popular English words from Wikipedia and US television and movies, and other common patterns like dates, repeats (`aaa`), sequences (`abcd`), keyboard patterns (`qwertyuiop`), and l33t speak.
`zxcvbn` is a password strength estimator inspired by password crackers. Through pattern matching and conservative estimation, it recognizes and weighs 30k common passwords, common names and surnames according to US census data, popular English words from Wikipedia and US television and movies, and other common patterns like dates, repeats (`aaa`), sequences (`abcd`), keyboard patterns (`qwertyuiop`), and l33t speak.

@@ -20,7 +20,14 @@ Consider using zxcvbn as an algorithmic alternative to password composition policy — it is more secure, flexible, and usable when sites require a minimal complexity score in place of annoying rules like "passwords must contain three of {lower, upper, numbers, symbols}".

* __More flexible__: zxcvbn allows many password styles to flourish so long as it detects sufficient complexity — passphrases are rated highly given enough uncommon words, keyboard patterns are ranked based on length and number of turns, and capitalization adds more complexity when it's unpredictaBle.
* __More usable__: Use zxcvbn to build simple, rule-free interfaces that give instant feedback. In addition to scoring, zxcvbn includes minimal, targeted verbal feedback that can help guide users towards less guessable passwords.
* __More usable__: zxcvbn is designed to power simple, rule-free interfaces that give instant feedback. In addition to strength estimation, zxcvbn includes minimal, targeted verbal feedback that can help guide users towards less guessable passwords.
At Dropbox we use zxcvbn on our [signup page](https://www.dropbox.com/register) and change/reset password flows. zxcvbn is designed for node and the browser, but we use our [python port](https://github.com/dropbox/python-zxcvbn) inside the Dropbox desktop client, [Objective C port](https://github.com/dropbox/zxcvbn-ios) in our iOS app, and Java port (not yet open sourced) on Android.
At Dropbox we use zxcvbn ([Release notes](https://github.com/dropbox/zxcvbn/releases)) on our web, desktop, iOS and Android clients. If Javascript doesn't work for you, others have graciously ported the library to these languages:
[Release notes](https://github.com/dropbox/zxcvbn/releases)
* [`zxcvbn4j`](https://github.com/nulab/zxcvbn4j) (Java)
* [`zxcvbn-ios`](https://github.com/dropbox/zxcvbn-ios) (Objective-C)
* [`python-zxcvbn`](https://github.com/dropbox/python-zxcvbn) (Python)
* [`zxcvbn-go`](https://github.com/nbutton23/zxcvbn-go) (Go)
* [`zxcvbn-ruby`](https://github.com/envato/zxcvbn-ruby) (Ruby)
* [`zxcvbn-php`](https://github.com/bjeavons/zxcvbn-php) (PHP)
* [`zxcvbn-cs`](https://github.com/mickford/zxcvbn-cs) (C#/.NET)
* [`szxcvbn`](https://github.com/tekul/szxcvbn) (Scala)

@@ -61,3 +68,3 @@ For more motivation, see:

## Node / npm
## Node / npm / MeteorJS

@@ -73,7 +80,2 @@ zxcvbn works identically on the server.

## Meteor (via [Atmosphere](https://atmospherejs.com/codetheweb/zxcvbn))
``` shell
meteor add codetheweb:zxcvbn
```
## RequireJS

@@ -147,3 +149,3 @@

result.crack_time_seconds # dictionary of back-of-the-envelope crack time
result.crack_times_seconds # dictionary of back-of-the-envelope crack time
# estimations, in seconds, based on a few scenarios:

@@ -171,5 +173,5 @@ {

result.crack_time_display # same keys as result.crack_time_seconds,
# with friendlier display string values:
# "less than a second", "3 hours", "centuries", etc.
result.crack_times_display # same keys as result.crack_times_seconds,
# with friendlier display string values:
# "less than a second", "3 hours", "centuries", etc.

@@ -225,3 +227,3 @@ result.score # Integer from 0-4 (useful for implementing a strength bar)

4. Include an inline `<script>` in `<head>` that asynchronously loads `zxcvbn.js` in the background. Despite the extra code I prefer this over (3) because it works in older browsers.
4. Include an inline `<script>` in `<head>` that asynchronously loads `zxcvbn.js` in the background. Advantage over (3): it works in older browsers.

@@ -228,0 +230,0 @@ ``` javascript

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

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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