You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 02d2a03
Browse filesBrowse the repository at this point in the historyBrowse files
fix(plugin-security): a row-level `check` refuses an operator the read refuses on a field declared JSON-stored, with the read's `INVALID_FILTER` / 400, so a policy whose read is refused no longer admits writes (#21254)
6
+
7
+
Clause-②: no
8
+
9
+
The read a row-level policy scopes refuses a scalar comparison, an ordering or a text operator (`@objectstack/core`'s `JSON_COLUMN_INCOMPATIBLE_OPERATORS`, and implicit equality) on a field the object declares JSON-stored: a structured-JSON type (`json`, `address`, …), or a multi-valued field (`tags`, `multiselect`, `checkboxes`, or a `select` / `lookup` / `user` / `file` / `image` flagged `multiple: true`). The write `check` evaluated the same operators against the stored list instead. Measured through `ObjectQL.insert` with `SecurityPlugin` on two SQLite driver families, as a member resolving a permission set, with the same predicate as `using` and `check`:
10
+
11
+
|`check`| written | write, before | read |
12
+
|---|---|---|---|
13
+
|`record.tags != 'x'`|`['x']` or `'x'`| admitted, stored `["x"]`| 400 |
14
+
|`!(record.tags in ['x'])`|`['x']`| admitted, stored `["x"]`| 400 |
|`record.meta != 'x'` / `record.meta == 'x'` (`meta` is `json`) | a scalar | admitted, stored | 400 |
18
+
19
+
Now the write check refuses every one of these with the read's answer: `INVALID_FILTER` / 400 and the read's words, which withhold the field and the operator. The refusal reads the object's declaration, never the record, so a policy is refused for every row or for none, on the insert, a by-id update and a predicate update. The diagnostic, which names the field, the operator and the policy, goes to the server log. Rows that already refused still store nothing; their answer is now the read's.
20
+
21
+
Unchanged: `contains` and its negation (`$contains` / `$notContains`), and the presence checks (`== null`, `!= null`), answer on such a field as before; a field declared neither way keeps every operator; an object whose schema cannot be loaded is judged as before. To repair a refused policy, test membership with `contains` (for example `!record.tags.contains('x')`).
fix(objectql): a driver error that leaves the engine no longer carries the failing statement or the caller's values
6
+
7
+
Clause-②: no
8
+
9
+
The engine has cut the bound statement out of its own log line for a failed driver call for a long time, but it rethrew the driver's raw error. Any in-process code that logged what it caught, such as an auth library's error logger, printed the statement and the row's values. The same cut now runs where the error leaves the engine, so no consumer needs a patch of its own.
10
+
11
+
-**Where.** Every engine operation that reaches a driver: `find`, `findOne`, `count`, `aggregate`, `insert` (batch included), `update` and `delete` (by id and by predicate), `execute`, `transaction`, `resolveSecretField` and `resolveInternalField`.
12
+
-**What is cut.** The statement and the caller's values, from the error's `message` and `stack`, from the properties drivers attach (mysql2's `sql` and `sqlMessage`; node-postgres' `detail`, `where` and `internalQuery`), and down the `cause` chain. A `DuplicateRecordError` keeps its own fields and carries a cut `cause`.
13
+
-**What stays.** The error's class (`instanceof` still holds), `name`, `code`, `errno`, `sqlState`, Postgres' identifier fields (`constraint`, `table`, `column`, …) and the database's own diagnostic. The message now reads as the statement's kind, a `[statement and bound values redacted]` marker and the diagnostic. A Postgres key-shaped `detail` keeps its column list. Every REST answer keeps its status, code and `field`.
14
+
-**What changes for a caller.** Code that read the statement or a value out of a driver error's message or properties now gets the marker instead. Branch on the class, `code` or `errno` instead. The driver error on a `DuplicateRecordError`'s `cause` is an equivalent copy, no longer the object the driver threw. An import's row report for a value-bearing database error no longer repeats the rejected value.
0 commit comments