316: MSSQL spike: candidate B guard using the ANTLR4 T-SQL grammar #338
No reviewers
Labels
No labels
bug
chore
documentation
enhancement
feature
ready
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
david/pi-extensions-and-skills!338
Loading…
Reference in a new issue
No description provided.
Delete branch "feature/issue-316/mssql-spike-candidate-b-guard-using-the-antlr4-t-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?
316: MSSQL spike: candidate B guard using the ANTLR4 T-SQL grammar
Implements 316.
Generated in 6m 29s.
Review findings
SHA-1 matches Maven's published value, so I'll pin the SHA-256.# Review Findings — 316
Summary
I fixed the Critical finding and four of the High, Medium and Low findings. Two findings remain.
NEXT VALUE FORand the lock-taking hints (XLOCK,UPDLOCK,HOLDLOCK,TABLOCKX,PAGLOCK). A header comment incandidate-b.mjsrecords that an allowlist is needed on top of the grammar, and that the guard must be paired with a read-only DB role.candidate-b.mjs:25): I chose to reject user-defined function calls and linked-server names, rather than accept them as residual risk. Both are covered by tests.CIis set.ci-coverage.mdalso records this.generate-grammar.sh): the SHA-256 is now pinned and mandatory, and the jar downloads to amktemppath. I computed the hash from the jar downloaded from Maven Central; its SHA-1 matches the SHA-1 Maven publishes.npm testwiring.;;): I documented the tolerance with an allowed-case test.Remaining findings:
SOURCE.mddate: the system clock and git history both show 11 November 2026, so the date is not in the future, and I left it. The upstream MITLICENSEfile is still unverified, and I could not do that offline.Gates:
candidate-b.test.mjspasses 42 of 42, also withCI=1.npm test: one failure, "direct invocation exits 0 and prints the Step-4 reminder". It also fails with my changes stashed, so it is unrelated.package.jsonhas no build or lint script, so I ran neither.Critical
findForbiddenConstructallows state-mutating or lock-taking constructs inside a plain SELECT. Probing confirmedSELECT NEXT VALUE FOR dbo.seqreturnsallowed: true, and that call consumes a sequence value, which is a write.WITH (XLOCK, HOLDLOCK)table hints are also allowed and can hold locks for a long time. As a "read-only guard" candidate this is a real bypass, so the spike's findings must record it. Suggested fix: reject theNEXT_VALUE_FORbuilt-in function context (TSqlParser.NEXT_VALUE_FORContext) and the lock-taking table hints (XLOCK,UPDLOCK,HOLDLOCK,TABLOCKX,PAGLOCK). Add rejection tests for each. Document in the spike results that an allowlist on top of the grammar is needed, and that the guard must be paired with a read-only DB role (least privilege).High
function_callrule are unchecked. Examples are user-defined scalar functions such asdbo.f(1), which was allowed in testing, and four-part linked-server names such assrv.db.dbo.t, which was allowed. Neither is covered by a test. Suggested fix: decide explicitly whether four-part names and unqualified UDF calls are in scope, then reject them (Full_table_nameContextwith alinkedServerorserverpart, andSCALAR_FUNCTIONContext) or document them as accepted residual risk. Add tests either way.NEXT VALUE FOR, lock hints, linked-server names, UDF calls,OPTION (...)andFOR XML. The only-allowed cases are five trivial SELECTs. Suggested fix: add allowed cases forOPTION,FOR XML PATH,OPENJSONand;;, and rejected cases for each construct the guard is meant to block. Record any known-allowed risky cases as explicit "known gap" tests.Medium
antlr4orgenerated/is missing (a clean checkout, sincegenerated/is git-ignored), all 26 tests are skipped, and the wired-innpm teststill exits 0. A CI run could therefore report green without exercising the guard. Suggested fix: fail when a CI environment variable is set, or have the spike's CI notes (ci-coverage.md) state explicitly that these tests are skipped without the generated parser.ANTLR_JAR_SHA256defaults to empty), so by default there is no integrity check on a build-time supply-chain artefact. Thecurlalso writes to a predictable path under/tmp. Suggested fix: pin the known SHA-256 in the script and make the check mandatory. Download to amktemppath.tryblock, but CPU time is not bounded. Suggested fix: add a maximum SQL length and a basic nesting-depth test, and record the parse-time measurements in the spike results.Low
isTerminalis used for the semicolon count, so the checksemicolons.length > 1is not clearly correct. Probing showedSELECT 1;;is allowed, becauseselect_statementabsorbs one;andsql_clausescan absorb another. This is harmless but inconsistent with the "exactly one statement" intent. Suggested fix: either document this tolerance with a test, or reject it.npm testin the rootpackage.jsonnow includesextensions/mssql/spike/*.test.mjs. Suggested fix: update the comment so it matches the actual state, or restrict that claim to dependencies.