{"record":{"id":"f1677f09750c43d6","repo":"mastra-ai/mastra","slug":"refusing-to-pass-unsafe-argument-to-shell-command","errorCode":null,"errorMessage":"Refusing to pass unsafe argument to shell command: ${JSON.stringify(arg)}","messagePattern":"Refusing to pass unsafe argument to shell command: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/deployer/src/deploy/log.ts","lineNumber":36,"sourceCode":"    },\n  });\n};\n\n/**\n * Args are joined into a shell command (`shell: true` is required for package\n * manager shims on Windows), so only allow characters that appear in package\n * specifiers and CLI flags — never shell metacharacters (CodeQL\n * js/shell-command-constructed-from-input).\n */\nconst SAFE_SHELL_ARG = /^[\\w@%+=:,./^~-]*$/;\n\nexport function createChildProcessLogger({ logger, root }: { logger: IMastraLogger; root: string }) {\n  const pinoStream = createPinoStream(logger);\n  return async ({ cmd, args, env }: { cmd: string; args: string[]; env: Record<string, string> }) => {\n    try {\n      for (const arg of args) {\n        if (!SAFE_SHELL_ARG.test(arg)) {\n          throw new Error(`Refusing to pass unsafe argument to shell command: ${JSON.stringify(arg)}`);\n        }\n      }\n      const subprocess = spawn(cmd, args, {\n        cwd: root,\n        shell: true,\n        env,\n        // No stdin for the child process — it doesn't need interactive input\n        stdio: ['ignore', 'pipe', 'pipe'],\n      });\n\n      let stdout = '';\n      let stderr = '';\n      subprocess.stdout?.on('data', chunk => {\n        stdout += chunk.toString();\n      });\n      subprocess.stderr?.on('data', chunk => {\n        stderr += chunk.toString();\n      });","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/deployer/src/deploy/log.ts#L18-L54","documentation":"createChildProcessLogger spawns child processes with shell:true, which makes each argument a shell-injection risk. Before spawning it validates every arg against SAFE_SHELL_ARG and throws if an argument contains characters outside the safe set. The error names the rejected argument via JSON.stringify so it is visible even if it contains quotes or control characters.","triggerScenarios":"Running a command (install/build/start) where any element of args contains characters disallowed by SAFE_SHELL_ARG — typically spaces, quotes, semicolons, backticks, $, or newline characters — passed through createChildProcessLogger.","commonSituations":"File paths with spaces on Windows/macOS; project names or env-derived values with special characters; a package name or directory interpolated into an arg from user input or CI variables.","solutions":["Remove/escape unsafe characters from the offending argument (rename directories to avoid spaces or shell metacharacters).","Ensure arguments are passed as separate array items, not as one pre-joined command string.","Sanitize or validate dynamic values (paths, names) before invoking deployer commands that spawn child processes."],"exampleFix":"// before\nspawn('pnpm', ['install --prefer-offline'], { shell: true })\n// after\nspawn('pnpm', ['install', '--prefer-offline'], { shell: true })","handlingStrategy":"validation","validationCode":"const SAFE_SHELL_ARG = /^[A-Za-z0-9_@%+=:,./-]+$/;\nargs.forEach(arg => { if (!SAFE_SHELL_ARG.test(arg)) throw new Error(`Unsafe shell arg: ${JSON.stringify(arg)}`); });","typeGuard":null,"tryCatchPattern":"try {\n  await runCommand({ cmd, args, env });\n} catch (e) {\n  if (String(e.message).startsWith('Refusing to pass unsafe argument')) {\n    console.error('Sanitize args (no spaces/quotes/metacharacters) before retrying.');\n  }\n  throw e;\n}","preventionTips":["Pass arguments as separate array elements, never a pre-joined command string","Avoid spaces and shell metacharacters in project paths and names","Sanitize env/CI-derived values before passing them as command args"],"tags":["security","shell","child-process","input-validation"],"backgroundTag":"unsafe-shell-argument","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}