Without execution timeouts or storage quotas, a single misbehaving plugin — whether malicious or accidentally written with an infinite loop — can block the Node.js event loop, starve other request handlers, or fill disk. CWE-400 (Uncontrolled Resource Consumption) and ISO 25010 performance-efficiency.resource-utilization both require that resource consumption be bounded. In a multi-tenant plugin environment this is especially severe: one customer's plugin degrades service for all customers, and the host has no mechanism to identify or interrupt the culprit.
Medium because unlimited plugin resource consumption — no timeouts, no storage quotas, no CPU caps — allows a single plugin to monopolize host resources and deny service to all other users.
Start with handler execution timeouts — they are the highest-impact resource limit and require no additional infrastructure. Race each handler invocation against a setTimeout reject and surface a clear timeout error attributing it to the specific plugin.
async function invokeWithTimeout(
handler: Function,
context: HookContext,
timeoutMs: number = 5000
): Promise<void> {
const timeout = new Promise<never>((_, reject) =>
setTimeout(() => reject(new Error(`Handler timed out after ${timeoutMs}ms`)), timeoutMs)
);
await Promise.race([handler(context), timeout]);
}
ID: plugin-extension-architecture.isolation-security.resource-limits
Severity: medium
What to look for: Check whether the plugin system enforces any resource limits on plugin execution. Look for:
Pass criteria: Count all enforced resource limits. At least one resource limit is enforced: execution timeouts on hook handlers, storage quotas, or rate limits on API calls. The limits are documented so plugin authors know the constraints. Exceeding a limit produces a clear error, not a silent failure. At least 1 resource limit must be enforced (memory, CPU time, API calls).
Fail criteria: Should not pass when plugins can allocate unlimited resources without any cap or throttle. No resource limits of any kind. A plugin can run indefinitely, allocate unlimited memory, store unlimited data, and call host APIs without throttling. A single plugin can monopolize host resources.
Skip (N/A) when: Plugins run in a controlled environment where the host process lifecycle is short (e.g., a build tool that runs for seconds then exits) AND only trusted plugins are used. Also skip if the runtime environment already enforces limits (e.g., plugins are Cloudflare Workers with built-in CPU/memory limits).
Detail on fail: "No resource limits exist. Hook handlers can run indefinitely — a plugin with an infinite loop will block the event loop and freeze the entire host. No timeouts, no memory monitoring, no storage quotas. A single misbehaving plugin can take down the entire application."
Remediation: Resource limits protect the host from both malicious and accidentally resource-hungry plugins. Start with execution timeouts — they're the highest-impact limit and the easiest to implement.
// Execution timeout for hook handlers:
async function invokeWithTimeout(
handler: Function,
context: HookContext,
timeoutMs: number = 5000
): Promise<void> {
const timeout = new Promise<never>((_, reject) =>
setTimeout(() => reject(new Error(`Handler timed out after ${timeoutMs}ms`)), timeoutMs)
);
await Promise.race([handler(context), timeout]);
}