Skip to content

fix(plugin-snowflake): give every statement builder one SQL escaper - #2467

Merged
datlechin merged 1 commit into
fix/snowflake-temporal-decodingfrom
fix/snowflake-sql-escaping
Aug 26, 2026
Merged

fix(plugin-snowflake): give every statement builder one SQL escaper#2467
datlechin merged 1 commit into
fix/snowflake-temporal-decodingfrom
fix/snowflake-sql-escaping

Conversation

@datlechin

Copy link
Copy Markdown
Member

Stacked on #2466. Base is fix/snowflake-temporal-decoding, a pointer at that PR's commit, because #2466's head lives on a fork and a base branch has to live in this repo. Retarget to main once #2466 merges.

Found while investigating #2454.

The defect

SnowflakeObjectQueries.escapeLiteral doubled the quote and left the backslash alone:

value.replacingOccurrences(of: "'", with: "''")

Snowflake reads \' inside a literal as a content quote, and the driver says so itself: requiresBackslashEscapingInLiterals is true. So a schema named

x\' OR 1=1 --

escaped to x\'' OR 1=1 --. The \' is content, the next ' closes the literal, OR 1=1 becomes live SQL and -- comments the tail. The routine listing then returns every schema's routines. routineDefinition feeds the same escaper into GET_DDL.

Schema and routine names arrive from fetchSchemas unsanitized, so anyone able to create an object in the account controls the string.

Why one escaper instead of one fix

Three copies of the literal escaper existed and only one was wrong. Five copies of the identifier quoter existed, all byte identical. Nothing made any of them agree, which is how the wrong one survived.

Writing a fourth correct copy leaves that intact, so SnowflakeSQL is now the single owner of escapeLiteral, quoteIdentifier and escapeLikePattern, and every statement builder calls it:

File Was
SnowflakeObjectQueries quote only, the defect
SnowflakeSchemaQueries own copy of all three
SnowflakeDDLGenerator own literal escaper and quoter
SnowflakeStatementGenerator own quoter
SnowflakeConnection own quoter
SnowflakePluginDriver own quoter and escapeStringLiteral

There is now no hand-rolled quote or backslash doubling anywhere else in the plugin.

SnowflakeObjectQueries moved out of SnowflakePluginDriver+Routines.swift into its own file. It is a pure SQL builder that was sharing a file with a driver extension, which is what kept it out of the test target and therefore untested.

Order matters

The backslash is doubled before the quotes. Doubling it afterwards would also double the backslashes in the quotes that step just added.

Tests

SnowflakeSQLTests, 11 cases: quote doubling, backslash doubling, escape order, the injection payload above (asserted against what a quote-only escaper produces, so the test states the difference rather than restating the implementation), identifier quoting and an identifier that tries to close its own quoting, LIKE wildcard escaping, and delegation checks pinning that the per-file wrappers stayed wrappers.

Verified: build PASS, 56/56 across the escaping suite and every statement-builder suite, AllPlugins compiles SnowflakeDriverPlugin clean, SwiftLint 0 violations.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@datlechin
datlechin merged commit 75b767e into fix/snowflake-temporal-decoding Aug 26, 2026
@datlechin
datlechin deleted the fix/snowflake-sql-escaping branch August 26, 2026 16:09
datlechin added a commit that referenced this pull request Aug 26, 2026
…dpoint sends (#2466)

* fix(plugin-snowflake): decode the wire encodings the query-request endpoint sends

Claude-Session: https://claude.ai/code/session_01Qk1xfY3vnneRifC22eV2r7

* fix(plugin-snowflake): give every statement builder one SQL escaper (#2467)

Claude-Session: https://claude.ai/code/session_01Qk1xfY3vnneRifC22eV2r7

* fix(plugin-snowflake): keep a timestamp offset the date parser cannot represent as text (#2468)

Claude-Session: https://claude.ai/code/session_01Qk1xfY3vnneRifC22eV2r7

* fix(plugin-snowflake): take the affected row count from the statement type, not a column name (#2469)

Claude-Session: https://claude.ai/code/session_01Qk1xfY3vnneRifC22eV2r7

* fix(plugin-snowflake): key the shared session on the saved connection and scope Stop to its own driver (#2470)

Claude-Session: https://claude.ai/code/session_01Qk1xfY3vnneRifC22eV2r7

---------

Co-authored-by: Ngo Quoc Dat <datlechin@gmail.com>
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