Commit 705ea6f
fix(table): reject hybrid predicate nodes at the wire instead of silently narrowing
Found by running the HTTP suite: a node carrying BOTH a group key and a leaf's
`field`/`op`/`value` returned 200, not 400.
Zod strips unrecognized keys by default, so `{ all: [...], field, op, value }`
parsed clean against the group branch with the leaf half quietly deleted. The
hybrid guard added in eaf4179 could never fire — the keys were gone before
`validatePredicate` ran. On the bulk paths that turns "delete archived rows for
tenant acme" into "delete EVERY row for tenant acme".
Both node shapes are now `strictObject`. Strict on the group alone would be
worse than the bug: the union would fall through to the leaf branch, which is
the more dangerous reading of the two.
The bulk schemas are unaffected by design — their legacy `$`-object branch
accepts any non-empty object, so it absorbs the hybrid WITHOUT stripping, the
route's `isTablePredicate` check routes it back to `validatePredicate`, and the
runtime guard rejects it there. Tests now pin both layers so removing either
one fails loudly.
Verified against a running server on the `hello` table: 18/18 HTTP checks pass,
including the previously-failing hybrid case.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011M563cbFy2S74GvSDf2C3R1 parent ead6391 commit 705ea6f
2 files changed
Lines changed: 87 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| 15 | + | |
15 | 16 | | |
16 | 17 | | |
17 | 18 | | |
| |||
149 | 150 | | |
150 | 151 | | |
151 | 152 | | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
319 | 319 | | |
320 | 320 | | |
321 | 321 | | |
322 | | - | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
323 | 333 | | |
324 | 334 | | |
325 | 335 | | |
| |||
333 | 343 | | |
334 | 344 | | |
335 | 345 | | |
336 | | - | |
| 346 | + | |
337 | 347 | | |
338 | 348 | | |
339 | 349 | | |
340 | 350 | | |
341 | 351 | | |
342 | | - | |
| 352 | + | |
343 | 353 | | |
344 | 354 | | |
345 | 355 | | |
| |||
0 commit comments