Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions typescript/src/ts/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ function isValidFunctionName(name: unknown): name is string {
export function createModuleTextFromProgram(jsonObject: object): Result<string> {
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 = "";
Expand All @@ -109,7 +109,7 @@ export function createModuleTextFromProgram(jsonObject: object): Result<string>
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 {
Expand Down Expand Up @@ -137,7 +137,7 @@ export function createModuleTextFromProgram(jsonObject: object): Result<string>
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 "";
Expand Down Expand Up @@ -213,11 +213,11 @@ export async function evaluateJsonProgram(program: Program, onCall: (func: strin
* @returns A `TypeChatJsonTranslator<Program>` instance.
*/
export function createProgramTranslator(model: TypeChatLanguageModel, schema: string): TypeChatJsonTranslator<Program> {
const validator = createTypeScriptJsonValidator<Program>(
schema,
"Program",
"API"
);
const validator = createTypeScriptJsonValidator<Program>(
schema,
"Program",
"API"
);
validator.createModuleTextFromJson = createModuleTextFromProgram;
const translator = createJsonTranslator<Program>(model, validator);
translator.createRequestPrompt = createRequestPrompt;
Expand Down
31 changes: 31 additions & 0 deletions typescript/test/program.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, unknown>)["__proto__"], "value");
});
Comment on lines +212 to +217
});

describe("@func name validation", () => {
const maliciousProgram: Program = {
"@steps": [
Expand Down