{"record":{"id":"745a2329f60e3626","repo":"CherryHQ/cherry-studio","slug":"invalid-args-path-traversal-detected-in-argument","errorCode":null,"errorMessage":"Invalid args: path traversal detected in argument at index ${index}","messagePattern":"Invalid args: path traversal detected in argument at index (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/mcp/McpPackageService.ts","lineNumber":190,"sourceCode":"export function validateArgs(args: string[]): string[] {\n  if (!Array.isArray(args)) {\n    throw new Error('Invalid args: must be an array')\n  }\n\n  return args.map((arg, index) => {\n    if (typeof arg !== 'string') {\n      throw new Error(`Invalid args: argument at index ${index} must be a string`)\n    }\n\n    // Check for null bytes\n    if (arg.includes('\\0')) {\n      throw new Error(`Invalid args: null byte detected in argument at index ${index}`)\n    }\n\n    // Check for path traversal in arguments that look like paths\n    // Only validate if the arg contains path separators (indicating it's meant to be a path)\n    if ((arg.includes('/') || arg.includes('\\\\')) && /(?:^|[/\\\\])\\.\\.(?:[/\\\\]|$)/.test(arg)) {\n      throw new Error(`Invalid args: path traversal detected in argument at index ${index}`)\n    }\n\n    return arg\n  })\n}\n\nexport function performVariableSubstitution(\n  value: string,\n  extractDir: string,\n  userConfig?: Record<string, any>\n): string {\n  let result = value\n\n  // Replace ${__dirname} with the extraction directory\n  result = result.replace(/\\$\\{__dirname\\}/g, extractDir)\n\n  // Replace ${HOME} with user's home directory\n  result = result.replace(/\\$\\{HOME\\}/g, application.getPath('sys.home'))","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/mcp/McpPackageService.ts#L172-L208","documentation":"Thrown by validateArgs during command-argument sanitization. An MCP package manifest's server.mcp_config.args entry contained a path separator (either / or \\) together with a parent-reference segment (..). The guard runs after variable substitution inside applyPlatformOverrides, so it inspects the final value the spawned process would receive, not the raw template. It exists because arguments are forwarded verbatim to a child process and a ../ segment is the canonical way to escape an intended directory.","triggerScenarios":"An mcpb/dxt manifest supplies an arg like \"../../etc/passwd\", \"..\\\\..\\\\evil\", or a template such as \"${user_config.dir}/../secret\" where user_config.dir resolves to a path with separators. Also reached via platform_overrides for the current OS where an overridden args array contains the offending value.","commonSituations":"A package author points an argument at a path outside the extracted package on purpose; a user_config value (free-text field in the package's UI) contains a relative path the package's own template turns into a traversal; a Windows-style backslash arg is loaded on a posix host or vice-versa and slips past the author's own checks.","solutions":["Inspect the manifest's server.mcp_config.args (and server.mcp_config.platform_overrides[process.platform].args) for any literal containing '..' and remove or rewrite it to stay inside the package directory.","If the argument is built from ${user_config.*}, check the value the user supplied in the package config form and replace ../ sequences with an absolute path inside the extract dir (use ${__dirname} instead of user-supplied paths).","Repackage the .mcpb/.dxt with corrected args and re-upload; the existing install is unchanged because the failure occurs before the directory swap."],"exampleFix":"// manifest.json - before\n\"args\": [\"${user_config.logDir}/../secrets/key\"]\n// after\n\"args\": [\"${__dirname}/secrets/key\"]","handlingStrategy":"validation","validationCode":"// Validate args before passing through applyPlatformOverrides / getResolvedMcpConfig.\nimport { validateArgs } from '@main/ai/mcp/McpPackageService'\n\nfunction isSafeArg(arg: unknown): boolean {\n  if (typeof arg !== 'string') return false\n  if (arg.includes('\\0')) return false\n  if ((arg.includes('/') || arg.includes('\\\\')) && /(?:^|[/\\\\])\\.\\.(?:[/\\\\]|$)/.test(arg)) return false\n  return true\n}\n\nfunction preflightArgs(args: unknown[]): boolean {\n  return Array.isArray(args) && args.every(isSafeArg)\n}","typeGuard":"function isPathSafeArg(arg: unknown): arg is string {\n  return typeof arg === 'string'\n    && !arg.includes('\\0')\n    && (!(arg.includes('/') || arg.includes('\\\\')) || !/(?:^|[/\\\\])\\.\\.(?:[/\\\\]|$)/.test(arg))\n}","tryCatchPattern":null,"preventionTips":["When authoring a manifest, prefer ${__dirname} for in-package paths over user-supplied relative paths.","Sanitize ${user_config.*} values in the package config UI before they reach substitution (strip ../ and control characters).","Run validateArgs on the resolved args array in a dry-run step during package build to catch traversal before publishing."],"tags":["security","path-traversal","mcp","manifest","validation"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}