ruvnet/ruflo · error · Error

Disallowed command: only npm/npx/pnpm/yarn commands are perm

Error message

Disallowed command: only npm/npx/pnpm/yarn commands are permitted

What it means

Publisher.execCommand() — used to run the buildCommand option of publishToNpm() — only accepts strings starting with 'npm run ', 'npm ', 'npx ', 'pnpm ', or 'yarn '. Anything else is treated as a potential command injection and rejected before the build executes.

Source

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

        encoding: 'utf-8',
        shell: false,
        stdio: returnOutput ? ['pipe', 'pipe', 'pipe'] : 'inherit'
      });
      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;
    }
  }
}

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Move the build into package.json scripts ("scripts": { "build": "node build.js" }) and pass buildCommand: 'npm run build'
  2. Or use an allowlisted prefix form: 'npx tsc -p .', 'pnpm build', 'yarn build'
  3. Or pass skipBuild: true and run your custom build yourself before calling publishToNpm

Example fix

// before
await publisher.publishToNpm({ buildCommand: 'node ./scripts/bundle.js' }); // throws

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

Strategy: validation

Validate before calling

const ALLOWED = ['npm run ', 'npm ', 'npx ', 'pnpm ', 'yarn '];
function isAllowedBuildCommand(cmd: string): boolean {
  return ALLOWED.some(p => cmd.startsWith(p)) && !/[;&|`$()<>]/.test(cmd);
}
if (!isAllowedBuildCommand(buildCommand)) buildCommand = 'npm run build';
await publisher.publishToNpm({ buildCommand });

Prevention

When it happens

Trigger: publishToNpm({ buildCommand: 'node build.js' }), 'make build', './scripts/build.sh', or 'tsc -p .' — none matches an allowed prefix.

Common situations: Packages whose build is a plain node script or make target; migrating a hand-rolled release script that invoked the build directly.

Related errors


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