Skip to content

fix(reports): owner-gate the saved-report schedule routes (#2980) - #6683

Merged
qq9340100 merged 1 commit into
mainfrom
claude/test-plan-review-t7gux8
Aug 8, 2026
Merged

fix(reports): owner-gate the saved-report schedule routes (#2980)#6683
qq9340100 merged 1 commit into
mainfrom
claude/test-plan-review-t7gux8

Conversation

@qq9340100

Copy link
Copy Markdown
Collaborator

What

Closes the D1 finding from the platform-checklist sweep (#6557 FOLLOW-UPS.md §1, tracked in #6681): the two saved-report schedule routes were not owner-isolated, while every sibling report route is.

packages/plugins/plugin-reports/src/report-service.tsunscheduleReport and listSchedules took the caller context as _context and never consulted it, querying under the RLS-bypassing system context. Any authenticated caller could therefore:

  • delete another owner's report schedule — a cross-owner destructive write; or
  • list another owner's schedules — leaking recipient addresses and cron —

simply by supplying an id. canAccessReport already gates getReport/deleteReport/run/saveReport/listReports (and scheduleReport via getReport), so only the delete/list schedule doors were open.

Fix

Both methods now resolve the schedule's parent report and require the caller to own it, mirroring the sibling routes:

  • unscheduleReport loads the schedule, then its report, and deletes only when canAccessReport holds. A cross-owner attempt throws REPORT_NOT_FOUND (deny-as-404, anti-enumeration — never a cross-owner 2xx); a genuinely-absent schedule stays idempotent, matching deleteReport.
  • listSchedules returns an empty list to any non-system caller who cannot access the report it is scoped to — the same non-leaking posture as listReports. The scheduler's system context still sees every schedule.
  • packages/rest/src/rest-server.ts — the DELETE-schedule route now runs the section's existing handleValidation helper in its catch, so REPORT_NOT_FOUND surfaces as 404 instead of 500 (same mapping the POST-schedule route already uses).

Includes a changeset (@objectstack/plugin-reports + @objectstack/rest, patch). No authoring-surface or metadata change; existing owner-path behavior is unchanged.

Tests

report-service.test.ts gains 5 owner-gate cases — cross-owner delete denied and schedule survives, unknown-id delete idempotent, cross-owner list empty, system-context list still sees schedules. pnpm --filter @objectstack/plugin-reports test → 42 passed.

Note: the full @objectstack/rest test suite (rest.test.ts) could not run in the authoring container because @objectstack/spec's build is independently red on this branch (an ADR-0087 governance guard: 62 schemas left the published set with no registered removal — unrelated to this diff). The REST change is a one-line reuse of a helper already invoked twice in the same function.

Follow-ups (tracked in #6681, not in this PR)


🤖 Generated with Claude Code

https://claude.ai/code/session_01L8aEBrJVxRnA5XVRkeVft9


Generated by Claude Code

`unscheduleReport` and `listSchedules` took the caller context as `_context`
and never consulted it, querying under the RLS-bypassing system context. Any
authenticated caller could delete another owner's report schedule (a cross-owner
destructive write) or list another owner's schedules (leaking recipients + cron)
by supplying an id, even though the sibling read/run/delete routes are all
owner-isolated.

Both now resolve the schedule's parent report and require the caller to own it,
mirroring the sibling routes:

- unscheduleReport: loads the schedule, then its report, and deletes only when
  canAccessReport holds; a cross-owner attempt throws REPORT_NOT_FOUND
  (mapped to 404 in the REST layer — deny-as-404, anti-enumeration), while a
  genuinely-absent schedule stays idempotent. Create was already gated via
  getReport, so only the delete/list doors were open.
- listSchedules: returns an empty list to any non-system caller who cannot
  access the report it is scoped to — the same non-leaking posture as
  listReports. The scheduler's system context still sees every schedule.

Tests: 5 new owner-gate cases in report-service.test.ts (cross-owner delete
denied + schedule survives, unknown-id idempotent, cross-owner list empty,
system context still lists). No authoring-surface or metadata change.

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

vercel Bot commented Aug 8, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 8, 2026 12:33pm

Request Review

@github-actions github-actions Bot added the size/s label Aug 8, 2026
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/plugin-reports, @objectstack/rest.

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

  • content/docs/ai/connect-mcp.mdx (via @objectstack/rest)
  • content/docs/api/client-sdk.mdx (via @objectstack/plugin-reports)
  • content/docs/api/error-handling-server.mdx (via @objectstack/rest)
  • content/docs/api/index.mdx (via @objectstack/rest)
  • content/docs/permissions/authentication.mdx (via @objectstack/rest)
  • content/docs/plugins/index.mdx (via @objectstack/rest)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-reports, @objectstack/rest)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/rest)
  • content/docs/protocol/kernel/i18n-standard.mdx (via packages/rest)
  • content/docs/releases/implementation-status.mdx (via @objectstack/rest)
  • content/docs/releases/v12.mdx (via @objectstack/rest)
  • content/docs/releases/v17.mdx (via @objectstack/rest)

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.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Aug 8, 2026
@qq9340100
qq9340100 added this pull request to the merge queue Aug 8, 2026
Merged via the queue into main with commit 2c2a212 Aug 8, 2026
25 checks passed
@qq9340100
qq9340100 deleted the claude/test-plan-review-t7gux8 branch August 8, 2026 13:05
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/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants