Uh oh!
There was an error while loading. Please reload this page.
docs(permissions): say that an empty RLS policy set is unfiltered, not deny-all - #8029
Merged
Merged
Conversation
…t deny-all (#7811) The layer-5 row of the evaluation-order table read "If **no** applicable policy compiles, the result is a deny-all sentinel (fail-closed)", which a reader can take as "an object with no RLS policies is invisible". The implementation's contract is narrower: `RLSCompiler.compileFilter` returns `null` (no filter) when no policy is applicable to the clause, and only returns the `RLS_DENY_FILTER` sentinel when applicable policies exist and every one of them fails to compile. The imprecision is not academic — it misled a reviewer into treating the sentence as evidence that a write path was already fail-closed. - `authorization.mdx`: state the narrow condition and name the empty case explicitly, so the wrong reading is closed rather than merely unstated. - `rls.mdx`: the fail-closed contract section and its callout were already exact; the summary bullet at the top of the page was not, and it is the first statement of the rule a reader meets. Docs-only; no behaviour change.
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
hotlong
marked this pull request as ready for review
August 12, 2026 13:29
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.
Fixes#7811
Docs-only. No code changed — the code is right and the prose was wrong.
The defect
content/docs/permissions/authorization.mdx:55, the layer-5 row of the evaluation-order table, read:which a reader can take as "an object with no RLS policies is invisible". That reading already cost something: a reviewer on #7792 read the sentence as documenting fixed behaviour and nearly had it cited as evidence in a security PR body.
The contract the code actually has
Verified against
packages/plugins/plugin-security/src/rls-compiler.tsrather than the card's line citation (RLSCompiler.compileFilter):policies.length === 0→null, i.e. no filter. An object with no RLS policies is readable, not invisible.applicable === 0→nullas well. A policy that carries no predicate for this clause (acheck-only policy seen on theusingread pass) is skipped without counting toward the deny — so even a non-empty policy list can legitimately yield no filter.RLS_DENY_FILTER, the zero-row sentinel.The separate
RLS_DENY_FILTERreturns insecurity-plugin.ts(getReadFilter) are the resolution-failed branch — sharing-scope resolution threw, an on-behalf-of context reached the analytics read path, permission-set resolution threw. Also fail-closed, also not "empty set denies".The changes
Two sites, chosen so a careful reader cannot come away with the wrong reading rather than merely not being told the right one:
authorization.mdx:55— states the narrow condition (applicable policies exist but none of them compiles) and then names the empty case explicitly: an object with no applicable policy is unfiltered at this layer, because RLS narrows what the earlier gates allowed and never denies on its own.rls.mdx:18— the fail-closed contract section (#the-fail-closed-contract) and its "no applicable policy means no restriction" callout were already exact and are untouched. The summary bullet at the top of the page was not: it said a policy that can't compile "denies everything", which overstates in a second direction too — one uncompilable policy alongside a compilable one is excluded from the OR-union, not a deny. The bullet is the first statement of the rule a reader meets, so it now carries the same qualifier the contract section does.The other six pages: checked, correct, not touched
The sweep is
git grep -ln 'fail-closed\|deny-all' -- content/docs/permissions→ 8 files. The six not in this diff each state a different layer's fail-closed rule correctly, and none makes a claim about the RLS policy set:attachments-access.mdxauthentication.mdxdelegated-administration.mdxfield-level-security.mdxpermissions-matrix.mdxsharingModel⇒private(ADR-0090 D1)sharing-rules.mdxprivatedefault; :74 depth scopes fail closed to owner-onlyA widened sweep for restatements of the compile rule specifically (
no applicable,no polic*,compiles,uncompilable,denies everything) across all 8 files returned only the two sites edited here.Verification
node scripts/pm/dispatch-gates.mjson the final surface, plus the convention-scoped ones — all green:skip-changeset: docs ship in no published package.Out of scope: #7665 remains open; #7792 remains open.
Generated by Claude Code