Skip to content

Commit 6ecf72b

Browse files
Harden debug option normalization against trap getters
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent 12b6589 commit 6ecf72b

3 files changed

Lines changed: 104 additions & 1 deletion

File tree

currentState.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ HyperAgent exposes a TypeScript SDK for browser automation with three primary pa
144144
- Hardened CDP frame-filter URL normalization to support protocol-relative and scheme-less frame URLs while avoiding path-only false positives in host-based ad-domain detection.
145145
- Refined CDP frame-filter URL normalization to correctly handle scheme-less `host:port` URLs (without misclassifying them as custom schemes), preserving ad-domain detection coverage in those cases.
146146
- Tightened frame-filter query-signal policy so tracking query parameters are treated as strong signals only for parseable URL contexts, preventing path-only query strings from being over-filtered.
147+
- Hardened global debug-option storage by normalizing option payloads to plain boolean fields at set-time, preventing trap-prone debug option getters from leaking into runtime reads.
147148
- Hardened prompt base-message materialization with trap-safe array reads so malformed/trap-prone seed message arrays no longer crash message assembly and readable entries are preserved.
148149
- Hardened constructor custom-action ingestion with trap-safe array reads so unreadable custom-action entries are skipped while valid entries continue to register.
149150
- Expanded top-level package exports for key workflow/config types at `@hyperbrowser/agent`.

src/debug/options.test.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { getDebugOptions, setDebugOptions } from "@/debug/options";
2+
3+
describe("debug options", () => {
4+
beforeEach(() => {
5+
setDebugOptions(undefined, false);
6+
});
7+
8+
it("stores boolean debug flags and enabled state", () => {
9+
setDebugOptions(
10+
{
11+
cdpSessions: true,
12+
traceWait: false,
13+
profileDomCapture: true,
14+
structuredSchema: false,
15+
},
16+
true
17+
);
18+
19+
expect(getDebugOptions()).toEqual({
20+
cdpSessions: true,
21+
traceWait: false,
22+
profileDomCapture: true,
23+
structuredSchema: false,
24+
enabled: true,
25+
});
26+
});
27+
28+
it("ignores non-boolean debug option values", () => {
29+
setDebugOptions(
30+
{
31+
cdpSessions: true,
32+
traceWait: "true" as unknown as boolean,
33+
},
34+
false
35+
);
36+
37+
expect(getDebugOptions()).toEqual({
38+
cdpSessions: true,
39+
enabled: false,
40+
});
41+
});
42+
43+
it("omits trap-prone debug option getters without throwing", () => {
44+
const trappedOptions = new Proxy(
45+
{
46+
cdpSessions: true,
47+
},
48+
{
49+
get: (target, prop, receiver) => {
50+
if (prop === "traceWait") {
51+
throw new Error("traceWait trap");
52+
}
53+
return Reflect.get(target, prop, receiver);
54+
},
55+
}
56+
);
57+
58+
expect(() =>
59+
setDebugOptions(
60+
trappedOptions as unknown as Parameters<typeof setDebugOptions>[0],
61+
true
62+
)
63+
).not.toThrow();
64+
65+
expect(getDebugOptions()).toEqual({
66+
cdpSessions: true,
67+
enabled: true,
68+
});
69+
});
70+
});

src/debug/options.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,46 @@ export interface HyperAgentDebugOptions {
55
structuredSchema?: boolean;
66
}
77

8+
const DEBUG_OPTION_KEYS: ReadonlyArray<keyof HyperAgentDebugOptions> = [
9+
"cdpSessions",
10+
"traceWait",
11+
"profileDomCapture",
12+
"structuredSchema",
13+
];
14+
815
let currentDebugOptions: HyperAgentDebugOptions = {};
916
let debugOptionsEnabled = false;
1017

18+
function safeReadOptionField(
19+
options: unknown,
20+
key: keyof HyperAgentDebugOptions
21+
): unknown {
22+
if (!options || (typeof options !== "object" && typeof options !== "function")) {
23+
return undefined;
24+
}
25+
try {
26+
return (options as Record<string, unknown>)[key];
27+
} catch {
28+
return undefined;
29+
}
30+
}
31+
32+
function normalizeDebugOptions(options?: HyperAgentDebugOptions): HyperAgentDebugOptions {
33+
const normalized: HyperAgentDebugOptions = {};
34+
for (const key of DEBUG_OPTION_KEYS) {
35+
const value = safeReadOptionField(options, key);
36+
if (typeof value === "boolean") {
37+
normalized[key] = value;
38+
}
39+
}
40+
return normalized;
41+
}
42+
1143
export function setDebugOptions(
1244
options?: HyperAgentDebugOptions,
1345
enabled = false
1446
): void {
15-
currentDebugOptions = options ?? {};
47+
currentDebugOptions = normalizeDebugOptions(options);
1648
debugOptionsEnabled = enabled;
1749
}
1850

0 commit comments

Comments
 (0)