Skip to content

Bind values as query parameters; validate identifiers - #4

Merged
thorwhalen merged 2 commits into
masterfrom
parameterize-queries
Sep 22, 2026
Merged

thorwhalen merged 2 commits into
masterfrom
parameterize-queries

Conversation

@thorwhalen

@thorwhalen thorwhalen commented Sep 22, 2026 •

Copy link
Copy Markdown
Member

Closes #3

  • Values (keys on read/delete, dict values on write) are bound with ? placeholders.
  • Table, primary-key and column names are delimited as [name] with ] doubled, so any name is exactly one identifier; table names may be schema-qualified (dbo.person → [dbo].[person]); already-bracketed names are accepted. Empty/over-long/control-character names raise ValueError.
  • Every connection-string value is brace-quoted (} doubled), so no value (e.g. a password) can add connection attributes.
  • Behaviour notes: None values now bind as NULL (previously the string 'None'); list/dict/tuple values and keys still bind as their str().

Behaviour for normal keys/values is unchanged (SQL Server converts bound parameter types as it did the literals). No fleet dependents.

Independent refute-review done; its findings (brace-wrapped values passed through unquoted, schema-qualified table names refused, pyodbc unpacking a lone tuple parameter) are fixed in the second commit.

Tests: odbcdol/tests/test_injection.py uses a stand-in pyodbc that records what would be executed, so it runs without a database; 24 tests, all fail on master.

🤖 Generated with Claude Code

thorwhalen and others added 2 commits September 22, 2026 15:11
Keys and values were formatted into SQL text (the delete did not even
quote the key), and connection-string values were not brace-quoted.
Values are now bound with ?, table/column names are validated and
bracket-quoted, and connection-string values that could start a new
attribute are brace-quoted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Every connection-string value is brace-quoted (a value already wrapped in
  braces could still inject attributes); DRIVER is passed bare.
- Identifiers are delimited ([...] with ] doubled) instead of pattern-
  checked, so any name is one identifier; table names may be schema-
  qualified (dbo.person) and pre-bracketed names are accepted.
- Parameters are passed as one tuple; container values/keys bind as their
  str() as before, never as pyodbc's parameter list.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@thorwhalen
thorwhalen merged commit dd355e1 into master Sep 22, 2026
12 checks passed
@thorwhalen
thorwhalen deleted the parameterize-queries branch September 22, 2026 15:16
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.

Build queries with bound parameters

1 participant