@nodesecure/js-x-ray 4.3.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -19
- package/index.d.ts +34 -99
- package/index.js +87 -98
- package/package.json +57 -57
- package/src/ASTDeps.js +63 -55
- package/src/Analysis.js +140 -152
- package/src/constants.js +0 -12
- package/src/probes/index.js +3 -1
- package/src/probes/isArrayExpression.js +27 -21
- package/src/probes/isAssignmentExpression.js +29 -22
- package/src/probes/isBinaryExpression.js +53 -39
- package/src/probes/isFunctionDeclaration.js +27 -20
- package/src/probes/isImportDeclaration.js +30 -26
- package/src/probes/isLiteral.js +52 -47
- package/src/probes/isLiteralRegex.js +31 -27
- package/src/probes/isObjectExpression.js +29 -23
- package/src/probes/isRegexObject.js +42 -38
- package/src/probes/isRequire.js +159 -160
- package/src/probes/isUnaryExpression.js +26 -18
- package/src/probes/isUnsafeCallee.js +23 -19
- package/src/probes/isVariableDeclaration.js +104 -104
- package/src/probes/isWeakCrypto.js +31 -0
- package/src/utils.js +166 -182
- package/src/warnings.js +70 -0
package/src/probes/isRequire.js
CHANGED
|
@@ -1,160 +1,159 @@
|
|
|
1
|
-
/* eslint-disable consistent-return */
|
|
2
|
-
|
|
3
|
-
// Import Internal Dependencies
|
|
4
|
-
import { isRequireGlobalMemberExpr, getMemberExprName, arrExprToString, concatBinaryExpr } from "../utils.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import {
|
|
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
|
-
};
|
|
1
|
+
/* eslint-disable consistent-return */
|
|
2
|
+
|
|
3
|
+
// Import Internal Dependencies
|
|
4
|
+
import { isRequireGlobalMemberExpr, getMemberExprName, arrExprToString, concatBinaryExpr } from "../utils.js";
|
|
5
|
+
|
|
6
|
+
// Import Third-party Dependencies
|
|
7
|
+
import { Hex } from "@nodesecure/sec-literal";
|
|
8
|
+
import { walk } from "estree-walker";
|
|
9
|
+
|
|
10
|
+
function validateNode(node, analysis) {
|
|
11
|
+
return [
|
|
12
|
+
isRequireIdentifiers(node, analysis) ||
|
|
13
|
+
isRequireResolve(node) ||
|
|
14
|
+
isRequireMemberExpr(node)
|
|
15
|
+
];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function isRequireResolve(node) {
|
|
19
|
+
if (node.type !== "CallExpression" || node.callee.type !== "MemberExpression") {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return node.callee.object.name === "require" && node.callee.property.name === "resolve";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isRequireMemberExpr(node) {
|
|
27
|
+
if (node.type !== "CallExpression" || node.callee.type !== "MemberExpression") {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return isRequireGlobalMemberExpr(getMemberExprName(node.callee));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isRequireIdentifiers(node, analysis) {
|
|
35
|
+
if (node.type !== "CallExpression") {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const fullName = node.callee.type === "MemberExpression" ? getMemberExprName(node.callee) : node.callee.name;
|
|
39
|
+
|
|
40
|
+
return analysis.requireIdentifiers.has(fullName);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function main(node, options) {
|
|
44
|
+
const { analysis } = options;
|
|
45
|
+
|
|
46
|
+
const arg = node.arguments[0];
|
|
47
|
+
switch (arg.type) {
|
|
48
|
+
// const foo = "http"; require(foo);
|
|
49
|
+
case "Identifier":
|
|
50
|
+
if (analysis.identifiers.has(arg.name)) {
|
|
51
|
+
analysis.dependencies.add(analysis.identifiers.get(arg.name), node.loc);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
analysis.addWarning("unsafe-import", null, node.loc);
|
|
55
|
+
}
|
|
56
|
+
break;
|
|
57
|
+
|
|
58
|
+
// require("http")
|
|
59
|
+
case "Literal":
|
|
60
|
+
analysis.dependencies.add(arg.value, node.loc);
|
|
61
|
+
break;
|
|
62
|
+
|
|
63
|
+
// require(["ht" + "tp"])
|
|
64
|
+
case "ArrayExpression": {
|
|
65
|
+
const value = arrExprToString(arg.elements, analysis.identifiers).trim();
|
|
66
|
+
if (value === "") {
|
|
67
|
+
analysis.addWarning("unsafe-import", null, node.loc);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
analysis.dependencies.add(value, node.loc);
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// require("ht" + "tp");
|
|
76
|
+
case "BinaryExpression": {
|
|
77
|
+
if (arg.operator !== "+") {
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const value = concatBinaryExpr(arg, analysis.identifiers);
|
|
82
|
+
if (value === null) {
|
|
83
|
+
analysis.addWarning("unsafe-import", null, node.loc);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
analysis.dependencies.add(value, node.loc);
|
|
87
|
+
}
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// require(Buffer.from("...", "hex").toString());
|
|
92
|
+
case "CallExpression": {
|
|
93
|
+
const { dependencies } = parseRequireCallExpression(arg);
|
|
94
|
+
dependencies.forEach((depName) => analysis.dependencies.add(depName, node.loc, true));
|
|
95
|
+
|
|
96
|
+
analysis.addWarning("unsafe-import", null, node.loc);
|
|
97
|
+
|
|
98
|
+
// We skip walking the tree to avoid anymore warnings...
|
|
99
|
+
return Symbol.for("skipWalk");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
default:
|
|
103
|
+
analysis.addWarning("unsafe-import", null, node.loc);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function parseRequireCallExpression(nodeToWalk) {
|
|
108
|
+
const dependencies = new Set();
|
|
109
|
+
|
|
110
|
+
walk(nodeToWalk, {
|
|
111
|
+
enter(node) {
|
|
112
|
+
if (node.type !== "CallExpression" || node.arguments.length === 0) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (node.arguments[0].type === "Literal" && Hex.isHex(node.arguments[0].value)) {
|
|
117
|
+
dependencies.add(Buffer.from(node.arguments[0].value, "hex").toString());
|
|
118
|
+
|
|
119
|
+
return this.skip();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const fullName = node.callee.type === "MemberExpression" ? getMemberExprName(node.callee) : node.callee.name;
|
|
123
|
+
switch (fullName) {
|
|
124
|
+
case "Buffer.from": {
|
|
125
|
+
const [element, convert] = node.arguments;
|
|
126
|
+
|
|
127
|
+
if (element.type === "ArrayExpression") {
|
|
128
|
+
const depName = arrExprToString(element);
|
|
129
|
+
if (depName.trim() !== "") {
|
|
130
|
+
dependencies.add(depName);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else if (element.type === "Literal" && convert.type === "Literal" && convert.value === "hex") {
|
|
134
|
+
const value = Buffer.from(element.value, "hex").toString();
|
|
135
|
+
dependencies.add(value);
|
|
136
|
+
}
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
case "require.resolve": {
|
|
140
|
+
const [element] = node.arguments;
|
|
141
|
+
|
|
142
|
+
if (element.type === "Literal") {
|
|
143
|
+
dependencies.add(element.value);
|
|
144
|
+
}
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
dependencies: [...dependencies]
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export default {
|
|
157
|
+
name: "isRequire",
|
|
158
|
+
validateNode, main, breakOnMatch: true, breakGroup: "import"
|
|
159
|
+
};
|
|
@@ -1,18 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
function
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @description Search for UnaryExpression AST Node
|
|
3
|
+
* @see https://github.com/estree/estree/blob/master/es5.md#unaryexpression
|
|
4
|
+
* @example
|
|
5
|
+
* -2
|
|
6
|
+
*/
|
|
7
|
+
function validateNode(node) {
|
|
8
|
+
return [
|
|
9
|
+
node.type === "UnaryExpression"
|
|
10
|
+
];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function main(node, options) {
|
|
14
|
+
const { analysis } = options;
|
|
15
|
+
|
|
16
|
+
// Example: !![]
|
|
17
|
+
// See: https://docs.google.com/document/d/11ZrfW0bDQ-kd7Gr_Ixqyk8p3TGvxckmhFH3Z8dFoPhY/edit#
|
|
18
|
+
if (node.argument.type === "UnaryExpression" && node.argument.argument.type === "ArrayExpression") {
|
|
19
|
+
analysis.counter.doubleUnaryArray++;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default {
|
|
24
|
+
name: "isUnaryExpression",
|
|
25
|
+
validateNode, main, breakOnMatch: false
|
|
26
|
+
};
|
|
@@ -1,19 +1,23 @@
|
|
|
1
|
-
// Require Internal Dependencies
|
|
2
|
-
import { isUnsafeCallee } from "../utils.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
// Require Internal Dependencies
|
|
2
|
+
import { isUnsafeCallee } from "../utils.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @description Detect unsafe statement
|
|
6
|
+
* @example
|
|
7
|
+
* eval("this");
|
|
8
|
+
* Function("return this")();
|
|
9
|
+
*/
|
|
10
|
+
function validateNode(node) {
|
|
11
|
+
return isUnsafeCallee(node);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function main(node, options) {
|
|
15
|
+
const { analysis, data: calleeName } = options;
|
|
16
|
+
|
|
17
|
+
analysis.addWarning("unsafe-stmt", calleeName, node.loc);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default {
|
|
21
|
+
name: "isUnsafeCallee",
|
|
22
|
+
validateNode, main, breakOnMatch: false
|
|
23
|
+
};
|
|
@@ -1,104 +1,104 @@
|
|
|
1
|
-
// Require Internal Dependencies
|
|
2
|
-
import { getIdName, getMemberExprName, isUnsafeCallee, isRequireGlobalMemberExpr } from "../utils.js";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
// CONSTANTS
|
|
6
|
-
const kUnsafeCallee = new Set(["eval", "Function"]);
|
|
7
|
-
|
|
8
|
-
// In case we are matching a Variable declaration, we have to save the identifier
|
|
9
|
-
// This allow the AST Analysis to retrieve required dependency when the stmt is mixed with variables.
|
|
10
|
-
function validateNode(node) {
|
|
11
|
-
return [
|
|
12
|
-
node.type === "VariableDeclaration"
|
|
13
|
-
];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function main(mainNode, options) {
|
|
17
|
-
const { analysis } = options;
|
|
18
|
-
|
|
19
|
-
analysis.varkinds[mainNode.kind]++;
|
|
20
|
-
|
|
21
|
-
for (const node of mainNode.declarations) {
|
|
22
|
-
analysis.idtypes.variableDeclarator++;
|
|
23
|
-
for (const name of getIdName(node.id)) {
|
|
24
|
-
analysis.identifiersName.push({ name, type: "variableDeclarator" });
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (node.init === null || node.id.type !== "Identifier") {
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (node.init.type === "Literal") {
|
|
32
|
-
analysis.identifiers.set(node.id.name, String(node.init.value));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Searching for someone who assign require to a variable, ex:
|
|
36
|
-
// const r = require
|
|
37
|
-
else if (node.init.type === "Identifier") {
|
|
38
|
-
if (kUnsafeCallee.has(node.init.name)) {
|
|
39
|
-
analysis.addWarning(
|
|
40
|
-
}
|
|
41
|
-
else if (analysis.requireIdentifiers.has(node.init.name)) {
|
|
42
|
-
analysis.requireIdentifiers.add(node.id.name);
|
|
43
|
-
analysis.addWarning(
|
|
44
|
-
}
|
|
45
|
-
else if (globalParts.has(node.init.name)) {
|
|
46
|
-
analysis.globalParts.set(node.id.name, node.init.name);
|
|
47
|
-
getRequirablePatterns(analysis.globalParts)
|
|
48
|
-
.forEach((name) => analysis.requireIdentifiers.add(name));
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Same as before but for pattern like process.mainModule and require.resolve
|
|
53
|
-
else if (node.init.type === "MemberExpression") {
|
|
54
|
-
const value = getMemberExprName(node.init);
|
|
55
|
-
const members = value.split(".");
|
|
56
|
-
|
|
57
|
-
if (analysis.globalParts.has(members[0]) || members.every((part) => globalParts.has(part))) {
|
|
58
|
-
analysis.globalParts.set(node.id.name, members.slice(1).join("."));
|
|
59
|
-
analysis.addWarning(
|
|
60
|
-
}
|
|
61
|
-
getRequirablePatterns(analysis.globalParts)
|
|
62
|
-
.forEach((name) => analysis.requireIdentifiers.add(name));
|
|
63
|
-
|
|
64
|
-
if (isRequireStatement(value)) {
|
|
65
|
-
analysis.requireIdentifiers.add(node.id.name);
|
|
66
|
-
analysis.addWarning(
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
else if (isUnsafeCallee(node.init)[0]) {
|
|
70
|
-
analysis.globalParts.set(node.id.name, "global");
|
|
71
|
-
globalParts.add(node.id.name);
|
|
72
|
-
analysis.requireIdentifiers.add(`${node.id.name}.${processMainModuleRequire}`);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function isRequireStatement(value) {
|
|
78
|
-
return value.startsWith("require") ||
|
|
79
|
-
value.startsWith(processMainModuleRequire) ||
|
|
80
|
-
isRequireGlobalMemberExpr(value);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function getRequirablePatterns(parts) {
|
|
84
|
-
const result = new Set();
|
|
85
|
-
|
|
86
|
-
for (const [id, path] of parts.entries()) {
|
|
87
|
-
if (path === "process") {
|
|
88
|
-
result.add(`${id}.mainModule.require`);
|
|
89
|
-
}
|
|
90
|
-
else if (path === "mainModule") {
|
|
91
|
-
result.add(`${id}.require`);
|
|
92
|
-
}
|
|
93
|
-
else if (path.includes("require")) {
|
|
94
|
-
result.add(id);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return [...result];
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export default {
|
|
102
|
-
name: "isVariableDeclaration",
|
|
103
|
-
validateNode, main, breakOnMatch: false
|
|
104
|
-
};
|
|
1
|
+
// Require Internal Dependencies
|
|
2
|
+
import { getIdName, getMemberExprName, isUnsafeCallee, isRequireGlobalMemberExpr } from "../utils.js";
|
|
3
|
+
import { globalParts, processMainModuleRequire } from "../constants.js";
|
|
4
|
+
|
|
5
|
+
// CONSTANTS
|
|
6
|
+
const kUnsafeCallee = new Set(["eval", "Function"]);
|
|
7
|
+
|
|
8
|
+
// In case we are matching a Variable declaration, we have to save the identifier
|
|
9
|
+
// This allow the AST Analysis to retrieve required dependency when the stmt is mixed with variables.
|
|
10
|
+
function validateNode(node) {
|
|
11
|
+
return [
|
|
12
|
+
node.type === "VariableDeclaration"
|
|
13
|
+
];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function main(mainNode, options) {
|
|
17
|
+
const { analysis } = options;
|
|
18
|
+
|
|
19
|
+
analysis.varkinds[mainNode.kind]++;
|
|
20
|
+
|
|
21
|
+
for (const node of mainNode.declarations) {
|
|
22
|
+
analysis.idtypes.variableDeclarator++;
|
|
23
|
+
for (const name of getIdName(node.id)) {
|
|
24
|
+
analysis.identifiersName.push({ name, type: "variableDeclarator" });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (node.init === null || node.id.type !== "Identifier") {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (node.init.type === "Literal") {
|
|
32
|
+
analysis.identifiers.set(node.id.name, String(node.init.value));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Searching for someone who assign require to a variable, ex:
|
|
36
|
+
// const r = require
|
|
37
|
+
else if (node.init.type === "Identifier") {
|
|
38
|
+
if (kUnsafeCallee.has(node.init.name)) {
|
|
39
|
+
analysis.addWarning("unsafe-assign", node.init.name, node.loc);
|
|
40
|
+
}
|
|
41
|
+
else if (analysis.requireIdentifiers.has(node.init.name)) {
|
|
42
|
+
analysis.requireIdentifiers.add(node.id.name);
|
|
43
|
+
analysis.addWarning("unsafe-assign", node.init.name, node.loc);
|
|
44
|
+
}
|
|
45
|
+
else if (globalParts.has(node.init.name)) {
|
|
46
|
+
analysis.globalParts.set(node.id.name, node.init.name);
|
|
47
|
+
getRequirablePatterns(analysis.globalParts)
|
|
48
|
+
.forEach((name) => analysis.requireIdentifiers.add(name));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Same as before but for pattern like process.mainModule and require.resolve
|
|
53
|
+
else if (node.init.type === "MemberExpression") {
|
|
54
|
+
const value = getMemberExprName(node.init);
|
|
55
|
+
const members = value.split(".");
|
|
56
|
+
|
|
57
|
+
if (analysis.globalParts.has(members[0]) || members.every((part) => globalParts.has(part))) {
|
|
58
|
+
analysis.globalParts.set(node.id.name, members.slice(1).join("."));
|
|
59
|
+
analysis.addWarning("unsafe-assign", value, node.loc);
|
|
60
|
+
}
|
|
61
|
+
getRequirablePatterns(analysis.globalParts)
|
|
62
|
+
.forEach((name) => analysis.requireIdentifiers.add(name));
|
|
63
|
+
|
|
64
|
+
if (isRequireStatement(value)) {
|
|
65
|
+
analysis.requireIdentifiers.add(node.id.name);
|
|
66
|
+
analysis.addWarning("unsafe-assign", value, node.loc);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else if (isUnsafeCallee(node.init)[0]) {
|
|
70
|
+
analysis.globalParts.set(node.id.name, "global");
|
|
71
|
+
globalParts.add(node.id.name);
|
|
72
|
+
analysis.requireIdentifiers.add(`${node.id.name}.${processMainModuleRequire}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function isRequireStatement(value) {
|
|
78
|
+
return value.startsWith("require") ||
|
|
79
|
+
value.startsWith(processMainModuleRequire) ||
|
|
80
|
+
isRequireGlobalMemberExpr(value);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function getRequirablePatterns(parts) {
|
|
84
|
+
const result = new Set();
|
|
85
|
+
|
|
86
|
+
for (const [id, path] of parts.entries()) {
|
|
87
|
+
if (path === "process") {
|
|
88
|
+
result.add(`${id}.mainModule.require`);
|
|
89
|
+
}
|
|
90
|
+
else if (path === "mainModule") {
|
|
91
|
+
result.add(`${id}.require`);
|
|
92
|
+
}
|
|
93
|
+
else if (path.includes("require")) {
|
|
94
|
+
result.add(id);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return [...result];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default {
|
|
102
|
+
name: "isVariableDeclaration",
|
|
103
|
+
validateNode, main, breakOnMatch: false
|
|
104
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// CONSTANTS
|
|
2
|
+
const kWeakAlgorithms = new Set(["md5", "sha1", "ripemd160", "md4", "md2"]);
|
|
3
|
+
|
|
4
|
+
function validateNode(node) {
|
|
5
|
+
const isCallExpression = node.type === "CallExpression";
|
|
6
|
+
const isSimpleIdentifier = isCallExpression &&
|
|
7
|
+
node.callee.type === "Identifier" &&
|
|
8
|
+
node.callee.name === "createHash";
|
|
9
|
+
const isMemberExpression = isCallExpression &&
|
|
10
|
+
node.callee.type === "MemberExpression" &&
|
|
11
|
+
node.callee.property.name === "createHash";
|
|
12
|
+
|
|
13
|
+
return [isSimpleIdentifier || isMemberExpression];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function main(node, { analysis }) {
|
|
17
|
+
const arg = node.arguments.at(0);
|
|
18
|
+
const isCryptoImported = analysis.dependencies.has("crypto");
|
|
19
|
+
|
|
20
|
+
if (
|
|
21
|
+
kWeakAlgorithms.has(arg.value) &&
|
|
22
|
+
isCryptoImported
|
|
23
|
+
) {
|
|
24
|
+
analysis.addWarning("weak-crypto", arg.value, node.loc);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default {
|
|
29
|
+
name: "isWeakCrypto",
|
|
30
|
+
validateNode, main, breakOnMatch: false
|
|
31
|
+
};
|