Skip to content

Commit 4e5f74c

Browse files
committed
added sanitization for sql schema
1 parent 1f45b76 commit 4e5f74c

File tree

8 files changed

+40
-12
lines changed

8 files changed

+40
-12
lines changed

apps/sim/app/api/tools/dynamodb/introspect/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { randomUUID } from 'crypto'
2+
import { createLogger } from '@sim/logger'
23
import { type NextRequest, NextResponse } from 'next/server'
34
import { z } from 'zod'
4-
import { createLogger } from '@/lib/logs/console/logger'
55
import { createRawDynamoDBClient, describeTable, listTables } from '@/app/api/tools/dynamodb/utils'
66

77
const logger = createLogger('DynamoDBIntrospectAPI')

apps/sim/app/api/tools/mongodb/introspect/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { randomUUID } from 'crypto'
2+
import { createLogger } from '@sim/logger'
23
import { type NextRequest, NextResponse } from 'next/server'
34
import { z } from 'zod'
4-
import { createLogger } from '@/lib/logs/console/logger'
55
import { createMongoDBConnection, executeIntrospect } from '../utils'
66

77
const logger = createLogger('MongoDBIntrospectAPI')

apps/sim/app/api/tools/mysql/introspect/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { randomUUID } from 'crypto'
2+
import { createLogger } from '@sim/logger'
23
import { type NextRequest, NextResponse } from 'next/server'
34
import { z } from 'zod'
4-
import { createLogger } from '@/lib/logs/console/logger'
55
import { createMySQLConnection, executeIntrospect } from '@/app/api/tools/mysql/utils'
66

77
const logger = createLogger('MySQLIntrospectAPI')

apps/sim/app/api/tools/neo4j/introspect/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { randomUUID } from 'crypto'
2+
import { createLogger } from '@sim/logger'
23
import { type NextRequest, NextResponse } from 'next/server'
34
import { z } from 'zod'
4-
import { createLogger } from '@/lib/logs/console/logger'
55
import { createNeo4jDriver } from '@/app/api/tools/neo4j/utils'
66
import type { Neo4jNodeSchema, Neo4jRelationshipSchema } from '@/tools/neo4j/types'
77

apps/sim/app/api/tools/postgresql/introspect/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { randomUUID } from 'crypto'
2+
import { createLogger } from '@sim/logger'
23
import { type NextRequest, NextResponse } from 'next/server'
34
import { z } from 'zod'
4-
import { createLogger } from '@/lib/logs/console/logger'
55
import { createPostgresConnection, executeIntrospect } from '@/app/api/tools/postgresql/utils'
66

77
const logger = createLogger('PostgreSQLIntrospectAPI')

apps/sim/app/api/tools/rds/introspect/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { randomUUID } from 'crypto'
2+
import { createLogger } from '@sim/logger'
23
import { type NextRequest, NextResponse } from 'next/server'
34
import { z } from 'zod'
4-
import { createLogger } from '@/lib/logs/console/logger'
55
import { createRdsClient, executeIntrospect, type RdsEngine } from '@/app/api/tools/rds/utils'
66

77
const logger = createLogger('RDSIntrospectAPI')

apps/sim/lib/core/security/input-validation.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,3 +1004,27 @@ export function validateGoogleCalendarId(
10041004

10051005
return { isValid: true, sanitized: value }
10061006
}
1007+
1008+
/**
1009+
* Sanitizes a SQL identifier by escaping single quotes
1010+
*
1011+
* This function escapes single quotes by doubling them (PostgreSQL/MySQL standard)
1012+
* to prevent SQL injection while allowing all valid identifier names.
1013+
*
1014+
* @param name - The identifier name to sanitize
1015+
* @param maxLength - Maximum length allowed (default: 63 for PostgreSQL)
1016+
* @returns The sanitized identifier
1017+
* @throws Error if name is empty or exceeds maxLength
1018+
*
1019+
* @example
1020+
* ```typescript
1021+
* const safeSchema = sanitizeSqlIdentifier(schema)
1022+
* const query = `SELECT * FROM ${safeSchema}.users`
1023+
* ```
1024+
*/
1025+
export function sanitizeSqlIdentifier(name: string, maxLength = 63): string {
1026+
if (!name || name.length > maxLength) {
1027+
throw new Error(`Invalid identifier: ${name}`)
1028+
}
1029+
return name.replace(/'/g, "''")
1030+
}

apps/sim/tools/supabase/introspect.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { createLogger } from '@/lib/logs/console/logger'
1+
import { createLogger } from '@sim/logger'
2+
import { sanitizeSqlIdentifier } from '@/lib/core/security/input-validation'
23
import type {
34
SupabaseColumnSchema,
45
SupabaseIntrospectParams,
@@ -150,14 +151,16 @@ SELECT json_build_object(
150151
/**
151152
* SQL query filtered by specific schema
152153
*/
153-
const getSchemaFilteredSQL = (schema: string) => `
154+
const getSchemaFilteredSQL = (schema: string) => {
155+
const safeSchema = sanitizeSqlIdentifier(schema)
156+
return `
154157
WITH table_info AS (
155158
SELECT
156159
t.table_schema,
157160
t.table_name
158161
FROM information_schema.tables t
159162
WHERE t.table_type = 'BASE TABLE'
160-
AND t.table_schema = '${schema}'
163+
AND t.table_schema = '${safeSchema}'
161164
),
162165
columns_info AS (
163166
SELECT
@@ -181,7 +184,7 @@ pk_info AS (
181184
ON tc.constraint_name = kcu.constraint_name
182185
AND tc.table_schema = kcu.table_schema
183186
WHERE tc.constraint_type = 'PRIMARY KEY'
184-
AND tc.table_schema = '${schema}'
187+
AND tc.table_schema = '${safeSchema}'
185188
),
186189
fk_info AS (
187190
SELECT
@@ -197,7 +200,7 @@ fk_info AS (
197200
JOIN information_schema.constraint_column_usage ccu
198201
ON ccu.constraint_name = tc.constraint_name
199202
WHERE tc.constraint_type = 'FOREIGN KEY'
200-
AND tc.table_schema = '${schema}'
203+
AND tc.table_schema = '${safeSchema}'
201204
),
202205
index_info AS (
203206
SELECT
@@ -207,7 +210,7 @@ index_info AS (
207210
CASE WHEN indexdef LIKE '%UNIQUE%' THEN true ELSE false END AS is_unique,
208211
indexdef
209212
FROM pg_indexes
210-
WHERE schemaname = '${schema}'
213+
WHERE schemaname = '${safeSchema}'
211214
)
212215
SELECT json_build_object(
213216
'tables', (
@@ -285,6 +288,7 @@ SELECT json_build_object(
285288
)
286289
) AS result;
287290
`
291+
}
288292

289293
/**
290294
* Tool for introspecting Supabase database schema

0 commit comments

Comments
 (0)