From cbe245ba33583606993ff8a96b9a94bc2bd9b497 Mon Sep 17 00:00:00 2001 From: Mat Milbury Date: Mon, 13 Apr 2026 18:59:26 +0000 Subject: [PATCH] fix(link-legacy-key): validate response with Zod and add fetch timeout Replace `as LinkResult` cast on the mcp-gateway response with Zod schema parsing to catch malformed responses at runtime instead of silently proceeding with wrong data. Add AbortSignal.timeout(10s) to the fetch call to prevent the login command from hanging indefinitely if mcp-gateway accepts TCP but never responds. Co-Authored-By: Claude Opus 4.6 (1M context) --- package-lock.json | 20 +++++++++++++++----- package.json | 3 ++- src/auth/link-legacy-key.ts | 10 ++++------ src/auth/validation.ts | 9 +++++++++ 4 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 src/auth/validation.ts diff --git a/package-lock.json b/package-lock.json index f37eb30..0f08a9e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,18 @@ { - "name": "eterna", - "version": "0.1.0", + "name": "@eterna-hybrid-exchange/cli", + "version": "0.1.2", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "eterna", - "version": "0.1.0", + "name": "@eterna-hybrid-exchange/cli", + "version": "0.1.2", "license": "MIT", "dependencies": { "commander": "^13.0.0", "open": "^10.0.0", - "ora": "^8.0.0" + "ora": "^8.0.0", + "zod": "^4.3.6" }, "bin": { "eterna": "dist/cli.js" @@ -3846,6 +3847,15 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index 1ca6b61..76ff1a2 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "dependencies": { "commander": "^13.0.0", "open": "^10.0.0", - "ora": "^8.0.0" + "ora": "^8.0.0", + "zod": "^4.3.6" }, "devDependencies": { "@eslint/js": "^9.39.4", diff --git a/src/auth/link-legacy-key.ts b/src/auth/link-legacy-key.ts index 8fed5c1..87b3c03 100644 --- a/src/auth/link-legacy-key.ts +++ b/src/auth/link-legacy-key.ts @@ -1,10 +1,7 @@ import { getMcpEndpoint } from "./config.js"; +import { LinkResultSchema } from "./validation.js"; -interface LinkResult { - linked: boolean; - agentName: string; - bybitSubMemberId: string | null; -} +const LINK_TIMEOUT_MS = 10_000; /** * Links a legacy mcp-gateway API key to the caller's OAuth identity. @@ -28,6 +25,7 @@ export async function linkLegacyKey( "Content-Type": "application/json", }, body: JSON.stringify({ legacyApiKey }), + signal: AbortSignal.timeout(LINK_TIMEOUT_MS), }); } catch { console.warn( @@ -37,7 +35,7 @@ export async function linkLegacyKey( } if (res.ok) { - const data = (await res.json()) as LinkResult; + const data = LinkResultSchema.parse(await res.json()); if (data.linked) { console.log( `✓ Legacy account linked — existing Bybit subaccount preserved`, diff --git a/src/auth/validation.ts b/src/auth/validation.ts new file mode 100644 index 0000000..2111441 --- /dev/null +++ b/src/auth/validation.ts @@ -0,0 +1,9 @@ +import { z } from "zod"; + +export const LinkResultSchema = z.object({ + linked: z.boolean(), + agentName: z.string(), + bybitSubMemberId: z.string().nullable(), +}); + +export type LinkResult = z.infer;