@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flisk/analyze-tracking",
3
- "version": "0.8.2",
3
+ "version": "0.8.3",
4
4
  "description": "Analyzes tracking code in a project and generates data schemas",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -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
- // crude check: Prism nodes have a `location` field
140
- if (child.location || child.type || child.constructor?.name?.endsWith('Node')) {
141
- await traverseNode(child, nodeVisitor, ancestors);
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
  };