Skip to content

Commit b8b1b8a

Browse files
committed
fix(core): typesVersions entry for v3/chat-client + inline CodeQL guards
- typesVersions: add `v3/chat-client` mapping. The export was declared in `tshy.exports` and the conditional export block but missing from `typesVersions` — `attw --pack` flagged "@trigger.dev/core/v3/chat-client" as `node10: 💀 Resolution failed`. - chat.store JSON Patch: add an `assertSafeKey` guard at the assignment sites in `removeAt` / `insertAt`. parseJsonPointer already rejects `__proto__` / `constructor` / `prototype`, but CodeQL's prototype-pollution analysis doesn't trace through the parser boundary — the local check at the assignment keeps the static analysis happy and is also a real defense-in-depth backstop against any future caller that bypasses parseJsonPointer.
1 parent 64699af commit b8b1b8a

2 files changed

Lines changed: 15 additions & 0 deletions

File tree

packages/core/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@
8989
"v3/errors": [
9090
"dist/commonjs/v3/errors.d.ts"
9191
],
92+
"v3/chat-client": [
93+
"dist/commonjs/v3/chat-client.d.ts"
94+
],
9295
"v3/logger-api": [
9396
"dist/commonjs/v3/logger-api.d.ts"
9497
],

packages/core/src/v3/chat-client.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,21 @@ function readPointer(doc: unknown, tokens: string[]): unknown {
113113
return cursor;
114114
}
115115

116+
// Defense-in-depth: parseJsonPointer already rejects these segments, but
117+
// CodeQL's prototype-pollution analysis doesn't trace through that boundary.
118+
// The local check at the assignment site keeps the static analysis happy and
119+
// guards against any future caller that bypasses parseJsonPointer.
120+
function assertSafeKey(token: string): void {
121+
if (FORBIDDEN_POINTER_SEGMENTS.has(token)) {
122+
throw new Error(`Refusing to mutate forbidden key "${token}"`);
123+
}
124+
}
125+
116126
function removeAt(parent: any, lastToken: string): void {
117127
if (Array.isArray(parent)) {
118128
parent.splice(Number(lastToken), 1);
119129
} else if (parent && typeof parent === "object") {
130+
assertSafeKey(lastToken);
120131
delete parent[lastToken];
121132
} else {
122133
throw new Error("Cannot remove: parent is not a container");
@@ -129,6 +140,7 @@ function insertAt(parent: any, lastToken: string, value: unknown, op: "add" | "r
129140
if (op === "add") parent.splice(idx, 0, value);
130141
else parent[idx] = value;
131142
} else if (parent && typeof parent === "object") {
143+
assertSafeKey(lastToken);
132144
parent[lastToken] = value;
133145
} else {
134146
throw new Error("Cannot insert: parent is not a container");

0 commit comments

Comments
 (0)