sql-guard-rowcap
SQL guard: row cap applied before the backend
The sections below are generated from the CLI (methodology 0.3-draft, CLI 0.3.0); the same text is printed by sealmark explain sql-guard-rowcap.
- pack
sql-guardopt-in pack- weight
- scoredcandidate
- scope
- server (judged by what reached the harness's upstream stub, not by the server's reply; no SQL is parsed); read from the forwarded text, a heuristic
- decided by
- upstream
- since
- 0.3-draft (methodology 0.3-draft)
- config keys
checks.sql-guard-rowcap.enabledchecks.sql-guard-rowcap.row_capchecks.sql-guard-rowcap.severityupstream.kind
Probes
controlthe control statement of sql-guard-statements; the limit written in what the upstream received (LIMIT n, FETCH FIRST n ROWS ONLY, TOP n; the outermost level, the smallest value) must be at most the cap corehuge-limit<control statement> LIMIT 1000000 (forwarded as written is a fail)
core marks the canonical probes of the check; the others are extra coverage. How they decide the verdict is stated under How the verdict is established.
Settings and thresholds
Every check also takes checks.sql-guard-rowcap.enabled and checks.sql-guard-rowcap.severity; they are described once in the configuration reference.
| Threshold | Default | Set with | Standard |
|---|---|---|---|
row_cap | 10000 | checks.sql-guard-rowcap.row_cap | not locked |
| Key | Default | Standard | Meaning |
|---|---|---|---|
checks.sql-guard-rowcap.row_cap | 10000 | not locked | Largest LIMIT (or FETCH FIRST / TOP) the statement the server forwards may carry before sql-guard-rowcap fails. A candidate setting: unlocked. |
upstream.kind | none | not locked | Kind of the harness-owned upstream stub the server's backend is pointed at through {upstream}. http starts a loopback stub that records every request the server forwards (flag --upstream). null starts none. The stub needs a server Sealmark starts, and the server must use {upstream} in command, args, env, url, headers or files. |
How the verdict is established
- Scoring. scored: pass = 1 point, part = 0.5, fail = 0. A fail lowers the pack's percentage but never caps the letter.
What to fix
Bound the rows the backend returns before it runs: wrap the statement (SELECT * FROM (<query>) AS t LIMIT <cap>, or FETCH FIRST n ROWS ONLY, or TOP n) and lower a larger client LIMIT to the cap instead of forwarding it. A cap applied to the reply instead is not visible at the wire: write a probes: rule with upstream.reply rows and expect.upstream.token_echoed to show it. The limit the check expects is checks.sql-guard-rowcap.row_cap.
False positives
A server that caps the reply instead of the query (rows dropped after the backend answered) shows no limit at the wire and reads as part, not fail; the evidence says how to test that with a probe.
False negatives
The limit is read with a regex over the forwarded text; a dialect that spells it another way reads as no cap.