openai / openai/codex

TS SDK: config/outputSchema plain-object guard silently accepts Date/Map/RegExp, corrupting serialized output

Open Beginner friendly
#41,022 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

Description

isPlainObject() in sdk/typescript/src/exec.ts and isJsonObject() in sdk/typescript/src/outputSchemaFile.ts are used as the validation gate before serializing user-supplied values (config overrides, turnOptions.outputSchema) to JSON/TOML. Both are defined identically:

function isPlainObject(value: unknown): value is CodexConfigObject {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

This accepts any non-array object — including Date, Map, RegExp, or any class instance — not just plain {}-style objects. Since these types don't round-trip through JSON.stringify/Object.entries the way a plain object does, the validation silently passes and the resulting output is wrong instead of raising the descriptive error the code clearly intends ("Codex config overrides must be a plain object" / "outputSchema must be a plain JSON object").

Steps to reproduce
function isPlainObject(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
function toTomlValue(value, path) {
  if (typeof value === "string") return JSON.stringify(value);
  if (typeof value === "number") return `${value}`;
  if (typeof value === "boolean") return value ? "true" : "false";
  if (Array.isArray(value)) return `[${value.map((v, i) => toTomlValue(v, `${path}[${i}]`)).join(", ")}]`;
  if (isPlainObject(value)) {
    const parts = [];
    for (const [k, c] of Object.entries(value)) {
      if (c === undefined) continue;
      parts.push(`${k} = ${toTomlValue(c, `${path}.${k}`)}`);
    }
    return `{${parts.join(", ")}}`;
  }
  throw new Error(`Unsupported value at ${path}`);
}

console.log(isPlainObject(new Date()));                 // true (should be false)
console.log(toTomlValue(new Date("2024-01-01"), "ts"));  // "{}" -- value silently dropped, no error
Expected behavior

Passing a Date/Map/RegExp/other non-plain object as a config override value or as turnOptions.outputSchema should throw a clear error ("Codex config overrides must be a plain object" / "outputSchema must be a plain JSON object"), the same as passing a string or number would if it reached the object branch.

Actual behavior

The value is accepted, then silently serialized to {} (for Map/RegExp) or a bare string (for Date) with no error — the caller has no signal that their config was dropped.

Environment
  • Commit: 7c37479 (main, 2026-08-27)
  • Files: sdk/typescript/src/exec.ts:345-347, sdk/typescript/src/outputSchemaFile.ts:38-40
  • sdk/typescript/tests/ has no coverage for isPlainObject/isJsonObject/createOutputSchemaFile (confirmed via grep across the whole tests/ directory).
Suggested fix

Tighten both guards to check the prototype instead of just excluding arrays:

 function isPlainObject(value: unknown): value is CodexConfigObject {
-  return typeof value === "object" && value !== null && !Array.isArray(value);
+  if (typeof value !== "object" || value === null || Array.isArray(value)) {
+    return false;
+  }
+  const proto = Object.getPrototypeOf(value);
+  return proto === Object.prototype || proto === null;
 }

(apply the same change to isJsonObject in outputSchemaFile.ts)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the isPlainObject guard in sdk/typescript/src/exec.ts and the matching isJsonObject guard in sdk/typescript/src/outputSchemaFile.ts. Add regression coverage under sdk/typescript/tests for Date, Map, RegExp, and class instances, then run the existing TypeScript test suite. Done means non-plain objects are rejected with the documented errors while plain and null-prototype objects remain accepted.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.