Skip to content

fix(ai): fail closed when a models endpoint validates a key on status alone - #4586

Open
Yeachan-Heo wants to merge 3 commits into
devfrom
fix/models-endpoint-validation-fail-open
Open

fix(ai): fail closed when a models endpoint validates a key on status alone#4586
Yeachan-Heo wants to merge 3 commits into
devfrom
fix/models-endpoint-validation-fail-open

Conversation

@Yeachan-Heo

@Yeachan-Heo Yeachan-Heo commented Aug 15, 2026

Copy link
Copy Markdown
Owner

Problem

validateApiKeyAgainstModelsEndpoint returned on any response.ok without reading the body. A captive portal, misrouting proxy, or broken gateway answering successfully with HTML, malformed JSON, or dataless JSON could silently validate and store an unchecked API key.

Fix

A successful models response must parse as JSON and carry a recognizable model list: an OpenAI-compatible data array, a gateway models array, or a bare array. HTML, malformed JSON, and dataless JSON fail closed. Empty authenticated arrays remain valid. Upstream response diagnostics are bounded to 200 characters in both models-endpoint and chat-completions validators, and successful non-200 2xx refusals report the actual status.

Affects Synthetic, DeepSeek, DeepInfra, Fireworks, BizRouter, NanoGPT, OpenGateway, ZenMux, and Fugu.

Fresh current-dev evidence

  • Head: 99afd9ec84da32d9e8ef8575aeee1834759fe658
  • Base: 91a35114d1263dd8422afa1c3e5a3b65d60ff724 (dev)
  • Current dev is an ancestor of the PR head.
  • Delta remains exactly three files: packages/ai/CHANGELOG.md, packages/ai/src/utils/oauth/api-key-validation.ts, and packages/ai/test/api-key-validation.test.ts (+187/-2). No shared current-dev TDZ repair was copied into this PR.
  • Canonical binary-diff digest: sha256:329abc6ffaad5149aa48e1de3577a55619f2df668d5f6c1dbf1bda77507ac00f
  • Shared and affected provider validation: 44/44 passed, including 12/12 shared validation cases.
  • Hermetic isolated login/OAuth sweep: 212/212 across 30 files.
  • bun --cwd=packages/ai run check: clean.
  • bun build packages/ai/src/index.ts --target bun: clean.
  • Every prior review, approval, digest, verdict, and CI result is stale. Fresh exact-head replacement CI and a write-authorized non-author approval are required.

gajae.pr-review-verdict.v1 needs-human sha256:329abc6ffaad5149aa48e1de3577a55619f2df668d5f6c1dbf1bda77507ac00f reviewer:human reviewer-id:pending evidence:exact-head-99afd9ec84-current-dev-91a35114-rebase-clean-three-file-delta-local-validation-44-auth-sweep-212-ai-check-build-clean-fresh-ci-and-non-author-review-pending


[repo owner's gaebal-gajae (clawdbot) 🦞]

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Exact-head review requested from @probepark, @HaD0Yun, and @IYENTeam for fa04a46e88714aca83ea485a156617ffe9629e95.

Canonical binary-diff digest: sha256:f41ab9620d52bd859f51daa325357832952e2c0d13e36c83fbc502c820ce390b against base 45885ea28793b0ace3523272b41f7ad07b1e88e5.

Adversarial evidence on this head:

  • 11/11 shared validator tests: HTML, malformed JSON, and dataless HTTP 200 fail closed; OpenAI data, gateway models, bare arrays, and empty authenticated arrays pass.
  • Oversized upstream response echoes are bounded for models-endpoint non-2xx, models-endpoint malformed 200, and chat-completions validation.
  • 43/43 affected provider compatibility tests pass.
  • 212/212 login/OAuth tests pass hermetically in isolated processes across 30 files.
  • AI package Biome/TypeScript check and Bun package bundle pass.

The PR body carries a canonical merge-blocked verdict until affected CI is green and one authorized non-author reviewer approves this exact head. Please review only this SHA; earlier approvals or runs are stale by construction.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Current dev advanced to 804314081fe9d3f4d34014d1385f09d8d49a7116 via #4555 after this exact-head evidence was frozen.

Re-evaluation against fa04a46e88714aca83ea485a156617ffe9629e95:

  • The base advance changes only Dev CI, coding-agent lock/session-index, Telegram baseline, and affected-path tooling files.
  • PR fix(ai): fail closed when a models endpoint validates a key on status alone #4586 changes only packages/ai/CHANGELOG.md, packages/ai/src/utils/oauth/api-key-validation.ts, and packages/ai/test/api-key-validation.test.ts.
  • Three-way merge-tree analysis reports no overlap or conflict markers.
  • The canonical three-dot binary-diff digest against current dev remains sha256:f41ab9620d52bd859f51daa325357832952e2c0d13e36c83fbc502c820ce390b.

Reconstruction is therefore not required; rebasing would only churn the head and stale clean disjoint evidence. The current exact-head run remains the applicable product run. Merge stays blocked pending its native-build completion and a fresh authorized exact-head approval.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Fresh exact-head review is required for 9ecf1e39f3b4f79f1ac91e64e32feb9315011cdb; all fa04a46e… evidence and approvals are stale.

Generation-1 adversarial QA found that a real dataless 204 failed closed but its new error text incorrectly said returned 200. That diagnostic defect was fixed forward by interpolating response.status and adding explicit 204 regression coverage.

Current exact-head evidence:

  • Digest: sha256:c5c7150dc48293c4bb0db1b560f263ab9ead1d931874b83ff9130fe75b2d3e18 against current dev 804314081fe9d3f4d34014d1385f09d8d49a7116.
  • 12/12 shared validation tests.
  • 44/44 affected provider compatibility tests.
  • 212/212 hermetic isolated login/OAuth tests across 30 files.
  • AI package Biome/TypeScript check and Bun bundle clean.
  • Current-dev overlap remains empty and merge-tree remains conflict-free.

@probepark @HaD0Yun @IYENTeam: please review and approve only this exact SHA. The canonical verdict remains merge-blocked until fresh product CI is green and one authorized non-author exact-head approval exists.


[repo owner's gaebal-gajae (clawdbot) 🦞]

Yeachan-Heo pushed a commit that referenced this pull request Aug 15, 2026
33 commits over 0.13.2: hotfixes (#4437 #4424 #4481 #4446 #4453 #4452),
session/storage resilience (#4396 #4411 #4373), security/stability (#4385
Synthetic models-endpoint validation, #4302 image-gen redaction), agent
escaped-non-ASCII managed recovery (#4515 release variant), Kiro OAuth
(#4304), Muse Spark 1.2, xAI + Grok CLI 4.6, Ouroboros/iTerm2 pet
(#4468 #4499), integration docs.

Release is HELD: no version bump, tag, or publish; binary stays gjc/0.13.2
until bun run release cuts 0.13.3 after dogfood verification. Pending before
the cut: integration groups 3-4 (#4421 #4450 #4470 #4495), validation
evidence, PR #4586 backport decision.
@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Exact-head product CI is now green for 9ecf1e39f3b4f79f1ac91e64e32feb9315011cdb.

Run 31883290025 completed with all product jobs successful, including:

  • affected-path plan, native build, TS package builds, focused api-key-validation.test.ts, evidence producer, aggregate affected validation, and virtual integration;
  • all GJC state gates.

The sole failing job is the intentionally blocked PR contract bootstrap, which requires a fresh authorized non-author exact-head approval and promoted canonical verdict. No product failure remains.

@probepark @HaD0Yun @IYENTeam: one exact-head approval is the only remaining merge prerequisite. Digest remains sha256:c5c7150dc48293c4bb0db1b560f263ab9ead1d931874b83ff9130fe75b2d3e18; current-dev overlap and merge-tree are clean. On approval, the verdict will be promoted and the PR squash-merged immediately.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo
Yeachan-Heo force-pushed the fix/models-endpoint-validation-fail-open branch from 9ecf1e3 to dec4937 Compare August 15, 2026 13:10
@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

PR #4586 has been authoritatively reconciled onto current dev.

  • New exact head: dec49376af3be424ca21047296ebc1de209ad928
  • Exact base: 804314081fe9d3f4d34014d1385f09d8d49a7116
  • Force update used an exact --force-with-lease against stale head 9ecf1e39f3b4f79f1ac91e64e32feb9315011cdb.
  • Current dev is an ancestor of the new head.
  • Preserved delta is tree-identical and remains exactly packages/ai/CHANGELOG.md, packages/ai/src/utils/oauth/api-key-validation.ts, and packages/ai/test/api-key-validation.test.ts.
  • Canonical binary-diff digest remains sha256:c5c7150dc48293c4bb0db1b560f263ab9ead1d931874b83ff9130fe75b2d3e18.
  • Fresh local evidence: affected validation/provider tests 44/44, isolated login/OAuth sweep 212/212, AI package check, and Bun bundle all pass.

Every run, review, and approval attached to 9ecf1e39… is stale and must not be reused. @probepark @HaD0Yun @IYENTeam: please review and approve only exact head dec49376af3be424ca21047296ebc1de209ad928. The canonical verdict remains merge-blocked until replacement CI is product-green and an effective write-authorized non-author approval exists.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@probepark probepark left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approve at 9ecf1e39f

fix(ai): fail closed when a models endpoint validates a key, head commit "report actual models
response status". 3 files, +187.

$ bun test <the PR's touched suites>
 12 pass  0 fail

Why the direction is right

Treating a models-endpoint response as key validation is only safe if a non-success status is
treated as "unknown", not "fine". Failing open here means a provider whose /models endpoint is
down, rate-limited, or returning a 403 gets recorded as a working credential, and the failure
resurfaces later as a confusing runtime error on the first real request instead of at validation
time.

Reporting the actual response status rather than collapsing it to a boolean is the part that makes
the fail-closed decision reviewable - a 401 and a 503 mean different things about the key, and a
caller that only sees false cannot distinguish "this key is wrong" from "we could not tell".

merge-approved.

Reviewed by @probepark - method: fresh-worktree run at the exact head.

@Yeachan-Heo Yeachan-Heo reopened this Aug 15, 2026
@Yeachan-Heo
Yeachan-Heo force-pushed the fix/models-endpoint-validation-fail-open branch from dec4937 to 90dbafa Compare August 15, 2026 13:28
@Yeachan-Heo
Yeachan-Heo requested a review from probepark August 15, 2026 13:29
@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Current dev advanced again and all prior evidence is stale.

PR #4586 has been reconciled onto exact base 87b540d2caa57af71737f2f39d65d5c7d9337b8d with new exact head 90dbafad3030fb15e6d9b67bf63d18209b26671e.

  • Force update used exact --force-with-lease against dec49376af3be424ca21047296ebc1de209ad928.
  • Current dev is an ancestor of the new head.
  • The sole overlap was packages/ai/CHANGELOG.md; the current-dev Cursor entry and PR fix(ai): fail closed when a models endpoint validates a key on status alone #4586 validation entry were both retained.
  • Product delta remains exactly three files, +187/-2.
  • New canonical digest: sha256:f8c6ade7e9ae3b8113bcf430a7a7b856597531da60f3b4f89da8cd4fa5c7136c.
  • Fresh local evidence: affected provider validation 44/44, isolated login/OAuth sweep 212/212, AI package check, and Bun bundle pass.

Review 4943913124 and every earlier approval are stale because they bind to dec49376…. @probepark @HaD0Yun @IYENTeam: please review and approve only exact head 90dbafad3030fb15e6d9b67bf63d18209b26671e. The canonical verdict is reset to merge-blocked until fresh replacement CI and an effective exact-head approval exist.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Exact-head replacement CI is product-green for 90dbafad3030fb15e6d9b67bf63d18209b26671e on base 87b540d2caa57af71737f2f39d65d5c7d9337b8d.

Authoritative run 31887362242 completed with every product/platform job successful:

  • affected-path plan, native build, TypeScript package builds, focused api-key-validation.test.ts, evidence producer, aggregate affected validation, and virtual integration;
  • all GJC state gates.

The sole failure is the intentional PR contract bootstrap because the current canonical verdict is merge-blocked and exact-head approvals remain zero. There is no product failure.

Fresh exact-head evidence is also clean: architect CLEAR/CLEAR/CLEAR APPROVE, baseline 12/12, adversarial 66/66, affected provider validation 44/44, isolated login/OAuth sweep 212/212, AI package check, and Bun bundle. Canonical digest: sha256:f8c6ade7e9ae3b8113bcf430a7a7b856597531da60f3b4f89da8cd4fa5c7136c.

@probepark @HaD0Yun @IYENTeam: one fresh write-authorized non-author APPROVED review at exact head 90dbafad3030fb15e6d9b67bf63d18209b26671e is the only remaining merge prerequisite. The verdict will be promoted and the PR squash-merged immediately after approval.


[repo owner's gaebal-gajae (clawdbot) 🦞]

Yeachan-Heo and others added 3 commits August 15, 2026 20:38
… alone

validateApiKeyAgainstModelsEndpoint returned on any response.ok without
reading the body, so a captive portal, misrouting proxy, or broken gateway
answering 200 with an HTML page silently validated and stored an API key that
was never actually checked. Found by an adversarial QA harness during the
0.13.3 release review: stubbed 200 responses with malformed JSON and with a
dataless JSON object were both accepted.

A 200 now requires a body that parses as JSON and carries a recognizable
model list: the OpenAI-compatible `data` array, a gateway `models` array, or
a bare array. Anything else fails with an actionable error naming the reason.
Upstream bodies echoed into validation errors are bounded to 200 characters
on both validators.

Constraint: all nine models-endpoint consumers are OpenAI-compatible /models URLs, so requiring a list shape is safe
Constraint: an empty model list still validates -- plan-restricted keys list zero models yet authenticate
Rejected: validating the chat-completions probe body too | its response shape is not a typed contract; separate change if wanted
Rejected: requiring exactly {object:"list",data:[]} | gateways answer with bare arrays and models fields
Confidence: high
Scope-risk: narrow
Reversibility: easy
Tested: 8 focused cases incl. captive-portal 200, dataless 200, bounded 500 echo, network failure; synthetic-login suite; 60-suite oauth sweep with zero introduced failures vs base
Not-tested: live provider endpoints
The models-endpoint hardening claimed bounded upstream details across both validation strategies, but chat-completions validation still echoed an unbounded response body. Bound that path too and add explicit malformed JSON and oversized 200-body coverage.

Lore-id: pr4586-bound-echoes

Constraint: models-endpoint validation must fail closed without rejecting empty authenticated model arrays

Tested: 43 provider validation and login tests; 212 isolated login/oauth tests; AI package check and Bun bundle

Scope-risk: narrow

Reversibility: easy
Successful-body validation covers every 2xx response, but its new refusal messages hard-coded 200. A real 204 therefore failed closed with misleading diagnostics. Interpolate the actual status and lock the 204 case.

Lore-id: pr4586-models-status

Constraint: every dataless successful response must fail closed with accurate status evidence

Tested: 44 affected provider tests; 212 isolated login/oauth tests; AI package check and Bun bundle

Scope-risk: narrow

Reversibility: easy
@Yeachan-Heo
Yeachan-Heo force-pushed the fix/models-endpoint-validation-fail-open branch from 90dbafa to 99afd9e Compare August 15, 2026 20:39
@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

PR #4586 has been reconstructed onto exact current dev.

  • Exact base: 91a35114d1263dd8422afa1c3e5a3b65d60ff724
  • Exact head: 99afd9ec84da32d9e8ef8575aeee1834759fe658
  • Force update used exact --force-with-lease against old head 90dbafad3030fb15e6d9b67bf63d18209b26671e.
  • Current dev is an ancestor; the delta remains exactly the three fix(ai): fail closed when a models endpoint validates a key on status alone #4586 files and does not duplicate any shared TDZ repair.
  • New canonical digest: sha256:329abc6ffaad5149aa48e1de3577a55619f2df668d5f6c1dbf1bda77507ac00f.
  • Fresh local verification: affected validation/provider tests 44/44, isolated login/OAuth 212/212, AI package check, and Bun bundle pass.

Every older review, approval, verdict, and CI receipt is stale. @probepark @HaD0Yun @IYENTeam: please review and approve only exact head 99afd9ec84da32d9e8ef8575aeee1834759fe658. The body carries one needs-human verdict until fresh replacement CI and an effective write-authorized non-author approval exist.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Exact-head replacement CI is product-green for 99afd9ec84da32d9e8ef8575aeee1834759fe658 on base 91a35114d1263dd8422afa1c3e5a3b65d60ff724.

Run 31907332548 completed with every product/platform job successful: native build, focused API-key validation, TypeScript builds, evidence producer, aggregate affected validation, virtual integration, and all GJC state gates. Its sole failure is exact and intentional: Verdict needs-human intentionally blocks merge.

Fresh current-head cohort evidence is clean: architect CLEAR/CLEAR/CLEAR APPROVE; baseline 12/12; adversarial 66/66; affected providers 44/44; isolated login/OAuth 212/212; AI check/build clean. Digest recomputes to sha256:329abc6ffaad5149aa48e1de3577a55619f2df668d5f6c1dbf1bda77507ac00f.

@probepark @HaD0Yun @IYENTeam: an APPROVED review bound to exact commit 99afd9ec84da32d9e8ef8575aeee1834759fe658 is the sole remaining merge prerequisite. Stale approval 4943913124 on dec49376… will not be reused. After an effective approval, the single verdict will be promoted, bootstrap revalidated, MERGE_READY posted, and #4586 squash-merged immediately.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@probepark probepark left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approve at 99afd9ec8

Head moved since my approval of dec49376a; re-verified at the exact head.

Differential

# base 91a35114d, with this head test files applied
 5 pass  7 fail

# head 99afd9ec8
 12 pass  0 fail

Seven failures on base - stronger evidence than the head-only run I had last time.

Substantive review stands: treating a models-endpoint response as key validation is only safe if a
non-success status means "unknown" rather than "fine". Reporting the actual status rather than
collapsing to a boolean is what makes the fail-closed decision reviewable, since a 401 and a 503 say
different things about the key.

merge-approved at 99afd9ec8.

Reviewed by @probepark - method: fresh-worktree run at the exact head, clean base worktree with the head test files to prove the differential.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants