Root cause
Authentication & access control introduced
fix: repair MCP stdio communication and unresponsive image uploads
How AI contributed: Direct introductionPublished Mar 9, 2026 in
lupinlin1/jimeng-web-mcp
The Claude-generated release converted the console logging to the project logger and added new debug lines that record the upload credential response in ImageUploader.getUploadAuth() and the fully expanded request URL in HttpClient.request(). The logged URL carries signed query parameters and cookie-derived tokens, and the logger masks only the context keys it recognises, so neither value is redacted. Anyone with access to the server logs can recover live upload credentials and signed request links. No fix has been released.
The sensitive-data logging entered with the AI-written change released in v2.1.2, the only version inside the affected range, and persisted for that entire version.
Root cause
fix: repair MCP stdio communication and unresponsive image uploads
Fix status
No minimum fix commit has been established for this finding.
Fix status unresolved
Case facts
@@ -13,6 +13,7 @@ import path from 'path'; import crc32 from 'crc32'; import { HttpClient } from './HttpClient.js'; import { retryAsync } from '../utils/retry.js';+import { logger } from '../utils/logger.js'; export interface UploadResult { uri: string; // 服务器返回的URL@@ -39,19 +40,25 @@ export class ImageUploader { * 上传单张图片 */ async upload(imagePath: string): Promise<UploadResult> {+ let uploadAuth; try { // 获取上传凭证- const uploadAuth = await this.getUploadAuth();+ uploadAuth = await this.getUploadAuth();+ } catch (error) {+ throw new Error(`图片上传失败 [${imagePath}] at 步骤1 (获取上传凭证): ${error}`);+ } - // 读取图片内容- const imageBuffer = await this.getFileContent(imagePath);+ // 读取图片内容+ const imageBuffer = await this.getFileContent(imagePath); - // 使用image-size库检测格式和尺寸(替代132行手动解析)- const metadata = this.detectFormat(imageBuffer);+ // 使用image-size库检测格式和尺寸(替代132行手动解析)+ const metadata = this.detectFormat(imageBuffer); - // 计算CRC32校验- const imageCrc32 = crc32(imageBuffer).toString(16);+ // 计算CRC32校验+ const imageCrc32 = crc32(imageBuffer).toString(16); + let uploadImgRes;+ try { // 准备上传参数 const getUploadImageProofRequestParams = { Action: 'ApplyImageUpload',@@ -75,7 +82,7 @@ export class ImageUploader { const getUploadImageProofUrl = 'https://imagex.bytedanceapi.com/'; // 获取图片上传凭证- const uploadImgRes = await this.httpClient.request({+ uploadImgRes = await this.httpClient.request({ method: 'GET', url: getUploadImageProofUrl + '?' + this.httpClient.httpBuildQuery(getUploadImageProofRequestParams), headers: requestHeadersInfo@@ -84,20 +91,29 @@ export class ImageUploader { if (uploadImgRes?.['Response']?.hasOwnProperty('Error')) { throw new Error(uploadImgRes['Response']['Error']['Message']); }+ } catch (error) {+ throw new Error(`图片上传失败 [${imagePath}] at 步骤2 (获取图片上传凭证): ${error}`);+ } - const UploadAddress = uploadImgRes.Result.UploadAddress;+ const UploadAddress = uploadImgRes.Result.UploadAddress; + try { // 上传图片 const uploadImgUrl = `https://${UploadAddress.UploadHosts[0]}/upload/v1/${UploadAddress.StoreInfos[0].StoreUri}`; // 图片上传步骤:带重试机制(仅此步骤重试)- const imageUploadRes = await this.uploadImageDataWithRetry(+ await this.uploadImageDataWithRetry( uploadImgUrl, imageBuffer, imageCrc32, UploadAddress.StoreInfos[0].Auth );+ } catch (error) {+ throw new Error(`图片上传失败 [${imagePath}] at 步骤3 (上传图片数据): ${error}`);+ } + let commitRes;+ try { // 提交上传 const commitImgParams = { Action: 'CommitImageUpload',@@ -123,7 +139,7 @@ export class ImageUploader { const commitImgUrl = 'https://imagex.bytedanceapi.com/'; - const commitRes = await this.httpClient.request({+ commitRes = await this.httpClient.request({ method: 'POST', url: commitImgUrl + '?' + this.httpClient.httpBuildQuery(commitImgParams),@@ -9,6 +9,7 @@ import { generateCookie } from '../utils/auth.js'; import { DEFAULT_ASSISTANT_ID, UA } from '../types/models.js'; import { generateUuid, jsonEncode, toUrlParams, generateMsToken, unixTimestamp } from '../utils/index.js'; import { generate_a_bogus } from '../utils/a_bogus.js';+import { logger } from '../utils/logger.js'; import crypto from 'crypto'; export interface RequestOptions {@@ -50,7 +51,7 @@ export class HttpClient { data = {}, headers = {}, params = {},- timeout = 60000+ timeout = 120000 // 增加超时时间到120秒 } = options; const baseUrl = 'https://jimeng.jianying.com';@@ -61,6 +62,7 @@ export class HttpClient { "Accept-Encoding": "gzip, deflate, br, zstd", "Accept-language": "zh-CN,zh;q=0.9", "Cache-control": "no-cache",+ "Content-Type": "application/json", // 🔥 添加Content-Type "Last-event-id": "undefined", Appid: DEFAULT_ASSISTANT_ID, Appvr: "5.8.0",@@ -84,6 +86,8 @@ export class HttpClient { ...headers }; + logger.debug(`[HttpClient] Request: ${method} ${fullUrl}`);+ try { const response: AxiosResponse<T> = await axios({ method: method.toLowerCase(),@@ -302,7 +306,7 @@ export class HttpClient { if (error.response) { throw new Error(`即梦API请求错误: ${JSON.stringify(error.response.data)}`); } else if (error.request) {- throw new Error(`即梦API请求失败: 无响应`);+ throw new Error(`[FINAL-DEBUG] HttpClient.handleError: Caught error with no response.`); } else { throw new Error(`即梦API请求失败: ${error.message}`); }@@ -126,23 +126,23 @@ export const createServer = (): McpServer => { }] }; } catch (error) {- // 🔍 Debug logging - 记录详细错误信息- console.error('🔍 [MCP Server] Error caught in generateImage tool:');- console.error('🔍 [MCP Server] Error type:', error?.constructor?.name);- console.error('🔍 [MCP Server] Error message:', error instanceof Error ? error.message : String(error));+ // 🔍 Debug logging - 记录详细错误信息 (使用logger以避免破坏stdio)+ logger.debug('🔍 [MCP Server] Error caught in generateImage tool');+ logger.debug(`🔍 [MCP Server] Error type: ${error?.constructor?.name}`);+ logger.debug(`🔍 [MCP Server] Error message: ${error instanceof Error ? error.message : String(error)}`); if (error instanceof Error && error.stack) {- console.error('🔍 [MCP Server] Error stack:', error.stack);+ logger.debug(`🔍 [MCP Server] Error stack: ${error.stack}`); }- + // 🔍 记录错误时的参数状态- console.error('🔍 [MCP Server] Parameters when error occurred:', JSON.stringify({+ logger.debug(`🔍 [MCP Server] Parameters when error occurred: ${JSON.stringify({ filePath: params.filePath, prompt: params.prompt ? `${params.prompt.substring(0, 100)}...` : undefined, model: params.model, aspectRatio: params.aspectRatio, sample_strength: params.sample_strength, negative_prompt: params.negative_prompt- }, null, 2));+ }, null, 2)}`); const errorMessage = error instanceof Error ? error.message : String(error); return {