Skip to content

fix(plugin-security): mask read-protected fields on write responses (FLS leak)#2692

Merged
os-zhuang merged 1 commit into
mainfrom
fix/fls-mask-write-response
Jul 8, 2026
Merged

fix(plugin-security): mask read-protected fields on write responses (FLS leak)#2692
os-zhuang merged 1 commit into
mainfrom
fix/fls-mask-write-response

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

What

Field-level security masking ran only on find/findOne results. The record echoed back by a write (insert/update) was never masked, so a caller with edit-but-not-field-read could PATCH/POST a record and read a read-protected field back out of the mutation response.

Field writes to protected fields were already denied (detectForbiddenWrites), so this is purely a read leak on the response image. The mask now covers insert/update results too — matching read behavior.

packages/plugins/plugin-security/src/security-plugin.ts — extend the op allow-list on the post-next() masking block from ['find','findOne'] to ['find','findOne','insert','update'].

How it was found

Dogfooding the permission stack end-to-end as an admin, in the browser:

  1. Studio → built a leave_request object + 请假中心 app in a writable package.
  2. Access pillar → 员工请假权限 (CRU, hr_note readable:false) and 人事请假权限 (full CRUD + view/modify-all, hr_note readable).
  3. Setup → created real users, assigned the permission sets.
  4. Drove the REST API as each identity.

Employee 张三 (hr_note not readable):

  • GET list / by-id → hr_note correctly stripped ✅
  • PATCH (editing an allowed field) → response included hr_note with the sensitive value ❌

Verification

  • New regression tests (update + insert echoed record) — fail without the fix, pass with it.
  • Full plugin-security suite: 129 passed.
  • Live end-to-end on a running showcase app after rebuild+restart:
    • 张三 PATCH response no longer contains hr_note
    • 李四 (hr_note readable) PATCH response still contains hr_note — no regression for authorized readers ✅
    • 张三 writing hr_note still 403 — write-protection intact ✅

🤖 Generated with Claude Code

…FLS leak)

Field-level security masking ran only on find/findOne results, so a caller with
edit-but-not-field-read could PATCH/POST a record and read a read-protected
field back out of the mutation response image. Field writes were already denied
(detectForbiddenWrites); this closes the read leak on the echoed post-image by
extending the mask to insert/update results.

Found by dogfooding the permission stack end-to-end (Studio package + permission
sets + real users): an employee permission set with hr_note readable:false could
still read hr_note via the PATCH response. Added regression tests for both
update and insert echoed records.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 8, 2026 4:05pm

Request Review

@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tests tooling and removed size/s labels Jul 8, 2026
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-security.

11 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/getting-started/cli.mdx (via @objectstack/plugin-security)
  • content/docs/permissions/access-recipes.mdx (via packages/plugins/plugin-security)
  • content/docs/permissions/authorization.mdx (via packages/plugins/plugin-security)
  • content/docs/permissions/permission-sets.mdx (via packages/plugins/plugin-security)
  • content/docs/permissions/permissions-matrix.mdx (via packages/plugins/plugin-security)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/plugin-security)
  • content/docs/plugins/index.mdx (via @objectstack/plugin-security)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-security)
  • content/docs/releases/implementation-status.mdx (via @objectstack/plugin-security)
  • content/docs/ui/dashboards.mdx (via @objectstack/plugin-security)
  • content/docs/ui/role-based-interfaces.mdx (via packages/plugins/plugin-security)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang os-zhuang merged commit 799b285 into main Jul 8, 2026
16 checks passed
@os-zhuang os-zhuang deleted the fix/fls-mask-write-response branch July 8, 2026 16:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant