{"record":{"id":"7431f17f769da684","repo":"affaan-m/ECC","slug":"binary-name-contains-unsafe-characters-binary","errorCode":null,"errorMessage":"Binary name contains unsafe characters: ${binary}","messagePattern":"Binary name contains unsafe characters: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/package-manager.js","lineNumber":335,"sourceCode":"  }\n}\n\n// Allowed characters in arguments: alphanumeric, whitespace, dashes, dots, slashes,\n// equals, colons, commas, quotes, @. Rejects shell metacharacters like ; | & ` $ ( ) { } < > !\nconst SAFE_ARGS_REGEX = /^[@a-zA-Z0-9\\s_./:=,'\"*+-]+$/;\n\n/**\n * Get the command to execute a package binary\n * @param {string} binary - Binary name (e.g., \"prettier\", \"eslint\")\n * @param {string} args - Arguments to pass\n * @throws {Error} If binary name or args contain unsafe characters\n */\nfunction getExecCommand(binary, args = '', options = {}) {\n  if (!binary || typeof binary !== 'string') {\n    throw new Error('Binary name must be a non-empty string');\n  }\n  if (!SAFE_NAME_REGEX.test(binary)) {\n    throw new Error(`Binary name contains unsafe characters: ${binary}`);\n  }\n  if (args && typeof args === 'string' && !SAFE_ARGS_REGEX.test(args)) {\n    throw new Error(`Arguments contain unsafe characters: ${args}`);\n  }\n\n  const pm = getPackageManager(options);\n  return `${pm.config.execCmd} ${binary}${args ? ' ' + args : ''}`;\n}\n\n/**\n * Interactive prompt for package manager selection\n * Returns a message for Claude to show to user\n *\n * NOTE: Does NOT spawn child processes to check availability.\n * Lists all supported PMs and shows how to configure preference.\n */\nfunction getSelectionPrompt() {\n  let message = '[PackageManager] No package manager preference detected.\\n';","sourceCodeStart":317,"sourceCodeEnd":353,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/package-manager.js#L317-L353","documentation":"Thrown by getExecCommand() when the binary name fails SAFE_NAME_REGEX = /^[@a-zA-Z0-9_./-]+$/. Same injection guard as the script path: only alphanumeric, dash, underscore, dot, slash, and @ are allowed, supporting scoped packages like @scope/cli.","triggerScenarios":"Passing a binary name with shell metacharacters or whitespace, e.g. getExecCommand('eslint; cat /etc/passwd'), getExecCommand('my tool'), getExecCommand('$(id)'), getExecCommand('prettier &').","commonSituations":"User-supplied tool name forwarded unsanitized; building a binary name by string concatenation with user input; a plugin system where a plugin declares a binary with disallowed characters; copy-paste introducing a stray space.","solutions":["Validate the binary name against the same allowlist before calling.","Restrict untrusted callers to a fixed catalog of known binaries.","Strip disallowed characters and confirm the cleaned name is non-empty.","Never derive a binary name by interpolating raw user input."],"exampleFix":"// before\ngetExecCommand(userTool, args, opts); // userTool = 'eslint; rm -rf node_modules'\n\n// after\nconst SAFE = /^[@a-zA-Z0-9_.\\/-]+$/;\nif (!SAFE.test(userTool)) throw new Error(`Invalid tool name: ${userTool}`);\ngetExecCommand(userTool, args, opts);","handlingStrategy":"validation","validationCode":"const SAFE_NAME = /^[@a-zA-Z0-9_.\\/-]+$/;\nif (!SAFE_NAME.test(binary)) {\n  throw new Error(`Rejected binary name: ${binary}`);\n}\ngetExecCommand(binary, args, opts);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never derive a binary name from raw user input.","Strip disallowed characters and re-validate the cleaned name.","Prefer a plugin manifest declaring allowed binary names."],"tags":["security","shell-injection","input-validation","package-manager"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}