{"record":{"id":"faa059e54ba7c489","repo":"ruvnet/ruflo","slug":"invalid-command-contains-shell-metacharacters","errorCode":null,"errorMessage":"Invalid command: contains shell metacharacters","messagePattern":"Invalid command: contains shell metacharacters","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/deployment/src/publisher.ts","lineNumber":228,"sourceCode":"      return returnOutput ? output : '';\n    } catch (error) {\n      throw error;\n    }\n  }\n\n  /**\n   * Execute command (for build scripts only - validated)\n   */\n  private execCommand(cmd: string, returnOutput = false): string {\n    // Only allow npm/npx build commands for safety\n    const allowedPrefixes = ['npm run ', 'npm ', 'npx ', 'pnpm ', 'yarn '];\n    const isAllowed = allowedPrefixes.some(prefix => cmd.startsWith(prefix));\n    if (!isAllowed) {\n      throw new Error(`Disallowed command: only npm/npx/pnpm/yarn commands are permitted`);\n    }\n    // Validate no dangerous shell metacharacters\n    if (/[;&|`$()<>]/.test(cmd)) {\n      throw new Error(`Invalid command: contains shell metacharacters`);\n    }\n    try {\n      const output = execSync(cmd, {\n        cwd: this.cwd,\n        encoding: 'utf-8',\n        stdio: returnOutput ? 'pipe' : 'inherit'\n      });\n      return returnOutput ? output : '';\n    } catch (error) {\n      throw error;\n    }\n  }\n}\n\n/**\n * Convenience function to publish to npm\n */\nexport async function publishToNpm(","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/deployment/src/publisher.ts#L210-L246","documentation":"The metacharacter guard applied to the buildCommand string handed to publishToNpm(): any of ; & | ` $ ( ) < > rejects the command before it reaches execSync (which does use a shell). Composite commands and command substitution are therefore blocked, not quoted.","triggerScenarios":"publishToNpm({ buildCommand: 'npm run clean && npm run build' }); 'npm run lint && npm test'; any build string with $( ), pipes, or output redirection.","commonSituations":"Release scripts that chain clean+build+test in one line; commands copied from CI YAML or package.json scripts that rely on && or subshells.","solutions":["Create a package.json script that does the chaining (\"build\": \"npm run clean && npm run bundle\") and pass 'npm run build'","Or pass skipBuild: true and run the composite build yourself before publishToNpm"],"exampleFix":"// before\nawait publisher.publishToNpm({ buildCommand: 'npm run clean && npm run build' }); // throws\n\n// after\n// package.json: \"build\": \"npm run clean && npm run bundle\"\nawait publisher.publishToNpm({ buildCommand: 'npm run build' });","handlingStrategy":"validation","validationCode":"function isSingleShellFreeCommand(cmd: string): boolean {\n  return !/[;&|`$()<>]/.test(cmd);\n}\nif (!isSingleShellFreeCommand(buildCommand)) {\n  throw new Error('move chaining into a package.json script; pass \"npm run <script>\"');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Put &&-chains inside package.json scripts, not in buildCommand","Treat any of ; & | ` $ ( ) < > in a command string as a hard error in your own pipelines too"],"tags":["build","security","command-injection","validation"],"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"}