# Hook unregistration supported

- **Pattern:** `ab-002081` (`plugin-extension-architecture.hook-system.unregistration`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002081
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002081)

## Why it matters

When hook handlers cannot be unregistered, disabling a plugin through the admin UI is cosmetic. The plugin's handlers keep firing on every request, every event, every lifecycle call — consuming CPU, accessing data, and potentially interfering with replacement plugins. CWE-404 (Improper Resource Shutdown) applies directly: the resource — an active event handler — is never released. In long-running hosts with hot-reload or dynamic plugin enable/disable, unregistration is the only mechanism that makes plugin state changes observable without a restart.

## Severity rationale

Medium because the inability to unregister hook handlers makes plugin disable purely cosmetic — handlers keep running after removal, making clean hot-reload architecturally impossible.

## Remediation

Have every registration call return a disposable. When the plugin manager deactivates a plugin, call `dispose()` on every disposable that plugin registered. This pattern composes cleanly with VS Code's `context.subscriptions` model.

```typescript
on(hook: string, handler: Function): Disposable {
  this.handlers.get(hook)?.push(handler);
  return {
    dispose: () => {
      const handlers = this.handlers.get(hook);
      if (handlers) {
        const idx = handlers.indexOf(handler);
        if (idx >= 0) handlers.splice(idx, 1);
      }
    }
  };
}
```

## Detection

- **ID:** `unregistration`
- **Severity:** `medium`
- **What to look for:** Check if plugins can cleanly remove their registered hook handlers. Look for:
  1. An `off()`, `removeListener()`, `removeHook()`, or `unsubscribe()` method
  2. Registration returning a disposable/unsubscribe function: `const unsub = hooks.on('event', handler); unsub();`
  3. Automatic cleanup tied to plugin deactivation (host removes all hooks registered by a deactivating plugin)
  4. VS Code pattern: `context.subscriptions.push(disposable)` — disposables auto-cleaned on deactivate
  Without unregistration, disabling a plugin leaves its handlers attached — they continue to run even after the plugin is "removed."
- **Pass criteria:** Count all registration/unregistration pairs. Hook handlers can be unregistered, either manually (via returned disposable or explicit removal API) or automatically (host removes all handlers when a plugin is deactivated/destroyed). The mechanism is documented. 100% of hook registrations must have a corresponding unregistration mechanism.
- **Fail criteria:** No way to remove a registered handler. Disabling or removing a plugin leaves its handlers attached and running. OR unregistration exists but doesn't work correctly (handlers still fire after removal).
- **Skip (N/A) when:** Plugins are loaded once at startup and never removed during the application's lifetime (e.g., build-time-only plugin systems like Babel plugins that run during compilation and then the process exits).
- **Detail on fail:** `"hooks.on() returns void. There is no off(), removeListener(), or dispose() method. Once a plugin registers a handler, it cannot be removed. Disabling a plugin via the UI still leaves its handlers firing on every request."`
- **Remediation:** Unregistration is essential for hot-reload, plugin disable/enable, and clean shutdown. Without it, the only way to remove a plugin's hooks is to restart the entire application.

  ```typescript
  // Return a disposable from registration:
  on(hook: string, handler: Function): Disposable {
    this.handlers.get(hook)?.push(handler);
    return {
      dispose: () => {
        const handlers = this.handlers.get(hook);
        if (handlers) {
          const idx = handlers.indexOf(handler);
          if (idx >= 0) handlers.splice(idx, 1);
        }
      }
    };
  }
  ```

---

## External references

- iso-25010 reliability.fault-tolerance
- cwe CWE-404

Taxons: error-resilience

HTML version: https://auditbuffet.com/patterns/ab-002081
