Skip to content

fix: FLOWISE-553, 598, 616#6464

Draft
0xi4o wants to merge 4 commits into
mainfrom
fix/typeorm
Draft

fix: FLOWISE-553, 598, 616#6464
0xi4o wants to merge 4 commits into
mainfrom
fix/typeorm

Conversation

@0xi4o
Copy link
Copy Markdown
Contributor

@0xi4o 0xi4o commented Jun 2, 2026

No description provided.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces security enhancements to sanitize TypeORM connection options via sanitizeDataSourceOptions and validate SQLite database paths using validateSQLitePath across multiple database, memory, and vector store nodes. The review feedback highlights critical security and compatibility improvements: using the in operator instead of hasOwnProperty to prevent prototype pollution bypasses, validating databaseFilePath in AgentMemory to block path traversal, adding tests for prototype chain pollution, and handling case-insensitive path comparisons on Windows to avoid false positives.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +17 to +21
for (const key of BLOCKED_DATASOURCE_KEYS) {
if (Object.prototype.hasOwnProperty.call(config, key)) {
throw new Error(`Disallowed TypeORM DataSource option: ${key}`)
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using Object.prototype.hasOwnProperty.call(config, key) only checks if the property is directly defined on the object itself. If a malicious payload is constructed such that the blocked keys (like entities or subscribers) are defined on the prototype chain (e.g., via prototype pollution or inheritance), this check will be bypassed, but TypeORM will still access and load them.

Using the in operator is much more secure as it inspects the prototype chain as well.

Suggested change
for (const key of BLOCKED_DATASOURCE_KEYS) {
if (Object.prototype.hasOwnProperty.call(config, key)) {
throw new Error(`Disallowed TypeORM DataSource option: ${key}`)
}
}
for (const key of BLOCKED_DATASOURCE_KEYS) {
if (key in config) {
throw new Error("Disallowed TypeORM DataSource option: " + key)
}
}

import { DataSource } from 'typeorm'
import { PostgresSaver } from './PostgresAgentMemory/pgSaver'
import { MySQLSaver } from './MySQLAgentMemory/mysqlSaver'
import { sanitizeDataSourceOptions } from '../../../src/sanitizeDataSourceOptions'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Import validateSQLitePath to secure the user-provided databaseFilePath against path traversal and arbitrary file write vulnerabilities.

Suggested change
import { sanitizeDataSourceOptions } from '../../../src/sanitizeDataSourceOptions'
import { sanitizeDataSourceOptions } from '../../../src/sanitizeDataSourceOptions'
import { validateSQLitePath } from '../../../src/validator'

Comment on lines +124 to 125
additionalConfiguration = sanitizeDataSourceOptions(additionalConfiguration)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The databaseFilePath input is user-controlled and is resolved directly using path.resolve(databaseFilePath) without validation if SQLite is selected. This allows path traversal and arbitrary file write/read vulnerabilities.

Validate databaseFilePath using validateSQLitePath if it is provided.

            additionalConfiguration = sanitizeDataSourceOptions(additionalConfiguration)
        }

        if (databaseType === 'sqlite' && databaseFilePath) {
            validateSQLitePath(databaseFilePath)
        }

Comment on lines +30 to +32
it.each(['entities', 'subscribers', 'migrations', 'extra'] as const)('throws when %s is present', (key) => {
expect(() => sanitizeDataSourceOptions({ [key]: ['/tmp/evil.js'] })).toThrow(`Disallowed TypeORM DataSource option: ${key}`)
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Add a test case to verify that blocked keys present on the prototype chain are also correctly detected and rejected.

        it.each(['entities', 'subscribers', 'migrations', 'extra'] as const)('throws when %s is present', (key) => {
            expect(() => sanitizeDataSourceOptions({ [key]: ['/tmp/evil.js'] })).toThrow("Disallowed TypeORM DataSource option: " + key)
        })

        it('throws when blocked key is present on the prototype chain', () => {
            const maliciousConfig = Object.create({ entities: ['/tmp/evil.js'] })
            expect(() => sanitizeDataSourceOptions(maliciousConfig)).toThrow('Disallowed TypeORM DataSource option: entities')
        })

Comment thread packages/components/src/validator.ts Outdated
Comment on lines +330 to +334
const normalizedResolved = path.normalize(resolvedPath)
const normalizedAllowed = path.normalize(allowedDir)
if (normalizedResolved !== normalizedAllowed && !normalizedResolved.startsWith(normalizedAllowed + path.sep)) {
throw new Error(`Invalid SQLite path: path must be within ${allowedDir}. Attempted path: ${resolvedPath}`)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

On Windows, path comparisons are case-insensitive. Since resolvedPath might preserve the casing of the user's input (e.g., lowercase drive letter c:\...) while allowedDir uses the system's casing (e.g., uppercase C:\...), the startsWith check can fail with a false positive, rejecting perfectly valid paths.

Convert both paths to lowercase when running on Windows before performing the comparison.

    let normalizedResolved = path.normalize(resolvedPath)
    let normalizedAllowed = path.normalize(allowedDir)
    if (process.platform === 'win32') {
        normalizedResolved = normalizedResolved.toLowerCase()
        normalizedAllowed = normalizedAllowed.toLowerCase()
    }
    if (normalizedResolved !== normalizedAllowed && !normalizedResolved.startsWith(normalizedAllowed + path.sep)) {
        throw new Error("Invalid SQLite path: path must be within " + allowedDir + ". Attempted path: " + resolvedPath)
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant