From 83e01c3077dcb1f3534101e7dd503f79494c0699 Mon Sep 17 00:00:00 2001 From: musistudio Date: Sat, 15 Aug 2026 23:16:15 +0800 Subject: [PATCH] Sanitize target model header while preserving Unicode body --- packages/core/src/media/executors.ts | 3 +- .../mcp/grok-media-service.test.mjs | 35 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/packages/core/src/media/executors.ts b/packages/core/src/media/executors.ts index 690c7a5f..ac62d117 100644 --- a/packages/core/src/media/executors.ts +++ b/packages/core/src/media/executors.ts @@ -8,6 +8,7 @@ import { setTimeout as delay } from "node:timers/promises"; import type { GatewayMediaProtocol } from "@ccr/core/contracts/app"; import type { ImageEditRequest, ImageGenerateRequest, MediaExecutionContext, MediaExecutionResult, VideoGenerateRequest } from "@ccr/core/media/contracts"; import { detectMediaType } from "@ccr/core/media/storage"; +import { sanitizeHeaderValue } from "@ccr/core/providers/runtime-topology"; import { fetchWithSystemProxy } from "@ccr/core/proxy/system-proxy-fetch"; const maxApiArtifactBytes = 250 * 1024 * 1024; @@ -185,7 +186,7 @@ export class GatewayMediaExecutor { : {}), ...(jsonBody ? { "content-type": "application/json" } : {}), ...(idempotencyKey ? { "idempotency-key": idempotencyKey } : {}), - "x-target-model": this.target.model, + "x-target-model": sanitizeHeaderValue(this.target.model), "x-target-provider": this.target.providerSelector }; } diff --git a/packages/core/test/integration/mcp/grok-media-service.test.mjs b/packages/core/test/integration/mcp/grok-media-service.test.mjs index f962c216..c52243b9 100644 --- a/packages/core/test/integration/mcp/grok-media-service.test.mjs +++ b/packages/core/test/integration/mcp/grok-media-service.test.mjs @@ -118,6 +118,41 @@ test("media gateway errors expose the concrete failed provider attempt", async ( ); }); +test("media gateway sanitizes target model header while preserving unicode body model", async (t) => { + const rawModel = "「中文模型」image-model"; + let requestBody; + let targetModelHeader; + const server = createServer(async (request, response) => { + if (request.method === "POST" && request.url === "/v1/images/generations") { + targetModelHeader = request.headers["x-target-model"]; + requestBody = JSON.parse((await consume(request)).toString("utf8")); + json(response, { data: [{ url: "https://media.example/artifact.png" }] }); + return; + } + response.writeHead(404).end(); + }); + if (!await listenOrSkip(t, server)) return; + await waitForTcpListener(server); + t.after(() => server.close()); + + const executor = new GatewayMediaExecutor({ + model: rawModel, + protocol: "openai_image_generations", + providerBaseUrl: "https://media.example/v1", + providerName: "Unicode Media Provider", + providerSelector: "unicode-media::openai_image_generations" + }, { baseUrl: baseUrl(server) }); + + await executor.imageGenerate({ prompt: "A blue cup" }, { + job: { id: "unicode-model-header-test" }, + onRemoteRequestId() {}, + signal: new AbortController().signal + }); + + assert.equal(targetModelHeader, "image-model"); + assert.equal(requestBody.model, rawModel); +}); + test("implicit media input roots reject the filesystem root and home directory", () => { const home = path.resolve(os.homedir()); assert.equal(mediaServiceForTest.isSafeImplicitWorkingDirectory(path.parse(home).root, home), false);