Skip to content

fix(api-scheduler): only allow full access when no app owns a scheduled action's namespace - #5908

Open
adrians5j wants to merge 2 commits into
release/6.5.0from
claude/scheduler-permissions-fail-closed
Open

adrians5j wants to merge 2 commits into
release/6.5.0from
claude/scheduler-permissions-fail-closed

Conversation

@adrians5j

Copy link
Copy Markdown
Member

What

Closes a permission gap in the scheduler that came in with the permissions refactor from 6.4.9's #5593 (dd941d6 on this branch).

SchedulerPermissionsResolver.forNamespace() returned undefined when no app claimed a namespace, and the get, get-target, list and cancel use cases treated undefined as "skip the check". So:

  • listing without a namespace returned every scheduled action, from CMS and Website Builder alike, to any signed-in user
  • an action under a namespace no app registers permissions for could be read or cancelled by anyone

The scheduler.action permission that the refactor removed always applied.

Over GraphQL the gap is narrow, because every scheduler query requires a namespace and the resolver passes it through. A caller would have to send a namespace nobody registered, which returns nothing today. Server code calling the use cases directly could hit the no-namespace case, though, and so would any future app that registers scheduled actions without registering permissions.

Fix

  • forNamespace() always returns permissions. When no app claims the namespace, or there's no namespace, it falls back to FullAccessOnlyPermissions. That lets through full-access identities and code running with authorization switched off, and nobody else.
  • The four use cases drop their if (permissions) and permissions ? … : false checks.
  • api-scheduler-server lists every pending action at startup, with no namespace, to re-arm the timers. That's a system task, so it now runs inside withoutAuthorization() and still sees everything. Without this, the self-hosted scheduler would stop restoring pending actions on restart.

FullAccessOnlyPermissions checks isAuthorizationEnabled() as well as hasFullAccess(), because hasFullAccess() reads the identity's permissions and ignores withoutAuthorization().

Tests

api-headless-cms-scheduler/__tests__/schedulerPermissions.test.ts is new:

Case Before After
CMS user lists the CMS namespace allowed allowed
CMS user lists without a namespace allowed Scheduler/NotAuthorized
CMS user lists a namespace no app claims allowed Scheduler/NotAuthorized
Full-access user lists without a namespace allowed allowed
CMS user lists without a namespace inside withoutAuthorization() allowed allowed

Checks

  • yarn build passes.
  • Tests pass for api-headless-cms-scheduler (8), api-scheduler (1), api-scheduler-server (16) and api-website-builder-scheduler (4).
  • oxfmt, oxlint and adio are clean.

The same change goes to next with the scheduler permissions port (#5907), so both branches stay identical.

Workspace: wby-next2 · 🧹 MERGE 6.5.0 => NEXT

🤖 Generated with Claude Code

…ed action's namespace

SchedulerPermissionsResolver.forNamespace() returned undefined when no app
claimed the namespace, and the use cases treated that as "no check". So
listing without a namespace, or getting, listing or cancelling under a
namespace no app registers permissions for, skipped the permission check
completely. The scheduler.action permission this replaced always applied.

The resolver now always returns permissions. When no app claims the
namespace, or there is none, it falls back to one that only lets full-access
identities and code running without authorization through. The use cases
drop their undefined checks.

api-scheduler-server lists every pending action at startup, with no
namespace, to re-arm their timers. That's a system task, so it now runs
without authorization and still sees every action.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 69c5018a-09df-4d76-9972-80bc51326b59

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

adrians5j added a commit that referenced this pull request Oct 5, 2026
…ed action's namespace

SchedulerPermissionsResolver.forNamespace() returned undefined when no app
claimed the namespace, and the use cases treated that as "no check". So
listing without a namespace, or getting, listing or cancelling under a
namespace no app registers permissions for, skipped the permission check
completely. The scheduler.action permission this replaced always applied.

The resolver now always returns permissions. When no app claims the
namespace, or there is none, it falls back to one that only lets full-access
identities and code running without authorization through. The use cases
drop their undefined checks.

api-scheduler-server lists every pending action at startup, with no
namespace, to re-arm their timers. That's a system task, so it now runs
without authorization and still sees every action.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

(cherry picked from commit c391add, #5908 on release/6.5.0)

On next the api-scheduler-server part drops out, because that package doesn't exist here. The fallback test cases are written for next's scheduler test handler.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant