mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-05 04:27:09 +00:00
fix(web-runtime): cap sanitize recursion to prevent stack overflow
State exposes document and window (per WebState in types.ts). A user extractor returning either crashes the runtime via stack overflow on the circular DOM/Window references. Track seen objects in a WeakSet and bail at depth 32 so the worst case becomes a truncated value, not a process kill.
This commit is contained in:
1 parent
fc1e6f9319
commit
9722839339
1 file changed
+18
-5
@@ -380,20 +380,33 @@ function evaluateExtractors(): Record<number, unknown> {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SANITIZE_MAX_DEPTH bounds how far sanitize will recurse. State exposes
|
||||||
|
// `document` and `window`, both of which contain cycles; without a depth or
|
||||||
|
// seen-set guard a user extractor returning either crashes the runtime via
|
||||||
|
// stack overflow.
|
||||||
|
const SANITIZE_MAX_DEPTH = 32;
|
||||||
|
|
||||||
function sanitize(value: unknown): unknown {
|
function sanitize(value: unknown): unknown {
|
||||||
|
return sanitizeAt(value, 0, new WeakSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizeAt(value: unknown, depth: number, seen: WeakSet<object>): unknown {
|
||||||
if (value === null || value === undefined) return value;
|
if (value === null || value === undefined) return value;
|
||||||
if (typeof value === "function") return undefined;
|
if (typeof value === "function") return undefined;
|
||||||
if (Array.isArray(value)) return value.map(sanitize);
|
if (typeof value !== "object") return value;
|
||||||
if (typeof value === "object") {
|
if (depth >= SANITIZE_MAX_DEPTH) return null;
|
||||||
|
if (seen.has(value as object)) return null;
|
||||||
|
seen.add(value as object);
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.map((item) => sanitizeAt(item, depth + 1, seen));
|
||||||
|
}
|
||||||
const out: Record<string, unknown> = {};
|
const out: Record<string, unknown> = {};
|
||||||
for (const key of Object.keys(value as Record<string, unknown>)) {
|
for (const key of Object.keys(value as Record<string, unknown>)) {
|
||||||
const sub = (value as Record<string, unknown>)[key];
|
const sub = (value as Record<string, unknown>)[key];
|
||||||
if (typeof sub === "function") continue;
|
if (typeof sub === "function") continue;
|
||||||
out[key] = sanitize(sub);
|
out[key] = sanitizeAt(sub, depth + 1, seen);
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function pickWeighted(handle: ActionGeneratorHandle): ActionGeneratorHandle | null {
|
function pickWeighted(handle: ActionGeneratorHandle): ActionGeneratorHandle | null {
|
||||||
|
|||||||
Reference in new issue
Block a user