From 3b9776947bd0f1e833edb21e91b95e36f0ca8c3c Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 20 Jul 2026 17:37:17 -0400 Subject: [PATCH] fix(input): reject non-OK HTTP responses when reading from a URL (#2572) quicktype's URL input reader handed the fetch response body straight to the JSON parser without checking response.ok, so an HTTP error (e.g. a 401 from an expired auth token) whose body happened to be valid JSON was silently treated as legitimate input, generating types from the error payload and exiting 0. Now readableFromFileOrURL throws a clear error including the HTTP status when the response is not OK. Co-Authored-By: gpt-5.6-sol via pi --- packages/quicktype-core/src/input/io/NodeIO.ts | 5 +++++ test/unit/url-input.test.ts | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/packages/quicktype-core/src/input/io/NodeIO.ts b/packages/quicktype-core/src/input/io/NodeIO.ts index a5df74f00f..137e8ecf71 100644 --- a/packages/quicktype-core/src/input/io/NodeIO.ts +++ b/packages/quicktype-core/src/input/io/NodeIO.ts @@ -107,6 +107,11 @@ export async function readableFromFileOrURL( const response = await globalThis.fetch(fileOrURL, { headers: parseHeaders(httpHeaders), }); + if (!response.ok) { + throw new Error( + `HTTP ${response.status} ${response.statusText}`, + ); + } return readableFromResponseBody(defined(response.body)); } diff --git a/test/unit/url-input.test.ts b/test/unit/url-input.test.ts index eeb17002c0..24807a8adb 100644 --- a/test/unit/url-input.test.ts +++ b/test/unit/url-input.test.ts @@ -19,6 +19,7 @@ import * as path from "node:path"; import { afterAll, beforeAll, describe, expect, test } from "vitest"; +import { readFromFileOrURL } from "../../packages/quicktype-core/src/input/io/NodeIO.js"; import { main as quicktype } from "../../src"; const files: Record = { @@ -74,6 +75,12 @@ async function generateTypeScript( beforeAll(async () => { server = http.createServer((request, response) => { + if (path.basename(request.url ?? "") === "unauthorized.json") { + response.writeHead(401, { "Content-Type": "application/json" }); + response.end('{"message":"Bad credentials"}'); + return; + } + const content = files[path.basename(request.url ?? "")]; if (content === undefined) { response.writeHead(404); @@ -105,6 +112,12 @@ describe("native fetch URL inputs", () => { expect(output).toContain("veryUniquePropertyName"); }); + test("rejects an HTTP error response", async () => { + await expect( + readFromFileOrURL(`${baseURL}/unauthorized.json`), + ).rejects.toThrow("HTTP 401 Unauthorized"); + }); + test("resolves a relative remote JSON Schema reference", async () => { const output = await generateTypeScript("schema", "main.schema"); expect(output).toContain("veryUniqueReferencedProperty");