All 25 checks with why-it-matters prose, severity, and cross-references to related audits.
A plugin system without distinct lifecycle stages cannot release resources cleanly. When plugins open database connections, start intervals, or attach event listeners but have no deactivation or destruction hook to call, those resources accumulate for the process lifetime. Disabling a plugin through the UI becomes theater — the plugin keeps running. Over time this produces connection pool exhaustion, timer accumulation, and memory growth that lead to host instability. ISO 25010 maintainability.modularity requires components to be independently removable; lifecycle hooks are the mechanism that makes removal safe.
Why this severity: Critical because missing deactivation and cleanup hooks make plugin removal impossible at runtime, turning every installed plugin into a permanent resource leak that can exhaust connections and crash the host.
plugin-extension-architecture.hook-system.lifecycle-hooksSee full patternWhen hook names are untyped strings with no validation and handler signatures are undocumented, plugin authors must read the host's source code to discover what hooks exist and what arguments their handlers receive. This blocks third-party plugin development, makes the API fragile across upgrades, and guarantees that handler signature mismatches appear only at runtime — not at compile time. ISO 25010 maintainability.modularity depends on well-defined, discoverable interfaces between components; an opaque hook API violates this structurally.
Why this severity: Critical because an undiscoverable, untyped hook registration API prevents plugin authors from writing correct handlers without reading host internals, collapsing the boundary between plugin and host.
plugin-extension-architecture.hook-system.hook-registrationSee full patternWhen multiple plugins register handlers for the same hook and execution order is undefined or depends on object property enumeration, plugin behavior becomes non-deterministic across JavaScript engines and load sequences. An auth plugin that must run before a logging plugin cannot declare that requirement. Debugging a production incident caused by handler ordering requires understanding internal host iteration behavior rather than a declared contract. ISO 25010 maintainability.modularity requires that component interactions be predictable; undefined execution order breaks this at the seam between every plugin pair.
Why this severity: High because undefined hook execution order makes multi-plugin behavior non-deterministic, turning plugin interactions into ordering-dependent bugs that are impossible to reproduce reliably.
plugin-extension-architecture.hook-system.execution-orderSee full patternMost real-world plugins need to do async work — call external APIs, query databases, read files. If the hook invocation loop does not await handler return values, any plugin that returns a Promise appears to work but silently drops its errors. The host continues as though the handler succeeded while the underlying operation failed. This creates data integrity issues in write operations, invisible failures in webhook processors, and phantom results in request pipelines. ISO 25010 reliability.fault-tolerance requires that errors propagate to where they can be handled, not vanish into unhandled rejections.
Why this severity: High because async handlers whose Promises are not awaited silently swallow errors, making plugin failures invisible and corrupting write pipelines without any logged indication.
plugin-extension-architecture.hook-system.async-hooksSee full patternUntyped hook payloads — parameters typed as `any`, `unknown`, or `Record<string, any>` — eliminate compile-time safety from every plugin in the ecosystem. Plugin authors get no autocompletion, no error on typos in property names, and no signal when a host update renames a payload field. Breakage appears only at runtime, often in production. ISO 25010 maintainability.analysability scores fall sharply when the API surface cannot be understood without running code; typed payloads are the primary mechanism for making plugin APIs analysable.
Why this severity: High because untyped hook payloads remove compile-time safety from the entire plugin ecosystem, causing payload-shape mismatches to surface only as runtime errors in plugin authors' production deployments.
plugin-extension-architecture.hook-system.type-safetySee full patternWithout before/after hook pairs, plugins are limited to one-sided interception. A caching plugin cannot check a cache before a request and store the result after it completes — it can only do one or the other. A metrics plugin cannot measure operation duration. A validation plugin cannot both validate input before a write and sanitize output after. This forces plugin authors to duplicate host internals or wire two separate plugins together, increasing coupling and the surface area for bugs. ISO 25010 maintainability.modularity requires that cross-cutting concerns be implementable without modifying core code.
Why this severity: Medium because the absence of before/after hook pairs prevents plugins from implementing caching, metrics, and two-phase transformations, limiting the plugin system to superficially useful interception only.
plugin-extension-architecture.hook-system.pre-post-hooksSee full patternWhen 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.
Why this severity: Medium because the inability to unregister hook handlers makes plugin disable purely cosmetic — handlers keep running after removal, making clean hot-reload architecturally impossible.
plugin-extension-architecture.hook-system.unregistrationSee full patternA plugin invocation loop without per-handler try/catch means a single third-party plugin throwing an uncaught error crashes the host, stops subsequent handlers from running, and surfaces as a 500 to every user — not just the user whose request triggered the broken plugin. CWE-755 (Improper Exception Handling) and ISO 25010 reliability.fault-tolerance both require that component failures be contained. The economic impact is proportional: one buggy plugin in an ecosystem of ten takes down applications that have nine functioning plugins installed.
Why this severity: Critical because an unguarded hook invocation loop lets a single plugin error crash the host and stop all other plugins' handlers from executing, making application reliability contingent on the worst plugin installed.
plugin-extension-architecture.isolation-security.error-boundariesSee full patternPassing the full host application instance to plugins — the Express `app` object, a raw database connection, or `process` — is functionally equivalent to giving every third-party plugin full administrative access to the application. Plugins can add routes, read or overwrite shared state, escalate their own permissions, or access data belonging to other plugins and tenants. OWASP A01 (Broken Access Control) and CWE-284 apply directly. The plugin surface becomes an attack surface: a malicious or compromised plugin can exfiltrate any data the host can reach.
Why this severity: Critical because exposing the full host instance to plugins grants third-party code unrestricted access to the database, internal state, and other plugins' data — OWASP A01 applied at the architecture level.
plugin-extension-architecture.isolation-security.resource-scopingSee full patternWithout a permission system, every plugin receives the same access to storage, hooks, and configuration regardless of what it actually needs. Users cannot review what a plugin will do before installing it, operators cannot audit which plugins have access to sensitive data, and the host cannot prevent a plugin from exceeding its intended scope. OWASP A01 (Broken Access Control) and CWE-284 cover exactly this pattern. The absence of declared and enforced permissions also eliminates the ability to revoke capabilities selectively — any restriction requires removing the entire plugin.
Why this severity: High because undeclared and unenforced permissions give every plugin identical unrestricted access, preventing users from making informed installation decisions or operators from applying least-privilege controls.
plugin-extension-architecture.isolation-security.sandboxingSee full patternWhen plugins receive mutable references to shared objects — config, `process.env`, shared caches — any plugin can modify state that all other plugins and the host depend on. Plugin A changes `config.apiUrl` and Plugin B silently uses the wrong endpoint. A plugin calling `require('dotenv').config()` overwrites environment variables for every module in the process. CWE-454 (Unintended Variable Set from External Scope) and CWE-829 cover this class. The failure mode is load-order-dependent and nearly impossible to reproduce in isolation, making it one of the hardest bugs to diagnose in plugin ecosystems.
Why this severity: High because plugins writing to shared global state or mutating passed-by-reference objects create load-order-dependent bugs between plugins that are invisible during individual plugin testing.
plugin-extension-architecture.isolation-security.no-global-pollutionSee full patternWhen removing a plugin leaves its intervals ticking, its event listeners attached, and its registered routes still responding to requests, the application enters a state where disabled plugins continue to affect users. CWE-404 (Improper Resource Shutdown) is the direct classification. In practice this means: a removed analytics plugin continues to log user data; a deactivated payment plugin still intercepts checkout webhooks; orphaned timers accumulate until they cause performance degradation. The only reliable remediation — a full restart — creates downtime every time an operator needs to remove a plugin.
Why this severity: Medium because absent resource cleanup on plugin removal leaves intervals, listeners, and routes active after disable, requiring a full application restart to fully remove a plugin's effects.
plugin-extension-architecture.isolation-security.resource-cleanupSee full patternWithout 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.
Why this severity: 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.
plugin-extension-architecture.isolation-security.resource-limitsSee full patternWithout a declared plugin API version, every host release is a potential silent breaking change for the entire plugin ecosystem. Plugins have no way to declare compatibility, so the host cannot warn before loading an incompatible plugin, and plugin authors have no signal to know which host version their code targets. ISO 25010 maintainability.modifiability requires that the impact of changes be assessable before they are made; API versioning is the mechanism that makes that assessment possible at the plugin-host boundary.
Why this severity: High because an undeclared plugin API version prevents compatibility checking at load time, causing hook signature changes in host updates to break all installed plugins silently and without warning.
plugin-extension-architecture.versioning-contracts.api-versionedSee full patternRenaming a hook, changing its payload shape, or removing a plugin context method without a major version bump breaks every plugin in the ecosystem instantly and silently. Plugin authors discover the change when their code crashes, not from a deprecation warning or migration guide. ISO 25010 maintainability.modifiability requires that the scope of a change be predictable; semver applied to the plugin API is the mechanism that communicates scope. Without it, every minor host release carries hidden risk for the plugin ecosystem.
Why this severity: High because breaking plugin API changes shipped in minor or patch releases break the installed plugin ecosystem without warning, removing the signal plugin authors rely on to assess upgrade risk.
plugin-extension-architecture.versioning-contracts.backwards-compatSee full patternA plugin system that loads plugins by file-system convention with no manifest requirement cannot perform compatibility checking, dependency resolution, or permission enforcement before executing plugin code. The host has no metadata about a plugin's intended capabilities until after it has already run. ISO 25010 maintainability.modularity requires that components declare their dependencies and interfaces; a missing manifest makes every plugin an opaque blob. Security review of what plugins are installed and what they require becomes impossible.
Why this severity: High because the absence of a required plugin manifest prevents the host from performing any metadata-driven decision — compatibility checking, dependency resolution, or permission scoping — before executing plugin code.
plugin-extension-architecture.versioning-contracts.plugin-manifestSee full patternWhen Plugin B depends on Plugin A but the host loads plugins alphabetically or in undefined order, Plugin B crashes with a cryptic error whenever its load happens to precede Plugin A's. ISO 25010 maintainability.modularity requires that inter-component dependencies be explicit and resolvable; implicit alphabetical load order is the opposite of this. The failure is also non-deterministic across environments — it may work in development (where plugin directories happen to be in the right order) and fail in production after a deploy changes the filesystem state.
Why this severity: Low because undeclared plugin dependencies produce ordering-dependent runtime crashes that appear only when load sequence happens to be wrong, making them hard to reproduce and hard to attribute.
plugin-extension-architecture.versioning-contracts.dependency-resolutionSee full patternWhen two plugins register the same route, command ID, or decorator key and the host silently accepts both, behavior depends on which plugin loaded last — an implementation detail that changes across restarts, deploys, and config edits. ISO 25010 maintainability.modularity requires that namespace boundaries be enforced; silent last-wins collision is a namespace boundary failure. Operators cannot predict or debug behavior that changes based on plugin load order with no logged conflict.
Why this severity: Low because undetected duplicate plugin IDs or namespace collisions produce load-order-dependent behavior that is indistinguishable from intentional configuration until a conflict silently activates the wrong handler.
plugin-extension-architecture.versioning-contracts.conflict-detectionSee full patternWhen a single failing or incompatible plugin crashes the entire host on startup, operators face a binary choice: disable all plugins or accept a broken application. Nine functioning plugins are blocked by the tenth's startup error. ISO 25010 reliability.availability requires that the system remain operational when individual components fail; graceful degradation is the mechanism that makes plugin system failures scope-contained. The absence of graceful degradation also makes plugin troubleshooting a binary-search exercise rather than a directed diagnosis.
Why this severity: High because an incompatible plugin that crashes the host on startup takes down all other plugins and the entire application, making one broken plugin an availability incident affecting all users.
plugin-extension-architecture.versioning-contracts.graceful-degradationSee full patternWithout a plugin authoring guide, a developer who wants to extend the system must reverse-engineer existing plugins and read the host's internal source code. This creates a barrier that collapses the potential plugin ecosystem to however many developers are willing to invest that archaeology time — typically zero third-party developers. ISO 25010 maintainability.analysability scores fall when the system cannot be understood without execution; an authoring guide is the primary mechanism for making the plugin API understandable before writing code.
Why this severity: High because the absence of an authoring guide makes third-party plugin development practically inaccessible, limiting the plugin ecosystem to developers willing to reverse-engineer host internals.
plugin-extension-architecture.discovery-docs.authoring-guideSee full patternWhen the plugin loading mechanism is undocumented, a developer who has built a working plugin cannot install it. They do not know whether to place files in a `plugins/` directory, add an entry to a config file, or call a registration function. ISO 25010 maintainability.modularity requires that the extension point be clearly defined; an undocumented loading mechanism makes the extension point inaccessible despite technically existing. The barrier affects plugin users as much as plugin authors — a correctly built plugin that cannot be installed provides no value.
Why this severity: High because an undocumented plugin loading mechanism makes the plugin system functionally inaccessible: correctly built plugins cannot be installed by developers who cannot find the loading path.
plugin-extension-architecture.discovery-docs.loading-mechanismSee full patternA plugin authoring guide without a working example leaves developers with no reference implementation to validate their understanding. ISO 25010 maintainability.analysability requires that the system behavior be understandable; a worked example is the fastest path from zero to a functioning plugin. Without one, developers commonly misconfigure lifecycle methods, miss required manifest fields, or wire hooks incorrectly — producing broken plugins that erode trust in the system, not in their implementation.
Why this severity: Low because the absence of a working example plugin forces every plugin author to start from first principles, increasing the error rate on first-time implementations and slowing ecosystem growth.
plugin-extension-architecture.discovery-docs.example-pluginSee full patternWithout test utilities, plugin authors must start the full host application and manually trigger hooks to verify their plugin's behavior. This makes the test cycle slow, fragile, and environment-dependent. Most plugin authors will skip testing entirely, producing a plugin ecosystem where bugs are discovered in production rather than in test suites. ISO 25010 maintainability.testability requires that components be verifiable in isolation; test utilities are the mechanism that makes plugin testability possible without running the full host stack.
Why this severity: Low because the absence of plugin test utilities makes writing isolated plugin tests impractical, driving most plugin authors to skip tests entirely and discover bugs in production.
plugin-extension-architecture.discovery-docs.testing-utilitiesSee full patternWhen plugin API changes are buried in general commit history or a changelog that does not distinguish plugin-API-affecting changes from UI tweaks and bug fixes, plugin authors have no reliable way to know what changed between host versions. They discover breaking changes when their plugin crashes in production after a host upgrade. ISO 25010 maintainability.modifiability requires that the impact of modifications be traceable; a plugin API changelog is the traceability artifact that makes API evolution navigable for the plugin ecosystem.
Why this severity: Low because an unmaintained plugin API changelog forces authors to read every commit between host versions to assess upgrade risk, making plugin maintenance expensive and driving authors to avoid upgrades.
plugin-extension-architecture.discovery-docs.api-changelogSee full patternWithout a discovery mechanism, third-party plugins die in obscurity regardless of quality. Users resort to searching GitHub for unvetted repos, which fragments the ecosystem, delays adoption, and increases supply-chain risk because developers install whatever first result appears. A registry, naming convention like `eslint-plugin-*`, or curated list concentrates discovery into a trusted surface, which drives plugin author motivation, accelerates user-experience wins, and gives the core project a lever to flag abandoned or malicious extensions.
Why this severity: Info because discovery is a growth and usability concern, not a security or correctness defect blocking release.
plugin-extension-architecture.discovery-docs.marketplaceSee full patternRun this audit in your AI coding tool (Claude Code, Cursor, Bolt, etc.) and submit results here for scoring and benchmarks.
Open Plugin / Extension Architecture Audit