Skip to content

Conversation

@aadamgough
Copy link
Collaborator

Summary

Snowflake integration. Still testing some auth stuff for creating and deleting tables.

Fixes #(issue)

Type of Change

  • New feature

Testing

Tested manually

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel
Copy link

vercel bot commented Nov 14, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Preview Comments Updated (UTC)
docs Skipped Skipped Nov 19, 2025 9:14pm

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Nov 14, 2025

Greptile Overview

Greptile Summary

This PR adds a comprehensive Snowflake integration with OAuth authentication and multiple database operation tools. The implementation includes proper PKCE flow for OAuth security and covers common database operations (query, insert, update, delete, and various list operations).

Key Changes:

  • Implemented OAuth 2.0 authorization flow with PKCE for Snowflake authentication
  • Added 13 Snowflake tools for database operations (execute query, CRUD operations, metadata listing)
  • Created block configuration for Snowflake integration in the workflow builder
  • Added utility functions for Snowflake API interaction and response parsing

Critical Issues:

  • SQL Injection Vulnerabilities: The delete_rows, insert_rows, and update_rows tools construct SQL queries by directly concatenating user-provided table names, schema names, database names, and column names without proper identifier quoting. This allows attackers to inject malicious SQL through these identifiers (e.g., a table name of users; DROP TABLE users-- would execute arbitrary SQL).

Recommendations:

  • All SQL identifiers (database, schema, table, column names) must be properly quoted using Snowflake's double-quote identifier quoting syntax
  • Consider using Snowflake's bind variable functionality for WHERE clauses instead of string concatenation
  • Add validation to ensure identifiers only contain allowed characters

Confidence Score: 1/5

  • This PR contains critical SQL injection vulnerabilities that must be fixed before merging
  • The SQL injection vulnerabilities in DELETE, INSERT, and UPDATE operations pose a critical security risk. Attackers could exploit these to access unauthorized data, modify or delete arbitrary tables, or potentially gain elevated privileges in the Snowflake environment. While the OAuth implementation is secure, these SQL injection flaws make the PR unsafe to merge in its current state.
  • apps/sim/tools/snowflake/delete_rows.ts, apps/sim/tools/snowflake/insert_rows.ts, and apps/sim/tools/snowflake/update_rows.ts must be fixed to properly quote SQL identifiers before this PR can be merged

Important Files Changed

File Analysis

Filename Score Overview
apps/sim/tools/snowflake/delete_rows.ts 1/5 Added delete functionality with critical SQL injection vulnerability in identifier concatenation
apps/sim/tools/snowflake/insert_rows.ts 1/5 Added insert functionality with SQL injection vulnerability in table/column name handling
apps/sim/tools/snowflake/update_rows.ts 1/5 Added update functionality with SQL injection vulnerability in identifier concatenation
apps/sim/tools/snowflake/execute_query.ts 4/5 Added execute query tool that passes user queries directly to Snowflake API
apps/sim/app/api/auth/snowflake/callback/route.ts 4/5 Implemented OAuth callback with PKCE verification and token storage
apps/sim/app/api/auth/snowflake/authorize/route.ts 4/5 Implemented OAuth authorization flow with PKCE for Snowflake

Sequence Diagram

sequenceDiagram
    participant User
    participant Frontend
    participant AuthAPI as Auth Routes
    participant Snowflake
    participant Database
    participant ToolAPI as Tool Execution

    Note over User,Database: OAuth Flow
    User->>Frontend: Initiate connection
    Frontend->>AuthAPI: Start OAuth
    AuthAPI->>AuthAPI: Generate PKCE
    AuthAPI->>Snowflake: Authorization request
    Snowflake->>User: Consent prompt
    User->>Snowflake: Grant permission
    Snowflake->>AuthAPI: Return authorization
    AuthAPI->>Snowflake: Exchange for credentials
    Snowflake->>AuthAPI: Provide credentials
    AuthAPI->>Database: Store credentials
    AuthAPI->>Frontend: Complete flow

    Note over User,ToolAPI: Query Execution
    User->>Frontend: Execute SQL operation
    Frontend->>ToolAPI: Operation request
    ToolAPI->>ToolAPI: Build SQL
    ToolAPI->>Database: Get credentials
    ToolAPI->>Snowflake: Execute query
    Snowflake->>ToolAPI: Query results
    ToolAPI->>Frontend: Return data
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

27 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines 14 to 29
function buildDeleteSQL(
database: string,
schema: string,
table: string,
whereClause?: string
): string {
const fullTableName = `${database}.${schema}.${table}`

let sql = `DELETE FROM ${fullTableName}`

// Add WHERE clause if provided
if (whereClause && whereClause.trim()) {
sql += ` WHERE ${whereClause}`
}

return sql
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: SQL injection vulnerability: database, schema, table, and whereClause are concatenated directly into SQL without any validation or identifier quoting. Malicious input like table = "users; DROP TABLE users--" could execute arbitrary SQL.

Suggested change
function buildDeleteSQL(
database: string,
schema: string,
table: string,
whereClause?: string
): string {
const fullTableName = `${database}.${schema}.${table}`
let sql = `DELETE FROM ${fullTableName}`
// Add WHERE clause if provided
if (whereClause && whereClause.trim()) {
sql += ` WHERE ${whereClause}`
}
return sql
function buildDeleteSQL(
database: string,
schema: string,
table: string,
whereClause?: string
): string {
// Snowflake identifier quoting requires double quotes for SQL identifiers
const quotedDatabase = `"${database.replace(/"/g, '""')}"`
const quotedSchema = `"${schema.replace(/"/g, '""')}"`
const quotedTable = `"${table.replace(/"/g, '""')}"`
const fullTableName = `${quotedDatabase}.${quotedSchema}.${quotedTable}`
let sql = `DELETE FROM ${fullTableName}`
// Add WHERE clause if provided
// Note: whereClause still needs proper parameterization - consider using Snowflake bind variables
if (whereClause && whereClause.trim()) {
sql += ` WHERE ${whereClause}`
}
return sql
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/tools/snowflake/delete_rows.ts
Line: 14:29

Comment:
**logic:** SQL injection vulnerability: `database`, `schema`, `table`, and `whereClause` are concatenated directly into SQL without any validation or identifier quoting. Malicious input like `table = "users; DROP TABLE users--"` could execute arbitrary SQL.

```suggestion
function buildDeleteSQL(
  database: string,
  schema: string,
  table: string,
  whereClause?: string
): string {
  // Snowflake identifier quoting requires double quotes for SQL identifiers
  const quotedDatabase = `"${database.replace(/"/g, '""')}"`
  const quotedSchema = `"${schema.replace(/"/g, '""')}"`
  const quotedTable = `"${table.replace(/"/g, '""')}"`
  const fullTableName = `${quotedDatabase}.${quotedSchema}.${quotedTable}`
  
  let sql = `DELETE FROM ${fullTableName}`
  
  // Add WHERE clause if provided
  // Note: whereClause still needs proper parameterization - consider using Snowflake bind variables
  if (whereClause && whereClause.trim()) {
    sql += ` WHERE ${whereClause}`
  }

  return sql
}
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 14 to 45
function buildInsertSQL(
database: string,
schema: string,
table: string,
columns: string[],
values: any[][]
): string {
const fullTableName = `${database}.${schema}.${table}`
const columnList = columns.join(', ')

// Build values clause for multiple rows
const valuesClause = values
.map((rowValues) => {
const formattedValues = rowValues.map((val) => {
if (val === null || val === undefined) {
return 'NULL'
}
if (typeof val === 'string') {
// Escape single quotes by doubling them
return `'${val.replace(/'/g, "''")}'`
}
if (typeof val === 'boolean') {
return val ? 'TRUE' : 'FALSE'
}
return String(val)
})
return `(${formattedValues.join(', ')})`
})
.join(', ')

return `INSERT INTO ${fullTableName} (${columnList}) VALUES ${valuesClause}`
}
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: SQL injection vulnerability: database, schema, table, and columns array are concatenated directly into SQL without identifier quoting. Malicious input in column names like "col1, (SELECT password FROM users)--" could expose data.

Suggested change
function buildInsertSQL(
database: string,
schema: string,
table: string,
columns: string[],
values: any[][]
): string {
const fullTableName = `${database}.${schema}.${table}`
const columnList = columns.join(', ')
// Build values clause for multiple rows
const valuesClause = values
.map((rowValues) => {
const formattedValues = rowValues.map((val) => {
if (val === null || val === undefined) {
return 'NULL'
}
if (typeof val === 'string') {
// Escape single quotes by doubling them
return `'${val.replace(/'/g, "''")}'`
}
if (typeof val === 'boolean') {
return val ? 'TRUE' : 'FALSE'
}
return String(val)
})
return `(${formattedValues.join(', ')})`
})
.join(', ')
return `INSERT INTO ${fullTableName} (${columnList}) VALUES ${valuesClause}`
}
function buildInsertSQL(
database: string,
schema: string,
table: string,
columns: string[],
values: any[][]
): string {
// Quote SQL identifiers to prevent injection
const quotedDatabase = `"${database.replace(/"/g, '""')}"`
const quotedSchema = `"${schema.replace(/"/g, '""')}"`
const quotedTable = `"${table.replace(/"/g, '""')}"`
const fullTableName = `${quotedDatabase}.${quotedSchema}.${quotedTable}`
// Quote column names as SQL identifiers
const columnList = columns.map(col => `"${col.replace(/"/g, '""')}"`).join(', ')
// Build values clause for multiple rows
const valuesClause = values
.map((rowValues) => {
const formattedValues = rowValues.map((val) => {
if (val === null || val === undefined) {
return 'NULL'
}
if (typeof val === 'string') {
// Escape single quotes by doubling them
return `'${val.replace(/'/g, "''")}'`
}
if (typeof val === 'boolean') {
return val ? 'TRUE' : 'FALSE'
}
return String(val)
})
return `(${formattedValues.join(', ')})`
})
.join(', ')
return `INSERT INTO ${fullTableName} (${columnList}) VALUES ${valuesClause}`
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/tools/snowflake/insert_rows.ts
Line: 14:45

Comment:
**logic:** SQL injection vulnerability: `database`, `schema`, `table`, and `columns` array are concatenated directly into SQL without identifier quoting. Malicious input in column names like `"col1, (SELECT password FROM users)--"` could expose data.

```suggestion
function buildInsertSQL(
  database: string,
  schema: string,
  table: string,
  columns: string[],
  values: any[][]
): string {
  // Quote SQL identifiers to prevent injection
  const quotedDatabase = `"${database.replace(/"/g, '""')}"`
  const quotedSchema = `"${schema.replace(/"/g, '""')}"`
  const quotedTable = `"${table.replace(/"/g, '""')}"`
  const fullTableName = `${quotedDatabase}.${quotedSchema}.${quotedTable}`
  
  // Quote column names as SQL identifiers
  const columnList = columns.map(col => `"${col.replace(/"/g, '""')}"`).join(', ')
  
  // Build values clause for multiple rows
  const valuesClause = values
    .map((rowValues) => {
      const formattedValues = rowValues.map((val) => {
        if (val === null || val === undefined) {
          return 'NULL'
        }
        if (typeof val === 'string') {
          // Escape single quotes by doubling them
          return `'${val.replace(/'/g, "''")}'`
        }
        if (typeof val === 'boolean') {
          return val ? 'TRUE' : 'FALSE'
        }
        return String(val)
      })
      return `(${formattedValues.join(', ')})`
    })
    .join(', ')

  return `INSERT INTO ${fullTableName} (${columnList}) VALUES ${valuesClause}`
}
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 14 to 51
function buildUpdateSQL(
database: string,
schema: string,
table: string,
updates: Record<string, any>,
whereClause?: string
): string {
const fullTableName = `${database}.${schema}.${table}`

// Build SET clause
const setClause = Object.entries(updates)
.map(([column, value]) => {
let formattedValue: string

if (value === null || value === undefined) {
formattedValue = 'NULL'
} else if (typeof value === 'string') {
// Escape single quotes by doubling them
formattedValue = `'${value.replace(/'/g, "''")}'`
} else if (typeof value === 'boolean') {
formattedValue = value ? 'TRUE' : 'FALSE'
} else {
formattedValue = String(value)
}

return `${column} = ${formattedValue}`
})
.join(', ')

let sql = `UPDATE ${fullTableName} SET ${setClause}`

// Add WHERE clause if provided
if (whereClause && whereClause.trim()) {
sql += ` WHERE ${whereClause}`
}

return sql
}
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: SQL injection vulnerability: database, schema, table, and column names in updates object are concatenated directly into SQL without identifier quoting. Malicious column names could inject arbitrary SQL.

Suggested change
function buildUpdateSQL(
database: string,
schema: string,
table: string,
updates: Record<string, any>,
whereClause?: string
): string {
const fullTableName = `${database}.${schema}.${table}`
// Build SET clause
const setClause = Object.entries(updates)
.map(([column, value]) => {
let formattedValue: string
if (value === null || value === undefined) {
formattedValue = 'NULL'
} else if (typeof value === 'string') {
// Escape single quotes by doubling them
formattedValue = `'${value.replace(/'/g, "''")}'`
} else if (typeof value === 'boolean') {
formattedValue = value ? 'TRUE' : 'FALSE'
} else {
formattedValue = String(value)
}
return `${column} = ${formattedValue}`
})
.join(', ')
let sql = `UPDATE ${fullTableName} SET ${setClause}`
// Add WHERE clause if provided
if (whereClause && whereClause.trim()) {
sql += ` WHERE ${whereClause}`
}
return sql
}
function buildUpdateSQL(
database: string,
schema: string,
table: string,
updates: Record<string, any>,
whereClause?: string
): string {
// Quote SQL identifiers to prevent injection
const quotedDatabase = `"${database.replace(/"/g, '""')}"`
const quotedSchema = `"${schema.replace(/"/g, '""')}"`
const quotedTable = `"${table.replace(/"/g, '""')}"`
const fullTableName = `${quotedDatabase}.${quotedSchema}.${quotedTable}`
// Build SET clause with quoted column identifiers
const setClause = Object.entries(updates)
.map(([column, value]) => {
// Quote column name as SQL identifier
const quotedColumn = `"${column.replace(/"/g, '""')}"`
let formattedValue: string
if (value === null || value === undefined) {
formattedValue = 'NULL'
} else if (typeof value === 'string') {
// Escape single quotes by doubling them
formattedValue = `'${value.replace(/'/g, "''")}'`
} else if (typeof value === 'boolean') {
formattedValue = value ? 'TRUE' : 'FALSE'
} else {
formattedValue = String(value)
}
return `${quotedColumn} = ${formattedValue}`
})
.join(', ')
let sql = `UPDATE ${fullTableName} SET ${setClause}`
// Add WHERE clause if provided
// Note: whereClause still needs proper parameterization
if (whereClause && whereClause.trim()) {
sql += ` WHERE ${whereClause}`
}
return sql
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/tools/snowflake/update_rows.ts
Line: 14:51

Comment:
**logic:** SQL injection vulnerability: `database`, `schema`, `table`, and column names in `updates` object are concatenated directly into SQL without identifier quoting. Malicious column names could inject arbitrary SQL.

```suggestion
function buildUpdateSQL(
  database: string,
  schema: string,
  table: string,
  updates: Record<string, any>,
  whereClause?: string
): string {
  // Quote SQL identifiers to prevent injection
  const quotedDatabase = `"${database.replace(/"/g, '""')}"`
  const quotedSchema = `"${schema.replace(/"/g, '""')}"`
  const quotedTable = `"${table.replace(/"/g, '""')}"`
  const fullTableName = `${quotedDatabase}.${quotedSchema}.${quotedTable}`
  
  // Build SET clause with quoted column identifiers
  const setClause = Object.entries(updates)
    .map(([column, value]) => {
      // Quote column name as SQL identifier
      const quotedColumn = `"${column.replace(/"/g, '""')}"`
      
      let formattedValue: string
      
      if (value === null || value === undefined) {
        formattedValue = 'NULL'
      } else if (typeof value === 'string') {
        // Escape single quotes by doubling them
        formattedValue = `'${value.replace(/'/g, "''")}'`
      } else if (typeof value === 'boolean') {
        formattedValue = value ? 'TRUE' : 'FALSE'
      } else {
        formattedValue = String(value)
      }
      
      return `${quotedColumn} = ${formattedValue}`
    })
    .join(', ')

  let sql = `UPDATE ${fullTableName} SET ${setClause}`
  
  // Add WHERE clause if provided
  // Note: whereClause still needs proper parameterization
  if (whereClause && whereClause.trim()) {
    sql += ` WHERE ${whereClause}`
  }

  return sql
}
```

How can I resolve this? If you propose a fix, please make it concise.

)
}

const clientId = env.SNOWFLAKE_CLIENT_ID

Choose a reason for hiding this comment

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

isn't snowflake require clientId and client secret of the user account ?

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.

3 participants