Skip to content

fix(policy): resolve this.relation.field against @@allow model, unnest array columns in subqueries#2734

Open
hechang27-sprt wants to merge 2 commits into
zenstackhq:devfrom
hechang27-sprt:fix/this-authscope-relation-resolution
Open

fix(policy): resolve this.relation.field against @@allow model, unnest array columns in subqueries#2734
hechang27-sprt wants to merge 2 commits into
zenstackhq:devfrom
hechang27-sprt:fix/this-authscope-relation-resolution

Conversation

@hechang27-sprt

@hechang27-sprt hechang27-sprt commented Jun 26, 2026

Copy link
Copy Markdown

Fixes #2733.

Problem

Two bugs in the policy plugin expression transformer:

  1. this.relation.field resolves against the wrong model inside auth().collection?[...] predicates. The expression transformer uses context.thisType to resolve the field definition but passes restContext (with modelOrType still pointing to the collection model) to transformRelationAccess. This causes Field "X" not found in model "<collection>" crashes.

  2. x in this.relation.arrayField generates broken SQL on PostgreSQL. The expression compiles to = ANY((SELECT arrayCol FROM ...)) where the subquery returns array-typed rows (integer[]). PostgreSQL ANY(subquery) requires scalar rows, producing operator does not exist: integer = integer[].

Fix

Fix 1: Resolve this.relation.field against the @@allow model

Pass modelOrType: context.thisType to transformRelationAccess when the receiver is this, so relation fields resolve against the model bearing the policy rather than the collection model.

- receiver = this.transformRelationAccess(expr.members[0], firstMemberFieldDef.type, restContext);
+ receiver = this.transformRelationAccess(expr.members[0], firstMemberFieldDef.type, {
+     ...restContext,
+     modelOrType: context.thisType,
+ });

Fix 2: Unnest array columns in subqueries on PostgreSQL

When a relation-chain subquery selects an array-typed column (e.g. this.group.visibleDocIds where visibleDocIds is Int[]), wrap the column with unnest() so ANY(subquery) receives scalar rows. Guarded to PostgreSQL only — other providers store arrays as JSON and need separate handling.

- currNode = ColumnNode.create(member);
+ currNode = fieldDef.array && this.schema.provider.type === "postgresql"
+     ? FunctionNode.create("unnest", [ColumnNode.create(member)])
+     : ColumnNode.create(member);

Verification

Two new PG-specific tests added to auth-access.test.ts:

Test Without fix With fix
this.author.level in auth().permissions?[p, ...] Field "author" not found in model "Permission" passes
this.id in this.group.visibleDocIds operator does not exist: integer = integer[] passes

All existing policy tests continue to pass on both PostgreSQL and SQLite.

…t array columns in subqueries

Fix zenstackhq#1: In _member() isThis branch, pass modelOrType: context.thisType
to transformRelationAccess() so that this.relation.field correctly
resolves relation fields against the @@Allow model (thisType) rather
than the collection predicate's model (modelOrType).

Fix zenstackhq#2: In _member() when the innermost field of a subquery selection
is an array type AND the provider is PostgreSQL, wrap the column with
unnest() so that ANY(subquery) receives scalar rows instead of array
rows. Other providers (SQLite, MySQL) leave the column as-is since
they store arrays differently (JSON) and would need different handling.
…field in operator

Two PG-specific tests covering the fixes:
- Fix zenstackhq#1: this.relation.field resolves against @@Allow model in
  collection predicates (e.g. auth().permissions?[p, p.clearance >=
  this.author.level])
- Fix zenstackhq#2: this.scalarField in this.relation.arrayField generates
  unnest() for PG subqueries (e.g. this.id in this.group.visibleDocIds)
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

An error occurred during the review process. Please try again later.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/zenstackhq/zenstack/issues/comments/4808645116","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- review_stack_entry_start -->\n\n[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/zenstackhq/zenstack/pull/2734?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)\n\n<!-- review_stack_entry_end -->\n<!-- This is an auto-generated comment: review in progress by coderabbit.ai -->\n\n> [!NOTE]\n> Currently processing new changes in this PR. This may take a few minutes, please wait...\n> \n> <details>\n> <summary>⚙️ Run configuration</summary>\n> \n> **Configuration used**: Path: .coderabbit.yaml\n> \n> **Review profile**: CHILL\n> \n> **Plan**: Pro\n> \n> **Run ID**: `6db5667e-2c09-4e16-9f54-cff855f6d775`\n> \n> </details>\n> \n> <details>\n> <summary>📥 Commits</summary>\n> \n> Reviewing files that changed from the base of the PR and between 8a317258b5e47ceabcf4a424b4453d77628f35e3 and 19658414e4c37a31a8affe6f015a7c668af5057a.\n> \n> </details>\n> \n> <details>\n> <summary>📒 Files selected for processing (2)</summary>\n> \n> * `packages/plugins/policy/src/expression-transformer.ts`\n> * `tests/e2e/orm/policy/auth-access.test.ts`\n> \n> </details>\n> \n> ```ascii\n>  ______________________________________________________________________________________________________________________________________________________________________________________________________\n> < Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. - Brian Kernighan >\n>  ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n>   \\\n>    \\   (\\__/)\n>        (•ㅅ•)\n>        /   づ\n> ```\n\n<!-- end of auto-generated comment: review in progress by coderabbit.ai -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing Touches</summary>\n\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=zenstackhq/zenstack&utm_content=2734)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n\n<sub>Comment `@coderabbitai help` to get the list of available commands.</sub>\n\n<!-- tips_end -->"},"request":{"retryCount":3,"signal":{},"retries":3,"retryAfter":16}}}

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: this.relationField resolves against collection model inside auth().collection?[...] predicates

1 participant