Skip to content

Commit f5ac2de

Browse files
committed
add depth check to not crash
1 parent 97c146a commit f5ac2de

File tree

1 file changed

+69
-10
lines changed

1 file changed

+69
-10
lines changed

apps/sim/lib/core/utils/display-filters.ts

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
*/
55
const MAX_STRING_LENGTH = 10000
66

7+
/**
8+
* Maximum recursion depth to prevent stack overflow
9+
*/
10+
const MAX_DEPTH = 50
11+
712
/**
813
* Truncates a string if it exceeds the maximum length
914
*/
@@ -66,6 +71,7 @@ const DISPLAY_FILTERS = [
6671
* Applies all registered filters recursively to the data structure.
6772
* Also truncates long strings to prevent database storage issues.
6873
*
74+
*
6975
* To add a new filter:
7076
* 1. Create a filter function that checks and transforms a specific data type
7177
* 2. Add it to the DISPLAY_FILTERS array above
@@ -74,6 +80,11 @@ const DISPLAY_FILTERS = [
7480
* @returns Filtered data with internal fields removed and long strings truncated
7581
*/
7682
export function filterForDisplay(data: any): any {
83+
const seen = new WeakSet()
84+
return filterForDisplayInternal(data, seen, 0)
85+
}
86+
87+
function filterForDisplayInternal(data: any, seen: WeakSet<object>, depth: number): any {
7788
// Handle null/undefined
7889
if (data === null || data === undefined) {
7990
return data
@@ -84,30 +95,78 @@ export function filterForDisplay(data: any): any {
8495
return truncateString(data)
8596
}
8697

87-
// Return primitives as-is
98+
// Return primitives as-is (number, boolean, bigint, symbol, function)
8899
if (typeof data !== 'object') {
89100
return data
90101
}
91102

103+
// Prevent infinite recursion from circular references
104+
if (seen.has(data)) {
105+
return '[Circular Reference]'
106+
}
107+
108+
// Prevent stack overflow from very deep nesting
109+
if (depth > MAX_DEPTH) {
110+
return '[Max Depth Exceeded]'
111+
}
112+
113+
// Handle special object types before adding to seen set
114+
// Date objects - convert to ISO string
115+
if (data instanceof Date) {
116+
return data.toISOString()
117+
}
118+
119+
// Error objects - preserve message and stack
120+
if (data instanceof Error) {
121+
return {
122+
name: data.name,
123+
message: truncateString(data.message),
124+
stack: data.stack ? truncateString(data.stack) : undefined,
125+
}
126+
}
127+
128+
// Buffer or TypedArray - don't serialize full content
129+
if (ArrayBuffer.isView(data) || data instanceof ArrayBuffer) {
130+
return `[Binary Data: ${data.byteLength} bytes]`
131+
}
132+
133+
// Map - convert to object
134+
if (data instanceof Map) {
135+
const obj: Record<string, any> = {}
136+
for (const [key, value] of data.entries()) {
137+
const keyStr = typeof key === 'string' ? key : String(key)
138+
obj[keyStr] = filterForDisplayInternal(value, seen, depth + 1)
139+
}
140+
return obj
141+
}
142+
143+
// Set - convert to array
144+
if (data instanceof Set) {
145+
return Array.from(data).map((item) => filterForDisplayInternal(item, seen, depth + 1))
146+
}
147+
148+
// Track this object to detect circular references
149+
seen.add(data)
150+
92151
// Apply all registered filters
93-
const filtered = data
94152
for (const filterFn of DISPLAY_FILTERS) {
95-
const result = filterFn(filtered)
96-
if (result !== filtered) {
153+
const result = filterFn(data)
154+
if (result !== data) {
97155
// Filter matched and transformed the data
98-
return result
156+
// Recursively filter the result in case it contains nested objects
157+
return filterForDisplayInternal(result, seen, depth + 1)
99158
}
100159
}
101160

102161
// No filters matched - recursively filter nested structures
103-
if (Array.isArray(filtered)) {
104-
return filtered.map(filterForDisplay)
162+
if (Array.isArray(data)) {
163+
return data.map((item) => filterForDisplayInternal(item, seen, depth + 1))
105164
}
106165

107166
// Recursively filter object properties
108-
const result: any = {}
109-
for (const [key, value] of Object.entries(filtered)) {
110-
result[key] = filterForDisplay(value)
167+
const result: Record<string, any> = {}
168+
for (const [key, value] of Object.entries(data)) {
169+
result[key] = filterForDisplayInternal(value, seen, depth + 1)
111170
}
112171
return result
113172
}

0 commit comments

Comments
 (0)