{"record":{"id":"59b908c44d4f5a39","repo":"ruvnet/ruflo","slug":"invalid-filename-filename","errorCode":null,"errorMessage":"Invalid filename: ${fileName}","messagePattern":"Invalid filename: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/transfer/storage/gcs.ts","lineNumber":123,"sourceCode":"    config?: GCSConfig;\n    metadata?: Record<string, string>;\n  } = {}\n): Promise<GCSUploadResult> {\n  const config = options.config || getGCSConfig();\n  if (!config) {\n    throw new Error(\n      'GCS not configured. Set GCS_BUCKET environment variable.\\n' +\n      'Or authenticate: gcloud auth login && gcloud config set project YOUR_PROJECT'\n    );\n  }\n\n  const contentId = generateContentId(content);\n  const checksum = crypto.createHash('sha256').update(content).digest('hex');\n  const fileName = options.name || `${contentId}.cfp.json`;\n\n  // Validate filename to prevent path traversal\n  if (!/^[a-zA-Z0-9._\\-]+$/.test(fileName) || fileName.includes('..')) {\n    throw new Error(`Invalid filename: ${fileName}`);\n  }\n\n  const objectPath = config.prefix ? `${config.prefix}/${fileName}` : fileName;\n\n  // S-1: Validate bucket name and object path to prevent command injection\n  if (!isValidBucketName(config.bucket)) {\n    throw new Error(`Invalid GCS bucket name: ${config.bucket}`);\n  }\n  if (!isValidObjectPath(objectPath)) {\n    throw new Error(`Invalid GCS object path: ${objectPath}`);\n  }\n\n  console.log(`[GCS] Uploading to gs://${config.bucket}/${objectPath}...`);\n\n  // Write content to temp file\n  const tempDir = process.env.TMPDIR || '/tmp';\n  const tempFile = path.join(tempDir, `claude-flow-upload-${Date.now()}.json`);\n  fs.writeFileSync(tempFile, content);","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/transfer/storage/gcs.ts#L105-L141","documentation":"uploadToGCS() derives fileName from options.name or a generated `${contentId}.cfp.json` and validates it against /^[a-zA-Z0-9._-]+$/ while also rejecting '..'. The regex forbids slashes, spaces, unicode, and any path separators — this is the path-traversal guard applied before the object path is built and handed to gcloud. Custom names with directories (e.g. 'nested/dir/file.json') or '..' sequences fail here.","triggerScenarios":"Calling uploadToGCS(content, { name: 'patterns/2024/foo.json' }) — the slash fails the regex; names containing spaces, ':' or unicode; a name like 'config..json' is caught only by the includes('..') check when the regex alone would pass.","commonSituations":"Using human-readable names with spaces or dates like 'my patterns (final).json'; attempting to upload into a subfolder by embedding the path in name (the API wants prefix in config instead); names copied from other object stores that allow richer characters.","solutions":["Use a flat, simple name: letters, digits, dot, underscore, hyphen only — e.g. 'pattern-2024-08.cfp.json'","To place the object under a folder, set config.prefix (GCSConfig) instead of putting the path in name","Sanitize user-supplied names before calling upload (replace disallowed chars, strip '..')","Omit options.name entirely to get the generated contentId-based name, which always passes"],"exampleFix":"// before\nawait uploadToGCS(content, { name: 'patterns/2024/pattern.json' }); // '/' fails regex -> Invalid filename\n\n// after\nawait uploadToGCS(content, {\n  name: 'pattern-2024-08-18.json',\n  config: { bucket: 'my-bucket', prefix: 'patterns/2024' }, // folder goes in prefix\n});","handlingStrategy":"validation","validationCode":"function sanitizeGcsFileName(name: string): string {\n  const cleaned = name.replace(/[^a-zA-Z0-9._-]/g, '-').replace(/\\.\\./g, '.');\n  if (!/^[a-zA-Z0-9._-]+$/.test(cleaned) || cleaned.includes('..')) {\n    throw new Error(`Filename cannot be sanitized: ${name}`);\n  }\n  return cleaned;\n}\nawait uploadToGCS(content, { name: sanitizeGcsFileName(userProvidedName) });","typeGuard":"function isValidGcsFileName(name: string): boolean {\n  return /^[a-zA-Z0-9._-]+$/.test(name) && !name.includes('..');\n}","tryCatchPattern":null,"preventionTips":["Keep object names flat (letters, digits, dot, underscore, hyphen) and put folder structure in config.prefix","Sanitize any user-supplied filename before it reaches uploadToGCS","Omit options.name when in doubt — the generated contentId-based name always passes validation"],"tags":["gcs","filename","validation","path-traversal","security"],"backgroundTag":"path-traversal-blocked","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"}