Sql driver: implement the §8.1 catalog identifier-validation gate (FF-1) #496
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The Sql driver's ISqlDialect/SqlServerDialect docs describe a design-§8.1 gate that validates an authored table/column against the live catalog before it is ever quoted into SQL text, rejecting unknown identifiers. That gate is NOT implemented and no task on feat/sql-poll-driver scheduled it; QuoteIdentifier (bracket-quoting, Cc+Cf rejection) is currently the sole defence. Risk is bounded — a hostile identifier is quoted into one nonexistent object, the query fails post-open, and the tag Bad-codes (BadCommunicationError) rather than executing anything — but the defence-in-depth and doc-honesty gap should be closed. Also fixed the docs on-branch to stop over-claiming. Reference: docs/plans/2026-07-15-sql-poll-driver-design.md §8.1.
Implemented on
fix/sql-driver-followups(4dc2ad80).SqlCatalogGate+SqlCatalogLoaderresolve every authored identifier against the live catalog at Initialize and replace it with the catalog’s own spelling, so the identifier text in an emitted poll query is a string the driver read back out ofListSchemas/ListTables/ListColumns— not operator input. Quoting becomes the backstop it was documented to be. The over-claiming docs onISqlDialect/SqlServerDialectare corrected.Decisions worth knowing:
BadNodeIdUnknown— §8.1’s stated outcome. My first wiring dropped it from both, deleting the node instead; an existing test caught it. A status code can only be published by a node that exists.ISqlDialectgainsDefaultSchemaSql(SELECT SCHEMA_NAME()). An unqualifiedTagValuesmust resolve in whatever schema the server reports; hardcodingdbowould be a silent lie on estates that map service accounts to their own default schema.ListTablesper distinct authored schema and oneListColumnsper distinct authored relation — never a full enumeration, nothing after Initialize, every authored name bound as a parameter.db.schema.tablecannot be validated from this connection’s catalog, so it is rejected with a message pointing at the fix (expose the data through a view in the connected database).Tests: 24 pure gate tests, 9 end-to-end driver tests against the real SQLite catalog, and 3 new live tests against the real SQL Server on
10.100.0.35— that suite now runs 21 green, exercising the realSELECT SCHEMA_NAME()+INFORMATION_SCHEMApath. Verified falsifiable: bypassing the gate turns exactly the three rejection assertions red and leaves the rest green.SqlInjectionRegressionTestswas deliberately not rewritten to expectBadNodeIdUnknown. It drivesSqlPollReaderdirectly, below the gate, and pins the quoting backstop on its own — defence in depth is only worth the name if each layer holds independently. Its scope note (which said the gate does not exist) now explains why it stays put.