@flisk/analyze-tracking 0.8.2 → 0.8.3
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/package.json +1 -1
- package/src/analyze/ruby/traversal.js +45 -23
package/package.json
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
* @module analyze/ruby/traversal
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
|
|
7
|
+
// Prevent infinite recursion in AST traversal
|
|
8
|
+
const MAX_RECURSION_DEPTH = 20;
|
|
9
|
+
|
|
6
10
|
/**
|
|
7
11
|
* Finds the wrapping function for a given node
|
|
8
12
|
* @param {Object} node - The current AST node
|
|
@@ -33,8 +37,9 @@ async function findWrappingFunction(node, ancestors) {
|
|
|
33
37
|
* @param {Object} node - The current AST node
|
|
34
38
|
* @param {Function} nodeVisitor - Function to call for each node
|
|
35
39
|
* @param {Array} ancestors - The ancestor nodes stack
|
|
40
|
+
* @param {number} depth - Current recursion depth to prevent infinite loops
|
|
36
41
|
*/
|
|
37
|
-
async function traverseNode(node, nodeVisitor, ancestors = []) {
|
|
42
|
+
async function traverseNode(node, nodeVisitor, ancestors = [], depth = 0) {
|
|
38
43
|
const {
|
|
39
44
|
ProgramNode,
|
|
40
45
|
StatementsNode,
|
|
@@ -53,6 +58,16 @@ async function traverseNode(node, nodeVisitor, ancestors = []) {
|
|
|
53
58
|
|
|
54
59
|
if (!node) return;
|
|
55
60
|
|
|
61
|
+
// Prevent infinite recursion with depth limit
|
|
62
|
+
if (depth > MAX_RECURSION_DEPTH) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Check for circular references - if this node is already in ancestors, skip it
|
|
67
|
+
if (ancestors.includes(node)) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
56
71
|
ancestors.push(node);
|
|
57
72
|
|
|
58
73
|
// Call the visitor for this node
|
|
@@ -62,71 +77,71 @@ async function traverseNode(node, nodeVisitor, ancestors = []) {
|
|
|
62
77
|
|
|
63
78
|
// Visit all child nodes based on node type
|
|
64
79
|
if (node instanceof ProgramNode) {
|
|
65
|
-
await traverseNode(node.statements, nodeVisitor, ancestors);
|
|
80
|
+
await traverseNode(node.statements, nodeVisitor, ancestors, depth + 1);
|
|
66
81
|
} else if (node instanceof StatementsNode) {
|
|
67
82
|
for (const child of node.body) {
|
|
68
|
-
await traverseNode(child, nodeVisitor, ancestors);
|
|
83
|
+
await traverseNode(child, nodeVisitor, ancestors, depth + 1);
|
|
69
84
|
}
|
|
70
85
|
} else if (node instanceof ClassNode) {
|
|
71
86
|
if (node.body) {
|
|
72
|
-
await traverseNode(node.body, nodeVisitor, ancestors);
|
|
87
|
+
await traverseNode(node.body, nodeVisitor, ancestors, depth + 1);
|
|
73
88
|
}
|
|
74
89
|
} else if (node instanceof ModuleNode) {
|
|
75
90
|
if (node.body) {
|
|
76
|
-
await traverseNode(node.body, nodeVisitor, ancestors);
|
|
91
|
+
await traverseNode(node.body, nodeVisitor, ancestors, depth + 1);
|
|
77
92
|
}
|
|
78
93
|
} else if (node instanceof DefNode) {
|
|
79
94
|
if (node.body) {
|
|
80
|
-
await traverseNode(node.body, nodeVisitor, ancestors);
|
|
95
|
+
await traverseNode(node.body, nodeVisitor, ancestors, depth + 1);
|
|
81
96
|
}
|
|
82
97
|
} else if (node instanceof IfNode) {
|
|
83
98
|
if (node.statements) {
|
|
84
|
-
await traverseNode(node.statements, nodeVisitor, ancestors);
|
|
99
|
+
await traverseNode(node.statements, nodeVisitor, ancestors, depth + 1);
|
|
85
100
|
}
|
|
86
101
|
if (node.subsequent) {
|
|
87
|
-
await traverseNode(node.subsequent, nodeVisitor, ancestors);
|
|
102
|
+
await traverseNode(node.subsequent, nodeVisitor, ancestors, depth + 1);
|
|
88
103
|
}
|
|
89
104
|
} else if (node instanceof BlockNode) {
|
|
90
105
|
if (node.body) {
|
|
91
|
-
await traverseNode(node.body, nodeVisitor, ancestors);
|
|
106
|
+
await traverseNode(node.body, nodeVisitor, ancestors, depth + 1);
|
|
92
107
|
}
|
|
93
108
|
} else if (node instanceof ArgumentsNode) {
|
|
94
109
|
const argsList = node.arguments || [];
|
|
95
110
|
for (const arg of argsList) {
|
|
96
|
-
await traverseNode(arg, nodeVisitor, ancestors);
|
|
111
|
+
await traverseNode(arg, nodeVisitor, ancestors, depth + 1);
|
|
97
112
|
}
|
|
98
113
|
} else if (node instanceof HashNode) {
|
|
99
114
|
for (const element of node.elements) {
|
|
100
|
-
await traverseNode(element, nodeVisitor, ancestors);
|
|
115
|
+
await traverseNode(element, nodeVisitor, ancestors, depth + 1);
|
|
101
116
|
}
|
|
102
117
|
} else if (node instanceof AssocNode) {
|
|
103
|
-
await traverseNode(node.key, nodeVisitor, ancestors);
|
|
104
|
-
await traverseNode(node.value, nodeVisitor, ancestors);
|
|
118
|
+
await traverseNode(node.key, nodeVisitor, ancestors, depth + 1);
|
|
119
|
+
await traverseNode(node.value, nodeVisitor, ancestors, depth + 1);
|
|
105
120
|
} else if (node instanceof CaseNode) {
|
|
106
121
|
// Traverse through each 'when' clause and the optional else clause
|
|
107
122
|
const whenClauses = node.whens || node.conditions || node.when_bodies || [];
|
|
108
123
|
for (const when of whenClauses) {
|
|
109
|
-
await traverseNode(when, nodeVisitor, ancestors);
|
|
124
|
+
await traverseNode(when, nodeVisitor, ancestors, depth + 1);
|
|
110
125
|
}
|
|
111
126
|
if (node.else_) {
|
|
112
|
-
await traverseNode(node.else_, nodeVisitor, ancestors);
|
|
127
|
+
await traverseNode(node.else_, nodeVisitor, ancestors, depth + 1);
|
|
113
128
|
} else if (node.elseBody) {
|
|
114
|
-
await traverseNode(node.elseBody, nodeVisitor, ancestors);
|
|
129
|
+
await traverseNode(node.elseBody, nodeVisitor, ancestors, depth + 1);
|
|
115
130
|
}
|
|
116
131
|
} else if (node instanceof WhenNode) {
|
|
117
132
|
// Handle a single when clause: traverse its condition(s) and body
|
|
118
133
|
if (Array.isArray(node.conditions)) {
|
|
119
134
|
for (const cond of node.conditions) {
|
|
120
|
-
await traverseNode(cond, nodeVisitor, ancestors);
|
|
135
|
+
await traverseNode(cond, nodeVisitor, ancestors, depth + 1);
|
|
121
136
|
}
|
|
122
137
|
} else if (node.conditions) {
|
|
123
|
-
await traverseNode(node.conditions, nodeVisitor, ancestors);
|
|
138
|
+
await traverseNode(node.conditions, nodeVisitor, ancestors, depth + 1);
|
|
124
139
|
}
|
|
125
140
|
if (node.statements) {
|
|
126
|
-
await traverseNode(node.statements, nodeVisitor, ancestors);
|
|
141
|
+
await traverseNode(node.statements, nodeVisitor, ancestors, depth + 1);
|
|
127
142
|
}
|
|
128
143
|
if (node.next) {
|
|
129
|
-
await traverseNode(node.next, nodeVisitor, ancestors);
|
|
144
|
+
await traverseNode(node.next, nodeVisitor, ancestors, depth + 1);
|
|
130
145
|
}
|
|
131
146
|
} else {
|
|
132
147
|
// Generic fallback: iterate over enumerable properties to find nested nodes
|
|
@@ -136,9 +151,16 @@ async function traverseNode(node, nodeVisitor, ancestors = []) {
|
|
|
136
151
|
|
|
137
152
|
const visitChild = async (child) => {
|
|
138
153
|
if (child && typeof child === 'object') {
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
|
|
154
|
+
// More restrictive check: ensure it's actually a Prism AST node
|
|
155
|
+
// Check for specific Prism node indicators and avoid circular references
|
|
156
|
+
if (
|
|
157
|
+
child.location &&
|
|
158
|
+
child.constructor &&
|
|
159
|
+
child.constructor.name &&
|
|
160
|
+
child.constructor.name.endsWith('Node') &&
|
|
161
|
+
!ancestors.includes(child)
|
|
162
|
+
) {
|
|
163
|
+
await traverseNode(child, nodeVisitor, ancestors, depth + 1);
|
|
142
164
|
}
|
|
143
165
|
}
|
|
144
166
|
};
|