ruvnet/ruflo · error · Error

Invalid command: contains shell metacharacters

Error message

Invalid command: contains shell metacharacters

What it means

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.

Source

Thrown at v3/@claude-flow/deployment/src/publisher.ts:228

      return returnOutput ? output : '';
    } catch (error) {
      throw error;
    }
  }

  /**
   * Execute command (for build scripts only - validated)
   */
  private execCommand(cmd: string, returnOutput = false): string {
    // Only allow npm/npx build commands for safety
    const allowedPrefixes = ['npm run ', 'npm ', 'npx ', 'pnpm ', 'yarn '];
    const isAllowed = allowedPrefixes.some(prefix => cmd.startsWith(prefix));
    if (!isAllowed) {
      throw new Error(`Disallowed command: only npm/npx/pnpm/yarn commands are permitted`);
    }
    // Validate no dangerous shell metacharacters
    if (/[;&|`$()<>]/.test(cmd)) {
      throw new Error(`Invalid command: contains shell metacharacters`);
    }
    try {
      const output = execSync(cmd, {
        cwd: this.cwd,
        encoding: 'utf-8',
        stdio: returnOutput ? 'pipe' : 'inherit'
      });
      return returnOutput ? output : '';
    } catch (error) {
      throw error;
    }
  }
}

/**
 * Convenience function to publish to npm
 */
export async function publishToNpm(

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Create a package.json script that does the chaining ("build": "npm run clean && npm run bundle") and pass 'npm run build'
  2. Or pass skipBuild: true and run the composite build yourself before publishToNpm

Example fix

// before
await publisher.publishToNpm({ buildCommand: 'npm run clean && npm run build' }); // throws

// after
// package.json: "build": "npm run clean && npm run bundle"
await publisher.publishToNpm({ buildCommand: 'npm run build' });
Defensive patterns

Strategy: validation

Validate before calling

function isSingleShellFreeCommand(cmd: string): boolean {
  return !/[;&|`$()<>]/.test(cmd);
}
if (!isSingleShellFreeCommand(buildCommand)) {
  throw new Error('move chaining into a package.json script; pass "npm run <script>"');
}

Prevention

When it happens

Trigger: publishToNpm({ buildCommand: 'npm run clean && npm run build' }); 'npm run lint && npm test'; any build string with $( ), pipes, or output redirection.

Common situations: Release scripts that chain clean+build+test in one line; commands copied from CI YAML or package.json scripts that rely on && or subshells.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/faa059e54ba7c489. Report an issue: GitHub.