UNPKG

21.8 kBJavaScriptView Raw
1// Generated by CoffeeScript 1.10.0
2var DATE_MAX_YEAR, DATE_MIN_YEAR, DATE_SPLITS, GRAPHS, L33T_TABLE, RANKED_DICTIONARIES, REGEXEN, SEQUENCES, adjacency_graphs, build_ranked_dict, frequency_lists, matching, scoring,
3 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; };
4
5frequency_lists = require('./frequency_lists');
6
7adjacency_graphs = require('./adjacency_graphs');
8
9scoring = require('./scoring');
10
11build_ranked_dict = function(ordered_list) {
12 var i, len1, o, result, word;
13 result = {};
14 i = 1;
15 for (o = 0, len1 = ordered_list.length; o < len1; o++) {
16 word = ordered_list[o];
17 result[word] = i;
18 i += 1;
19 }
20 return result;
21};
22
23RANKED_DICTIONARIES = {
24 passwords: build_ranked_dict(frequency_lists.passwords),
25 english: build_ranked_dict(frequency_lists.english),
26 surnames: build_ranked_dict(frequency_lists.surnames),
27 male_names: build_ranked_dict(frequency_lists.male_names),
28 female_names: build_ranked_dict(frequency_lists.female_names)
29};
30
31GRAPHS = {
32 qwerty: adjacency_graphs.qwerty,
33 dvorak: adjacency_graphs.dvorak,
34 keypad: adjacency_graphs.keypad,
35 mac_keypad: adjacency_graphs.mac_keypad
36};
37
38SEQUENCES = {
39 lower: 'abcdefghijklmnopqrstuvwxyz',
40 upper: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
41 digits: '0123456789'
42};
43
44L33T_TABLE = {
45 a: ['4', '@'],
46 b: ['8'],
47 c: ['(', '{', '[', '<'],
48 e: ['3'],
49 g: ['6', '9'],
50 i: ['1', '!', '|'],
51 l: ['1', '|', '7'],
52 o: ['0'],
53 s: ['$', '5'],
54 t: ['+', '7'],
55 x: ['%'],
56 z: ['2']
57};
58
59REGEXEN = {
60 recent_year: /19\d\d|200\d|201\d/g
61};
62
63DATE_MAX_YEAR = 2050;
64
65DATE_MIN_YEAR = 1000;
66
67DATE_SPLITS = {
68 4: [[1, 2], [2, 3]],
69 5: [[1, 3], [2, 3]],
70 6: [[1, 2], [2, 4], [4, 5]],
71 7: [[1, 3], [2, 3], [4, 5], [4, 6]],
72 8: [[2, 4], [4, 6]]
73};
74
75matching = {
76 empty: function(obj) {
77 var k;
78 return ((function() {
79 var results;
80 results = [];
81 for (k in obj) {
82 results.push(k);
83 }
84 return results;
85 })()).length === 0;
86 },
87 extend: function(lst, lst2) {
88 return lst.push.apply(lst, lst2);
89 },
90 translate: function(string, chr_map) {
91 var chr;
92 return ((function() {
93 var len1, o, ref, results;
94 ref = string.split('');
95 results = [];
96 for (o = 0, len1 = ref.length; o < len1; o++) {
97 chr = ref[o];
98 results.push(chr_map[chr] || chr);
99 }
100 return results;
101 })()).join('');
102 },
103 mod: function(n, m) {
104 return ((n % m) + m) % m;
105 },
106 sorted: function(matches) {
107 return matches.sort(function(m1, m2) {
108 return (m1.i - m2.i) || (m1.j - m2.j);
109 });
110 },
111 omnimatch: function(password) {
112 var len1, matcher, matchers, matches, o;
113 matches = [];
114 matchers = [this.dictionary_match, this.reverse_dictionary_match, this.l33t_match, this.spatial_match, this.repeat_match, this.sequence_match, this.regex_match, this.date_match];
115 for (o = 0, len1 = matchers.length; o < len1; o++) {
116 matcher = matchers[o];
117 this.extend(matches, matcher.call(this, password));
118 }
119 return this.sorted(matches);
120 },
121 dictionary_match: function(password, _ranked_dictionaries) {
122 var dictionary_name, i, j, len, matches, o, p, password_lower, rank, ranked_dict, ref, ref1, ref2, word;
123 if (_ranked_dictionaries == null) {
124 _ranked_dictionaries = RANKED_DICTIONARIES;
125 }
126 matches = [];
127 len = password.length;
128 password_lower = password.toLowerCase();
129 for (dictionary_name in _ranked_dictionaries) {
130 ranked_dict = _ranked_dictionaries[dictionary_name];
131 for (i = o = 0, ref = len; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) {
132 for (j = p = ref1 = i, ref2 = len; ref1 <= ref2 ? p < ref2 : p > ref2; j = ref1 <= ref2 ? ++p : --p) {
133 if (password_lower.slice(i, +j + 1 || 9e9) in ranked_dict) {
134 word = password_lower.slice(i, +j + 1 || 9e9);
135 rank = ranked_dict[word];
136 matches.push({
137 pattern: 'dictionary',
138 i: i,
139 j: j,
140 token: password.slice(i, +j + 1 || 9e9),
141 matched_word: word,
142 rank: rank,
143 dictionary_name: dictionary_name,
144 reversed: false,
145 l33t: false
146 });
147 }
148 }
149 }
150 }
151 return this.sorted(matches);
152 },
153 reverse_dictionary_match: function(password, _ranked_dictionaries) {
154 var len1, match, matches, o, ref, reversed_password;
155 if (_ranked_dictionaries == null) {
156 _ranked_dictionaries = RANKED_DICTIONARIES;
157 }
158 reversed_password = password.split('').reverse().join('');
159 matches = this.dictionary_match(reversed_password, _ranked_dictionaries);
160 for (o = 0, len1 = matches.length; o < len1; o++) {
161 match = matches[o];
162 match.token = match.token.split('').reverse().join('');
163 match.reversed = true;
164 ref = [password.length - 1 - match.j, password.length - 1 - match.i], match.i = ref[0], match.j = ref[1];
165 }
166 return this.sorted(matches);
167 },
168 set_user_input_dictionary: function(ordered_list) {
169 return RANKED_DICTIONARIES['user_inputs'] = build_ranked_dict(ordered_list.slice());
170 },
171 relevant_l33t_subtable: function(password, table) {
172 var chr, len1, letter, o, password_chars, ref, relevant_subs, sub, subs, subtable;
173 password_chars = {};
174 ref = password.split('');
175 for (o = 0, len1 = ref.length; o < len1; o++) {
176 chr = ref[o];
177 password_chars[chr] = true;
178 }
179 subtable = {};
180 for (letter in table) {
181 subs = table[letter];
182 relevant_subs = (function() {
183 var len2, p, results;
184 results = [];
185 for (p = 0, len2 = subs.length; p < len2; p++) {
186 sub = subs[p];
187 if (sub in password_chars) {
188 results.push(sub);
189 }
190 }
191 return results;
192 })();
193 if (relevant_subs.length > 0) {
194 subtable[letter] = relevant_subs;
195 }
196 }
197 return subtable;
198 },
199 enumerate_l33t_subs: function(table) {
200 var chr, dedup, helper, k, keys, l33t_chr, len1, len2, o, p, ref, sub, sub_dict, sub_dicts, subs;
201 keys = (function() {
202 var results;
203 results = [];
204 for (k in table) {
205 results.push(k);
206 }
207 return results;
208 })();
209 subs = [[]];
210 dedup = function(subs) {
211 var assoc, deduped, label, len1, members, o, sub, v;
212 deduped = [];
213 members = {};
214 for (o = 0, len1 = subs.length; o < len1; o++) {
215 sub = subs[o];
216 assoc = (function() {
217 var len2, p, results;
218 results = [];
219 for (v = p = 0, len2 = sub.length; p < len2; v = ++p) {
220 k = sub[v];
221 results.push([k, v]);
222 }
223 return results;
224 })();
225 assoc.sort();
226 label = ((function() {
227 var len2, p, results;
228 results = [];
229 for (v = p = 0, len2 = assoc.length; p < len2; v = ++p) {
230 k = assoc[v];
231 results.push(k + ',' + v);
232 }
233 return results;
234 })()).join('-');
235 if (!(label in members)) {
236 members[label] = true;
237 deduped.push(sub);
238 }
239 }
240 return deduped;
241 };
242 helper = function(keys) {
243 var dup_l33t_index, first_key, i, l33t_chr, len1, len2, next_subs, o, p, q, ref, ref1, rest_keys, sub, sub_alternative, sub_extension;
244 if (!keys.length) {
245 return;
246 }
247 first_key = keys[0];
248 rest_keys = keys.slice(1);
249 next_subs = [];
250 ref = table[first_key];
251 for (o = 0, len1 = ref.length; o < len1; o++) {
252 l33t_chr = ref[o];
253 for (p = 0, len2 = subs.length; p < len2; p++) {
254 sub = subs[p];
255 dup_l33t_index = -1;
256 for (i = q = 0, ref1 = sub.length; 0 <= ref1 ? q < ref1 : q > ref1; i = 0 <= ref1 ? ++q : --q) {
257 if (sub[i][0] === l33t_chr) {
258 dup_l33t_index = i;
259 break;
260 }
261 }
262 if (dup_l33t_index === -1) {
263 sub_extension = sub.concat([[l33t_chr, first_key]]);
264 next_subs.push(sub_extension);
265 } else {
266 sub_alternative = sub.slice(0);
267 sub_alternative.splice(dup_l33t_index, 1);
268 sub_alternative.push([l33t_chr, first_key]);
269 next_subs.push(sub);
270 next_subs.push(sub_alternative);
271 }
272 }
273 }
274 subs = dedup(next_subs);
275 return helper(rest_keys);
276 };
277 helper(keys);
278 sub_dicts = [];
279 for (o = 0, len1 = subs.length; o < len1; o++) {
280 sub = subs[o];
281 sub_dict = {};
282 for (p = 0, len2 = sub.length; p < len2; p++) {
283 ref = sub[p], l33t_chr = ref[0], chr = ref[1];
284 sub_dict[l33t_chr] = chr;
285 }
286 sub_dicts.push(sub_dict);
287 }
288 return sub_dicts;
289 },
290 l33t_match: function(password, _ranked_dictionaries, _l33t_table) {
291 var chr, k, len1, len2, match, match_sub, matches, o, p, ref, ref1, sub, subbed_chr, subbed_password, token, v;
292 if (_ranked_dictionaries == null) {
293 _ranked_dictionaries = RANKED_DICTIONARIES;
294 }
295 if (_l33t_table == null) {
296 _l33t_table = L33T_TABLE;
297 }
298 matches = [];
299 ref = this.enumerate_l33t_subs(this.relevant_l33t_subtable(password, _l33t_table));
300 for (o = 0, len1 = ref.length; o < len1; o++) {
301 sub = ref[o];
302 if (this.empty(sub)) {
303 break;
304 }
305 subbed_password = this.translate(password, sub);
306 ref1 = this.dictionary_match(subbed_password, _ranked_dictionaries);
307 for (p = 0, len2 = ref1.length; p < len2; p++) {
308 match = ref1[p];
309 token = password.slice(match.i, +match.j + 1 || 9e9);
310 if (token.toLowerCase() === match.matched_word) {
311 continue;
312 }
313 match_sub = {};
314 for (subbed_chr in sub) {
315 chr = sub[subbed_chr];
316 if (token.indexOf(subbed_chr) !== -1) {
317 match_sub[subbed_chr] = chr;
318 }
319 }
320 match.l33t = true;
321 match.token = token;
322 match.sub = match_sub;
323 match.sub_display = ((function() {
324 var results;
325 results = [];
326 for (k in match_sub) {
327 v = match_sub[k];
328 results.push(k + " -> " + v);
329 }
330 return results;
331 })()).join(', ');
332 matches.push(match);
333 }
334 }
335 return this.sorted(matches.filter(function(match) {
336 return match.token.length > 1;
337 }));
338 },
339 spatial_match: function(password, _graphs) {
340 var graph, graph_name, matches;
341 if (_graphs == null) {
342 _graphs = GRAPHS;
343 }
344 matches = [];
345 for (graph_name in _graphs) {
346 graph = _graphs[graph_name];
347 this.extend(matches, this.spatial_match_helper(password, graph, graph_name));
348 }
349 return this.sorted(matches);
350 },
351 SHIFTED_RX: /[~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:"ZXCVBNM<>?]/,
352 spatial_match_helper: function(password, graph, graph_name) {
353 var adj, adjacents, cur_char, cur_direction, found, found_direction, i, j, last_direction, len1, matches, o, prev_char, shifted_count, turns;
354 matches = [];
355 i = 0;
356 while (i < password.length - 1) {
357 j = i + 1;
358 last_direction = null;
359 turns = 0;
360 if ((graph_name === 'qwerty' || graph_name === 'dvorak') && this.SHIFTED_RX.exec(password.charAt(i))) {
361 shifted_count = 1;
362 } else {
363 shifted_count = 0;
364 }
365 while (true) {
366 prev_char = password.charAt(j - 1);
367 found = false;
368 found_direction = -1;
369 cur_direction = -1;
370 adjacents = graph[prev_char] || [];
371 if (j < password.length) {
372 cur_char = password.charAt(j);
373 for (o = 0, len1 = adjacents.length; o < len1; o++) {
374 adj = adjacents[o];
375 cur_direction += 1;
376 if (adj && adj.indexOf(cur_char) !== -1) {
377 found = true;
378 found_direction = cur_direction;
379 if (adj.indexOf(cur_char) === 1) {
380 shifted_count += 1;
381 }
382 if (last_direction !== found_direction) {
383 turns += 1;
384 last_direction = found_direction;
385 }
386 break;
387 }
388 }
389 }
390 if (found) {
391 j += 1;
392 } else {
393 if (j - i > 2) {
394 matches.push({
395 pattern: 'spatial',
396 i: i,
397 j: j - 1,
398 token: password.slice(i, j),
399 graph: graph_name,
400 turns: turns,
401 shifted_count: shifted_count
402 });
403 }
404 i = j;
405 break;
406 }
407 }
408 }
409 return matches;
410 },
411 repeat_match: function(password) {
412 var base_analysis, base_guesses, base_matches, base_token, greedy, greedy_match, i, j, lastIndex, lazy, lazy_anchored, lazy_match, match, matches, ref;
413 matches = [];
414 greedy = /(.+)\1+/g;
415 lazy = /(.+?)\1+/g;
416 lazy_anchored = /^(.+?)\1+$/;
417 lastIndex = 0;
418 while (lastIndex < password.length) {
419 greedy.lastIndex = lazy.lastIndex = lastIndex;
420 greedy_match = greedy.exec(password);
421 lazy_match = lazy.exec(password);
422 if (greedy_match == null) {
423 break;
424 }
425 if (greedy_match[0].length > lazy_match[0].length) {
426 match = greedy_match;
427 base_token = lazy_anchored.exec(match[0])[1];
428 } else {
429 match = lazy_match;
430 base_token = match[1];
431 }
432 ref = [match.index, match.index + match[0].length - 1], i = ref[0], j = ref[1];
433 base_analysis = scoring.most_guessable_match_sequence(base_token, this.omnimatch(base_token));
434 base_matches = base_analysis.match_sequence;
435 base_guesses = base_analysis.guesses;
436 matches.push({
437 pattern: 'repeat',
438 i: i,
439 j: j,
440 token: match[0],
441 base_token: base_token,
442 base_guesses: base_guesses,
443 base_matches: base_matches,
444 repeat_count: match[0].length / base_token.length
445 });
446 lastIndex = j + 1;
447 }
448 return matches;
449 },
450 sequence_match: function(password) {
451 var direction, i, j, len1, matches, next_sequence_position, o, ref, ref1, sequence, sequence_name, sequence_position;
452 matches = [];
453 for (sequence_name in SEQUENCES) {
454 sequence = SEQUENCES[sequence_name];
455 ref = [1, -1];
456 for (o = 0, len1 = ref.length; o < len1; o++) {
457 direction = ref[o];
458 i = 0;
459 while (i < password.length) {
460 if (ref1 = password.charAt(i), indexOf.call(sequence, ref1) < 0) {
461 i += 1;
462 continue;
463 }
464 j = i + 1;
465 sequence_position = sequence.indexOf(password.charAt(i));
466 while (j < password.length) {
467 next_sequence_position = this.mod(sequence_position + direction, sequence.length);
468 if (sequence.indexOf(password.charAt(j)) !== next_sequence_position) {
469 break;
470 }
471 j += 1;
472 sequence_position = next_sequence_position;
473 }
474 j -= 1;
475 if (j - i + 1 > 1) {
476 matches.push({
477 pattern: 'sequence',
478 i: i,
479 j: j,
480 token: password.slice(i, +j + 1 || 9e9),
481 sequence_name: sequence_name,
482 sequence_space: sequence.length,
483 ascending: direction === 1
484 });
485 }
486 i = j + 1;
487 }
488 }
489 }
490 return this.sorted(matches);
491 },
492 regex_match: function(password, _regexen) {
493 var matches, name, regex, rx_match, token;
494 if (_regexen == null) {
495 _regexen = REGEXEN;
496 }
497 matches = [];
498 for (name in _regexen) {
499 regex = _regexen[name];
500 regex.lastIndex = 0;
501 while (rx_match = regex.exec(password)) {
502 token = rx_match[0];
503 matches.push({
504 pattern: 'regex',
505 token: token,
506 i: rx_match.index,
507 j: rx_match.index + rx_match[0].length - 1,
508 regex_name: name,
509 regex_match: rx_match
510 });
511 }
512 }
513 return this.sorted(matches);
514 },
515 date_match: function(password) {
516 var best_candidate, candidate, candidates, distance, dmy, i, j, k, l, len1, len2, matches, maybe_date_no_separator, maybe_date_with_separator, metric, min_distance, o, p, q, r, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, rx_match, s, t, token;
517 matches = [];
518 maybe_date_no_separator = /^\d{4,8}$/;
519 maybe_date_with_separator = /^(\d{1,4})([\s\/\\_.-])(\d{1,2})\2(\d{1,4})$/;
520 for (i = o = 0, ref = password.length - 4; 0 <= ref ? o <= ref : o >= ref; i = 0 <= ref ? ++o : --o) {
521 for (j = p = ref1 = i + 3, ref2 = i + 7; ref1 <= ref2 ? p <= ref2 : p >= ref2; j = ref1 <= ref2 ? ++p : --p) {
522 if (j >= password.length) {
523 break;
524 }
525 token = password.slice(i, +j + 1 || 9e9);
526 if (!maybe_date_no_separator.exec(token)) {
527 continue;
528 }
529 candidates = [];
530 ref3 = DATE_SPLITS[token.length];
531 for (q = 0, len1 = ref3.length; q < len1; q++) {
532 ref4 = ref3[q], k = ref4[0], l = ref4[1];
533 dmy = this.map_ints_to_dmy([parseInt(token.slice(0, k)), parseInt(token.slice(k, l)), parseInt(token.slice(l))]);
534 if (dmy != null) {
535 candidates.push(dmy);
536 }
537 }
538 if (!(candidates.length > 0)) {
539 continue;
540 }
541 best_candidate = candidates[0];
542 metric = function(candidate) {
543 return Math.abs(candidate.year - scoring.REFERENCE_YEAR);
544 };
545 min_distance = metric(candidates[0]);
546 ref5 = candidates.slice(1);
547 for (r = 0, len2 = ref5.length; r < len2; r++) {
548 candidate = ref5[r];
549 distance = metric(candidate);
550 if (distance < min_distance) {
551 ref6 = [candidate, distance], best_candidate = ref6[0], min_distance = ref6[1];
552 }
553 }
554 matches.push({
555 pattern: 'date',
556 token: token,
557 i: i,
558 j: j,
559 separator: '',
560 year: best_candidate.year,
561 month: best_candidate.month,
562 day: best_candidate.day
563 });
564 }
565 }
566 for (i = s = 0, ref7 = password.length - 6; 0 <= ref7 ? s <= ref7 : s >= ref7; i = 0 <= ref7 ? ++s : --s) {
567 for (j = t = ref8 = i + 5, ref9 = i + 9; ref8 <= ref9 ? t <= ref9 : t >= ref9; j = ref8 <= ref9 ? ++t : --t) {
568 if (j >= password.length) {
569 break;
570 }
571 token = password.slice(i, +j + 1 || 9e9);
572 rx_match = maybe_date_with_separator.exec(token);
573 if (rx_match == null) {
574 continue;
575 }
576 dmy = this.map_ints_to_dmy([parseInt(rx_match[1]), parseInt(rx_match[3]), parseInt(rx_match[4])]);
577 if (dmy == null) {
578 continue;
579 }
580 matches.push({
581 pattern: 'date',
582 token: token,
583 i: i,
584 j: j,
585 separator: rx_match[2],
586 year: dmy.year,
587 month: dmy.month,
588 day: dmy.day
589 });
590 }
591 }
592 return this.sorted(matches.filter(function(match) {
593 var is_submatch, len3, other_match, u;
594 is_submatch = false;
595 for (u = 0, len3 = matches.length; u < len3; u++) {
596 other_match = matches[u];
597 if (match === other_match) {
598 continue;
599 }
600 if (other_match.i <= match.i && other_match.j >= match.j) {
601 is_submatch = true;
602 break;
603 }
604 }
605 return !is_submatch;
606 }));
607 },
608 map_ints_to_dmy: function(ints) {
609 var dm, int, len1, len2, len3, o, over_12, over_31, p, possible_year_splits, q, ref, ref1, rest, under_1, y;
610 if (ints[1] > 31 || ints[1] <= 0) {
611 return;
612 }
613 over_12 = 0;
614 over_31 = 0;
615 under_1 = 0;
616 for (o = 0, len1 = ints.length; o < len1; o++) {
617 int = ints[o];
618 if ((99 < int && int < DATE_MIN_YEAR) || int > DATE_MAX_YEAR) {
619 return;
620 }
621 if (int > 31) {
622 over_31 += 1;
623 }
624 if (int > 12) {
625 over_12 += 1;
626 }
627 if (int <= 0) {
628 under_1 += 1;
629 }
630 }
631 if (over_31 >= 2 || over_12 === 3 || under_1 >= 2) {
632 return;
633 }
634 possible_year_splits = [[ints[2], ints.slice(0, 2)], [ints[0], ints.slice(1, 3)]];
635 for (p = 0, len2 = possible_year_splits.length; p < len2; p++) {
636 ref = possible_year_splits[p], y = ref[0], rest = ref[1];
637 if ((DATE_MIN_YEAR <= y && y <= DATE_MAX_YEAR)) {
638 dm = this.map_ints_to_dm(rest);
639 if (dm != null) {
640 return {
641 year: y,
642 month: dm.month,
643 day: dm.day
644 };
645 } else {
646 return;
647 }
648 }
649 }
650 for (q = 0, len3 = possible_year_splits.length; q < len3; q++) {
651 ref1 = possible_year_splits[q], y = ref1[0], rest = ref1[1];
652 dm = this.map_ints_to_dm(rest);
653 if (dm != null) {
654 y = this.two_to_four_digit_year(y);
655 return {
656 year: y,
657 month: dm.month,
658 day: dm.day
659 };
660 }
661 }
662 },
663 map_ints_to_dm: function(ints) {
664 var d, len1, m, o, ref, ref1;
665 ref = [ints, ints.slice().reverse()];
666 for (o = 0, len1 = ref.length; o < len1; o++) {
667 ref1 = ref[o], d = ref1[0], m = ref1[1];
668 if ((1 <= d && d <= 31) && (1 <= m && m <= 12)) {
669 return {
670 day: d,
671 month: m
672 };
673 }
674 }
675 },
676 two_to_four_digit_year: function(year) {
677 if (year > 99) {
678 return year;
679 } else if (year > 50) {
680 return year + scoring.REFERENCE_YEAR - 100;
681 } else {
682 return year + scoring.REFERENCE_YEAR;
683 }
684 }
685};
686
687module.exports = matching;
688
689//# sourceMappingURL=matching.js.map