ruvnet/ruflo · error · Error
Invalid filename: ${fileName}
Error message
Invalid filename: ${fileName} What it means
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.
Source
Thrown at v3/@claude-flow/cli/src/transfer/storage/gcs.ts:123
config?: GCSConfig;
metadata?: Record<string, string>;
} = {}
): Promise<GCSUploadResult> {
const config = options.config || getGCSConfig();
if (!config) {
throw new Error(
'GCS not configured. Set GCS_BUCKET environment variable.\n' +
'Or authenticate: gcloud auth login && gcloud config set project YOUR_PROJECT'
);
}
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);View on GitHub (pinned to fa13ee4ad6)
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
Example fix
// before
await uploadToGCS(content, { name: 'patterns/2024/pattern.json' }); // '/' fails regex -> Invalid filename
// after
await uploadToGCS(content, {
name: 'pattern-2024-08-18.json',
config: { bucket: 'my-bucket', prefix: 'patterns/2024' }, // folder goes in prefix
}); Defensive patterns
Strategy: validation
Validate before calling
function sanitizeGcsFileName(name: string): string {
const cleaned = name.replace(/[^a-zA-Z0-9._-]/g, '-').replace(/\.\./g, '.');
if (!/^[a-zA-Z0-9._-]+$/.test(cleaned) || cleaned.includes('..')) {
throw new Error(`Filename cannot be sanitized: ${name}`);
}
return cleaned;
}
await uploadToGCS(content, { name: sanitizeGcsFileName(userProvidedName) }); Type guard
function isValidGcsFileName(name: string): boolean {
return /^[a-zA-Z0-9._-]+$/.test(name) && !name.includes('..');
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
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
- Invalid GCS object path: ${objectPath}
- basePath contains disallowed characters
- memory path contains disallowed characters
- unknown strategy "${name}". Available: ${roster.map((r) => r
- localSingleEntryPageRank: sourceIndex ${src} out of range [0
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/59b908c44d4f5a39.
Report an issue: GitHub.