affaan-m/ECC · error · Error

Script name contains unsafe characters: ${script}

Error message

Script name contains unsafe characters: ${script}

What it means

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.

Source

Thrown at scripts/lib/package-manager.js:301

  return config;
}

// Allowed characters in script/binary names: alphanumeric, dash, underscore, dot, slash, @
// This prevents shell metacharacter injection while allowing scoped packages (e.g., @scope/pkg)
const SAFE_NAME_REGEX = /^[@a-zA-Z0-9_./-]+$/;

/**
 * Get the command to run a script
 * @param {string} script - Script name (e.g., "dev", "build", "test")
 * @param {object} options - { projectDir }
 * @throws {Error} If script name contains unsafe characters
 */
function getRunCommand(script, options = {}) {
  if (!script || typeof script !== 'string') {
    throw new Error('Script name must be a non-empty string');
  }
  if (!SAFE_NAME_REGEX.test(script)) {
    throw new Error(`Script name contains unsafe characters: ${script}`);
  }

  const pm = getPackageManager(options);

  switch (script) {
    case 'install':
      return pm.config.installCmd;
    case 'test':
      return pm.config.testCmd;
    case 'build':
      return pm.config.buildCmd;
    case 'dev':
      return pm.config.devCmd;
    default:
      return `${pm.config.runCmd} ${script}`;
  }
}

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Pass flags via getExecCommand(binary, args) instead of stuffing them into the script name.
  2. Strip or reject non-whitelist characters before calling: keep only [@a-zA-Z0-9_./-].
  3. Use a fixed allowlist of script names you accept from untrusted input.
  4. If a script name legitimately contains other characters, alias it in package.json and call the alias.

Example fix

// before
getRunCommand('test --watch', opts); // space rejected

// after
getRunCommand('test', opts) + ' --watch'; // flags appended by caller
// or define "test:watch" in package.json and call that
Defensive patterns

Strategy: validation

Validate before calling

const SAFE_NAME = /^[@a-zA-Z0-9_.\/-]+$/;
if (!SAFE_NAME.test(script)) {
  throw new Error(`Rejected script name: ${script}`);
}
getRunCommand(script, opts);

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/5325f167b12be428. Report an issue: GitHub.