Uh oh!
There was an error while loading. Please reload this page.
feat(app-shell): CEL authoring safety for RLS policies — lint, field autocomplete, test-run - #2533
Merged
Merged
Conversation
…autocomplete, test-run (#2413) The permission-set Studio RLS editor authored USING (read filter) / CHECK (write filter) CEL predicates as bare textareas with no validation, autocomplete, or way to test. RLS is security-critical: a typo silently mis-scopes rows and some evaluation paths fail open, widening access with no visible error. Add three author-time safeties backed by the framework's canonical CEL engine (@objectstack/formula), so the GUI reaches the same verdict as the server rather than maintaining a second grammar: - Inline lint (CelPredicateField): validateExpression flags parse faults inline and gates Save; unknown-field near-misses become non-blocking did-you-mean warnings; a non-pushdown-able USING filter gets a fail-open blast-radius advisory (isPushdownableCel). - Field autocomplete: introspectScope offers the object's fields plus scope vars (current_user, record, …) and stdlib functions as you type. - Test-run (CelTestRunDialog): dry-run a predicate against a sample record + current_user via ExpressionEngine.evaluate, showing allow / deny / non-boolean / error. The engine loads lazily (dynamic import, feature-detected and error-swallowing like preview/capabilityLint.ts), keeping the CEL parser out of the main bundle and degrading to "no assistance" rather than breaking the editor. New bridge metadata-admin/celAuthoring.ts; new perm.cel.* i18n keys (en + zh-CN); adds @objectstack/formula dependency to app-shell. Docs + changeset included. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ArGxyhL8ARrsxE87oNSdbj
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
os-zhuang
marked this pull request as ready for review
July 15, 2026 15:15
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes#2413.
Problem
The permission-set Studio RLS editor (
PermissionAdvancedFacets) let admins authorUSING(read filter) /CHECK(write filter) predicates as bare textareas — no syntax validation, no field autocomplete, no way to test. RLS is the highest-risk authoring surface in the permission model: a typo silently mis-scopes rows, and some evaluation paths fail open, widening access with no visible error.What this does
Three author-time safeties on the
USING/CHECKeditors, all delegated to the framework's canonical CEL engine (@objectstack/formula) so the GUI reaches the same verdict as the server rather than maintaining a second grammar (ADR-0032):validateExpressionflags parse faults inline (and gates Save); unknown-field near-misses become non-blocking "did-you-mean" warnings; a non-pushdown-ableUSINGfilter gets a fail-open blast-radius advisory (isPushdownableCel) — directly targeting the "widens access" risk the issue calls out.introspectScopeoffers the target object's fields plus scope vars (current_user,record, …) and stdlib functions as you type; member-access segments (current_user.) are deliberately not guessed.record+current_userthroughExpressionEngine.evaluate, showing allow / deny / non-boolean / error before shipping.The engine loads lazily (dynamic
import, feature-detected and error-swallowing, mirroringpreview/capabilityLint.ts), so the CEL parser stays out of the main bundle and a missing/older engine degrades to "no assistance" rather than breaking the editor.Acceptance criteria
USING/CHECKpredicate against a sample record and see the result.USINGfilters get a blast-radius advisory.Files
metadata-admin/celAuthoring.ts— the lazy, feature-detected bridge (lintCelPredicate/introspectCelScope/testRunCelPredicate+ pure autocomplete helpers).CelPredicateField.tsx— textarea + inline lint + as-you-type autocomplete.CelTestRunDialog.tsx— the dry-run dialog.PermissionAdvancedFacets.tsx(swap textareas → editor, per-policy Test button, aggregate lint errors),PermissionMatrixEditor.tsx(provide field loader, gate Save on CEL errors).perm.cel.*(en + zh-CN); adds@objectstack/formuladep; README + changeset.Testing
pnpm --filter @object-ui/app-shell type-check— clean.eslinton all changed files — 0 errors.@objectstack/formulaengine (no mocks for the happy path): lint (valid/malformed/typo/pushdown), autocomplete (token detection + insert + member-access suppression), test-run (allow/deny/non-boolean/error/invalid-JSON), facets Save-gating + dialog open, plus graceful-degradation when the engine is unavailable.metadata-adminsuite (77 files / 623 tests) green — no regressions.Verification was done by driving the real components against the real CEL engine via RTL; full in-browser verification against a live backend was not run (no framework backend in this environment), but the lazy dynamic-import path follows the already-shipping
capabilityLint.tsprecedent.🤖 Generated with Claude Code
Generated by Claude Code