Skip to content

fix(security): prevent structured-output format downgrade - #2394

Merged
HAYDEN-OAI merged 2 commits into
mainfrom
agent/structured-text-format-discriminator-msyvp8pz
Aug 18, 2026
Merged

HAYDEN-OAI merged 2 commits into
mainfrom
agent/structured-text-format-discriminator-msyvp8pz

Conversation

@HAYDEN-OAI

Copy link
Copy Markdown
Contributor

Summary

  • Canonicalize parseable Responses text formats to type: 'json_schema' after copying caller-supplied metadata in the shared handwritten parser factory.
  • Prevent untyped or JavaScript metadata from downgrading Zod v3, Zod v4, Zod v4 Mini, and Standard Schema helpers to unconstrained text or json_object while their local parser remains deceptively branded.
  • Preserve strict schemas, names, descriptions, intentional Standard Schema overrides, getter behavior, frozen inputs, symbol metadata, prototype safety, existing parser descriptors, custom parsers, and unbranded text formats.

Security impact

Before this change, both zodTextFormat(schema, name, { type: 'text' }) and standardTextFormat(schema, name, { type: 'json_object' }) could silently replace the required format discriminator. Real client.responses.parse(...) requests then serialized the downgraded discriminator while retaining an SDK-branded local parser, disabling API-side structured-output enforcement.

The regression suite proves the outgoing HTTP request and locally validated parsed result across all four public validator families, including malicious text, json_object, null, undefined, numeric, object, getter, inherited, non-enumerable, and direct-factory inputs.

Validation

  • Regression-first: 46 of the 59 focused security/compatibility cases fail on unmodified main; all 59 pass after the one-line fix.
  • Full handwritten suite: 106 files, 3,225 tests passed.
  • Full generated API suite: 559 tests passed (one existing skipped).
  • Repository formatting/lint: zero warnings or errors.
  • Repository TypeScript compilation.
  • Clean package build, including CommonJS/ESM imports.
  • Published declaration/source checks with TypeScript 4.9 and current TypeScript.
  • Packed npm artifact: CommonJS, ESM, and 267/304 source checks across 1,216 source maps.

Only the handwritten shared parser and one new dedicated regression file are changed; active Zod helper work remains untouched.

@openai-sdks

openai-sdks Bot commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor

OkTest Summary

✅ 237/237 SDK tests passed in 8.845s for Node SDK PR #2394.

Test results — 42 files
Test Result Time
tests/chat-completions-complex-body.test.ts ✅ Passed 166ms
tests/chat-completions-create.test.ts ✅ Passed 154ms
tests/chat-completions-stream.test.ts ✅ Passed 171ms
tests/files-content-binary.test.ts ✅ Passed 102ms
tests/files-create-multipart.test.ts ✅ Passed 111ms
tests/files-list-pagination.test.ts ✅ Passed 133ms
tests/initialize-config.test.ts ✅ Passed 156ms
tests/instance-isolation.test.ts ✅ Passed 69ms
tests/models-list.test.ts ✅ Passed 146ms
tests/responses-background-lifecycle.test.ts ✅ Passed 120ms
tests/responses-body-method-errors.test.ts ✅ Passed 216ms
tests/responses-cancel-timeout.test.ts ✅ Passed 168ms
tests/responses-cancel.test.ts ✅ Passed 155ms
tests/responses-compact-retries.test.ts ✅ Passed 268ms
tests/responses-compact.test.ts ✅ Passed 115ms
tests/responses-create-advanced-stream.test.ts ✅ Passed 86ms
tests/responses-create-advanced.test.ts ✅ Passed 102ms
tests/responses-create-disconnect.test.ts ✅ Passed 1.107s
tests/responses-create-errors.test.ts ✅ Passed 187ms
tests/responses-create-malformed-api-responses.test.ts ✅ Passed 92ms
tests/responses-create-retries.test.ts ✅ Passed 352ms
tests/responses-create-stream-failures.test.ts ✅ Passed 138ms
tests/responses-create-stream-timeout.test.ts ✅ Passed 2.131s
tests/responses-create-stream-wire.test.ts ✅ Passed 1.956s
tests/responses-create-stream.test.ts ✅ Passed 49ms
tests/responses-create-terminal-states.test.ts ✅ Passed 177ms
tests/responses-create-timeout.test.ts ✅ Passed 159ms
tests/responses-create.test.ts ✅ Passed 115ms
tests/responses-delete.test.ts ✅ Passed 472ms
tests/responses-input-items-errors.test.ts ✅ Passed 202ms
tests/responses-input-items-list.test.ts ✅ Passed 398ms
tests/responses-input-items-options.test.ts ✅ Passed 80ms
tests/responses-input-tokens-count-timeout.test.ts ✅ Passed 159ms
tests/responses-input-tokens-count.test.ts ✅ Passed 128ms
tests/responses-malformed-inputs.test.ts ✅ Passed 1.591s
tests/responses-not-found-errors.test.ts ✅ Passed 334ms
tests/responses-parse.test.ts ✅ Passed 140ms
tests/responses-retrieve-retries.test.ts ✅ Passed 177ms
tests/responses-retrieve.test.ts ✅ Passed 159ms
tests/responses-stored-method-errors.test.ts ✅ Passed 450ms
tests/retry-behavior.test.ts ✅ Passed 2.978s
tests/sdk-error-shape.test.ts ✅ Passed 302ms

View OkTest run #32162920649

SDK merge (d224b8f39589) · head (105774859e8b) · base (17cf54c5fdce) · OkTest (7e7850017529)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fdcb84b7e4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/parser.ts Resolved

@jbeckwith-oai jbeckwith-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] The structured-output downgrade remains possible through an own enumerable metadata serializer. At

const obj = { ...response_format, type: 'json_schema' as const };
, object spread retains a supplied toJSON even after forcing the in-memory format type to json_schema. Both Zod and Standard Schema helpers forward that callable, and FallbackEncoder then runs JSON.stringify(body), invoking it. Independently reproduced: the runtime format remains branded with type=json_schema and strict=true, while the actual HTTP request sends {"type":"text"}. This requires programmable metadata with an own enumerable callable; plain JSON input cannot introduce the function, and local response validation still runs. However, within this security fix, it preserves exactly the server-side schema downgrade the change is intended to prevent. Strip or neutralize metadata-provided serializer hooks in makeParseableTextFormat, and add direct-factory plus real responses.parse() wire-level regressions for both helper families.

@jbeckwith-oai jbeckwith-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed the updated head with an independent second pass. The previously reported wire-format downgrade is fixed: the shared Responses text-format factory forces the trusted json_schema discriminator and removes copied toJSON hooks before request serialization. New public fetch regressions cover Zod v3/v4/Mini, Standard Schema, direct factory calls, callable/accessor serializers, frozen metadata, and inherited/non-enumerable hooks. Existing schema/name/strict precedence, parser branding, metadata compatibility, and validation behavior remain intact. Exact-head CI is green. No actionable findings.

@HAYDEN-OAI
HAYDEN-OAI added this pull request to the merge queue Aug 18, 2026
Merged via the queue into main with commit dd945e6 Aug 18, 2026
27 checks passed
@HAYDEN-OAI
HAYDEN-OAI deleted the agent/structured-text-format-discriminator-msyvp8pz branch August 18, 2026 17:50
@openai-sdks openai-sdks Bot mentioned this pull request Aug 18, 2026

This branch was successfully deployed

1 active deployment
ci — 10577485 Deployed Aug 18, 2026 by HAYDEN-OAI via ecosystem tests #8403
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants