sql-guard-rowcap

SQL guard: row cap applied before the backend

shipped

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-guard opt-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

  • control the 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 core
  • huge-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.

ThresholdDefaultSet withStandard
row_cap10000checks.sql-guard-rowcap.row_capnot locked
KeyDefaultStandardMeaning
checks.sql-guard-rowcap.row_cap10000not lockedLargest LIMIT (or FETCH FIRST / TOP) the statement the server forwards may carry before sql-guard-rowcap fails. A candidate setting: unlocked.
upstream.kindnonenot lockedKind 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.