fix: enforce plugin entry limit during discovery - #146
Open
GautamSharma99 wants to merge 1 commit into
Open
Conversation
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
Fixes #134.
Enforce the configured-plugin 4,096-entry ceiling while directory entries are discovered instead of after an entire directory has already been loaded into memory.
Previously, both plugin fingerprinting and marketplace copying called
readdir()for each directory. A directory containing hundreds of thousands of children was therefore materialized as one complete name array before any of those children reached the existing entry counter. Cancellation also could not be observed until that full read completed.Changes
Incremental directory iteration
Configured plugin traversal now uses one shared asynchronous
opendir()iterator for both:The iterator reads one
Direntat a time withDir.read()and yields only its name. No traversal path creates a complete array of directory children.Discovery-time entry enforcement
The traversal root remains counted as the first entry, preserving the existing 4,096-entry semantics.
Each child is now counted immediately after it is yielded and before it is added to the traversal queue. The 4,097th discovered path therefore raises the existing
PluginBootstrapErrorimmediately, without reading the remaining names in that directory.This applies identically to fingerprint construction and safe-copy traversal.
Cancellation during discovery
The abort signal is checked:
Cancellation can now interrupt a large, flat directory rather than waiting for every name to be allocated first.
Directory-handle cleanup
The shared iterator closes its directory handle in a
finallyblock. Handles are therefore released after:Existing integrity boundaries preserved
The change retains the existing protections around configured plugin directories:
lstat()before processing;Fingerprint paths remain sorted before hashing, so switching to incremental directory iteration does not change fingerprint determinism.
Security and resource impact
A malicious or accidentally enormous flat plugin directory can no longer bypass the practical value of the configured 4,096-entry limit by forcing one unbounded
readdir()allocation.Directory plugins now have the same entry-by-entry resource-boundary behavior already used for ZIP plugin extraction, and cancellation remains responsive throughout discovery.
Tests
Added regression coverage for:
AbortErrorreason;Existing configured-plugin, marketplace projection, symlink rejection, queued-directory identity race, ZIP limit, and runtime tests continue to pass.
Verification
pnpm run typespnpm run formatpnpm run buildPATH="/opt/homebrew/bin:$PATH" pnpm run testFull test result: