-
-
Notifications
You must be signed in to change notification settings - Fork 24.5k
fix: FLOWISE-553, 598, 616 #6464
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 2 commits
b282e6f
1ce73c6
9e2be04
fe09b73
0abe280
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ import { SqliteSaver } from './SQLiteAgentMemory/sqliteSaver' | |
| import { DataSource } from 'typeorm' | ||
| import { PostgresSaver } from './PostgresAgentMemory/pgSaver' | ||
| import { MySQLSaver } from './MySQLAgentMemory/mysqlSaver' | ||
| import { sanitizeDataSourceOptions } from '../../../src/sanitizeDataSourceOptions' | ||
|
|
||
| class AgentMemory_Memory implements INode { | ||
| label: string | ||
|
|
@@ -96,6 +97,8 @@ class AgentMemory_Memory implements INode { | |
| label: 'Additional Connection Configuration', | ||
| name: 'additionalConfig', | ||
| type: 'json', | ||
| description: | ||
| 'Optional TypeORM connection options (e.g. ssl, connectTimeout). entities, subscribers, migrations, and extra are not allowed.', | ||
| additionalParams: true, | ||
| optional: true | ||
| } | ||
|
|
@@ -118,6 +121,7 @@ class AgentMemory_Memory implements INode { | |
| } catch (exception) { | ||
| throw new Error('Invalid JSON in the Additional Configuration: ' + exception) | ||
| } | ||
| additionalConfiguration = sanitizeDataSourceOptions(additionalConfiguration) | ||
| } | ||
|
Comment on lines
+124
to
125
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Validate additionalConfiguration = sanitizeDataSourceOptions(additionalConfiguration)
}
if (databaseType === 'sqlite' && databaseFilePath) {
validateSQLitePath(databaseFilePath)
} |
||
|
|
||
| const threadId = options.sessionId || options.chatId | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| import { sanitizeDataSourceOptions } from './sanitizeDataSourceOptions' | ||
|
|
||
| describe('sanitizeDataSourceOptions', () => { | ||
| it('returns empty object for empty input', () => { | ||
| expect(sanitizeDataSourceOptions({})).toEqual({}) | ||
| }) | ||
|
|
||
| it('returns empty object for nullish-like invalid input', () => { | ||
| expect(sanitizeDataSourceOptions(null as any)).toEqual({}) | ||
| expect(sanitizeDataSourceOptions(undefined as any)).toEqual({}) | ||
| }) | ||
|
|
||
| it('passes through safe connection options', () => { | ||
| const config = { | ||
| ssl: { rejectUnauthorized: false }, | ||
| connectTimeout: 10000, | ||
| poolSize: 5 | ||
| } | ||
| expect(sanitizeDataSourceOptions(config)).toEqual(config) | ||
| }) | ||
|
|
||
| it('returns a shallow copy and does not mutate the input', () => { | ||
| const config = { poolSize: 5 } | ||
| const result = sanitizeDataSourceOptions(config) | ||
| expect(result).toEqual(config) | ||
| expect(result).not.toBe(config) | ||
| }) | ||
|
|
||
| describe('blocked keys', () => { | ||
| 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}`) | ||
| }) | ||
|
Comment on lines
+30
to
+32
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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')
}) |
||
|
|
||
| it('throws when multiple blocked keys are present', () => { | ||
| expect(() => | ||
| sanitizeDataSourceOptions({ | ||
| entities: ['/tmp/a.js'], | ||
| extra: { foo: 'bar' } | ||
| }) | ||
| ).toThrow('Disallowed TypeORM DataSource option:') | ||
| }) | ||
| }) | ||
| }) | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,24 @@ | ||||||||||||||||||||||
| import { ICommonObject } from './Interface' | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** TypeORM DataSource options that can load and execute arbitrary local JavaScript files. */ | ||||||||||||||||||||||
| const BLOCKED_DATASOURCE_KEYS = ['entities', 'subscribers', 'migrations', 'extra'] as const | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| export type BlockedDataSourceKey = (typeof BLOCKED_DATASOURCE_KEYS)[number] | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Rejects user-supplied TypeORM DataSource options that can lead to arbitrary code execution | ||||||||||||||||||||||
| * when passed to `new DataSource(options).initialize()`. | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| export function sanitizeDataSourceOptions(config: ICommonObject): ICommonObject { | ||||||||||||||||||||||
| if (!config || typeof config !== 'object' || Array.isArray(config)) { | ||||||||||||||||||||||
| return {} | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| for (const key of BLOCKED_DATASOURCE_KEYS) { | ||||||||||||||||||||||
| if (Object.prototype.hasOwnProperty.call(config, key)) { | ||||||||||||||||||||||
| throw new Error(`Disallowed TypeORM DataSource option: ${key}`) | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
Comment on lines
+21
to
+25
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using Using the
Suggested change
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| return { ...config } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import
validateSQLitePathto secure the user-provideddatabaseFilePathagainst path traversal and arbitrary file write vulnerabilities.