feat(core): run tasks from resource events - #1176
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
c246229 to
085586d
Compare
5d630da to
1dcf60b
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 4972148. Configure here.
| if (result.status === "already_exists") { | ||
| await state.set(key, count, ttlMs); | ||
| } | ||
| return result; |
There was a problem hiding this comment.
Retry failures burn dispatch quota
Medium Severity
dispatchWithinRateLimit increments the per-task hourly counter before dispatch() runs, and only decrements on already_exists. Validation, credential binding, and verify in dispatchEventTask happen after that increment. Duplicate provider retries that fail before a dispatch record exists therefore keep consuming quota, which can rate-limit the task and silently drop later matched events for the rest of the window.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 4972148. Configure here.
4972148 to
b6bb22f
Compare
| identifier: z.string().describe("Plugin-defined resource identifier."), | ||
| namespace: eventNamespaceSchema(catalog).describe( | ||
| "Enabled plugin namespace for this resource.", | ||
| ), |
There was a problem hiding this comment.
Subscriptions accept arbitrary namespace/identifier with no resource access check
The subscribe tool takes a model-supplied identifier and namespace and only validates them against the enabled-plugin catalog — nothing verifies that this conversation or its users are authorized to see the resource (e.g., a private GitHub repo), so any Slack thread can subscribe to events for resources it cannot access and receive their summaries and untrusted payload content.
Evidence
subscribeInputSchemavalidatesnamespaceonly viaeventNamespaceSchema(catalog)(membership in enabled plugin namespaces) andeventsviapluginSupportsEvent;identifieris a barez.string()with no ownership or access validation.createSubscribeToResourceEventsTool.executecallscreateResourceEventSubscriptionstoringnamespace/identifierverbatim after only conversation-shape checks (requireConversationContextonly requires a Slack thread id).findMatchingResourceEventSubscriptionsinchat/resource-events/store.tsmatches purely onnamespace + identifier + eventTypewith no per-conversation permission filter, andenqueueResourceEventNotificationdeliverstrustedSummaryplusuntrustedText(provider payload content, e.g. PR/issue data) into the subscribing thread.- No credential delegation or destination-permission check exists on this subscription path (the PR's scheduler-aligned permissions apply to event tasks, not these conversation subscriptions). The subscribe tool also has no
approvalMode, so it executes without human review.
Also found at 1 additional location
packages/junior/src/chat/tools/index.ts:141
Identified by Warden · security-review · H7P-GW5


Junior can now persist event tasks that run when provider plugins publish normalized resource events. Tasks use scheduler-aligned destination permissions and credential delegation, support multiple selected event types and multiple tasks for the same event, and dispatch with retry-safe Redis deduplication and rate limits.
The plugin contract binds each event to its plugin manifest namespace and matches both event tasks and resource subscriptions on
namespace + identifier + event type; the model-facing tools expose installed plugin namespaces as an enum. GitHub now publishes pull request, deployment, and issue events, including repository-wide or exact-issue selectors and multi-state issue tasks.Webhook verification remains provider-owned and event payloads remain untrusted at the core dispatch boundary. Existing ephemeral resource-subscription records use a versioned Redis key and expire normally instead of requiring migration; durable event tasks are stored in SQL.
Refs #1174