{"record":{"id":"bb5de0f313277e30","repo":"FlowiseAI/Flowise","slug":"invalid-filename-after-sanitization","errorCode":null,"errorMessage":"Invalid filename after sanitization","messagePattern":"Invalid filename after sanitization","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/components/src/storage/BaseStorageProvider.ts","lineNumber":54,"sourceCode":"    abstract removeSpecificFileFromUpload(filePath: string): Promise<void>\n    abstract removeSpecificFileFromStorage(...paths: string[]): Promise<StorageSizeResult>\n    abstract removeFolderFromStorage(...paths: string[]): Promise<StorageSizeResult>\n    abstract getStorageSize(orgId: string): Promise<number>\n    abstract getMulterStorage(): any\n    abstract getLoggerTransports(logType: 'server' | 'error' | 'requests' | 'audit', config?: any): any[]\n\n    /**\n     * Shared utility for sanitizing filenames to prevent path traversal and other issues\n     */\n    protected sanitizeFilename(filename: string): string {\n        if (!filename || isUnsafeFilePath(filename)) {\n            throw new Error('Invalid or unsafe fileName detected')\n        }\n        const sanitizedFilename = sanitize(filename)\n        // Remove leading dots to prevent hidden files or relative path jumps\n        const cleaned = sanitizedFilename.replace(/^\\.+/, '')\n        if (!cleaned || cleaned.includes('/') || cleaned.includes('\\\\')) {\n            throw new Error('Invalid filename after sanitization')\n        }\n        return cleaned\n    }\n\n    /**\n     * Shared utility for getting the base storage path\n     */\n    protected getStoragePath(): string {\n        const storagePath = process.env.BLOB_STORAGE_PATH\n            ? path.join(process.env.BLOB_STORAGE_PATH)\n            : path.join(getUserHome(), '.flowise', 'storage')\n\n        if (!fs.existsSync(storagePath)) {\n            fs.mkdirSync(storagePath, { recursive: true })\n        }\n        return storagePath\n    }\n","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/FlowiseAI/Flowise/blob/abe4a8601a058047b350c260676826e21dd14101/packages/components/src/storage/BaseStorageProvider.ts#L36-L72","documentation":"Second guard in sanitizeFilename, run after the `sanitize()` library call and after stripping leading dots. It catches filenames that became empty or still contain a path separator after sanitization — i.e. inputs that survived the first check but are still unsafe to use as a single path component.","triggerScenarios":"A filename composed entirely of dot/special characters that sanitize() reduces to an empty string, or an input whose sanitized form still contains `/` or `\\`. The guard is `!cleaned || cleaned.includes('/') || cleaned.includes('\\\\')` at BaseStorageProvider.ts:53.","commonSituations":"Filenames like `....`, `...//`, or strings of only forbidden characters; locale/encoding edge cases that defeat the sanitizer.","solutions":["Validate that the caller-supplied filename yields a non-empty basename with no separators before invoking storage.","Fall back to a generated safe name (e.g. a UUID) when the sanitized result is empty.","Treat this as an attack signal and log/reject the request upstream."],"exampleFix":"// before\nconst name = '....'\n// after\nconst name = sanitizeUserFileName(fileName) || crypto.randomUUID()","handlingStrategy":"validation","validationCode":"import sanitize from 'sanitize-filename'\nfunction safeFilenameOrUuid(filename: string, fallback: string): string {\n  const cleaned = sanitize(filename).replace(/^(\\.+)/, '')\n  if (!cleaned || cleaned.includes('/') || cleaned.includes('\\\\')) return fallback\n  return cleaned\n}","typeGuard":"function isNonEmptySanitized(name: string): boolean {\n  const c = name.replace(/^(\\.+)/, '')\n  return c.length > 0 && !c.includes('/') && !c.includes('\\\\')\n}","tryCatchPattern":"try {\n  await provider.streamStorageFile(chatflowId, chatId, fileName, orgId)\n} catch (e) {\n  if (/after sanitization/.test(e.message)) return res.status(400).send('invalid filename')\n  throw e\n}","preventionTips":["Reject filenames that reduce to empty after sanitization.","Fall back to a UUID filename when sanitization yields nothing.","Treat repeated empty-after-sanitize inputs as abusive."],"tags":["security","path-traversal","storage","validation"],"backgroundTag":null,"analyzedSha":"abe4a8601a058047b350c260676826e21dd14101","analyzedAt":"2026-08-12T16:04:40.823Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}