@verdaccio/package-filter 13.0.0-next-8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Verdaccio contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,277 @@
1
+ # @verdaccio/package-filter
2
+
3
+ [![Verdaccio Home](https://img.shields.io/badge/Homepage-Verdaccio-405236?style=flat)](https://verdaccio.org)
4
+ [![MIT License](https://img.shields.io/github/license/verdaccio/verdaccio?label=License&color=405236)](https://github.com/verdaccio/verdaccio/blob/master/LICENSE)
5
+ [![Verdaccio Latest](https://img.shields.io/npm/v/verdaccio?label=Latest%20Version&color=405236)](https://github.com/verdaccio/verdaccio)
6
+
7
+ [![Documentation](https://img.shields.io/badge/Help-Verdaccio?style=flat&logo=Verdaccio&label=Verdaccio&color=cd4000)](https://verdaccio.org/docs)
8
+ [![Discord](https://img.shields.io/badge/Chat-Discord?style=flat&logo=Discord&label=Discord&color=cd4000)](https://discord.com/channels/388674437219745793)
9
+
10
+ A built-in Verdaccio filter plugin for controlling which package versions are visible to consumers. It intercepts every manifest response and removes or replaces versions that match configurable rules.
11
+
12
+ ## Use Cases
13
+
14
+ - **Supply-chain security** - block known-malicious packages, scopes, or version ranges.
15
+ - **Version quarantine** - hide versions younger than N days so newly published code has time to be reviewed before adoption.
16
+ - **Date freeze** - pin the registry to a point-in-time snapshot (e.g., only serve versions published before a specific date).
17
+ - **Emergency response** - immediately block a compromised version while keeping older safe versions available.
18
+
19
+ ## How It Works
20
+
21
+ The plugin implements Verdaccio's `ManifestFilter` interface. Every time Verdaccio serves a package manifest (via `npm install`, `npm view`, search, or the web UI), the manifest passes through `filter_metadata()` before reaching the client.
22
+
23
+ The processing pipeline:
24
+
25
+ ```
26
+ Incoming manifest
27
+ 1. Clone manifest (avoids mutating cached data)
28
+ 2. Apply block/replace rules by package name, scope, or version range
29
+ 3. Apply date-based filtering (minAgeDays / dateThreshold)
30
+ 4. Cleanup: remove orphaned dist-tags, time entries, and dist files
31
+ 5. Recalculate "latest" tag from remaining versions
32
+ Output filtered manifest
33
+ ```
34
+
35
+ Filtered versions are removed from the manifest metadata only. Tarballs already downloaded or cached are not affected.
36
+
37
+ ## Configuration
38
+
39
+ The plugin is enabled by default in Verdaccio's `config.yaml`. With no rules configured, it acts as a no-op passthrough.
40
+
41
+ ### Minimal Configuration (no-op)
42
+
43
+ ```yaml
44
+ filters:
45
+ '@verdaccio/package-filter':
46
+ ```
47
+
48
+ ### Block Versions by Age
49
+
50
+ Hide versions published less than N days ago. This is a global rule applied to all packages.
51
+
52
+ ```yaml
53
+ filters:
54
+ '@verdaccio/package-filter':
55
+ minAgeDays: 30
56
+ ```
57
+
58
+ ### Block Versions by Date
59
+
60
+ Only serve versions published before a specific date.
61
+
62
+ ```yaml
63
+ filters:
64
+ '@verdaccio/package-filter':
65
+ dateThreshold: '2024-01-01'
66
+ ```
67
+
68
+ When both `minAgeDays` and `dateThreshold` are set, the **earlier** cutoff wins (more versions are filtered).
69
+
70
+ ### Block by Scope
71
+
72
+ Block all packages under a scope.
73
+
74
+ ```yaml
75
+ filters:
76
+ '@verdaccio/package-filter':
77
+ block:
78
+ - scope: '@evilscope'
79
+ ```
80
+
81
+ ### Block by Package Name
82
+
83
+ Block all versions of a specific package.
84
+
85
+ ```yaml
86
+ filters:
87
+ '@verdaccio/package-filter':
88
+ block:
89
+ - package: 'malicious-pkg'
90
+ - package: '@coolauthor/stolen'
91
+ ```
92
+
93
+ ### Block by Version Range
94
+
95
+ Block specific semver ranges of a package. Uses [semver](https://www.npmjs.com/package/semver) syntax.
96
+
97
+ ```yaml
98
+ filters:
99
+ '@verdaccio/package-filter':
100
+ block:
101
+ - package: '@coolauthor/stolen'
102
+ versions: '>2.0.1'
103
+ ```
104
+
105
+ Multiple version ranges for the same package are merged:
106
+
107
+ ```yaml
108
+ filters:
109
+ '@verdaccio/package-filter':
110
+ block:
111
+ - package: 'some-pkg'
112
+ versions: '>2.0.0'
113
+ - package: 'some-pkg'
114
+ versions: '<1.3.0'
115
+ ```
116
+
117
+ This leaves only versions in `[1.3.0, 2.0.0]` visible.
118
+
119
+ ### Replace Strategy
120
+
121
+ Instead of removing blocked versions, substitute them with the nearest older safe version. Useful when a blocked version is a transitive dependency and removing it would break installs.
122
+
123
+ ```yaml
124
+ filters:
125
+ '@verdaccio/package-filter':
126
+ block:
127
+ - package: '@coolauthor/stolen'
128
+ versions: '>2.0.1'
129
+ strategy: replace
130
+ ```
131
+
132
+ With `replace`, `npm install @coolauthor/stolen@3.0.0` still resolves, but the client receives the content of `2.0.1`.
133
+
134
+ ### Whitelisting Blocked Packages
135
+
136
+ In some cases, you may need to bypass your own rules and whitelist certain scopes, packages, or package versions even though they fall within a blocked area. For example, this might happen when you own some private registry or you really need the latest version of some package and you ensured that its code is safe. You can configure whitelist rules with the `allow` clause, which follows the same rules as `block`. Rules specified in `allow` take precedence over all blocking rules (even `minAgeDays` and `dateThreshold`).
137
+
138
+ ```yaml
139
+ filters:
140
+ '@verdaccio/package-filter':
141
+ minAgeDays: 30 # Block versions younger than 30 days
142
+ allow:
143
+ - scope: '@my-company-scope' # Don't block the scope that belongs to you
144
+ - package: '@coolauthor/not-stolen' # Don't block package you really trust
145
+ - package: semver
146
+ versions: '7.7.3' # Don't block specific package version that you know is not malicious
147
+ ```
148
+
149
+ You can also combine `allow` with `block` rules to create fine-grained exceptions:
150
+
151
+ ```yaml
152
+ filters:
153
+ '@verdaccio/package-filter':
154
+ block:
155
+ - scope: '@untrusted'
156
+ allow:
157
+ - package: '@untrusted/but-verified'
158
+ - package: 'some-pkg'
159
+ versions: '2.1.0'
160
+ ```
161
+
162
+ Allow rules are checked before block rules. The granularity levels:
163
+
164
+ | Allow rule | Effect |
165
+ | ------------------------------------ | ----------------------------------- |
166
+ | `scope: '@x'` | Entire scope bypasses all rules |
167
+ | `package: 'x'` | Entire package bypasses all rules |
168
+ | `package: 'x'` + `versions: '1.0.0'` | Only matching versions are exempted |
169
+
170
+ ### Full Example
171
+
172
+ ```yaml
173
+ filters:
174
+ '@verdaccio/package-filter':
175
+ minAgeDays: 7
176
+ dateThreshold: '2025-01-01'
177
+ block:
178
+ - scope: '@malicious'
179
+ - package: 'typosquat-pkg'
180
+ - package: 'compromised-lib'
181
+ versions: '>=3.0.0'
182
+ - package: 'legacy-lib'
183
+ versions: '>=2.0.0'
184
+ strategy: replace
185
+ allow:
186
+ - scope: '@my-org'
187
+ - package: 'compromised-lib'
188
+ versions: '3.0.1'
189
+ ```
190
+
191
+ ### Disabling the Plugin
192
+
193
+ Remove or comment out the `filters` section in `config.yaml`:
194
+
195
+ ```yaml
196
+ # filters:
197
+ # '@verdaccio/package-filter':
198
+ ```
199
+
200
+ ## Manifest Cleanup
201
+
202
+ After filtering, the plugin automatically cleans up the manifest:
203
+
204
+ - **Dist-tags**: tags pointing to removed versions are deleted.
205
+ - **Latest tag**: if `latest` was removed, the most recent remaining stable version becomes `latest`. If no stable version exists, the most recent pre-release is used.
206
+ - **Time entries**: publish timestamps for removed versions are deleted.
207
+ - **Created/modified**: recalculated from remaining time entries.
208
+ - **Dist files**: `_distfiles` entries not referenced by any remaining version are removed.
209
+
210
+ ## Debugging
211
+
212
+ The plugin uses the [`debug`](https://www.npmjs.com/package/debug) library under the `verdaccio:plugin:package-filter` namespace.
213
+
214
+ ```bash
215
+ # See all plugin debug output
216
+ DEBUG=verdaccio:plugin:package-filter* verdaccio
217
+
218
+ # See only config parsing
219
+ DEBUG=verdaccio:plugin:package-filter:config verdaccio
220
+
221
+ # See only filtering decisions
222
+ DEBUG=verdaccio:plugin:package-filter:filter verdaccio
223
+
224
+ # See manifest cleanup details
225
+ DEBUG=verdaccio:plugin:package-filter:manifest verdaccio
226
+
227
+ # Combine with other verdaccio debug namespaces
228
+ DEBUG=verdaccio:plugin:package-filter*,verdaccio:storage verdaccio
229
+ ```
230
+
231
+ ## Plugin History
232
+
233
+ Originally authored by Ansile as [verdaccio-plugin-secfilter](https://github.com/Ansile/verdaccio-plugin-secfilter) (MIT license). Forked by Vitalii Sugrobov as [verdaccio-plugin-delay-filter](https://github.com/vsugrob/verdaccio-plugin-delay-filter). Now maintained as a built-in plugin in the Verdaccio monorepo.
234
+
235
+ ## Donations
236
+
237
+ Verdaccio is run by **volunteers**; nobody is working full-time on it. If you find this project to be useful and would like to support its development, consider making a donation - **your logo might end up in this readme.**
238
+
239
+ **[Donate](https://opencollective.com/verdaccio)** starting from _\$1/month_ or just one single contribution.
240
+
241
+ ## Report a vulnerability
242
+
243
+ If you want to report a security vulnerability, please follow the steps which we have defined for you in our [security policy](https://github.com/verdaccio/verdaccio/security/policy).
244
+
245
+ ## Open Collective Sponsors
246
+
247
+ Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/verdaccio/contribute)]
248
+
249
+ [![sponsor](https://opencollective.com/verdaccio/sponsor/0/avatar.svg)](https://opencollective.com/verdaccio/sponsor/0/website)
250
+ [![sponsor](https://opencollective.com/verdaccio/sponsor/1/avatar.svg)](https://opencollective.com/verdaccio/sponsor/1/website)
251
+ [![sponsor](https://opencollective.com/verdaccio/sponsor/2/avatar.svg)](https://opencollective.com/verdaccio/sponsor/2/website)
252
+ [![sponsor](https://opencollective.com/verdaccio/sponsor/3/avatar.svg)](https://opencollective.com/verdaccio/sponsor/3/website)
253
+ [![sponsor](https://opencollective.com/verdaccio/sponsor/4/avatar.svg)](https://opencollective.com/verdaccio/sponsor/4/website)
254
+ [![sponsor](https://opencollective.com/verdaccio/sponsor/5/avatar.svg)](https://opencollective.com/verdaccio/sponsor/5/website)
255
+ [![sponsor](https://opencollective.com/verdaccio/sponsor/6/avatar.svg)](https://opencollective.com/verdaccio/sponsor/6/website)
256
+ [![sponsor](https://opencollective.com/verdaccio/sponsor/7/avatar.svg)](https://opencollective.com/verdaccio/sponsor/7/website)
257
+ [![sponsor](https://opencollective.com/verdaccio/sponsor/8/avatar.svg)](https://opencollective.com/verdaccio/sponsor/8/website)
258
+ [![sponsor](https://opencollective.com/verdaccio/sponsor/9/avatar.svg)](https://opencollective.com/verdaccio/sponsor/9/website)
259
+
260
+ ## Open Collective Backers
261
+
262
+ Thank you to all our backers! [[Become a backer](https://opencollective.com/verdaccio/contribute)]
263
+
264
+ [![backers](https://opencollective.com/verdaccio/backers.svg?width=890)](https://opencollective.com/verdaccio/contributes)
265
+
266
+ ## Contributors
267
+
268
+ This project exists thanks to all the people who contribute. [[Contribute](https://github.com/verdaccio/verdaccio/blob/master/CONTRIBUTING.md)].
269
+
270
+ [![contributors](https://opencollective.com/verdaccio/contributors.svg?width=890&button=true)](https://github.com/verdaccio/verdaccio/graphs/contributors)
271
+
272
+ ## License
273
+
274
+ Verdaccio is [MIT licensed](https://github.com/verdaccio/verdaccio/blob/master/LICENSE).
275
+
276
+ The Verdaccio documentation and logos (excluding /thanks, e.g., .md, .png, .sketch files within the /assets folder) are
277
+ [Creative Commons licensed](https://creativecommons.org/licenses/by/4.0/).
package/build/index.js ADDED
@@ -0,0 +1,525 @@
1
+ "use strict";
2
+ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
+ const buildDebug = require("debug");
4
+ const core = require("@verdaccio/core");
5
+ const semver = require("semver");
6
+ const debug$5 = buildDebug("verdaccio:plugin:package-filter:config");
7
+ function parseConfigRules(configRules) {
8
+ const ruleMap = /* @__PURE__ */ new Map();
9
+ for (const rule of configRules) {
10
+ if ("scope" in rule && typeof rule.scope === "string") {
11
+ if (!rule.scope.startsWith("@")) {
12
+ throw new TypeError(`Scope value must start with @, found: ${rule.scope}`);
13
+ }
14
+ ruleMap.set(rule.scope, "scope");
15
+ continue;
16
+ }
17
+ if ("package" in rule && !("versions" in rule)) {
18
+ ruleMap.set(rule.package, "package");
19
+ continue;
20
+ }
21
+ if ("package" in rule && "versions" in rule) {
22
+ const previousConfig = ruleMap.get(rule.package) || { versions: [] };
23
+ if (typeof previousConfig === "string") {
24
+ throw new Error(
25
+ `Package ${rule.package} is already specified by another strict rule ${previousConfig}`
26
+ );
27
+ }
28
+ const range = new semver.Range(rule.versions);
29
+ ruleMap.set(rule.package, {
30
+ versions: [...previousConfig.versions, range],
31
+ strategy: rule.strategy ?? "block"
32
+ });
33
+ continue;
34
+ }
35
+ throw new TypeError(`Could not parse rule ${JSON.stringify(rule, null, 4)}`);
36
+ }
37
+ return ruleMap;
38
+ }
39
+ function parseConfig(config) {
40
+ debug$5("parsing config: %o", config);
41
+ const blockMap = parseConfigRules(config.block ?? []);
42
+ const allowMap = parseConfigRules(config.allow ?? []);
43
+ debug$5("parsed %d block rules, %d allow rules", blockMap.size, allowMap.size);
44
+ const dateThreshold = config.dateThreshold ? new Date(config.dateThreshold) : null;
45
+ if (dateThreshold && isNaN(dateThreshold.getTime())) {
46
+ throw new TypeError(`Invalid date ${config.dateThreshold} was provided for dateThreshold`);
47
+ }
48
+ const minAgeDays = config.minAgeDays ? Number(config.minAgeDays) : null;
49
+ let minAgeMs = null;
50
+ if (minAgeDays !== null) {
51
+ if (isNaN(minAgeDays) || minAgeDays < 0) {
52
+ throw new TypeError(`Invalid number ${config.minAgeDays} was provided for minAgeDays`);
53
+ }
54
+ minAgeMs = minAgeDays * 24 * 60 * 60 * 1e3;
55
+ }
56
+ return {
57
+ dateThreshold,
58
+ minAgeMs,
59
+ blockRules: blockMap,
60
+ allowRules: allowMap
61
+ };
62
+ }
63
+ var MatchType = /* @__PURE__ */ ((MatchType2) => {
64
+ MatchType2["SCOPE"] = "scope";
65
+ MatchType2["PACKAGE"] = "package";
66
+ MatchType2["VERSIONS"] = "versions";
67
+ return MatchType2;
68
+ })(MatchType || {});
69
+ const debug$4 = buildDebug("verdaccio:plugin:package-filter:filter");
70
+ function splitName(name) {
71
+ if (!name) {
72
+ return { name: "" };
73
+ }
74
+ const parts = name.split("/");
75
+ if (parts.length > 1) {
76
+ return {
77
+ scope: parts[0],
78
+ name: parts[1]
79
+ };
80
+ } else {
81
+ return {
82
+ name: parts[0]
83
+ };
84
+ }
85
+ }
86
+ function matchRules(manifest, rules) {
87
+ const { scope } = splitName(manifest.name);
88
+ if (scope) {
89
+ const rule2 = rules.get(scope);
90
+ if (rule2 === "scope") {
91
+ debug$4("scope match: %s matched rule for %s", manifest.name, scope);
92
+ return {
93
+ type: MatchType.SCOPE,
94
+ rule: rule2,
95
+ scope,
96
+ versions: Object.keys(manifest.versions)
97
+ };
98
+ }
99
+ }
100
+ const rule = rules.get(manifest.name);
101
+ if (!rule) {
102
+ return void 0;
103
+ }
104
+ if (rule === "package") {
105
+ debug$4("package match: %s", manifest.name);
106
+ return {
107
+ type: MatchType.PACKAGE,
108
+ rule,
109
+ package: manifest.name,
110
+ versions: Object.keys(manifest.versions)
111
+ };
112
+ }
113
+ if (rule === "scope") {
114
+ throw new Error('Unexpected case - rule for package should never be "scope"');
115
+ }
116
+ const versionRanges = rule.versions;
117
+ if (versionRanges.length === 0) {
118
+ return void 0;
119
+ }
120
+ const matchedVersions = [];
121
+ Object.keys(manifest.versions).forEach((version) => {
122
+ versionRanges.forEach((versionRange) => {
123
+ if (semver.satisfies(version, versionRange, {
124
+ includePrerelease: true,
125
+ loose: true
126
+ })) {
127
+ matchedVersions.push(version);
128
+ }
129
+ });
130
+ });
131
+ if (matchedVersions.length > 0) {
132
+ debug$4(
133
+ "version match: %s matched %d versions: %o",
134
+ manifest.name,
135
+ matchedVersions.length,
136
+ matchedVersions
137
+ );
138
+ }
139
+ return {
140
+ type: MatchType.VERSIONS,
141
+ rule,
142
+ versions: matchedVersions
143
+ };
144
+ }
145
+ const debug$3 = buildDebug("verdaccio:plugin:package-filter:filter");
146
+ function filterBlockedVersions(manifest, blockRules, allowRules, logger) {
147
+ const allowMatch = matchRules(manifest, allowRules);
148
+ if (allowMatch && (allowMatch.type === MatchType.SCOPE || allowMatch.type === MatchType.PACKAGE)) {
149
+ logger.trace({ name: manifest.name }, "package @{name} is allow-listed, skipping block rules");
150
+ return manifest;
151
+ }
152
+ const blockMatch = matchRules(manifest, blockRules);
153
+ if (!blockMatch) {
154
+ return manifest;
155
+ }
156
+ logger.trace(
157
+ { name: manifest.name, type: blockMatch.type },
158
+ "block rule matched for @{name} (type: @{type})"
159
+ );
160
+ const whitelistedVersions = allowMatch ? allowMatch.versions : [];
161
+ let blockRule = {
162
+ versions: [new semver.Range("*")],
163
+ strategy: "block"
164
+ };
165
+ if (blockMatch.type === MatchType.SCOPE) {
166
+ if (whitelistedVersions.length === 0) {
167
+ debug$3("blocking all versions of %s (scope %s blocked)", manifest.name, blockMatch.scope);
168
+ logger.trace(
169
+ { name: manifest.name, scope: blockMatch.scope },
170
+ "all versions of @{name} blocked (scope @{scope})"
171
+ );
172
+ return {
173
+ ...manifest,
174
+ versions: {},
175
+ readme: `All packages in scope ${blockMatch.scope} are blocked by rule`
176
+ };
177
+ }
178
+ } else if (blockMatch.type === MatchType.PACKAGE) {
179
+ if (whitelistedVersions.length === 0) {
180
+ debug$3("blocking all versions of %s (package blocked)", manifest.name);
181
+ logger.trace({ name: manifest.name }, "all versions of @{name} blocked (package rule)");
182
+ return {
183
+ ...manifest,
184
+ versions: {},
185
+ readme: `All package versions are blocked by rule`
186
+ };
187
+ }
188
+ } else {
189
+ blockRule = { ...blockRule, ...blockMatch.rule };
190
+ }
191
+ const versionRanges = blockRule.versions;
192
+ if (blockRule.strategy === "block") {
193
+ const blockedVersions = blockMatch.versions.filter((v) => !whitelistedVersions.includes(v));
194
+ for (const version of blockedVersions) {
195
+ delete manifest.versions[version];
196
+ }
197
+ if (blockedVersions.length > 0) {
198
+ debug$3(
199
+ "blocked %d versions of %s: %o",
200
+ blockedVersions.length,
201
+ manifest.name,
202
+ blockedVersions
203
+ );
204
+ logger.trace(
205
+ {
206
+ name: manifest.name,
207
+ count: blockedVersions.length,
208
+ versions: blockedVersions.join(", ")
209
+ },
210
+ "@{count} versions of @{name} blocked: @{versions}"
211
+ );
212
+ manifest.readme = (manifest.readme || "") + `
213
+ Some versions(${blockedVersions.length}) of package are blocked by rules: ${versionRanges.map(
214
+ (range) => range.raw
215
+ )}`;
216
+ }
217
+ return manifest;
218
+ }
219
+ const nonBlockedVersions = { ...manifest.versions };
220
+ const newVersionsMapping = {};
221
+ versionRanges.forEach((versionRange) => {
222
+ const allVersions = Object.keys(nonBlockedVersions);
223
+ let lastNonBlockedVersion = null;
224
+ allVersions.forEach((version) => {
225
+ if (!whitelistedVersions.includes(version) && semver.satisfies(version, versionRange, {
226
+ includePrerelease: true,
227
+ loose: true
228
+ })) {
229
+ delete nonBlockedVersions[version];
230
+ newVersionsMapping[version] = lastNonBlockedVersion;
231
+ } else {
232
+ lastNonBlockedVersion = version;
233
+ }
234
+ });
235
+ });
236
+ debug$3("replacing versions for %s: %o", manifest.name, newVersionsMapping);
237
+ const removedVersions = Object.entries(newVersionsMapping).filter(
238
+ ([_, replace]) => replace === null
239
+ );
240
+ const replacedVersions = Object.entries(newVersionsMapping).filter(
241
+ ([_, replace]) => replace !== null
242
+ );
243
+ removedVersions.forEach(([version]) => {
244
+ debug$3("no version to replace %s in %s", version, manifest.name);
245
+ logger.trace(
246
+ { name: manifest.name, version },
247
+ "version @{version} of @{name} removed (no replacement available)"
248
+ );
249
+ delete manifest.versions[version];
250
+ });
251
+ replacedVersions.forEach(([version, replaceVersion]) => {
252
+ logger.trace(
253
+ { name: manifest.name, version, replaceVersion },
254
+ "version @{version} of @{name} replaced with @{replaceVersion}"
255
+ );
256
+ manifest.versions[version] = {
257
+ ...manifest.versions[replaceVersion],
258
+ version
259
+ };
260
+ });
261
+ if (removedVersions.length > 0) {
262
+ manifest.readme += `
263
+ Some versions of package could not be replaced and thus are fully blocked (${removedVersions.length}): ${removedVersions.map((a) => a[0])}`;
264
+ }
265
+ if (replacedVersions.length > 0) {
266
+ manifest.readme += `
267
+ Some versions of package are replaced by other(${replacedVersions.length}): ${replacedVersions.map((a) => `${a[0]} => ${a[1]}`)}`;
268
+ }
269
+ return manifest;
270
+ }
271
+ const debug$2 = buildDebug("verdaccio:plugin:package-filter:filter");
272
+ function filterVersionsByPublishDate(manifest, dateThreshold, allowRules) {
273
+ const allowMatch = matchRules(manifest, allowRules);
274
+ if (allowMatch && (allowMatch.type === MatchType.SCOPE || allowMatch.type === MatchType.PACKAGE)) {
275
+ return manifest;
276
+ }
277
+ const { versions, time, name } = manifest;
278
+ if (!time) {
279
+ throw new TypeError(`Time of publication was not provided for package ${name}`);
280
+ }
281
+ const whitelistedVersions = allowMatch ? allowMatch.versions : [];
282
+ const clearVersions = [];
283
+ Object.keys(versions).forEach((version) => {
284
+ if (whitelistedVersions.includes(version)) {
285
+ return;
286
+ }
287
+ const publishTime = time[version];
288
+ if (!publishTime) {
289
+ throw new TypeError(
290
+ `Time of publication was not provided for package ${name}, version ${version}`
291
+ );
292
+ }
293
+ if (new Date(publishTime) > dateThreshold) {
294
+ clearVersions.push(version);
295
+ }
296
+ });
297
+ clearVersions.forEach((version) => {
298
+ delete manifest.versions[version];
299
+ });
300
+ if (clearVersions.length > 0) {
301
+ debug$2(
302
+ "date filter removed %d versions from %s: %o",
303
+ clearVersions.length,
304
+ manifest.name,
305
+ clearVersions
306
+ );
307
+ }
308
+ return manifest;
309
+ }
310
+ function jsonLogReplacer(_key, value) {
311
+ if (value instanceof Map) {
312
+ return Object.fromEntries(value);
313
+ }
314
+ if (value instanceof semver.Range) {
315
+ return value.range;
316
+ }
317
+ return value;
318
+ }
319
+ const debug$1 = buildDebug("verdaccio:plugin:package-filter:manifest");
320
+ function cleanupTags(manifest) {
321
+ const distTags = manifest[core.DIST_TAGS];
322
+ Object.entries(distTags).forEach(([tag, tagVersion]) => {
323
+ if (!manifest.versions[tagVersion]) {
324
+ debug$1("removing orphaned dist-tag %s -> %s from %s", tag, tagVersion, manifest.name);
325
+ delete distTags[tag];
326
+ }
327
+ });
328
+ }
329
+ function cleanupTime(manifest) {
330
+ const time = manifest.time;
331
+ if (!time) {
332
+ return;
333
+ }
334
+ Object.keys(time).forEach((version) => {
335
+ if (!manifest.versions[version]) {
336
+ delete time[version];
337
+ }
338
+ });
339
+ }
340
+ function getLatestVersion(manifest, versions) {
341
+ const time = manifest.time;
342
+ if (!time) {
343
+ const sortedVersions = versions.sort(semver.rcompare);
344
+ return sortedVersions[0];
345
+ }
346
+ const timedVersions = versions.map((v) => ({
347
+ version: v,
348
+ time: time[v]
349
+ })).filter((v) => v.time);
350
+ if (timedVersions.length === 0) {
351
+ return void 0;
352
+ }
353
+ const timeOrderedVersions = timedVersions.sort(
354
+ (a, b) => new Date(b.time).getTime() - new Date(a.time).getTime()
355
+ );
356
+ return timeOrderedVersions[0].version;
357
+ }
358
+ function setupLatestTag(manifest) {
359
+ const distTags = manifest[core.DIST_TAGS];
360
+ if (distTags.latest) {
361
+ return;
362
+ }
363
+ const versions = Object.keys(manifest.versions);
364
+ if (versions.length === 0) {
365
+ return;
366
+ }
367
+ const distTagsVersions = Object.values(distTags);
368
+ const untaggedVersions = versions.filter((v) => semver.valid(v) && !distTagsVersions.includes(v));
369
+ if (untaggedVersions.length === 0) {
370
+ return;
371
+ }
372
+ const stableVersions = untaggedVersions.filter((v) => !semver.prerelease(v));
373
+ const latestStableVersion = getLatestVersion(manifest, stableVersions);
374
+ if (latestStableVersion) {
375
+ debug$1("reassigned latest tag to stable version %s for %s", latestStableVersion, manifest.name);
376
+ distTags.latest = latestStableVersion;
377
+ return;
378
+ }
379
+ const latestVersion = getLatestVersion(manifest, untaggedVersions);
380
+ if (!latestVersion) {
381
+ return;
382
+ }
383
+ debug$1("reassigned latest tag to pre-release version %s for %s", latestVersion, manifest.name);
384
+ distTags.latest = latestVersion;
385
+ }
386
+ function setupCreatedAndModified(manifest) {
387
+ const time = manifest.time;
388
+ if (!time) {
389
+ return;
390
+ }
391
+ const times = Object.values(time);
392
+ if (times.length === 0) {
393
+ return;
394
+ }
395
+ const sortedTimes = times.sort((a, b) => new Date(a).getTime() - new Date(b).getTime());
396
+ time.created = sortedTimes[0];
397
+ time.modified = sortedTimes[sortedTimes.length - 1];
398
+ }
399
+ function cleanupDistFiles(manifest) {
400
+ const distFiles = manifest._distfiles;
401
+ Object.entries(distFiles).forEach(([key, file]) => {
402
+ const fileUrl = file.url;
403
+ const versionPointingToFile = Object.values(manifest.versions).find(
404
+ (v) => v.dist?.tarball === fileUrl
405
+ );
406
+ if (!versionPointingToFile) {
407
+ delete distFiles[key];
408
+ }
409
+ });
410
+ }
411
+ function getManifestClone(manifest) {
412
+ return {
413
+ ...manifest,
414
+ versions: {
415
+ ...manifest.versions
416
+ },
417
+ [core.DIST_TAGS]: {
418
+ ...manifest[core.DIST_TAGS]
419
+ },
420
+ time: {
421
+ ...manifest.time
422
+ },
423
+ _distfiles: {
424
+ ...manifest._distfiles
425
+ }
426
+ };
427
+ }
428
+ const debug = buildDebug("verdaccio:plugin:package-filter");
429
+ class PackageFilterPlugin extends core.pluginUtils.Plugin {
430
+ config;
431
+ parsedConfig;
432
+ logger;
433
+ constructor(config, options) {
434
+ super(config, options);
435
+ this.config = config ?? {};
436
+ this.logger = options.logger;
437
+ this.parsedConfig = parseConfig(this.config);
438
+ debug(
439
+ "plugin loaded: block rules: %d, allow rules: %d",
440
+ this.parsedConfig.blockRules.size,
441
+ this.parsedConfig.allowRules.size
442
+ );
443
+ this.logger.debug(
444
+ { config: JSON.stringify(this.parsedConfig, jsonLogReplacer) },
445
+ "package-filter loaded with config: @{config}"
446
+ );
447
+ this.logger.trace(
448
+ {
449
+ blockRules: this.parsedConfig.blockRules.size,
450
+ allowRules: this.parsedConfig.allowRules.size
451
+ },
452
+ "package-filter plugin initialized: @{blockRules} block rules, @{allowRules} allow rules"
453
+ );
454
+ if (this.parsedConfig.dateThreshold) {
455
+ debug("date threshold: %s", this.parsedConfig.dateThreshold.toISOString());
456
+ this.logger.trace(
457
+ { dateThreshold: this.parsedConfig.dateThreshold.toISOString() },
458
+ "package-filter date threshold: @{dateThreshold}"
459
+ );
460
+ }
461
+ if (this.parsedConfig.minAgeMs) {
462
+ const minAgeDays = this.parsedConfig.minAgeMs / (24 * 60 * 60 * 1e3);
463
+ debug("min age: %d days", minAgeDays);
464
+ this.logger.trace({ minAgeDays }, "package-filter min age: @{minAgeDays} days");
465
+ }
466
+ }
467
+ async filter_metadata(manifest) {
468
+ const { dateThreshold, minAgeMs, blockRules, allowRules } = this.parsedConfig;
469
+ const versionCount = Object.keys(manifest.versions ?? {}).length;
470
+ debug("filtering manifest for %s (%d versions)", manifest.name, versionCount);
471
+ this.logger.trace(
472
+ { name: manifest.name, versionCount },
473
+ "package-filter processing @{name} (@{versionCount} versions)"
474
+ );
475
+ let newManifest = getManifestClone(manifest);
476
+ if (blockRules.size > 0) {
477
+ newManifest = filterBlockedVersions(newManifest, blockRules, allowRules, this.logger);
478
+ }
479
+ let earliestDateThreshold = null;
480
+ if (minAgeMs) {
481
+ earliestDateThreshold = new Date(Date.now() - minAgeMs);
482
+ }
483
+ if (dateThreshold && (!earliestDateThreshold || dateThreshold < earliestDateThreshold)) {
484
+ earliestDateThreshold = dateThreshold;
485
+ }
486
+ if (earliestDateThreshold) {
487
+ debug(
488
+ "applying date filter for %s, threshold: %s",
489
+ manifest.name,
490
+ earliestDateThreshold.toISOString()
491
+ );
492
+ this.logger.trace(
493
+ { name: manifest.name, threshold: earliestDateThreshold.toISOString() },
494
+ "applying date filter for @{name}, cutoff: @{threshold}"
495
+ );
496
+ newManifest = filterVersionsByPublishDate(newManifest, earliestDateThreshold, allowRules);
497
+ }
498
+ cleanupTags(newManifest);
499
+ setupLatestTag(newManifest);
500
+ cleanupTime(newManifest);
501
+ setupCreatedAndModified(newManifest);
502
+ cleanupDistFiles(newManifest);
503
+ const filteredCount = Object.keys(newManifest.versions).length;
504
+ const removedCount = versionCount - filteredCount;
505
+ if (filteredCount !== versionCount) {
506
+ debug(
507
+ "filtered %s: %d -> %d versions (%d removed)",
508
+ manifest.name,
509
+ versionCount,
510
+ filteredCount,
511
+ removedCount
512
+ );
513
+ this.logger.trace(
514
+ { name: manifest.name, before: versionCount, after: filteredCount, removed: removedCount },
515
+ "package-filter @{name}: @{before} -> @{after} versions (@{removed} removed)"
516
+ );
517
+ } else {
518
+ debug("no versions filtered for %s", manifest.name);
519
+ }
520
+ return newManifest;
521
+ }
522
+ }
523
+ exports.PackageFilterPlugin = PackageFilterPlugin;
524
+ exports.default = PackageFilterPlugin;
525
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/config/parser.ts","../src/filtering/types.ts","../src/filtering/matcher.ts","../src/filtering/packageVersion.ts","../src/filtering/publishDate.ts","../src/utils/jsonUtils.ts","../src/utils/manifestUtils.ts","../src/packageFilter.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Range } from 'semver';\n\nimport type { ConfigRule, ParsedConfig, ParsedRule, PluginConfig } from './types';\n\nconst debug = buildDebug('verdaccio:plugin:package-filter:config');\n\nfunction parseConfigRules(configRules: ConfigRule[]): Map<string, ParsedRule> {\n const ruleMap = new Map<string, ParsedRule>();\n for (const rule of configRules) {\n if ('scope' in rule && typeof rule.scope === 'string') {\n if (!rule.scope.startsWith('@')) {\n throw new TypeError(`Scope value must start with @, found: ${rule.scope}`);\n }\n\n ruleMap.set(rule.scope, 'scope');\n continue;\n }\n\n if ('package' in rule && !('versions' in rule)) {\n ruleMap.set(rule.package, 'package');\n continue;\n }\n\n if ('package' in rule && 'versions' in rule) {\n const previousConfig = ruleMap.get(rule.package) || { versions: [] };\n\n if (typeof previousConfig === 'string') {\n throw new Error(\n `Package ${rule.package} is already specified by another strict rule ${previousConfig}`\n );\n }\n\n // Merge version ranges of the rules for the same package\n const range = new Range(rule.versions);\n ruleMap.set(rule.package, {\n versions: [...previousConfig.versions, range],\n strategy: rule.strategy ?? 'block',\n });\n\n continue;\n }\n\n throw new TypeError(`Could not parse rule ${JSON.stringify(rule, null, 4)}`);\n }\n\n return ruleMap;\n}\n\nexport function parseConfig(config: PluginConfig): ParsedConfig {\n debug('parsing config: %o', config);\n const blockMap = parseConfigRules(config.block ?? []);\n const allowMap = parseConfigRules(config.allow ?? []);\n debug('parsed %d block rules, %d allow rules', blockMap.size, allowMap.size);\n const dateThreshold = config.dateThreshold ? new Date(config.dateThreshold) : null;\n if (dateThreshold && isNaN(dateThreshold.getTime())) {\n throw new TypeError(`Invalid date ${config.dateThreshold} was provided for dateThreshold`);\n }\n\n const minAgeDays = config.minAgeDays ? Number(config.minAgeDays) : null;\n let minAgeMs: number | null = null;\n if (minAgeDays !== null) {\n if (isNaN(minAgeDays) || minAgeDays < 0) {\n throw new TypeError(`Invalid number ${config.minAgeDays} was provided for minAgeDays`);\n }\n\n minAgeMs = minAgeDays * 24 * 60 * 60 * 1000;\n }\n\n return {\n dateThreshold,\n minAgeMs,\n blockRules: blockMap,\n allowRules: allowMap,\n };\n}\n","import type { PackageScopeLevel, ParsedConfigRule } from '../config/types';\n\nexport enum MatchType {\n SCOPE = 'scope',\n PACKAGE = 'package',\n VERSIONS = 'versions',\n}\n\nexport interface MatchResultVersions {\n versions: string[];\n}\n\nexport interface MatchScopeResult extends MatchResultVersions {\n type: MatchType.SCOPE;\n rule: PackageScopeLevel;\n scope: string;\n}\n\nexport interface MatchPackageResult extends MatchResultVersions {\n type: MatchType.PACKAGE;\n rule: PackageScopeLevel;\n package: string;\n}\n\nexport interface MatchVersionsResult extends MatchResultVersions {\n type: MatchType.VERSIONS;\n rule: ParsedConfigRule;\n}\n\nexport type MatchResult = MatchScopeResult | MatchPackageResult | MatchVersionsResult;\n","import buildDebug from 'debug';\nimport { satisfies } from 'semver';\n\nimport type { Manifest } from '@verdaccio/types';\n\nimport type { ParsedRule } from '../config/types';\nimport type { MatchResult } from './types';\nimport { MatchType } from './types';\n\nconst debug = buildDebug('verdaccio:plugin:package-filter:filter');\n\n/**\n * Split a package name into name itself and scope.\n */\nfunction splitName(name: string): { name: string; scope?: string } {\n if (!name) {\n return { name: '' };\n }\n\n const parts = name.split('/');\n\n if (parts.length > 1) {\n return {\n scope: parts[0],\n name: parts[1],\n };\n } else {\n return {\n name: parts[0],\n };\n }\n}\n\n/**\n * Try to find a rule that matches the package.\n * If found, returns the rule and the matched package versions from the manifest.\n */\nexport function matchRules(\n manifest: Manifest,\n rules: Map<string, ParsedRule>\n): MatchResult | undefined {\n const { scope } = splitName(manifest.name);\n if (scope) {\n const rule = rules.get(scope);\n if (rule === 'scope') {\n debug('scope match: %s matched rule for %s', manifest.name, scope);\n return {\n type: MatchType.SCOPE,\n rule,\n scope,\n versions: Object.keys(manifest.versions),\n };\n }\n }\n\n const rule = rules.get(manifest.name);\n if (!rule) {\n // No match\n return undefined;\n }\n\n if (rule === 'package') {\n debug('package match: %s', manifest.name);\n return {\n type: MatchType.PACKAGE,\n rule,\n package: manifest.name,\n versions: Object.keys(manifest.versions),\n };\n }\n\n if (rule === 'scope') {\n throw new Error('Unexpected case - rule for package should never be \"scope\"');\n }\n\n const versionRanges = rule.versions;\n if (versionRanges.length === 0) {\n // No match\n return undefined;\n }\n\n const matchedVersions: string[] = [];\n Object.keys(manifest.versions).forEach((version) => {\n versionRanges.forEach((versionRange) => {\n if (\n satisfies(version, versionRange, {\n includePrerelease: true,\n loose: true,\n })\n ) {\n matchedVersions.push(version);\n }\n });\n });\n\n if (matchedVersions.length > 0) {\n debug(\n 'version match: %s matched %d versions: %o',\n manifest.name,\n matchedVersions.length,\n matchedVersions\n );\n }\n\n return {\n type: MatchType.VERSIONS,\n rule,\n versions: matchedVersions,\n };\n}\n","import buildDebug from 'debug';\nimport { Range, satisfies } from 'semver';\n\nimport type { Logger, Manifest } from '@verdaccio/types';\n\nimport type { ParsedConfigRule, ParsedRule } from '../config/types';\nimport { matchRules } from './matcher';\nimport { MatchType } from './types';\n\nconst debug = buildDebug('verdaccio:plugin:package-filter:filter');\n\n/**\n * Filter out all blocked package versions.\n * If package or scope is blocked, then block all versions.\n *\n * TODO: consider adding a `prerelease` filter option to block/allow\n * prerelease versions independently. Currently prereleases are matched by\n * semver ranges with `includePrerelease: true`, but there's no dedicated toggle.\n *\n * Example config (not yet implemented):\n * block:\n * - package: 'foo'\n * prerelease: true # block 1.0.0-beta.1, 2.0.0-rc.1, keep 1.0.0, 2.0.0\n * - package: 'bar'\n * prerelease: false # block 1.0.0, 2.0.0, keep 1.0.0-beta.1\n *\n * Today the only workaround is using awkward ranges like \">=1.0.0-0 <1.0.0\".\n */\nexport function filterBlockedVersions(\n manifest: Manifest,\n blockRules: Map<string, ParsedRule>,\n allowRules: Map<string, ParsedRule>,\n logger: Logger\n): Manifest {\n const allowMatch = matchRules(manifest, allowRules);\n if (\n allowMatch &&\n (allowMatch.type === MatchType.SCOPE || allowMatch.type === MatchType.PACKAGE)\n ) {\n // An entire scope or package is whitelisted\n logger.trace({ name: manifest.name }, 'package @{name} is allow-listed, skipping block rules');\n return manifest;\n }\n\n const blockMatch = matchRules(manifest, blockRules);\n if (!blockMatch) {\n // No rule is blocking this package\n return manifest;\n }\n\n logger.trace(\n { name: manifest.name, type: blockMatch.type },\n 'block rule matched for @{name} (type: @{type})'\n );\n\n const whitelistedVersions: string[] = allowMatch ? allowMatch.versions : [];\n let blockRule: ParsedConfigRule = {\n versions: [new Range('*')],\n strategy: 'block',\n };\n\n if (blockMatch.type === MatchType.SCOPE) {\n if (whitelistedVersions.length === 0) {\n debug('blocking all versions of %s (scope %s blocked)', manifest.name, blockMatch.scope);\n logger.trace(\n { name: manifest.name, scope: blockMatch.scope },\n 'all versions of @{name} blocked (scope @{scope})'\n );\n return {\n ...manifest,\n versions: {},\n readme: `All packages in scope ${blockMatch.scope} are blocked by rule`,\n };\n }\n } else if (blockMatch.type === MatchType.PACKAGE) {\n if (whitelistedVersions.length === 0) {\n debug('blocking all versions of %s (package blocked)', manifest.name);\n logger.trace({ name: manifest.name }, 'all versions of @{name} blocked (package rule)');\n return {\n ...manifest,\n versions: {},\n readme: `All package versions are blocked by rule`,\n };\n }\n } else {\n blockRule = { ...blockRule, ...blockMatch.rule };\n }\n\n const versionRanges = blockRule.versions;\n\n if (blockRule.strategy === 'block') {\n const blockedVersions = blockMatch.versions.filter((v) => !whitelistedVersions.includes(v));\n for (const version of blockedVersions) {\n delete manifest.versions[version];\n }\n\n if (blockedVersions.length > 0) {\n debug(\n 'blocked %d versions of %s: %o',\n blockedVersions.length,\n manifest.name,\n blockedVersions\n );\n logger.trace(\n {\n name: manifest.name,\n count: blockedVersions.length,\n versions: blockedVersions.join(', '),\n },\n '@{count} versions of @{name} blocked: @{versions}'\n );\n // Add debug info for devs\n manifest.readme =\n (manifest.readme || '') +\n `\\nSome versions(${blockedVersions.length}) of package are blocked by rules: ${versionRanges.map(\n (range) => range.raw\n )}`;\n }\n\n return manifest;\n }\n\n // Process block rule strategy 'replace'.\n // We assume that the order of versions is already sorted.\n const nonBlockedVersions = { ...manifest.versions };\n const newVersionsMapping: Record<string, string | null> = {};\n\n versionRanges.forEach((versionRange) => {\n const allVersions = Object.keys(nonBlockedVersions);\n\n let lastNonBlockedVersion: string | null = null;\n\n allVersions.forEach((version) => {\n if (\n !whitelistedVersions.includes(version) &&\n satisfies(version, versionRange, {\n includePrerelease: true,\n loose: true,\n })\n ) {\n delete nonBlockedVersions[version];\n newVersionsMapping[version] = lastNonBlockedVersion;\n } else {\n lastNonBlockedVersion = version;\n }\n });\n });\n\n debug('replacing versions for %s: %o', manifest.name, newVersionsMapping);\n\n const removedVersions = Object.entries(newVersionsMapping).filter(\n ([_, replace]) => replace === null\n ) as [string, null][];\n const replacedVersions = Object.entries(newVersionsMapping).filter(\n ([_, replace]) => replace !== null\n ) as [string, string][];\n\n removedVersions.forEach(([version]) => {\n debug('no version to replace %s in %s', version, manifest.name);\n logger.trace(\n { name: manifest.name, version },\n 'version @{version} of @{name} removed (no replacement available)'\n );\n delete manifest.versions[version];\n });\n\n replacedVersions.forEach(([version, replaceVersion]) => {\n logger.trace(\n { name: manifest.name, version, replaceVersion },\n 'version @{version} of @{name} replaced with @{replaceVersion}'\n );\n manifest.versions[version] = {\n ...manifest.versions[replaceVersion],\n version,\n };\n });\n\n if (removedVersions.length > 0) {\n manifest.readme +=\n `\\nSome versions of package could not be replaced and thus are fully blocked (${removedVersions.length}):` +\n ` ${removedVersions.map((a) => a[0])}`;\n }\n\n if (replacedVersions.length > 0) {\n manifest.readme +=\n `\\nSome versions of package are replaced by other(${replacedVersions.length}):` +\n ` ${replacedVersions.map((a) => `${a[0]} => ${a[1]}`)}`;\n }\n\n return manifest;\n}\n","import buildDebug from 'debug';\n\nimport type { Manifest } from '@verdaccio/types';\n\nimport type { ParsedRule } from '../config/types';\nimport { matchRules } from './matcher';\nimport { MatchType } from './types';\n\nconst debug = buildDebug('verdaccio:plugin:package-filter:filter');\n\n/**\n * Filter out all package versions that were published after dateThreshold.\n */\nexport function filterVersionsByPublishDate(\n manifest: Manifest,\n dateThreshold: Date,\n allowRules: Map<string, ParsedRule>\n): Manifest {\n const allowMatch = matchRules(manifest, allowRules);\n if (\n allowMatch &&\n (allowMatch.type === MatchType.SCOPE || allowMatch.type === MatchType.PACKAGE)\n ) {\n // An entire scope or package is whitelisted\n return manifest;\n }\n\n const { versions, time, name } = manifest;\n\n if (!time) {\n throw new TypeError(`Time of publication was not provided for package ${name}`);\n }\n\n const whitelistedVersions: string[] = allowMatch ? allowMatch.versions : [];\n const clearVersions: string[] = [];\n\n Object.keys(versions).forEach((version) => {\n if (whitelistedVersions.includes(version)) {\n return;\n }\n\n const publishTime = time[version];\n\n if (!publishTime) {\n throw new TypeError(\n `Time of publication was not provided for package ${name}, version ${version}`\n );\n }\n\n if (new Date(publishTime) > dateThreshold) {\n // clear untrusted version\n clearVersions.push(version);\n }\n });\n\n // delete version from versions\n clearVersions.forEach((version) => {\n delete manifest.versions[version];\n });\n\n if (clearVersions.length > 0) {\n debug(\n 'date filter removed %d versions from %s: %o',\n clearVersions.length,\n manifest.name,\n clearVersions\n );\n }\n\n return manifest;\n}\n","import { Range } from 'semver';\n\nexport function jsonLogReplacer(_key: string, value: unknown) {\n if (value instanceof Map) {\n return Object.fromEntries(value);\n }\n\n if (value instanceof Range) {\n return value.range;\n }\n\n return value;\n}\n","import buildDebug from 'debug';\nimport semver from 'semver';\n\nimport { DIST_TAGS } from '@verdaccio/core';\nimport type { Manifest } from '@verdaccio/types';\n\nconst debug = buildDebug('verdaccio:plugin:package-filter:manifest');\n\n/**\n * Delete `dist-tags` entries corresponding to missing versions.\n */\nexport function cleanupTags(manifest: Manifest): void {\n const distTags = manifest[DIST_TAGS];\n Object.entries(distTags).forEach(([tag, tagVersion]) => {\n if (!manifest.versions[tagVersion]) {\n debug('removing orphaned dist-tag %s -> %s from %s', tag, tagVersion, manifest.name);\n delete distTags[tag];\n }\n });\n}\n\n/**\n * Delete `time` entries corresponding to missing versions.\n */\nexport function cleanupTime(manifest: Manifest): void {\n const time = manifest.time;\n if (!time) {\n return;\n }\n\n Object.keys(time).forEach((version) => {\n if (!manifest.versions[version]) {\n delete time[version];\n }\n });\n}\n\n/**\n * Get the latest version from a list of versions,\n * ordered by time of their publication stored in the manifest.\n */\nexport function getLatestVersion(manifest: Manifest, versions: string[]): string | undefined {\n const time = manifest.time;\n if (!time) {\n // No time information, it's the best we can do\n const sortedVersions = versions.sort(semver.rcompare);\n return sortedVersions[0];\n }\n\n const timedVersions = versions\n .map((v) => ({\n version: v,\n time: time[v],\n }))\n .filter((v) => v.time);\n\n if (timedVersions.length === 0) {\n return undefined;\n }\n\n const timeOrderedVersions = timedVersions.sort(\n (a, b) => new Date(b.time).getTime() - new Date(a.time).getTime()\n );\n return timeOrderedVersions[0].version;\n}\n\n/**\n * Set the latest tag if dist-tags/latest is missing.\n * The last stable version available is used when possible.\n * Otherwise, it uses the latest version not found in dist-tags.\n */\nexport function setupLatestTag(manifest: Manifest): void {\n const distTags = manifest[DIST_TAGS];\n if (distTags.latest) {\n // Tag 'latest' must only be fixed when latest version was blocked\n return;\n }\n\n const versions = Object.keys(manifest.versions);\n if (versions.length === 0) {\n return;\n }\n\n const distTagsVersions = Object.values(distTags);\n const untaggedVersions = versions.filter((v) => semver.valid(v) && !distTagsVersions.includes(v));\n if (untaggedVersions.length === 0) {\n return;\n }\n\n // Try stable versions first (no \"-next\" or \"-beta\", etc.)\n const stableVersions = untaggedVersions.filter((v) => !semver.prerelease(v));\n const latestStableVersion = getLatestVersion(manifest, stableVersions);\n if (latestStableVersion) {\n debug('reassigned latest tag to stable version %s for %s', latestStableVersion, manifest.name);\n distTags.latest = latestStableVersion;\n return;\n }\n\n // Fallback to all untagged versions\n const latestVersion = getLatestVersion(manifest, untaggedVersions);\n if (!latestVersion) {\n return;\n }\n\n debug('reassigned latest tag to pre-release version %s for %s', latestVersion, manifest.name);\n distTags.latest = latestVersion;\n}\n\n/**\n * Set the created and modified times.\n */\nexport function setupCreatedAndModified(manifest: Manifest): void {\n const time = manifest.time;\n if (!time) {\n return;\n }\n\n const times = Object.values(time);\n if (times.length === 0) {\n return;\n }\n\n const sortedTimes = times.sort((a, b) => new Date(a).getTime() - new Date(b).getTime());\n time.created = sortedTimes[0];\n time.modified = sortedTimes[sortedTimes.length - 1];\n}\n\n/**\n * Remove `_distfiles` entries which are not used by any version.\n */\nexport function cleanupDistFiles(manifest: Manifest): void {\n const distFiles = manifest._distfiles;\n Object.entries(distFiles).forEach(([key, file]) => {\n const fileUrl = file.url;\n const versionPointingToFile = Object.values(manifest.versions).find(\n (v) => v.dist?.tarball === fileUrl\n );\n\n if (!versionPointingToFile) {\n delete distFiles[key];\n }\n });\n}\n\n/**\n * Creates a copy of a manifest suitable for safe, localized mutation.\n *\n * The returned object is shallow-cloned, except for `versions`, `dist-tags`,\n * `time`, and `_distfiles`, which are cloned as independent maps so they can be\n * filtered or modified without affecting the original manifest.\n */\nexport function getManifestClone(manifest: Readonly<Manifest>): Manifest {\n return {\n ...manifest,\n versions: {\n ...manifest.versions,\n },\n [DIST_TAGS]: {\n ...manifest[DIST_TAGS],\n },\n time: {\n ...manifest.time,\n },\n _distfiles: {\n ...manifest._distfiles,\n },\n };\n}\n","import buildDebug from 'debug';\n\nimport { pluginUtils } from '@verdaccio/core';\nimport type { Logger, Manifest } from '@verdaccio/types';\n\nimport { parseConfig } from './config/parser';\nimport type { ParsedConfig, PluginConfig } from './config/types';\nimport { filterBlockedVersions } from './filtering/packageVersion';\nimport { filterVersionsByPublishDate } from './filtering/publishDate';\nimport { jsonLogReplacer } from './utils/jsonUtils';\nimport {\n cleanupDistFiles,\n cleanupTags,\n cleanupTime,\n getManifestClone,\n setupCreatedAndModified,\n setupLatestTag,\n} from './utils/manifestUtils';\n\nconst debug = buildDebug('verdaccio:plugin:package-filter');\n\nexport class PackageFilterPlugin\n extends pluginUtils.Plugin<PluginConfig>\n implements pluginUtils.ManifestFilter<PluginConfig>\n{\n public readonly config: PluginConfig;\n private readonly parsedConfig: ParsedConfig;\n protected readonly logger: Logger;\n\n public constructor(config: PluginConfig, options: pluginUtils.PluginOptions) {\n super(config, options);\n this.config = config ?? {};\n this.logger = options.logger;\n this.parsedConfig = parseConfig(this.config);\n\n debug(\n 'plugin loaded: block rules: %d, allow rules: %d',\n this.parsedConfig.blockRules.size,\n this.parsedConfig.allowRules.size\n );\n this.logger.debug(\n { config: JSON.stringify(this.parsedConfig, jsonLogReplacer) },\n 'package-filter loaded with config: @{config}'\n );\n this.logger.trace(\n {\n blockRules: this.parsedConfig.blockRules.size,\n allowRules: this.parsedConfig.allowRules.size,\n },\n 'package-filter plugin initialized: @{blockRules} block rules, @{allowRules} allow rules'\n );\n if (this.parsedConfig.dateThreshold) {\n debug('date threshold: %s', this.parsedConfig.dateThreshold.toISOString());\n this.logger.trace(\n { dateThreshold: this.parsedConfig.dateThreshold.toISOString() },\n 'package-filter date threshold: @{dateThreshold}'\n );\n }\n if (this.parsedConfig.minAgeMs) {\n const minAgeDays = this.parsedConfig.minAgeMs / (24 * 60 * 60 * 1000);\n debug('min age: %d days', minAgeDays);\n this.logger.trace({ minAgeDays }, 'package-filter min age: @{minAgeDays} days');\n }\n }\n\n public async filter_metadata(manifest: Readonly<Manifest>): Promise<Manifest> {\n const { dateThreshold, minAgeMs, blockRules, allowRules } = this.parsedConfig;\n const versionCount = Object.keys(manifest.versions ?? {}).length;\n debug('filtering manifest for %s (%d versions)', manifest.name, versionCount);\n this.logger.trace(\n { name: manifest.name, versionCount },\n 'package-filter processing @{name} (@{versionCount} versions)'\n );\n\n let newManifest = getManifestClone(manifest);\n if (blockRules.size > 0) {\n newManifest = filterBlockedVersions(newManifest, blockRules, allowRules, this.logger);\n }\n\n let earliestDateThreshold: Date | null = null;\n if (minAgeMs) {\n earliestDateThreshold = new Date(Date.now() - minAgeMs);\n }\n\n if (dateThreshold && (!earliestDateThreshold || dateThreshold < earliestDateThreshold)) {\n earliestDateThreshold = dateThreshold;\n }\n\n if (earliestDateThreshold) {\n debug(\n 'applying date filter for %s, threshold: %s',\n manifest.name,\n earliestDateThreshold.toISOString()\n );\n this.logger.trace(\n { name: manifest.name, threshold: earliestDateThreshold.toISOString() },\n 'applying date filter for @{name}, cutoff: @{threshold}'\n );\n newManifest = filterVersionsByPublishDate(newManifest, earliestDateThreshold, allowRules);\n }\n\n cleanupTags(newManifest);\n setupLatestTag(newManifest);\n cleanupTime(newManifest);\n setupCreatedAndModified(newManifest);\n cleanupDistFiles(newManifest);\n\n const filteredCount = Object.keys(newManifest.versions).length;\n const removedCount = versionCount - filteredCount;\n if (filteredCount !== versionCount) {\n debug(\n 'filtered %s: %d -> %d versions (%d removed)',\n manifest.name,\n versionCount,\n filteredCount,\n removedCount\n );\n this.logger.trace(\n { name: manifest.name, before: versionCount, after: filteredCount, removed: removedCount },\n 'package-filter @{name}: @{before} -> @{after} versions (@{removed} removed)'\n );\n } else {\n debug('no versions filtered for %s', manifest.name);\n }\n\n return newManifest;\n }\n}\n"],"names":["debug","Range","MatchType","rule","satisfies","DIST_TAGS","pluginUtils"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@verdaccio/package-filter",
3
+ "version": "13.0.0-next-8.2",
4
+ "description": "Package filter plugin for Verdaccio that allows blocking packages by name, scope, version or date",
5
+ "keywords": [
6
+ "private",
7
+ "package",
8
+ "repository",
9
+ "registry",
10
+ "enterprise",
11
+ "modules",
12
+ "proxy",
13
+ "server",
14
+ "verdaccio",
15
+ "plugin",
16
+ "filter"
17
+ ],
18
+ "author": "Vitalii Sugrobov <vsugrob@hotmail.com>",
19
+ "contributors": [
20
+ "Ansile <ansilet@yandex.ru> (original project)"
21
+ ],
22
+ "license": "MIT",
23
+ "homepage": "https://verdaccio.org",
24
+ "repository": {
25
+ "type": "https",
26
+ "url": "https://github.com/verdaccio/verdaccio",
27
+ "directory": "packages/plugins/package-filter"
28
+ },
29
+ "bugs": {
30
+ "url": "https://github.com/verdaccio/verdaccio/issues"
31
+ },
32
+ "main": "build/index.js",
33
+ "types": "build/index.d.ts",
34
+ "engines": {
35
+ "node": ">=18"
36
+ },
37
+ "dependencies": {
38
+ "@verdaccio/core": "8.0.0-next-8.35",
39
+ "debug": "4.4.3",
40
+ "semver": "7.7.4"
41
+ },
42
+ "devDependencies": {
43
+ "@types/debug": "4.1.12",
44
+ "@verdaccio/config": "8.0.0-next-8.35",
45
+ "@verdaccio/logger": "8.0.0-next-8.35",
46
+ "@verdaccio/types": "13.0.0-next-8.11",
47
+ "vite": "7.3.1",
48
+ "vitest": "3.2.4"
49
+ },
50
+ "funding": {
51
+ "type": "opencollective",
52
+ "url": "https://opencollective.com/verdaccio"
53
+ },
54
+ "scripts": {
55
+ "clean": "rimraf ./build",
56
+ "build": "vite build --config vite.build.config.mjs",
57
+ "watch": "vite build --watch --config vite.build.config.mjs",
58
+ "test": "vitest run"
59
+ }
60
+ }