Skip to content

fix(metadata): sanitize interpolated identifiers (SQL injection) + tests — rebased #2#12

Merged
ephpm-claude[bot] merged 14 commits into
mainfrom
pr2-rebased-on-main
Jul 16, 2026
Merged

fix(metadata): sanitize interpolated identifiers (SQL injection) + tests — rebased #2#12
ephpm-claude[bot] merged 14 commits into
mainfrom
pr2-rebased-on-main

Conversation

@ephpm-claude

@ephpm-claude ephpm-claude Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Rebase of #2 onto current main (after #11), conflicts resolved and tests adapted to #11's async handler API.

Why #2 stalled: it predated this repo's CI (ci.yml landed 2026-07-10, ~67d after #2's commit) so it never got a signal, then went stale/conflicted and its tests stopped compiling against #11.

Security substance (still valid, now more relevant): metadata.rs::to_sqlite_sql() format!-interpolates table/schema identifiers into SQLite/PRAGMA SQL. #11 added new interpolation sites (TableExists name='{table}', SHOW FULL COLUMNS pragma_table_info) fed by the same extractors #2 wraps in sanitize_identifier() — so this fix covers #11's new surface.

Closes #2. Design note for review: sanitizer silently strips injected chars (safe: mangled name -> empty result) rather than rejecting — worth a conscious call.

🤖 Generated with Claude Code

luthermonson and others added 14 commits May 3, 2026 14:12
Add 51 tests across all three source files covering PacketType::from_u8,
sqlite_to_tds_type, value_to_tds_type, build_columns, decode_utf16le,
TdsSession transaction state management, and skip_all_headers.
Add sanitize_identifier() that strips non-alphanumeric/underscore/period
characters from table and schema names before they are interpolated into
generated SQL. Applied at extraction time in extract_table_name(),
extract_where_value_original(), and sp_columns parsing so all
MetadataQuery variants are protected.
Cover edge case values (integer/float extremes, empty text/blob, large
payloads, Unicode/emoji), error paths (nonexistent table, wrong param
count, SQL syntax errors), mixed-type rows, sequential query-execute
patterns, and hrana client conversion fallbacks (non-numeric integer
parse, invalid base64, overflow) plus URL trailing-slash handling.
Adds 70+ roundtrip tests via translate() covering complex SELECTs
(GROUP BY, HAVING, DISTINCT, LIMIT/OFFSET, aggregates, CASE WHEN,
subqueries), JOINs (INNER, LEFT, RIGHT, CROSS, self-join, multi-join),
set operations (UNION, INTERSECT, EXCEPT), CTEs (simple, multiple,
recursive), complex DML (INSERT...SELECT, multi-row INSERT, UPDATE/DELETE
with subqueries), DDL (CREATE/DROP TABLE IF [NOT] EXISTS, CREATE INDEX,
CREATE UNIQUE INDEX), and transaction statements (BEGIN, COMMIT,
ROLLBACK, SAVEPOINT, RELEASE, ROLLBACK TO). Tests cover MySQL,
PostgreSQL, and TDS dialects.
Tests builder construction, chaining, invalid address handling,
the no-frontends error path, and serve() smoke tests for each frontend.
Add comprehensive tests for the handler module covering ok_response,
translate_sql, transaction state logic, param_to_value conversion
dispatch, and handler initialization. Add edge case tests for types.rs
covering empty strings, whitespace, additional SQL type variants, and
case sensitivity for BYTEA.
Tests cover sqlite_to_pg_type type mapping (21 tests), value_to_pg_type
inference (5 tests), encode_value serialization (11 tests),
extract_params portal parameter extraction (15 tests), and pg_error
construction (2 tests). Added bytes as a dev-dependency for constructing
binary parameter fixtures.
…n after #11

Brings the stale `add-comprehensive-tests` branch (PR #2, open since
2026-05-04) up to date with current main (699e634, the #11 framework
wire-compat merge). PR #2 had diverged (ahead 13 / behind 11) and was
`mergeable_state: dirty`.

Changes to make it apply cleanly on top of #11:

- metadata.rs: keep the PR's `sanitize_identifier()` fix (strips anything
  that isn't alphanumeric / `_` / `.` from extracted table & schema
  identifiers before they are `format!`-interpolated into generated SQL).
  This now also guards the new #11 interpolation sites — the Laravel
  `Schema::hasTable` existence probe (`name='{table}'`) and the WordPress
  `SHOW FULL COLUMNS` path (`pragma_table_info('{table}')`).
- metadata.rs: add injection tests for every `{table}`/`{schema}`
  interpolation site introduced/kept by #11 (TableExists, ShowColumns,
  ShowCreateTable, ShowIndex, InformationSchemaColumns, PgCatalogColumns).
- litewire-mysql/handler.rs: adapt the PR's handler tests to #11's new
  `LiteWireHandler::new(backend, cache).await` (async, 2-arg) signature and
  the `backend` -> `conn` field rename. `memory_handler()` now block_on's
  the async constructor; a `memory_handler_async()` helper serves the
  `#[tokio::test]` cases.
- rusqlite_backend.rs: preserve main's per-connection isolation tests (#6)
  and append the PR's new backend edge-case tests.
- Replace `3.14` test literals with `2.5` to satisfy clippy::approx_constant.

Verified: cargo fmt --check, cargo test --workspace (635 pass, 0 fail),
cargo clippy --workspace --all-targets -D warnings all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ChVRC9ZrvQarrctDwSy1S5
@ephpm-claude
ephpm-claude Bot merged commit 7a0b59c into main Jul 16, 2026
3 checks passed
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