ruvnet/ruflo · error · Error

Invalid GCS object path: ${objectPath}

Error message

Invalid GCS object path: ${objectPath}

What it means

After the filename check, uploadToGCS composes objectPath = config.prefix ? `${prefix}/${fileName}` : fileName and validates the whole path through isValidObjectPath() — the second half of the S-1 command-injection guard, since the path becomes a gcloud argument. A prefix containing spaces, shell metacharacters, absolute-path markers, or traversal sequences makes the combined path fail even when the filename itself is clean.

Source

Thrown at v3/@claude-flow/cli/src/transfer/storage/gcs.ts:133

  }

  const contentId = generateContentId(content);
  const checksum = crypto.createHash('sha256').update(content).digest('hex');
  const fileName = options.name || `${contentId}.cfp.json`;

  // Validate filename to prevent path traversal
  if (!/^[a-zA-Z0-9._\-]+$/.test(fileName) || fileName.includes('..')) {
    throw new Error(`Invalid filename: ${fileName}`);
  }

  const objectPath = config.prefix ? `${config.prefix}/${fileName}` : fileName;

  // S-1: Validate bucket name and object path to prevent command injection
  if (!isValidBucketName(config.bucket)) {
    throw new Error(`Invalid GCS bucket name: ${config.bucket}`);
  }
  if (!isValidObjectPath(objectPath)) {
    throw new Error(`Invalid GCS object path: ${objectPath}`);
  }

  console.log(`[GCS] Uploading to gs://${config.bucket}/${objectPath}...`);

  // Write content to temp file
  const tempDir = process.env.TMPDIR || '/tmp';
  const tempFile = path.join(tempDir, `claude-flow-upload-${Date.now()}.json`);
  fs.writeFileSync(tempFile, content);

  try {
    // Build gcloud args (array form prevents shell injection)
    const uploadArgs = ['storage', 'cp', tempFile, `gs://${config.bucket}/${objectPath}`];
    if (config.projectId) uploadArgs.push(`--project=${config.projectId}`);
    uploadArgs.push(`--content-type=${options.contentType || 'application/json'}`);

    execFileSync('gcloud', uploadArgs, { encoding: 'utf-8', stdio: 'pipe' });

    // Set metadata if provided

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Keep prefix segments simple: lowercase/digits/hyphens/underscores, no leading slash, no spaces or shell metacharacters — e.g. 'patterns/2024'
  2. Sanitize user-supplied prefixes before passing GCSConfig (strip leading '/', collapse '//', reject metacharacters)
  3. Drop the prefix entirely (flat layout) if you do not need folder structure
  4. Treat any metacharacter in this value as hostile input — the guard exists because the path reaches a shell-launched gcloud command

Example fix

// before
await uploadToGCS(content, { config: { bucket: 'my-bucket', prefix: 'my patterns/2024' } });
// space in prefix -> Invalid GCS object path: my patterns/2024/pattern.json

// after
await uploadToGCS(content, { config: { bucket: 'my-bucket', prefix: 'patterns/2024' } });
Defensive patterns

Strategy: validation

Validate before calling

function sanitizeGcsPrefix(prefix: string): string {
  const cleaned = prefix.replace(/\/+/g, '/').replace(/^\/+/, '').replace(/\/+$/, '');
  if (!/^[a-zA-Z0-9._\/-]*$/.test(cleaned) || cleaned.includes('..')) {
    throw new Error(`Unsafe GCS prefix: ${JSON.stringify(prefix)}`);
  }
  return cleaned;
}

await uploadToGCS(content, {
  config: { bucket, prefix: sanitizeGcsPrefix(userPrefix) },
});

Type guard

function isValidGcsObjectPath(objectPath: string): boolean {
  return /^[a-zA-Z0-9._\/-]+$/.test(objectPath)
    && !objectPath.includes('..')
    && !objectPath.startsWith('/')
    && !/[;&|$`"'\s]/.test(objectPath);
}

Prevention

When it happens

Trigger: GCSConfig.prefix values like 'my patterns/', '/abs/path', 'a;rm -rf', '../escape', or prefixes with quotes/backticks; a clean fileName combined with a prefix containing spaces or unicode; empty-but-whitespace prefixes.

Common situations: User-defined prefixes from config files or CLI flags; prefixes mirroring local directory trees with spaces; injection attempts on shared systems; trailing slashes doubling up ('prefix//' plus filename).

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


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