diff --git a/typescript/src/ts/program.ts b/typescript/src/ts/program.ts index b771c653..149e69a5 100644 --- a/typescript/src/ts/program.ts +++ b/typescript/src/ts/program.ts @@ -99,7 +99,7 @@ function isValidFunctionName(name: unknown): name is string { export function createModuleTextFromProgram(jsonObject: object): Result { const steps = (jsonObject as Program)["@steps"]; if (!(Array.isArray(steps) && steps.every(step => typeof step === "object" && step !== null && Object.prototype.hasOwnProperty.call(step, "@func")))) { - return error("JSON object is not a valid program", { data: jsonObject }); + return error("JSON object is not a valid program", { data: jsonObject }); } let hasError = false; let functionBody = ""; @@ -109,7 +109,7 @@ export function createModuleTextFromProgram(jsonObject: object): Result currentStep++; } return hasError ? - error("JSON program contains an invalid expression", { data: jsonObject }) : + error("JSON program contains an invalid expression", { data: jsonObject }) : success(`import { API } from "./schema";\nfunction program(api: API) {\n${functionBody}}`); function exprToString(expr: unknown): string { @@ -137,7 +137,7 @@ export function createModuleTextFromProgram(jsonObject: object): Result return `[${arrayToString(obj)}]`; } else { - return `{ ${Object.keys(obj).map(key => `${JSON.stringify(key)}: ${exprToString(obj[key])}`).join(", ")} }`; + return `{ ${Object.keys(obj).map(key => `${key === "__proto__" ? `["__proto__"]` : JSON.stringify(key)}: ${exprToString(obj[key])}`).join(", ")} }`; } hasError = true; return ""; @@ -213,11 +213,11 @@ export async function evaluateJsonProgram(program: Program, onCall: (func: strin * @returns A `TypeChatJsonTranslator` instance. */ export function createProgramTranslator(model: TypeChatLanguageModel, schema: string): TypeChatJsonTranslator { - const validator = createTypeScriptJsonValidator( - schema, - "Program", - "API" - ); + const validator = createTypeScriptJsonValidator( + schema, + "Program", + "API" + ); validator.createModuleTextFromJson = createModuleTextFromProgram; const translator = createJsonTranslator(model, validator); translator.createRequestPrompt = createRequestPrompt; diff --git a/typescript/test/program.test.ts b/typescript/test/program.test.ts index 5eaa8183..f7cdbd1c 100644 --- a/typescript/test/program.test.ts +++ b/typescript/test/program.test.ts @@ -186,6 +186,37 @@ describe("hasOwnProperty shadowing in parsed programs", () => { }); }); +describe("__proto__ as object property key", () => { + it("generates a computed property name for a string value", () => { + const program = JSON.parse('{"@steps":[{"@func":"first","@args":[{"__proto__":"hello"}]}]}'); + const result = createModuleTextFromProgram(program); + assert.equal(result.success, true); + assert.match((result as { data: string }).data, /\["__proto__"\]: "hello"/); + assert.doesNotMatch((result as { data: string }).data, /"__proto__": "hello"/); + }); + + it("generates a computed property name for a numeric value", () => { + const program = JSON.parse('{"@steps":[{"@func":"first","@args":[{"__proto__":42}]}]}'); + const result = createModuleTextFromProgram(program); + assert.equal(result.success, true); + assert.match((result as { data: string }).data, /\["__proto__"\]: 42/); + }); + + it("generates a computed property name for a null value", () => { + const program = JSON.parse('{"@steps":[{"@func":"first","@args":[{"__proto__":null}]}]}'); + const result = createModuleTextFromProgram(program); + assert.equal(result.success, true); + assert.match((result as { data: string }).data, /\["__proto__"\]: null/); + }); + + it("evaluates an object with __proto__ as an own data property", async () => { + const program = JSON.parse('{"@steps":[{"@func":"first","@args":[{"__proto__":"value"}]}]}'); + const result = await evaluateJsonProgram(program, async (_func, args) => args[0]); + assert.ok(Object.prototype.hasOwnProperty.call(result, "__proto__")); + assert.equal((result as Record)["__proto__"], "value"); + }); +}); + describe("@func name validation", () => { const maliciousProgram: Program = { "@steps": [