Skip to content

Conversation

@alexolivier
Copy link
Contributor

@alexolivier alexolivier commented Feb 2, 2026

Summary

  • New @cerbos/orm-convex adapter that converts Cerbos PlanResources responses into Convex-compatible filter functions for ctx.db.query("table").filter(filterFn)
  • Supports comparison (eq, ne, lt, le, gt, ge), logical (and, or, not), collection (in via composed OR), isSet, bare boolean operands, and nested field access
  • Throws for unsupported operators (contains, startsWith, endsWith, collection ops)

Test plan

  • 27 unit tests using mock filter builder with Cerbos sidecar for real plan generation
  • 8 integration tests against self-hosted Convex backend via Docker Compose
  • CI workflow with unit tests (Node 20/22/24) and integration tests (self-hosted Convex + Cerbos)

Closes #105

Converts Cerbos PlanResources responses into Convex-compatible filter
functions for use with ctx.db.query("table").filter(filterFn).

Supports eq, ne, lt, le, gt, ge, and, or, not, in (composed as OR of
eq), isSet, bare boolean operands, and nested field access via dot
notation. Throws for unsupported operators (contains, startsWith, etc).

Includes 27 unit tests with mock filter builder and 8 integration tests
against a self-hosted Convex backend via Docker Compose.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

Signed-off-by: Alex Olivier <[email protected]>
The integration test imports Convex generated types that only exist after
codegen runs. Exclude it from tsconfig so the prepare build succeeds,
and add an explicit codegen step in the integration test CI job.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

Signed-off-by: Alex Olivier <[email protected]>
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e25432f8cc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Convex treats null and undefined as distinct: null is a stored value,
undefined means a missing field. The adapter was converting all nulls
from Cerbos plans to undefined, which broke null comparisons.

Now null passes through as-is. The isSet operator still correctly
compares against undefined for field-existence checks.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

Signed-off-by: Alex Olivier <[email protected]>
Instead of throwing for string/collection operators (contains, startsWith,
endsWith, hasIntersection, exists, all, etc.), return a JS postFilter
predicate alongside the DB filter. AND expressions are split: pushable
children go to the DB filter, unsupported children to postFilter. OR
expressions with any unsupported child go entirely to postFilter.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

Signed-off-by: Alex Olivier <[email protected]>
Co-Authored-By: Claude Opus 4.5 <[email protected]>

Signed-off-by: Alex Olivier <[email protected]>
postFilter bypasses the DB-level authorization boundary since data is
fetched before filtering is applied. Default to throwing with a
descriptive error; callers must set { allowPostFilter: true } to opt in.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

Signed-off-by: Alex Olivier <[email protected]>
Co-Authored-By: Claude Opus 4.5 <[email protected]>

Signed-off-by: Alex Olivier <[email protected]>
@alexolivier
Copy link
Contributor Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2e5ed1e339

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 95 to 99
const canPushToDb = (expression: PlanExpressionOperand): boolean => {
if (isValue(expression) || isVariable(expression)) return true;
if (!isExpression(expression)) return false;
if (!DB_PUSHABLE_OPERATORS.has(expression.operator)) return false;
return expression.operands.every(canPushToDb);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject non-literal operands in canPushToDb

When a policy compares two variables (e.g. request.resource.attr.owner == request.principal.id) or uses in with a variable array (e.g. request.resource.attr.group in request.principal.attr.groups), canPushToDb currently returns true because it only checks operator membership and recursively accepts any variables/values. That causes buildFilters to treat the expression as DB-pushable and call translateExpression, which then throws (requires one field and one value operand) instead of falling back to a postFilter (even when allowPostFilter is true). This makes common policy patterns fail at runtime. Consider tightening canPushToDb to validate operand shapes for eq/ne/lt/... and in (literal value/array vs variable) so non-pushable comparisons become post-filters instead of hard errors.

Useful? React with 👍 / 👎.

canPushToDb now validates that comparison operators (eq, ne, lt, le, gt,
ge, in, isSet) have at least one variable and one literal value operand.
Previously, expressions like `R.attr.owner == R.attr.manager` were
incorrectly marked as DB-pushable, causing translateExpression to throw
instead of falling back to postFilter.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

Signed-off-by: Alex Olivier <[email protected]>
@alexolivier alexolivier merged commit 97b81fd into main Feb 2, 2026
5 checks passed
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.

[feature] Convex Query adapter

1 participant