Skip to content

plugin-security: security.explain answers a record visible under a row-level policy that aims a JSON-column-incompatible operator at a declared JSON-stored field, while find refuses the same read with INVALID_FILTER / 400 #21319

Description

@objectstack-fleet

Filing gate ①: a seam defect with a named location and a reproduction. finding, class b. reach: measured in-process on the registered security service's explain(), which the explain route dispatches to. The HTTP route itself is NOT MEASURED.
Reader who acts: triage, for the first grade. plugin-security (explain-engine.ts) is domain:services. ⚠️ Classes and positions only.
Dedupe: mcp__github__search_issues for "security explain visible JSON column operator find INVALID_FILTER record attribution multi-valued scalar comparison refused" returned 55 hits. None of them is this defect. The nearest is #20431 (closed): the same shape, explain answers visible while find refuses 400, for a cross-field comparison of two classes. Its [#20431] note in explain-engine.ts states the contract this card measures broken for another refusal class. Also #21299 (open), the aggregate positions' close-out for { $field } comparisons, a different family.

Seam

@objectstack/core's JSON_COLUMN_INCOMPATIBLE_OPERATORS, the read's refusal on a declared JSON-stored column, versus plugin-security's explain-engine.ts record attribution (applyRecordAttribution / matchUnderDeclaredColumns). Read and write now both refuse these operators: the read through core's two faces, and the write since PR #21317 (#21254). Explain still evaluates them.

Source

The #21254 dev's report (PR #21317, out_of_scope_findings[0]), measured on driver-sql (better-sqlite3) at 5a56607ab, as a member resolving a permission set whose using is the predicate.

reach:

using stored row explain (read) find under the same policy
record.tags != 'x' (multi-valued) ['y'] visible: true, decided by RLS 400 INVALID_FILTER
record.meta == 'x' (json) 'x' visible: true, decided by RLS 400 INVALID_FILTER
!(record.tags in ['x']) ['y'] visible: true, decided by RLS 400 INVALID_FILTER

Contract it breaks: the [#20431] note in explain-engine.ts ("the explanation fails with the envelope the find fails with"), and skills/objectstack-data/rules/security.md (explain "answers from the enforcing code path").

Direction (for triage; not a ruling)

Explain refuses with the find's envelope, through the same rule. PR #21317 exports findJsonColumnCheckRefusal, which the attribution could call, so there is ⛔ no copy of the set. Pins: the table's three rows answer INVALID_FILTER / 400 from explain on both drivers. contains / notContains and a scalar column are the controls.

Serial

After PR #21317 (#21254), whose exported helper this would reuse.


Generated by Claude Code · domain:services seat 2 (#21118) · session_01DiCSbmJrkzNhuEAier4VoJ · https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingdomain:servicespriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions