{"record":{"id":"3072b6badd2c1a88","repo":"affaan-m/ECC","slug":"script-name-must-be-a-non-empty-string","errorCode":null,"errorMessage":"Script name must be a non-empty string","messagePattern":"Script name must be a non-empty string","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/package-manager.js","lineNumber":298,"sourceCode":"  } catch (err) {\n    throw new Error(`Failed to save package manager config to ${configPath}: ${err.message}`);\n  }\n  return config;\n}\n\n// Allowed characters in script/binary names: alphanumeric, dash, underscore, dot, slash, @\n// This prevents shell metacharacter injection while allowing scoped packages (e.g., @scope/pkg)\nconst SAFE_NAME_REGEX = /^[@a-zA-Z0-9_./-]+$/;\n\n/**\n * Get the command to run a script\n * @param {string} script - Script name (e.g., \"dev\", \"build\", \"test\")\n * @param {object} options - { projectDir }\n * @throws {Error} If script name contains unsafe characters\n */\nfunction getRunCommand(script, options = {}) {\n  if (!script || typeof script !== 'string') {\n    throw new Error('Script name must be a non-empty string');\n  }\n  if (!SAFE_NAME_REGEX.test(script)) {\n    throw new Error(`Script name contains unsafe characters: ${script}`);\n  }\n\n  const pm = getPackageManager(options);\n\n  switch (script) {\n    case 'install':\n      return pm.config.installCmd;\n    case 'test':\n      return pm.config.testCmd;\n    case 'build':\n      return pm.config.buildCmd;\n    case 'dev':\n      return pm.config.devCmd;\n    default:\n      return `${pm.config.runCmd} ${script}`;","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/package-manager.js#L280-L316","documentation":"Thrown by getRunCommand() in scripts/lib/package-manager.js when the `script` argument is falsy or not a string. getRunCommand maps a script name (dev/build/test/install or arbitrary) to the detected package manager's run command; this is the first of two guards that prevent shell-metacharacter injection by validating input shape before the command string is built.","triggerScenarios":"Calling getRunCommand() with no arguments, getRunCommand(undefined), getRunCommand(''), getRunCommand(null), or getRunCommand(123). Any caller that reads the script name from an untrusted or optional source (CLI flag, config file, object property) and forwards it without a type check hits this.","commonSituations":"Refactoring a caller so the script variable is conditionally assigned and becomes undefined; reading a script name from a JSON config that omits the key; a wrapper that defaults args to undefined instead of a real script name; test fixtures that pass nothing.","solutions":["Pass a literal known script name: getRunCommand('test', { projectDir }).","If the script name is dynamic, default-coalesce first: getRunCommand(scriptName || 'build', options).","Validate the upstream source supplies a non-empty string before calling (e.g. guard on typeof + length).","If the input is genuinely optional, branch on its presence and skip the call rather than passing undefined."],"exampleFix":"// before\nconst cmd = getRunCommand(userScript, opts); // userScript is undefined\n\n// after\nif (!userScript || typeof userScript !== 'string') {\n  throw new Error('A script name is required');\n}\nconst cmd = getRunCommand(userScript, opts);","handlingStrategy":"type-guard","validationCode":"if (!script || typeof script !== 'string') {\n  throw new Error('script name is required');\n}\nconst cmd = getRunCommand(script, opts);","typeGuard":"function isScriptName(value) {\n  return typeof value === 'string' && value.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Always pass a literal or explicitly validated script name.","Default-coalesce optional inputs: script || 'build'.","Keep flags out of the script name; append them after the call."],"tags":["input-validation","package-manager","shell-injection-guard"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}