{"record":{"id":"5325f167b12be428","repo":"affaan-m/ECC","slug":"script-name-contains-unsafe-characters-script","errorCode":null,"errorMessage":"Script name contains unsafe characters: ${script}","messagePattern":"Script name contains unsafe characters: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/package-manager.js","lineNumber":301,"sourceCode":"  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}`;\n  }\n}\n","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/package-manager.js#L283-L319","documentation":"Thrown by getRunCommand() when the script name is a string but fails SAFE_NAME_REGEX = /^[@a-zA-Z0-9_./-]+$/. This is the shell-injection guard: it rejects metacharacters like ; | & ` $ ( ) while still permitting scoped package names (@scope/pkg) and slashes.","triggerScenarios":"Passing a script name containing spaces, shell metacharacters, parentheses, or quotes, e.g. getRunCommand('test; rm -rf /'), getRunCommand('build && deploy'), getRunCommand('my script'), getRunCommand('$(whoami)'). Any user-controlled or env-derived script name that was not sanitized.","commonSituations":"Concatenating user input into a script name; a config file or environment variable carrying a tainted value; a script name with a space from a copy-paste; an attempt to pass CLI flags through the script argument instead of a dedicated args path.","solutions":["Pass flags via getExecCommand(binary, args) instead of stuffing them into the script name.","Strip or reject non-whitelist characters before calling: keep only [@a-zA-Z0-9_./-].","Use a fixed allowlist of script names you accept from untrusted input.","If a script name legitimately contains other characters, alias it in package.json and call the alias."],"exampleFix":"// before\ngetRunCommand('test --watch', opts); // space rejected\n\n// after\ngetRunCommand('test', opts) + ' --watch'; // flags appended by caller\n// or define \"test:watch\" in package.json and call that","handlingStrategy":"validation","validationCode":"const SAFE_NAME = /^[@a-zA-Z0-9_.\\/-]+$/;\nif (!SAFE_NAME.test(script)) {\n  throw new Error(`Rejected script name: ${script}`);\n}\ngetRunCommand(script, opts);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat the script name as untrusted input; never interpolate user text into it.","Restrict dynamic script names to an allowlist from package.json.","Route flags through args, not the script name."],"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"}