{"record":{"id":"3778115d87188979","repo":"CherryHQ/cherry-studio","slug":"invalid-mcp-package-env-null-byte-detected-in-val","errorCode":null,"errorMessage":"Invalid MCP package env: null byte detected in value of environment variable \"${key}\"","messagePattern":"Invalid MCP package env: null byte detected in value of environment variable \"(.+?)\"","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/mcp/McpPackageService.ts","lineNumber":268,"sourceCode":"  extractDir: string,\n  userConfig?: Record<string, any>\n): Record<string, string> {\n  const resolvedEnv: Record<string, string> = {}\n\n  for (const [key, value] of Object.entries(env)) {\n    if (key.includes('\\0')) {\n      throw new Error('Invalid MCP package env: null byte detected in environment variable name')\n    }\n\n    // Denylist process-affecting variables (DYLD_* on macOS, plus exact matches above).\n    const canonicalKey = key.toUpperCase()\n    if (DXT_ENV_DENYLIST.includes(canonicalKey) || canonicalKey.startsWith('DYLD_')) {\n      throw new Error(`Invalid MCP package env: environment variable \"${key}\" is not allowed`)\n    }\n\n    const substituted = performVariableSubstitution(value, extractDir, userConfig)\n    if (substituted.includes('\\0')) {\n      throw new Error(`Invalid MCP package env: null byte detected in value of environment variable \"${key}\"`)\n    }\n\n    resolvedEnv[key] = substituted\n  }\n\n  return resolvedEnv\n}\n\nexport function validatePackageUploadPayload(\n  fileBuffer: ArrayBuffer | NodeJS.ArrayBufferView,\n  fileName: string,\n  packageFormat: McpPackageFormat\n): Buffer {\n  if (typeof fileName !== 'string') {\n    throw new Error('Invalid MCP package upload: file name must be a string')\n  }\n\n  const trimmedFileName = fileName.trim()","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/mcp/McpPackageService.ts#L250-L286","documentation":"Thrown by buildResolvedEnv after variable substitution: an env value, once ${__dirname}, ${HOME}, ${user_config.*} etc. are expanded, contained a NUL byte. The check runs on the substituted value (performVariableSubstitution output), so the null can come from the manifest literally or be introduced by a user_config value injected via the ${user_config.KEY} template.","triggerScenarios":"Manifest env value contains a literal \\0; or a ${user_config.field} placeholder is filled by a user-supplied value that contains \\0 and the substitution replaces the placeholder with it.","commonSituations":"A user-config text field accepted a pasted binary blob; a manifest author copied a value from a terminal that included a control character; a malicious package tried to smuggle a null past key-level validation by putting it in the value.","solutions":["Identify whether the null originated in the manifest value or in a ${user_config.*} substitution. Inspect the manifest's env values first.","If it came from user_config, sanitize the user-supplied value in the package config form before it reaches the substitution (strip \\0 and other control characters).","Repackage the manifest with a clean value or re-enter the user config and retry."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function hasNoNullByte(value: unknown): boolean {\n  return typeof value === 'string' && !value.includes('\\0')\n}\nfunction cleanEnvValues(env: Record<string, string>, userConfig?: Record<string, any>): boolean {\n  // Mirror performVariableSubstitution only for user_config; __dirname/HOME do not inject nulls.\n  return Object.entries(env).every(([, v]) => {\n    if (!v.includes('\\0')) return true\n    if (!userConfig) return false\n    const substituted = v.replace(/\\$\\{user_config\\.([^}]+)\\}/g, (_m, k) => userConfig[k] ?? _m)\n    return !substituted.includes('\\0')\n  })\n}","typeGuard":"function isNullByteFree(value: unknown): value is string {\n  return typeof value === 'string' && !value.includes('\\0')\n}","tryCatchPattern":null,"preventionTips":["Strip control characters (including \\0) from user_config form fields on the renderer before submission.","In a manifest linter, flag env values that contain control characters or ${user_config.*} placeholders whose schema accepts free text.","When authoring manifests, avoid embedding raw bytes; use base64 if binary data must be conveyed and decode in the server."],"tags":["security","null-byte","mcp","manifest","validation","env","variable-substitution"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}