{"record":{"id":"bd4bbfbaf62312c3","repo":"ruvnet/ruflo","slug":"invalid-command-contains-shell-metacharacters-bd4bbf","errorCode":null,"errorMessage":"Invalid command: contains shell metacharacters","messagePattern":"Invalid command: contains shell metacharacters","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/deployment/src/validator.ts","lineNumber":275,"sourceCode":"  }\n\n  /**\n   * Allowed commands for security validation\n   */\n  private static readonly ALLOWED_COMMAND_PREFIXES = [\n    'npm run ',\n    'npm ',\n    'npx ',\n    'git ',\n  ];\n\n  /**\n   * Execute command safely with validation\n   */\n  private execCommand(cmd: string, returnOutput = false): string {\n    // Validate: check for shell metacharacters\n    if (/[;&|`$()<>]/.test(cmd)) {\n      throw new Error(`Invalid command: contains shell metacharacters`);\n    }\n\n    // Validate: must start with allowed prefix\n    const isAllowed = Validator.ALLOWED_COMMAND_PREFIXES.some(\n      prefix => cmd.startsWith(prefix)\n    );\n    if (!isAllowed) {\n      throw new Error(`Command not allowed: ${cmd.split(' ')[0]}`);\n    }\n\n    try {\n      const output = execSync(cmd, {\n        cwd: this.cwd,\n        encoding: 'utf-8',\n        stdio: returnOutput ? 'pipe' : 'inherit',\n        timeout: 60000, // 60 second timeout for builds\n        maxBuffer: 50 * 1024 * 1024, // 50MB buffer for build output\n      });","sourceCodeStart":257,"sourceCodeEnd":293,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/deployment/src/validator.ts#L257-L293","documentation":"Validator.execCommand() — used to run the testCommand and buildCommand options of validate() — rejects any command containing ; & | ` $ ( ) < > before executing it with execSync. Chained commands, pipes, and substitutions are blocked rather than quoted.","triggerScenarios":"validate({ testCommand: 'npm test && npm run lint' }); buildCommand: 'npm run build 2>&1'; any option string containing &&, |, $( ), or redirection.","commonSituations":"Copying a combined lint+test line from CI config into ValidationOptions; build commands with subshells or output redirects.","solutions":["Point testCommand/buildCommand at single package.json scripts: 'npm test', 'npm run build'","Move any chaining inside the script definition itself, not into the command string passed to validate()"],"exampleFix":"// before\nvalidate({ testCommand: 'npm run lint && npm test' }); // throws\n\n// after\n// package.json: \"verify\": \"npm run lint && npm test\"\nvalidate({ testCommand: 'npm run verify' });","handlingStrategy":"validation","validationCode":"function isValidatorSafeCommand(cmd: string): boolean {\n  return !/[;&|`$()<>]/.test(cmd) && ['npm run ', 'npm ', 'npx ', 'git '].some(p => cmd.startsWith(p));\n}\nif (!isValidatorSafeCommand(testCommand)) throw new Error('use a single npm script for testCommand');\nawait validate({ testCommand, buildCommand });","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Reference one package.json script per validate() option — no chaining","Keep pipes/redirects inside the script file, not in the command string"],"tags":["validation","security","command-injection","testing"],"backgroundTag":"command-injection-guard","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}