ronantakizawa/codereview-bench huggingface dataset:
Repository: FlowiseAI/Flowise
File: packages/components/credentials/ElectricsearchUserPassword.credential.ts (TypeScript)

Code before:
import { INodeParams, INodeCredential } from '../src/Interface'

class ElasticSearchUserPassword implements INodeCredential {
    label: string
    name: string
    version: number
    description: string
    inputs: INodeParams[]

    constructor() {
        this.label = 'ElasticSearch User Password'
        this.name = 'elasticSearchUserPassword'
        this.version = 1.0
        this.description =
            'Refer to <a target="_blank" href="https://www.elastic.co/guide/en/kibana/current/tutorial-secure-access-to-kibana.html">official guide</a> on how to get User Password from ElasticSearch'
        this.inputs = [
            {
                label: 'Cloud ID or custom server URL',
                name: 'cloudId',
                type: 'string'
            },
            {
                label: 'ElasticSearch User',
                name: 'username',
                type: 'string'
            },
            {
                label: 'ElasticSearch Password',
                name: 'password',
                type: 'password'
            }
        ]
    }
}

module.exports = { credClass: ElasticSearchUserPassword }


Review comment (security):
can we add a `description` to explain how to get Cloud ID OR what is custom server URL? Better if it has link to docs like [here](https://github.com/FlowiseAI/Flowise/blob/main/packages/components/credentials/AWSCredential.credential.ts#L16)

Code after:
import { INodeParams, INodeCredential } from '../src/Interface'

class ElasticSearchUserPassword implements INodeCredential {
    label: string
    name: string
    version: number
    description: string
    inputs: INodeParams[]

    constructor() {
        this.label = 'ElasticSearch User Password'
        this.name = 'elasticSearchUserPassword'
        this.version = 1.0
        this.description = `Use Cloud ID field to enter your Elastic Cloud ID or the URL of the Elastic server instance.
        Refer to <a target="_blank" href="https://www.elastic.co/guide/en/elasticsearch/reference/current/setting-up-authentication.html">official guide</a> on how to get User Password from ElasticSearch.`
        this.inputs = [
            {
                label: 'Cloud ID',
                name: 'cloudId',
                type: 'string'
            },
            {
                label: 'ElasticSearch User',
                name: 'username',
                type: 'string'
            },
            {
                label: 'ElasticSearch Password',
                name: 'password',
                type: 'password'
            }
        ]
    }
}

module.exports = { credClass: ElasticSearchUserPassword }
Repository: FlowiseAI/Flowise
File: packages/components/src/utils.ts (TypeScript)

Code before:
                message: m.content as string,
                type: 'userMessage'
            })
        } else if (m._getType() === 'ai') {
            formatmessages.push({
                message: m.content as string,
                type: 'apiMessage'
            })
        } else if (m._getType() === 'system') {
            formatmessages.push({
                message: m.content as string,
                type: 'apiMessage'
            })
        }
    }
    return formatmessages
}

/**
 * Convert MultiOptions String to String Array
 * @param {string} inputString
 * @returns {string[]}
 */
export const convertMultiOptionsToStringArray = (inputString: string): string[] => {
    let ArrayString: string[] = []
    if (inputString) ArrayString = JSON.parse(inputString)

    return ArrayString
}


Review comment (suggestion):
oops, I meant the other way around. this would enable us to catch all misinformed JSON as well, in addition to null/undefined/empty...

try {
ArrayString = JSON.parse(....)
}catch(Error e) {
ArrayString = []
}

Code after:
                message: m.content as string,
                type: 'userMessage'
            })
        } else if (m._getType() === 'ai') {
            formatmessages.push({
                message: m.content as string,
                type: 'apiMessage'
            })
        } else if (m._getType() === 'system') {
            formatmessages.push({
                message: m.content as string,
                type: 'apiMessage'
            })
        }
    }
    return formatmessages
}

/**
 * Convert MultiOptions String to String Array
 * @param {string} inputString
 * @returns {string[]}
 */
export const convertMultiOptionsToStringArray = (inputString: string): string[] => {
    let ArrayString: string[] = []
    try {
        ArrayString = JSON.parse(inputString)
    } catch (e) {
        ArrayString = []
    }
    return ArrayString
}
Repository: FlowiseAI/Flowise
File: packages/components/credentials/PineconeApi.credential.ts (TypeScript)

Code before:
import { INodeParams, INodeCredential } from '../src/Interface'

class PineconeApi implements INodeCredential {
    label: string
    name: string
    version: number
    description: string
    inputs: INodeParams[]

    constructor() {
        this.label = 'Pinecone API'
        this.name = 'pineconeApi'
        this.version = 1.0
        this.inputs = [
            {
                label: 'Pinecone Api Key',
                name: 'pineconeApiKey',
                type: 'password'
            },
            {
                label: 'Pinecone Environment',
                name: 'pineconeEnv',
                type: 'string'
            }
        ]
    }
}

module.exports = { credClass: PineconeApi }


Review comment (security):
I dont think we want to remove this `environment`. As some are still using it. A better way is to create a separate credential file specifically for the serverless

Code after:
import { INodeParams, INodeCredential } from '../src/Interface'

class PineconeApi implements INodeCredential {
    label: string
    name: string
    version: number
    description: string
    inputs: INodeParams[]

    constructor() {
        this.label = 'Pinecone API'
        this.name = 'pineconeApi'
        this.version = 1.0
        this.inputs = [
            {
                label: 'Pinecone Api Key',
                name: 'pineconeApiKey',
                type: 'password'
            }
        ]
    }
}

module.exports = { credClass: PineconeApi }
Repository: FlowiseAI/Flowise
File: packages/server/src/DataSource.ts (TypeScript)

Code before:
            })
            break
        default:
            homePath = process.env.DATABASE_PATH ?? flowisePath
            appDataSource = new DataSource({
                type: 'sqlite',
                database: path.resolve(homePath, 'database.sqlite'),
                synchronize: false,
                migrationsRun: false,
                entities: Object.values(entities),
                migrations: sqliteMigrations
            })
            break
    }
}

export function getDataSource(): DataSource {
    if (appDataSource === undefined) {
        init()
    }
    return appDataSource
}

const getDatabaseSSLFromEnv = () => {
    if (process.env.DATABASE_SSL_KEY_BASE64) {
        return {
            rejectUnauthorized: false,
            ca: Buffer.from(process.env.DATABASE_SSL_KEY_BASE64, 'base64')
        }
    } else if (process.env.DATABASE_SSL === 'true') {
        return true
    }
    return {}
}


Review comment (suggestion):
See comment above. getDatabaseSSLFromEnv should return
```
{
  ssl: {
    self signed ssl attributes
  }
}
```

and below should return 
{
  ssl: true
}

the final return of {} will return an empty object that will spread to nothing and allow pg node to continue to use PGSQLMODE as the lib is designed.

mysql2 does not appear to be impacted by using the same approach and should work by using the same spread methodology. mysql2 does not reference any env var internally.

Code after:
            })
            break
        default:
            homePath = process.env.DATABASE_PATH ?? flowisePath
            appDataSource = new DataSource({
                type: 'sqlite',
                database: path.resolve(homePath, 'database.sqlite'),
                synchronize: false,
                migrationsRun: false,
                entities: Object.values(entities),
                migrations: sqliteMigrations
            })
            break
    }
}

export function getDataSource(): DataSource {
    if (appDataSource === undefined) {
        init()
    }
    return appDataSource
}

const getDatabaseSSLFromEnv = () => {
    if (process.env.DATABASE_SSL_KEY_BASE64) {
        return {
            rejectUnauthorized: false,
            ca: Buffer.from(process.env.DATABASE_SSL_KEY_BASE64, 'base64')
        }
    } else if (process.env.DATABASE_SSL === 'true') {
        return true
    }
    return undefined
}
Repository: FlowiseAI/Flowise
File: packages/server/src/DataSource.ts (TypeScript)

Code before:
        default:
            homePath = process.env.DATABASE_PATH ?? flowisePath
            appDataSource = new DataSource({
                type: 'sqlite',
                database: path.resolve(homePath, 'database.sqlite'),
                synchronize: false,
                migrationsRun: false,
                entities: Object.values(entities),
                migrations: sqliteMigrations
            })
            break
    }
}

export function getDataSource(): DataSource {
    if (appDataSource === undefined) {
        init()
    }
    return appDataSource
}

const getDatabaseSSLFromEnv = () => {
    if (process.env.DATABASE_SSL_KEY_BASE64) {
        return {
            rejectUnauthorized: false,
            ca: Buffer.from(process.env.DATABASE_SSL_KEY_BASE64, 'base64')
        }
    } else if (process.env.DATABASE_SSL === 'true') {
        return true
    }
    return {}
}


Review comment (suggestion):
@Jaredude have you tested it? 

I think it should be like this:

`Buffer.from(process.env.DATABASE_SSL_KEY_BASE64, 'base64').toString('utf-8')`



Code after:
        default:
            homePath = process.env.DATABASE_PATH ?? flowisePath
            appDataSource = new DataSource({
                type: 'sqlite',
                database: path.resolve(homePath, 'database.sqlite'),
                synchronize: false,
                migrationsRun: false,
                entities: Object.values(entities),
                migrations: sqliteMigrations
            })
            break
    }
}

export function getDataSource(): DataSource {
    if (appDataSource === undefined) {
        init()
    }
    return appDataSource
}

const getDatabaseSSLFromEnv = () => {
    if (process.env.DATABASE_SSL_KEY_BASE64) {
        return {
            rejectUnauthorized: false,
            ca: Buffer.from(process.env.DATABASE_SSL_KEY_BASE64, 'base64')
        }
    } else if (process.env.DATABASE_SSL === 'true') {
        return true
    }
    return undefined
}
Repository: FlowiseAI/Flowise
File: packages/components/nodes/memory/ZepMemoryCloud/ZepMemoryCloud.ts (TypeScript)

Code before:
import { IMessage, INode, INodeData, INodeParams, MemoryMethods, MessageType } from '../../../src/Interface'
import { convertBaseMessagetoIMessage, getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
import { ZepMemory, ZepMemoryInput } from '@getzep/zep-cloud/langchain'

import { ICommonObject } from '../../../src'
import { InputValues, MemoryVariables, OutputValues } from 'langchain/memory'
import { BaseMessage } from 'langchain/schema'

class ZepMemoryCloud_Memory implements INode {
    label: string
    name: string
    version: number
    description: string
    type: string
    icon: string
    category: string
    baseClasses: string[]
    credential: INodeParams
    inputs: INodeParams[]

    constructor() {
        this.label = 'Zep Memory - Cloud'
        this.name = 'Zep Memory - Cloud'
        this.version = 2.0
        this.type = 'ZepMemory'
        this.icon = 'zep.svg'

Review comment (suggestion):
instead of creating a new node, what do u think if we just add a new `options` field to the `ZepMemory` that allow users to select OpenSource or Cloud, then according to selection, use the respective library for init

Code after:
import { IMessage, INode, INodeData, INodeParams, MemoryMethods, MessageType } from '../../../src/Interface'
import { convertBaseMessagetoIMessage, getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
import { ZepMemory, ZepMemoryInput } from '@getzep/zep-cloud/langchain'

import { ICommonObject } from '../../../src'
import { InputValues, MemoryVariables, OutputValues } from 'langchain/memory'
import { BaseMessage } from 'langchain/schema'

class ZepMemoryCloud_Memory implements INode {
    label: string
    name: string
    version: number
    description: string
    type: string
    icon: string
    category: string
    baseClasses: string[]
    credential: INodeParams
    inputs: INodeParams[]

    constructor() {
        this.label = 'Zep Memory - Cloud'
        this.name = 'ZepMemoryCloud'
        this.version = 2.0
        this.type = 'ZepMemory'
        this.icon = 'zep.svg'
Repository: FlowiseAI/Flowise
File: packages/ui/src/store/constant.js (JavaScript)

Code before:
// constant
export const gridSpacing = 3
export const drawerWidth = 260
export const appDrawerWidth = 320
export const headerHeight = 80
export const maxScroll = 100000
export const baseURL =
    import.meta.env.PROD === true
        ? window.location.origin
        : window.location.origin.replace(`:${import.meta.env.VITE_PORT ?? '8080'}`, ':3000')
export const uiBaseURL = window.location.origin
export const FLOWISE_CREDENTIAL_ID = 'FLOWISE_CREDENTIAL_ID'
export const REDACTED_CREDENTIAL_VALUE = '_FLOWISE_BLANK_07167752-1a71-43b1-bf8f-4f32252165db'


Review comment (suggestion):
@mokeyish just noticed there are 2 urls:
`baseURL` and `uiBaseURL` with same value `window.location.origin`, do we need both?

Also when running `pnpm dev` on `localhost:8080`, the `baseURL` becomes `localhost:8080`. This makes the `ChatMessage.jsx` not able to receive streaming because the server is `localhost:3000` - https://github.com/FlowiseAI/Flowise/blob/main/packages/ui/src/views/chatmessage/ChatMessage.jsx#L609

Code after:
// constant
export const gridSpacing = 3
export const drawerWidth = 260
export const appDrawerWidth = 320
export const headerHeight = 80
export const maxScroll = 100000
export const baseURL = window.location.origin
export const uiBaseURL = window.location.origin
export const FLOWISE_CREDENTIAL_ID = 'FLOWISE_CREDENTIAL_ID'
export const REDACTED_CREDENTIAL_VALUE = '_FLOWISE_BLANK_07167752-1a71-43b1-bf8f-4f32252165db'
Repository: FlowiseAI/Flowise
File: packages/components/src/handler.ts (TypeScript)

Code before:
        return JSON.stringify(obj, null, spaces)
    } catch (err) {
        return fallback
    }
}

function elapsed(run: Run): string {
    if (!run.end_time) return ''
    const elapsed = run.end_time - run.start_time
    if (elapsed < 1000) {
        return `${elapsed}ms`
    }
    return `${(elapsed / 1000).toFixed(2)}s`
}

export class ConsoleCallbackHandler extends BaseTracer {
    name = 'console_callback_handler' as const
    logger: Logger

    protected persistRun(_run: Run) {
        return Promise.resolve()
    }

    constructor(logger: Logger) {
        super()
        this.logger = logger
        logger.level = getEnvironmentVariable('LOG_LEVEL') ?? 'info'
    }

    // utility methods

    getParents(run: Run) {
        const parents: Run[] = []
        let currentRun = run
        while (currentRun.parent_run_id) {
            const parent = this.runMap.get(currentRun.parent_run_id)
            if (parent) {
                parents.push(parent)
                currentRun = parent
            } else {
                break
            }
        }
        return parents
    }

    getBreadcrumbs(run: Run) {
        const parents = this.getParents(run).reverse()
        const string = [...parents, run]
            .map((parent) => {
                const name = `${parent.execution_order}:${parent.run_type}:${parent.name}`

Review comment (bug):
we might not need to specify this, as its already specified [here](https://github.com/FlowiseAI/Flowise/blob/bugfix/Remove-Doc-Store-File/packages/server/src/utils/config.ts#L12)

Code after:
    } catch (err) {
        return fallback
    }
}

function elapsed(run: Run): string {
    if (!run.end_time) return ''
    const elapsed = run.end_time - run.start_time
    if (elapsed < 1000) {
        return `${elapsed}ms`
    }
    return `${(elapsed / 1000).toFixed(2)}s`
}

export class ConsoleCallbackHandler extends BaseTracer {
    name = 'console_callback_handler' as const
    logger: Logger

    protected persistRun(_run: Run) {
        return Promise.resolve()
    }

    constructor(logger: Logger) {
        super()
        this.logger = logger
        logger.level = getEnvironmentVariable('LOG_LEVEL') ?? 'info'
    }

    getParents(run: Run) {
        const parents: Run[] = []
        let currentRun = run
        while (currentRun.parent_run_id) {
            const parent = this.runMap.get(currentRun.parent_run_id)
            if (parent) {
                parents.push(parent)
                currentRun = parent
            } else {
                break
            }
        }
        return parents
    }

    getBreadcrumbs(run: Run) {
        const parents = this.getParents(run).reverse()
        const string = [...parents, run]
            .map((parent) => {
                const name = `${parent.execution_order}:${parent.run_type}:${parent.name}`
                return name
            })
            .join(' > ')
Repository: FlowiseAI/Flowise
File: packages/components/credentials/OpenSearchUrl.credential.ts (TypeScript)

Code before:
import { INodeParams, INodeCredential } from '../src/Interface'

class OpenSearchUrl implements INodeCredential {
    label: string
    name: string
    version: number
    description: string
    inputs: INodeParams[]

    constructor() {
        this.label = 'OpenSearch'
        this.name = 'openSearchUrl'
        this.version = 1.0
        this.inputs = [
            {
                label: 'OpenSearch Url',
                name: 'openSearchUrl',
                type: 'string'
            }
        ]
    }
}

module.exports = { credClass: OpenSearchUrl }


Review comment (security):
it is better to have `user` and `password` on `this.credential`, example [here](https://github.com/FlowiseAI/Flowise/blob/main/packages/components/nodes/vectorstores/Pinecone/Pinecone.ts#L35)

Code after:

class OpenSearchUrl implements INodeCredential {
    label: string
    name: string
    version: number
    description: string
    inputs: INodeParams[]

    constructor() {
        this.label = 'OpenSearch'
        this.name = 'openSearchUrl'
        this.version = 2.0
        this.inputs = [
            {
                label: 'OpenSearch Url',
                name: 'openSearchUrl',
                type: 'string'
            },
            {
                label: 'User',
                name: 'user',
                type: 'string',
                placeholder: '<OPENSEARCH_USERNAME>',
                optional: true
            },
            {
                label: 'Password',
                name: 'password',
                type: 'password',
                placeholder: '<OPENSEARCH_PASSWORD>',
                optional: true
            }
        ]
    }
}

module.exports = { credClass: OpenSearchUrl }
Repository: FlowiseAI/Flowise
File: packages/components/src/validator.ts (TypeScript)

Code before:
    validateFilename(filename)

    if (!mimetype || typeof mimetype !== 'string') {
        throw new Error('Invalid MIME type: MIME type is required and must be a string')
    }

    const normalizedExt = extractFileExtension(filename)

    if (!normalizedExt) {
        // Files without extensions are rejected for security
        throw new Error('File type not allowed: files must have a valid file extension')
    }

    // Get the expected extension from mapMimeTypeToExt (returns extension without dot)
    const expectedExt = mapMimeTypeToExt(mimetype)

    if (!expectedExt) {
        // If mapMimeTypeToExt doesn't recognize the MIME type, it's not supported
        throw new Error(`MIME type "${mimetype}" is not supported or does not have a valid file extension mapping`)
    }

    // Ensure the file extension matches the expected extension for the MIME type
    if (normalizedExt !== expectedExt) {
        throw new Error(
            `MIME type mismatch: file extension "${normalizedExt}" does not match declared MIME type "${mimetype}". Expected: ${expectedExt}`
        )
    }
}


Review comment (suggestion):
I'm still learning through the `mapMimeTypeToExt` function. Should it support image or media types? Ignore this comment if it's not related to this PR.

Code after:
 * Validates that file extension matches the declared MIME type
 *
 * This function addresses CVE-2025-61687 by preventing MIME type spoofing attacks.
 * It ensures that the file extension matches the declared MIME type, preventing
 * attackers from uploading malicious files (e.g., .js file with text/plain MIME type).
 *
 * @param {string} filename The original filename
 * @param {string} mimetype The declared MIME type
 * @returns {void} Throws an error if validation fails
 */
export const validateMimeTypeAndExtensionMatch = (filename: string, mimetype: string): void => {
    validateFilename(filename)

    if (!mimetype || typeof mimetype !== 'string') {
        throw new Error('Invalid MIME type: MIME type is required and must be a string')
    }

    const normalizedExt = extractFileExtension(filename)

    if (!normalizedExt) {
        // Files without extensions are rejected for security
        throw new Error('File type not allowed: files must have a valid file extension')
    }

    // Get the expected extension from mapMimeTypeToExt (returns extension without dot)
    const expectedExt = mapMimeTypeToExt(mimetype)

    if (!expectedExt) {
        // If mapMimeTypeToExt doesn't recognize the MIME type, it's not supported
        throw new Error(`MIME type "${mimetype}" is not supported or does not have a valid file extension mapping`)
    }

    // Ensure the file extension matches the expected extension for the MIME type
    if (normalizedExt !== expectedExt) {
        throw new Error(
            `MIME type mismatch: file extension "${normalizedExt}" does not match declared MIME type "${mimetype}". Expected: ${expectedExt}`
        )
    }
}