/
utils.js
166 lines (146 loc) · 5.02 KB
/
utils.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
const MATH_LOG_2 = Math.log(2);
/**
* Charset especially designed to ignore common regular expressions (eg [] and {}), imports/requires (/.), and css classes (-), and other special characters,
* which raise a lot of false postives and aren't usually in passwords/secrets
*/
const CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+=!|*^@~`$%+?\"'_<>".split("");
const DEFAULT_TOLERANCE = 4;
const DEFAULT_ADDTIONAL_REGEXES = {};
function isPlainObject(obj) {
return typeof obj === "object" && obj.constructor === Object;
}
function compileListOfPatterns(patterns = [], name) {
if (!Array.isArray(patterns)) {
if (typeof patterns === "string" || patterns instanceof RegExp) {
patterns = [patterns];
} else {
throw new Error(`Expected '${name}' to be an a array, a string, or a RegExp`);
}
}
const compiledPatterns = [];
for (let i = 0; i < patterns.length; i++) {
try {
compiledPatterns[i] = patterns[i] instanceof RegExp ? patterns[i] : new RegExp(String(patterns[i]));
} catch (e) {
throw new Error("Failed to compiled the regexp " + patterns[i]);
}
}
return compiledPatterns;
}
function booleanOption(value,name,defaultValue){
value = value || defaultValue;
if(typeof value !== "boolean"){
throw new Error(`The option '${name}' must be boolean`);
}
return value;
}
function checkOptions({
tolerance,
additionalRegexes,
ignoreContent,
ignoreModules,
ignoreIdentifiers,
additionalDelimiters,
ignoreCase
}) {
ignoreModules = booleanOption(ignoreModules,'ignoreModules',true);
ignoreCase = booleanOption(ignoreCase,'ignoreCase',false);
tolerance = tolerance || DEFAULT_TOLERANCE;
if (typeof tolerance !== "number" || tolerance <= 0) {
throw new Error("The option tolerance must be a postive (eg greater than zero) number");
}
additionalRegexes = additionalRegexes || DEFAULT_ADDTIONAL_REGEXES;
if (!isPlainObject(additionalRegexes)) {
throw new Error("Expected additionalRegexes to be a plain object");
}
const compiledRegexes = {};
for (const regexName in additionalRegexes) {
if (additionalRegexes.hasOwnProperty(regexName)) {
try {
compiledRegexes[regexName] =
additionalRegexes[regexName] instanceof RegExp
? additionalRegexes[regexName]
: new RegExp(String(additionalRegexes[regexName]));
} catch (e) {
throw new Error(
"Could not compile the regexp " + regexName + " with the value " + additionalRegexes[regexName]
);
}
}
}
return {
tolerance,
additionalRegexes: compiledRegexes,
ignoreContent: compileListOfPatterns(ignoreContent),
ignoreModules,
ignoreIdentifiers: compileListOfPatterns(ignoreIdentifiers),
additionalDelimiters: compileListOfPatterns(additionalDelimiters),
ignoreCase
};
}
/**
* From https://github.com/dxa4481/truffleHog/blob/dev/truffleHog/truffleHog.py#L85
* @param {*} str
*/
function shannonEntropy(str) {
if (!str) return 0;
let entropy = 0;
const len = str.length;
for (let i = 0; i < CHARSET.length; ++i) {
//apparently this is the fastest way to char count in js
const ratio = (str.split(CHARSET[i]).length - 1) / len;
if (ratio > 0) entropy += -(ratio * (Math.log(ratio) / MATH_LOG_2));
}
return entropy;
}
const MODULE_FUNCTIONS = ["import", "require"];
/**
* Used to detect "import()" and "require()"
* Inspired by https://github.com/benmosher/eslint-plugin-import/blob/45bfe472f38ef790c11efe45ffc59808c67a3f94/src/core/staticRequire.js
* @param {*} node
*/
function isStaticImportOrRequire(node) {
return (
node &&
node.callee &&
node.callee.type === "Identifier" &&
MODULE_FUNCTIONS.indexOf(node.callee.name) !== -1 &&
node.arguments.length === 1 &&
node.arguments[0].type === "Literal" &&
typeof node.arguments[0].value === "string"
);
}
function isImportString(node) {
return node && node.parent && node.parent.type === "ImportDeclaration";
}
function isModulePathString(node) {
return isStaticImportOrRequire(node.parent) || isImportString(node) || false;
}
const VARORPROP = ["AssignmentExpression", "Property", "VariableDeclarator"];
function getPropertyName(node) {
return node.parent.key && node.parent.key.type === "Identifier" && node.parent.key.name;
}
function getIdentifierName(node) {
if (!node || !node.parent) return false;
switch (node.parent.type) {
case "VariableDeclarator":
return getVarName(node);
case "AssignmentExpression":
return getAssignmentName(node);
case "Property":
return getPropertyName(node);
default:
return false;
}
}
function getVarName(node) {
return node.parent.id && node.parent.id.name;
}
function getAssignmentName(node) {
return (
node.parent.left && node.parent.property && node.parent.property.type === "Identifier" && node.parent.property.name
);
}
const HIGH_ENTROPY = "HIGH_ENTROPY";
const PATTERN_MATCH = "PATTERN_MATCH";
module.exports = { getIdentifierName, shannonEntropy, checkOptions, HIGH_ENTROPY, PATTERN_MATCH, isModulePathString };