Skip to content

ADR-0096 (execution-surface identity admission) + fixes for the 3 confirmed-exploitable holes it surfaced (#2980, #2981, #2982)#2975

Merged
os-zhuang merged 3 commits into
mainfrom
claude/security-unbounded-rls-fls-qusv11
Jul 15, 2026
Merged

ADR-0096 (execution-surface identity admission) + fixes for the 3 confirmed-exploitable holes it surfaced (#2980, #2981, #2982)#2975
os-zhuang merged 3 commits into
mainfrom
claude/security-unbounded-rls-fls-qusv11

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #2964 / #2849. Two parts: the decision record (ADR-0096) and the three confirmed-exploitable fixes its evidence sweep surfaced. See the branch/PR note at the bottom — these can be split if a separate fast-merge PR is preferred.

Part 1 — ADR-0096: Execution-Surface Identity Admission (docs)

Names the failure class behind #2849 and #2308: a metadata-driven execution surface reaches the data engine without an ExecutionContext, and the SecurityPlugin's empty-principal fall-open rewards the omission with full authority — silently. Decisions:

Part 2 — the three confirmed-exploitable fixes

Each fixed at its own enforcement point; no behaviour change for correctly-scoped callers. Tests added; plugin-reports 38, service-knowledge 22, plugin-sharing 85 green; eslint + doc-authoring guard clean; changeset added.

Scheduled-reports behavior change (#2980). Correctly fixing the exfiltration requires running the report under the owner's resolved RLS, which needs the ADR-0073 M2 principal resolver (not available to the reports plugin today). Until it's wired, scheduled reports fail closed (skipped + marked failed) rather than run elevated. Interactive run/runAdHoc are unaffected. Flagged for reviewer sign-off.

Branch/PR note. These fixes and the ADR share the designated task branch, so they landed on one PR. If you'd prefer the three P1/P2 fixes as a separate fast-merge PR decoupled from the ADR's architectural review, say so and I'll split them out.

🤖 Generated with Claude Code

https://claude.ai/code/session_0155bjzJZxZaZTKSMSvg6Aum

Generalizes #2849 from an action-shaped bug to its class: metadata-driven
execution surfaces that reach the data engine without an ExecutionContext
silently inherit full authority via the security plugin's empty-principal
fall-open (#2308 and #2849 are two instances found only by adversarial
review).

Decides one invariant and four mechanisms: every engine call carries a real
principal or an explicit audited SystemGrant (D1); a single elevation
constructor systemContext(reason) (D2); identity-required engine signatures
so forgetting context is a compile error, not a fall-open (D3); an
execution-surface admission test wired into the authz conformance matrix
with a CI meta-test (D4); and a staged fail-closed strict mode (D5).
Re-bases #2849 Phase 2/3 as D6: action-level runAs adopts ADR-0073's
three-posture vocabulary and lands as this mechanism's first application.

Staged per ADR-0049: v1 is the decision record + systemContext + honest
matrix rows + the ai.exposed/trusted-body lint; signature migration and
the strict-mode default flip are evidence-gated M2+.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0155bjzJZxZaZTKSMSvg6Aum
@vercel

vercel Bot commented Jul 15, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Canceled Canceled Jul 15, 2026 4:32pm

Request Review

claude added 2 commits July 15, 2026 15:29
A four-axis sweep (context-less calls, elevation literals, fall-open
seams, execution-surface identity) confirms #2849 is a populated class,
not a lone bug. Records the findings as the ADR's evidence base:

- E1: the empty-principal fall-open predicate replicated across getReadFilter,
  the tenant-scope path, guest routes, custom API endpoints, the authored-body
  sandbox, MCP resource reads, and messaging.
- E2: three confirmed-exploitable trusted-implicit surfaces, spun out as
  #2980 (reports IDOR + scheduled-report RLS bypass), #2981 (knowledge/RAG
  retrieval fall-open), #2982 (bulk-write OWD gap) — fixed independently.
- E3: structural middleware-bypass paths (executeAction, raw execute, driver
  getters).
- E4: unknown seams (GraphQL, realtime, blob RLS, chatWithTools contract).
- E5: the D2 migration sizing (~300 sites / ~50 constructors / ~25 files) and
  its existing prototype (resolveRunDataContext).

Wires the seed rows into D4, the exploitable instances into 'what lands now',
and the References/Trigger notes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0155bjzJZxZaZTKSMSvg6Aum
…write OWD gap (#2980, #2981, #2982)

Three confirmed-exploitable execution-surface authz holes from the ADR-0096
class sweep, each fixed at its enforcement point; no change for correctly-
scoped callers.

- #2980 reports: getReport/deleteReport/saveReport/listReports were caller-
  identity-blind (read/wrote sys_saved_report with a system context) → any
  authenticated user could read/delete/overwrite/enumerate any report. Now
  owner-scoped (system read of the protection-locked metadata object, authz
  by owner match); create/overwrite can't spoof ownership. Scheduled dispatch
  no longer runs isSystem (which emailed the target object's whole table past
  the owner's RLS): resolves the owner via a new resolveOwnerContext seam and
  fails closed when unresolved (ADR-0073 user-less identity consumer).
- #2981 knowledge/RAG: applyPermissionFilter returned all hits on missing OR
  system context. Missing identity now fails closed (drops object-backed hits,
  keeps ACL-less file/http); only an explicit system context passes through.
- #2982 sharing: bulk update({multi:true})/deleteMany had no single id to
  canEdit-gate, skipping owner scoping on private/public_read objects. New
  SharingService.buildWriteFilter (edit-set analogue of buildReadFilter) is
  AND-ed into the multi-write AST, incl. the on-behalf-of delegator intersection.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0155bjzJZxZaZTKSMSvg6Aum
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/plugin-reports, @objectstack/plugin-sharing, packages/services.

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

  • content/docs/automation/webhooks.mdx (via packages/services)
  • content/docs/kernel/runtime-services/audit-service.mdx (via packages/services)
  • content/docs/kernel/runtime-services/index.mdx (via packages/services)
  • content/docs/kernel/runtime-services/settings-service.mdx (via packages/services)
  • content/docs/permissions/authorization.mdx (via packages/plugins/plugin-sharing)
  • content/docs/permissions/permissions-matrix.mdx (via packages/plugins/plugin-sharing)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-reports, @objectstack/plugin-sharing, packages/services)
  • content/docs/protocol/kernel/i18n-standard.mdx (via packages/services)
  • content/docs/protocol/objectql/security.mdx (via packages/plugins/plugin-sharing)

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 changed the title docs(adr): ADR-0096 — execution-surface identity admission (generalizes #2849) ADR-0096 (execution-surface identity admission) + fixes for the 3 confirmed-exploitable holes it surfaced (#2980, #2981, #2982) Jul 15, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review July 15, 2026 16:31
@os-zhuang
os-zhuang merged commit e07645c into main Jul 15, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the claude/security-unbounded-rls-fls-qusv11 branch July 15, 2026 16:32
os-zhuang pushed a commit that referenced this pull request Jul 16, 2026
…d statuses, mark PRIORITIZATION stale

Follow-up to the ADR-0086 re-evaluation (#2557): an audit of docs/adr found
three hygiene defects; this fixes the mechanical ones.

1. Resolve the ADR-0096 numbering collision. Two unrelated ADRs landed as
   0096 on the same day; the first-merged one (execution-surface identity
   admission, #2975) keeps 0096, and the connector ADR (#2977) is renumbered
   to ADR-0097. All connector-semantic "ADR-0096" references in code comments,
   test titles, and zod describe/message strings are updated to ADR-0097;
   execution-surface references (http-dispatcher, authz-conformance,
   graphql/realtime contracts, service-realtime README) are untouched.
   CHANGELOGs and commit history are left as historical record.

2. Flip Status lines of implemented ADRs that were still Proposed, with
   evidence: 0093 (tenancy service + membership reconciler + dogfood test)
   and 0095 (tenant-layer.ts + posture-ladder.ts + matrix-gate tests).
   Verified code+tests on main before flipping; other stale-looking ADRs
   (0049/0056/0057/0066/...) are left for per-ADR verification.

3. Mark docs/adr/PRIORITIZATION.md as a historical snapshot (it predates
   ADR-0050+) and record the status-hygiene rule: the PR that lands an ADR's
   implementation flips its Status line with one line of evidence.
os-zhuang pushed a commit that referenced this pull request Jul 16, 2026
…d statuses, mark PRIORITIZATION stale

Follow-up to the ADR-0086 re-evaluation (#2557): an audit of docs/adr found
three hygiene defects; this fixes the mechanical ones.

1. Resolve the ADR-0096 numbering collision. Two unrelated ADRs landed as
   0096 on the same day; the first-merged one (execution-surface identity
   admission, #2975) keeps 0096, and the connector ADR (#2977) is renumbered
   to ADR-0097. All connector-semantic "ADR-0096" references in code comments,
   test titles, and zod describe/message strings are updated to ADR-0097;
   execution-surface references (http-dispatcher, authz-conformance,
   graphql/realtime contracts, service-realtime README) are untouched.
   CHANGELOGs and commit history are left as historical record.

2. Flip Status lines of implemented ADRs that were still Proposed, with
   evidence: 0093 (tenancy service + membership reconciler + dogfood test)
   and 0095 (tenant-layer.ts + posture-ladder.ts + matrix-gate tests).
   Verified code+tests on main before flipping; other stale-looking ADRs
   (0049/0056/0057/0066/...) are left for per-ADR verification.

3. Mark docs/adr/PRIORITIZATION.md as a historical snapshot (it predates
   ADR-0050+) and record the status-hygiene rule: the PR that lands an ADR's
   implementation flips its Status line with one line of evidence.
os-zhuang added a commit that referenced this pull request Jul 16, 2026
…d statuses, mark PRIORITIZATION stale (#3032)

* docs(adr): status-hygiene sweep — fix 0096 collision, flip implemented statuses, mark PRIORITIZATION stale

Follow-up to the ADR-0086 re-evaluation (#2557): an audit of docs/adr found
three hygiene defects; this fixes the mechanical ones.

1. Resolve the ADR-0096 numbering collision. Two unrelated ADRs landed as
   0096 on the same day; the first-merged one (execution-surface identity
   admission, #2975) keeps 0096, and the connector ADR (#2977) is renumbered
   to ADR-0097. All connector-semantic "ADR-0096" references in code comments,
   test titles, and zod describe/message strings are updated to ADR-0097;
   execution-surface references (http-dispatcher, authz-conformance,
   graphql/realtime contracts, service-realtime README) are untouched.
   CHANGELOGs and commit history are left as historical record.

2. Flip Status lines of implemented ADRs that were still Proposed, with
   evidence: 0093 (tenancy service + membership reconciler + dogfood test)
   and 0095 (tenant-layer.ts + posture-ladder.ts + matrix-gate tests).
   Verified code+tests on main before flipping; other stale-looking ADRs
   (0049/0056/0057/0066/...) are left for per-ADR verification.

3. Mark docs/adr/PRIORITIZATION.md as a historical snapshot (it predates
   ADR-0050+) and record the status-hygiene rule: the PR that lands an ADR's
   implementation flips its Status line with one line of evidence.

* chore: retrigger CI

* docs: rename remaining connector-semantic ADR-0096 refs introduced by #3016/#3029

---------

Co-authored-by: Claude <noreply@anthropic.com>
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 size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants