fix(plugins): scope capability calls to activated sessions and bound net.fetch memory#594
Merged
Merged
Conversation
…net.fetch memory - Enforce per-session activation on every plugin capability call. assertSessionActive now checks the operator-set activeSessions in addition to the static manifest.sessions, across all capability verbs (messages.sendText/reply, engine reads, conversation.send, handover.set, mappings.upsert/get/ getByProvider). Previously the capability path gated only on manifest.sessions (default ['*']), so a plugin activated for one session could send/read/flip handover on any other. Defaults (activeSessions ?? ['*'], sessionScoped:false) preserve every single-tenant and global-built-in flow. - Bound concurrent plugin net.fetch buffering with a global reject-when-full semaphore, so total host-side response buffering stays at most MAX_INFLIGHT_FETCHES × the body cap regardless of plugin or worker count (previously each of many concurrent fetches could buffer the full cap and OOM the host).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two plugin-runtime hardening fixes: tenant scoping of capability calls, and a memory bound on plugin fetches.
Changes
messages.sendText/reply, engine reads,conversation.send,handover.set,mappings.upsert/get/getByProvider) gated only on the staticmanifest.sessions(default['*']). A general adapter necessarily ships['*']and is meant to be scoped by operator activation, so a plugin activated for one session could still send/read/flip handover on any other.assertSessionActivenow ANDs the operator-setactiveSessions(the same gate hook dispatch uses) with the manifest check. Defaults (activeSessions ?? ['*'],sessionScoped:false) preserve every single-tenant and global-built-in flow — it only ever tightens.net.fetch. Each plugin fetch buffers up to the body cap host-side (outside the worker heap cap), so many concurrent fetches across plugins/workers could OOM the host. A global reject-when-full semaphore bounds total buffering toMAX_INFLIGHT_FETCHES × body cap, mirroring the existing sandbox in-flight-cap pattern.Notes
Residual (separate follow-up): the activation gate is per-plugin, not per-instance — a full-admin provisioning the same plugin for multiple tenants leaves it active for all of them; true per-instance isolation would bind the capability call's sessionId to the dispatching instance.
Verification
npm run build✓ ·npm test✓ (1863/1863, +6) · lint ✓. New tests: capability calls denied when deactivated for the session (messages + engine), allowed when activated, allowed when never restricted, global plugin unaffected; net.fetch rejects at the concurrency cap and recovers after slots free.