verdaccio-security-filter 1.3.1 → 1.3.3
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/build/index.d.ts +0 -58
- package/build/index.js +1 -97
- package/build/lib/author-checker.d.ts +0 -19
- package/build/lib/author-checker.js +5 -31
- package/build/lib/cve-checker.d.ts +0 -37
- package/build/lib/cve-checker.js +0 -44
- package/build/lib/license-checker.d.ts +0 -21
- package/build/lib/license-checker.js +0 -30
- package/build/lib/logger.d.ts +0 -21
- package/build/lib/logger.js +0 -21
- package/build/lib/lru-cache.d.ts +0 -3
- package/build/lib/lru-cache.js +0 -3
- package/build/lib/metrics.d.ts +0 -36
- package/build/lib/metrics.js +0 -38
- package/build/lib/package-age-checker.d.ts +0 -22
- package/build/lib/package-age-checker.js +0 -29
- package/build/lib/whitelist-checker.d.ts +0 -26
- package/build/lib/whitelist-checker.js +0 -37
- package/build/types.d.ts +0 -3
- package/package.json +8 -3
package/build/index.d.ts
CHANGED
|
@@ -2,15 +2,6 @@ import { IPluginMiddleware, IPluginStorageFilter, PluginOptions, IBasicAuth, ISt
|
|
|
2
2
|
import { Application } from 'express';
|
|
3
3
|
import { SecurityConfig } from './types';
|
|
4
4
|
import { SecurityLogger } from './lib/logger';
|
|
5
|
-
/**
|
|
6
|
-
* Security Filter Plugin for Verdaccio
|
|
7
|
-
* Works with both middleware and filter interfaces for complete protection
|
|
8
|
-
* Compatible with Verdaccio 6.x and 7.x
|
|
9
|
-
*
|
|
10
|
-
* Two-layer protection:
|
|
11
|
-
* - Layer 1 (filter_metadata): CVE/License/Age checks when metadata is available
|
|
12
|
-
* - Layer 2 (middleware): Whitelist/Pattern/Scope checks + tarball blocking
|
|
13
|
-
*/
|
|
14
5
|
export default class SecurityFilterPlugin implements IPluginMiddleware<SecurityConfig>, IPluginStorageFilter<SecurityConfig> {
|
|
15
6
|
logger: SecurityLogger;
|
|
16
7
|
config: SecurityConfig;
|
|
@@ -21,66 +12,17 @@ export default class SecurityFilterPlugin implements IPluginMiddleware<SecurityC
|
|
|
21
12
|
private readonly licenseChecker;
|
|
22
13
|
private readonly packageAgeChecker;
|
|
23
14
|
private readonly authorChecker;
|
|
24
|
-
/**
|
|
25
|
-
* Creates a new SecurityFilterPlugin instance
|
|
26
|
-
* @param {SecurityConfig} config - Plugin configuration
|
|
27
|
-
* @param {PluginOptions} options - Verdaccio plugin options
|
|
28
|
-
*/
|
|
29
15
|
constructor(config: SecurityConfig, options: PluginOptions<SecurityConfig>);
|
|
30
|
-
/**
|
|
31
|
-
* Parse and validate version range rules
|
|
32
|
-
* @private
|
|
33
|
-
*/
|
|
34
16
|
private _parseVersionRangeRules;
|
|
35
|
-
/**
|
|
36
|
-
* Log initialization details
|
|
37
|
-
* @private
|
|
38
|
-
*/
|
|
39
17
|
private _logInitialization;
|
|
40
|
-
/**
|
|
41
|
-
* Get list of enabled features
|
|
42
|
-
* @private
|
|
43
|
-
*/
|
|
44
18
|
private _getEnabledFeatures;
|
|
45
|
-
/**
|
|
46
|
-
* Check if package name matches blocked patterns
|
|
47
|
-
* @private
|
|
48
|
-
*/
|
|
49
19
|
private _isBlockedByPattern;
|
|
50
|
-
/**
|
|
51
|
-
* Check if package scope is allowed
|
|
52
|
-
* @private
|
|
53
|
-
*/
|
|
54
20
|
private _isScopeAllowed;
|
|
55
|
-
/**
|
|
56
|
-
* Get version range rule for a specific package and version
|
|
57
|
-
* @private
|
|
58
|
-
*/
|
|
59
21
|
private _getVersionRangeRule;
|
|
60
|
-
/**
|
|
61
|
-
* Register Express middleware to intercept package requests
|
|
62
|
-
* Intercepts both metadata and tarball requests
|
|
63
|
-
*/
|
|
64
22
|
register_middlewares(app: Application, _auth: IBasicAuth<SecurityConfig>, _storage: IStorageManager<SecurityConfig>): void;
|
|
65
|
-
/**
|
|
66
|
-
* Filter package metadata before returning to client
|
|
67
|
-
* This provides CVE, License, and Age checking when metadata is available
|
|
68
|
-
*/
|
|
69
23
|
filter_metadata(packageInfo: Package): Promise<Package>;
|
|
70
|
-
/**
|
|
71
|
-
* Check if package should be blocked (used by middleware)
|
|
72
|
-
* @private
|
|
73
|
-
*/
|
|
74
24
|
private _checkPackageBlock;
|
|
75
|
-
/**
|
|
76
|
-
* Get applied security rules for a package
|
|
77
|
-
* @private
|
|
78
|
-
*/
|
|
79
25
|
private _getAppliedRules;
|
|
80
|
-
/**
|
|
81
|
-
* Cleanup plugin resources
|
|
82
|
-
* Should be called when Verdaccio shuts down
|
|
83
|
-
*/
|
|
84
26
|
destroy(): void;
|
|
85
27
|
}
|
|
86
28
|
export { SecurityLogger } from './lib/logger';
|
package/build/index.js
CHANGED
|
@@ -54,24 +54,9 @@ const cve_checker_1 = require("./lib/cve-checker");
|
|
|
54
54
|
const license_checker_1 = require("./lib/license-checker");
|
|
55
55
|
const package_age_checker_1 = require("./lib/package-age-checker");
|
|
56
56
|
const author_checker_1 = require("./lib/author-checker");
|
|
57
|
-
/**
|
|
58
|
-
* Security Filter Plugin for Verdaccio
|
|
59
|
-
* Works with both middleware and filter interfaces for complete protection
|
|
60
|
-
* Compatible with Verdaccio 6.x and 7.x
|
|
61
|
-
*
|
|
62
|
-
* Two-layer protection:
|
|
63
|
-
* - Layer 1 (filter_metadata): CVE/License/Age checks when metadata is available
|
|
64
|
-
* - Layer 2 (middleware): Whitelist/Pattern/Scope checks + tarball blocking
|
|
65
|
-
*/
|
|
66
57
|
class SecurityFilterPlugin {
|
|
67
|
-
/**
|
|
68
|
-
* Creates a new SecurityFilterPlugin instance
|
|
69
|
-
* @param {SecurityConfig} config - Plugin configuration
|
|
70
|
-
* @param {PluginOptions} options - Verdaccio plugin options
|
|
71
|
-
*/
|
|
72
58
|
constructor(config, options) {
|
|
73
59
|
this.config = config || {};
|
|
74
|
-
// Set default error handling strategy if not provided
|
|
75
60
|
if (!this.config.errorHandling) {
|
|
76
61
|
this.config.errorHandling = {
|
|
77
62
|
onFilterError: 'fail-open',
|
|
@@ -79,23 +64,18 @@ class SecurityFilterPlugin {
|
|
|
79
64
|
onLicenseCheckError: 'fail-open',
|
|
80
65
|
};
|
|
81
66
|
}
|
|
82
|
-
// Initialize enhanced logger
|
|
83
67
|
this.logger = new logger_1.SecurityLogger(options.logger, this.config.logger);
|
|
84
|
-
// Initialize metrics collector
|
|
85
68
|
this.metrics = new metrics_1.MetricsCollector(this.config.metrics);
|
|
86
|
-
// Initialize whitelist checker
|
|
87
69
|
this.whitelistChecker = new whitelist_checker_1.WhitelistChecker(this.config.whitelist);
|
|
88
|
-
// Initialize security checkers
|
|
89
70
|
this.cveChecker = new cve_checker_1.CVEChecker(this.config.cveCheck);
|
|
90
71
|
this.licenseChecker = new license_checker_1.LicenseChecker(this.config.licenses);
|
|
91
72
|
this.packageAgeChecker = new package_age_checker_1.PackageAgeChecker(this.config.packageAge);
|
|
92
73
|
this.authorChecker = new author_checker_1.AuthorChecker(this.config.authorFilter);
|
|
93
|
-
// Security rules configuration
|
|
94
74
|
this.securityRules = {
|
|
95
75
|
blockedVersions: this.config.blockedVersions || [],
|
|
96
76
|
blockedPatterns: this.config.blockedPatterns || [],
|
|
97
77
|
minPackageSize: this.config.minPackageSize || 0,
|
|
98
|
-
maxPackageSize: this.config.maxPackageSize || 100 * 1024 * 1024,
|
|
78
|
+
maxPackageSize: this.config.maxPackageSize || 100 * 1024 * 1024,
|
|
99
79
|
allowedScopes: this.config.allowedScopes || [],
|
|
100
80
|
blockedScopes: this.config.blockedScopes || [],
|
|
101
81
|
enforceChecksum: this.config.enforceChecksum !== false,
|
|
@@ -103,10 +83,6 @@ class SecurityFilterPlugin {
|
|
|
103
83
|
};
|
|
104
84
|
this._logInitialization();
|
|
105
85
|
}
|
|
106
|
-
/**
|
|
107
|
-
* Parse and validate version range rules
|
|
108
|
-
* @private
|
|
109
|
-
*/
|
|
110
86
|
_parseVersionRangeRules(rules) {
|
|
111
87
|
return rules.map((rule, index) => {
|
|
112
88
|
if (!rule.package || !rule.range || !rule.strategy) {
|
|
@@ -117,7 +93,6 @@ class SecurityFilterPlugin {
|
|
|
117
93
|
this.logger.warn(`Fallback strategy requires fallbackVersion for ${rule.package}, skipping`);
|
|
118
94
|
return null;
|
|
119
95
|
}
|
|
120
|
-
// Validate semver range
|
|
121
96
|
try {
|
|
122
97
|
const validRange = semver.validRange(rule.range);
|
|
123
98
|
if (!validRange) {
|
|
@@ -132,10 +107,6 @@ class SecurityFilterPlugin {
|
|
|
132
107
|
return rule;
|
|
133
108
|
}).filter((rule) => rule !== null);
|
|
134
109
|
}
|
|
135
|
-
/**
|
|
136
|
-
* Log initialization details
|
|
137
|
-
* @private
|
|
138
|
-
*/
|
|
139
110
|
_logInitialization() {
|
|
140
111
|
var _a;
|
|
141
112
|
const features = this._getEnabledFeatures();
|
|
@@ -159,10 +130,6 @@ class SecurityFilterPlugin {
|
|
|
159
130
|
this.logger.info(`Author filtering enabled: ${summary.blockedAuthors} authors, ${summary.blockedEmails} emails, ${summary.blockedRegions} regions blocked`);
|
|
160
131
|
}
|
|
161
132
|
}
|
|
162
|
-
/**
|
|
163
|
-
* Get list of enabled features
|
|
164
|
-
* @private
|
|
165
|
-
*/
|
|
166
133
|
_getEnabledFeatures() {
|
|
167
134
|
var _a, _b, _c, _d, _e;
|
|
168
135
|
const features = [];
|
|
@@ -188,40 +155,25 @@ class SecurityFilterPlugin {
|
|
|
188
155
|
features.push('scope-blocking');
|
|
189
156
|
return features;
|
|
190
157
|
}
|
|
191
|
-
/**
|
|
192
|
-
* Check if package name matches blocked patterns
|
|
193
|
-
* @private
|
|
194
|
-
*/
|
|
195
158
|
_isBlockedByPattern(packageName) {
|
|
196
159
|
return this.securityRules.blockedPatterns.some(pattern => {
|
|
197
160
|
const regex = new RegExp(pattern);
|
|
198
161
|
return regex.test(packageName);
|
|
199
162
|
});
|
|
200
163
|
}
|
|
201
|
-
/**
|
|
202
|
-
* Check if package scope is allowed
|
|
203
|
-
* @private
|
|
204
|
-
*/
|
|
205
164
|
_isScopeAllowed(packageName) {
|
|
206
|
-
// If not a scoped package, check if allowedScopes is empty
|
|
207
165
|
if (!packageName.startsWith('@')) {
|
|
208
166
|
return this.securityRules.allowedScopes.length === 0;
|
|
209
167
|
}
|
|
210
168
|
const scope = packageName.split('/')[0];
|
|
211
|
-
// Check blacklist
|
|
212
169
|
if (this.securityRules.blockedScopes.includes(scope)) {
|
|
213
170
|
return false;
|
|
214
171
|
}
|
|
215
|
-
// Check whitelist (if exists)
|
|
216
172
|
if (this.securityRules.allowedScopes.length > 0) {
|
|
217
173
|
return this.securityRules.allowedScopes.includes(scope);
|
|
218
174
|
}
|
|
219
175
|
return true;
|
|
220
176
|
}
|
|
221
|
-
/**
|
|
222
|
-
* Get version range rule for a specific package and version
|
|
223
|
-
* @private
|
|
224
|
-
*/
|
|
225
177
|
_getVersionRangeRule(packageName, version) {
|
|
226
178
|
return this.securityRules.versionRangeRules.find(rule => {
|
|
227
179
|
if (rule.package !== packageName) {
|
|
@@ -236,43 +188,28 @@ class SecurityFilterPlugin {
|
|
|
236
188
|
}
|
|
237
189
|
}) || null;
|
|
238
190
|
}
|
|
239
|
-
/**
|
|
240
|
-
* Register Express middleware to intercept package requests
|
|
241
|
-
* Intercepts both metadata and tarball requests
|
|
242
|
-
*/
|
|
243
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
244
191
|
register_middlewares(app, _auth, _storage) {
|
|
245
192
|
this.logger.info('[Middleware] Registering security filter middleware');
|
|
246
193
|
this.logger.info(`[Middleware] Config mode: ${this.config.mode || 'NOT SET'}`);
|
|
247
|
-
// Middleware to intercept all package requests
|
|
248
194
|
app.use((req, res, next) => __awaiter(this, void 0, void 0, function* () {
|
|
249
195
|
try {
|
|
250
|
-
// Extract package name and version from URL
|
|
251
|
-
// Tarball URLs: /:package/-/:filename.tgz
|
|
252
|
-
// Scoped tarball URLs: /@scope/:package/-/:filename.tgz
|
|
253
|
-
// Metadata URLs: /:package or /@scope/:package
|
|
254
196
|
const urlMatch = req.url.match(/^\/(@[^/]+\/)?([^/]+)(?:\/-\/([^/]+\.tgz))?/);
|
|
255
197
|
if (!urlMatch) {
|
|
256
|
-
// Not a package-related request, pass through
|
|
257
198
|
this.logger.debug(`[Middleware] Non-package request: ${req.method} ${req.url}`);
|
|
258
199
|
return next();
|
|
259
200
|
}
|
|
260
201
|
const scope = urlMatch[1] || '';
|
|
261
202
|
const packageName = scope + urlMatch[2];
|
|
262
203
|
const tarballFile = urlMatch[3];
|
|
263
|
-
// Extract version from tarball filename if present
|
|
264
204
|
let version;
|
|
265
205
|
if (tarballFile) {
|
|
266
|
-
// This is a tarball download request - block it immediately
|
|
267
206
|
const versionMatch = tarballFile.match(/-([\d.]+(?:-[a-zA-Z0-9.]+)?)\.tgz$/);
|
|
268
207
|
version = versionMatch ? versionMatch[1] : undefined;
|
|
269
208
|
this.logger.info(`[Middleware] TARBALL REQUEST: ${packageName}@${version || 'unknown'}`);
|
|
270
|
-
// Apply all security checks for tarball downloads
|
|
271
209
|
const blockResult = yield this._checkPackageBlock(packageName, version);
|
|
272
210
|
if (blockResult.blocked) {
|
|
273
211
|
this.logger.warn(`[Middleware] [X] BLOCKED TARBALL: ${packageName}@${version || '*'} - ${blockResult.reason}`);
|
|
274
212
|
this.metrics.recordBlock(packageName, version || '*', blockResult.reason);
|
|
275
|
-
// Return 403 Forbidden with detailed error
|
|
276
213
|
res.status(403).json({
|
|
277
214
|
error: 'Package blocked by security filter',
|
|
278
215
|
package: packageName,
|
|
@@ -285,9 +222,7 @@ class SecurityFilterPlugin {
|
|
|
285
222
|
this.logger.info(`[Middleware] [OK] ALLOWED TARBALL: ${packageName}@${version || 'unknown'}`);
|
|
286
223
|
}
|
|
287
224
|
else {
|
|
288
|
-
// This is a metadata request - intercept response and modify it
|
|
289
225
|
this.logger.info(`[Middleware] METADATA REQUEST: ${packageName} - will intercept response`);
|
|
290
|
-
// Check if package should be blocked
|
|
291
226
|
const blockResult = yield this._checkPackageBlock(packageName);
|
|
292
227
|
if (blockResult.blocked) {
|
|
293
228
|
this.logger.warn(`[Middleware] [X] BLOCKED METADATA: ${packageName} - ${blockResult.reason}`);
|
|
@@ -322,7 +257,6 @@ class SecurityFilterPlugin {
|
|
|
322
257
|
this.logger.info(`[Middleware] [OK] ALLOWED METADATA: ${packageName}`);
|
|
323
258
|
}
|
|
324
259
|
}
|
|
325
|
-
// Continue to next middleware
|
|
326
260
|
next();
|
|
327
261
|
}
|
|
328
262
|
catch (error) {
|
|
@@ -332,22 +266,16 @@ class SecurityFilterPlugin {
|
|
|
332
266
|
}));
|
|
333
267
|
this.logger.info('[Middleware] Security filter middleware registered successfully');
|
|
334
268
|
}
|
|
335
|
-
/**
|
|
336
|
-
* Filter package metadata before returning to client
|
|
337
|
-
* This provides CVE, License, and Age checking when metadata is available
|
|
338
|
-
*/
|
|
339
269
|
filter_metadata(packageInfo) {
|
|
340
270
|
return __awaiter(this, void 0, void 0, function* () {
|
|
341
271
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
342
272
|
const packageName = packageInfo.name;
|
|
343
273
|
this.logger.info(`[filter_metadata] --> Processing: ${packageName}`);
|
|
344
274
|
try {
|
|
345
|
-
// 1. Whitelist/blacklist check (basic filtering)
|
|
346
275
|
const blockResult = yield this._checkPackageBlock(packageName);
|
|
347
276
|
if (blockResult.blocked) {
|
|
348
277
|
this.logger.warn(`[filter_metadata] BLOCKED: ${packageName}@* - ${blockResult.reason}`);
|
|
349
278
|
this.metrics.recordBlock(packageName, '*', blockResult.reason);
|
|
350
|
-
// Return empty versions with security field
|
|
351
279
|
return Object.assign(Object.assign({}, packageInfo), { versions: {}, 'dist-tags': {}, security: {
|
|
352
280
|
blocked: true,
|
|
353
281
|
reason: blockResult.reason,
|
|
@@ -360,7 +288,6 @@ class SecurityFilterPlugin {
|
|
|
360
288
|
rules: this._getAppliedRules(packageName),
|
|
361
289
|
} });
|
|
362
290
|
}
|
|
363
|
-
// 2. CVE Check - check versions in controlled batches
|
|
364
291
|
if ((_a = this.config.cveCheck) === null || _a === void 0 ? void 0 : _a.enabled) {
|
|
365
292
|
const versions = Object.keys(packageInfo.versions || {});
|
|
366
293
|
const vulnerableVersions = [];
|
|
@@ -379,7 +306,6 @@ class SecurityFilterPlugin {
|
|
|
379
306
|
}
|
|
380
307
|
});
|
|
381
308
|
}
|
|
382
|
-
// If configured to auto-block and vulnerabilities found
|
|
383
309
|
if (this.config.cveCheck.autoBlock && vulnerableVersions.length > 0) {
|
|
384
310
|
const reason = `Package has ${vulnerableVersions.length} vulnerable version(s)`;
|
|
385
311
|
this.logger.warn(`[filter_metadata] CVE BLOCKED: ${packageName} - ${reason}`);
|
|
@@ -396,7 +322,6 @@ class SecurityFilterPlugin {
|
|
|
396
322
|
} });
|
|
397
323
|
}
|
|
398
324
|
}
|
|
399
|
-
// 3. License Check - check latest version
|
|
400
325
|
if (this.config.licenses && this.config.licenses.allowed && this.config.licenses.allowed.length > 0) {
|
|
401
326
|
const latestVersion = (_b = packageInfo['dist-tags']) === null || _b === void 0 ? void 0 : _b.latest;
|
|
402
327
|
if (latestVersion && ((_c = packageInfo.versions) === null || _c === void 0 ? void 0 : _c[latestVersion])) {
|
|
@@ -418,7 +343,6 @@ class SecurityFilterPlugin {
|
|
|
418
343
|
}
|
|
419
344
|
}
|
|
420
345
|
}
|
|
421
|
-
// 4. Package Age Check
|
|
422
346
|
if ((_d = this.config.packageAge) === null || _d === void 0 ? void 0 : _d.enabled) {
|
|
423
347
|
const ageResult = this.packageAgeChecker.checkPackageAge(packageInfo);
|
|
424
348
|
if (!ageResult.allowed && !ageResult.warnOnly) {
|
|
@@ -439,7 +363,6 @@ class SecurityFilterPlugin {
|
|
|
439
363
|
this.logger.warn(`[filter_metadata] AGE WARNING: ${packageName} - ${ageResult.reason} (warn-only mode)`);
|
|
440
364
|
}
|
|
441
365
|
}
|
|
442
|
-
// 5. Author Filter Check
|
|
443
366
|
if ((_e = this.config.authorFilter) === null || _e === void 0 ? void 0 : _e.enabled) {
|
|
444
367
|
const latestVersion = (_f = packageInfo['dist-tags']) === null || _f === void 0 ? void 0 : _f.latest;
|
|
445
368
|
if (latestVersion && ((_g = packageInfo.versions) === null || _g === void 0 ? void 0 : _g[latestVersion])) {
|
|
@@ -499,34 +422,25 @@ class SecurityFilterPlugin {
|
|
|
499
422
|
}
|
|
500
423
|
});
|
|
501
424
|
}
|
|
502
|
-
/**
|
|
503
|
-
* Check if package should be blocked (used by middleware)
|
|
504
|
-
* @private
|
|
505
|
-
*/
|
|
506
425
|
_checkPackageBlock(packageName, version) {
|
|
507
426
|
return __awaiter(this, void 0, void 0, function* () {
|
|
508
|
-
// 1. Whitelist mode
|
|
509
427
|
if (this.config.mode === 'whitelist') {
|
|
510
428
|
const whitelistCheck = this.whitelistChecker.isWhitelisted(packageName, version);
|
|
511
429
|
if (!whitelistCheck.allowed) {
|
|
512
430
|
return { blocked: true, reason: whitelistCheck.reason || 'Package is not in whitelist' };
|
|
513
431
|
}
|
|
514
432
|
}
|
|
515
|
-
// 2. Blocked patterns
|
|
516
433
|
if (this._isBlockedByPattern(packageName)) {
|
|
517
434
|
return { blocked: true, reason: 'Package name matches blocked pattern' };
|
|
518
435
|
}
|
|
519
|
-
// 3. Scopes
|
|
520
436
|
if (!this._isScopeAllowed(packageName)) {
|
|
521
437
|
return { blocked: true, reason: 'Package scope not allowed' };
|
|
522
438
|
}
|
|
523
|
-
// 4. Exact version blocking
|
|
524
439
|
if (version) {
|
|
525
440
|
const versionKey = `${packageName}@${version}`;
|
|
526
441
|
if (this.securityRules.blockedVersions.includes(versionKey)) {
|
|
527
442
|
return { blocked: true, reason: 'Exact version match in blocklist' };
|
|
528
443
|
}
|
|
529
|
-
// 5. Version range rules
|
|
530
444
|
const rangeRule = this._getVersionRangeRule(packageName, version);
|
|
531
445
|
if (rangeRule && rangeRule.strategy === 'block') {
|
|
532
446
|
return { blocked: true, reason: `Version matches blocked range: ${rangeRule.range}` };
|
|
@@ -535,13 +449,8 @@ class SecurityFilterPlugin {
|
|
|
535
449
|
return { blocked: false, reason: '' };
|
|
536
450
|
});
|
|
537
451
|
}
|
|
538
|
-
/**
|
|
539
|
-
* Get applied security rules for a package
|
|
540
|
-
* @private
|
|
541
|
-
*/
|
|
542
452
|
_getAppliedRules(packageName) {
|
|
543
453
|
const rules = {};
|
|
544
|
-
// Check which rules apply to this package
|
|
545
454
|
if (this.config.mode === 'whitelist') {
|
|
546
455
|
rules.mode = 'whitelist';
|
|
547
456
|
rules.whitelisted = false;
|
|
@@ -572,10 +481,6 @@ class SecurityFilterPlugin {
|
|
|
572
481
|
}
|
|
573
482
|
return rules;
|
|
574
483
|
}
|
|
575
|
-
/**
|
|
576
|
-
* Cleanup plugin resources
|
|
577
|
-
* Should be called when Verdaccio shuts down
|
|
578
|
-
*/
|
|
579
484
|
destroy() {
|
|
580
485
|
this.logger.info('[Cleanup] Shutting down security filter plugin');
|
|
581
486
|
try {
|
|
@@ -589,7 +494,6 @@ class SecurityFilterPlugin {
|
|
|
589
494
|
}
|
|
590
495
|
}
|
|
591
496
|
exports.default = SecurityFilterPlugin;
|
|
592
|
-
// Export utility classes for testing
|
|
593
497
|
var logger_2 = require("./lib/logger");
|
|
594
498
|
Object.defineProperty(exports, "SecurityLogger", { enumerable: true, get: function () { return logger_2.SecurityLogger; } });
|
|
595
499
|
var metrics_2 = require("./lib/metrics");
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { Version } from '@verdaccio/types';
|
|
2
2
|
import { AuthorFilterConfig, AuthorCheckResult } from '../types';
|
|
3
|
-
/**
|
|
4
|
-
* AuthorChecker validates package authors and maintainers
|
|
5
|
-
* against configured security policies.
|
|
6
|
-
*/
|
|
7
3
|
export declare class AuthorChecker {
|
|
8
4
|
private readonly config?;
|
|
9
5
|
private readonly blockedAuthors;
|
|
@@ -13,25 +9,10 @@ export declare class AuthorChecker {
|
|
|
13
9
|
private readonly blockedEmailDomains;
|
|
14
10
|
private readonly regionDomainMap;
|
|
15
11
|
constructor(config?: AuthorFilterConfig);
|
|
16
|
-
/**
|
|
17
|
-
* Check if package author/maintainer is allowed
|
|
18
|
-
*/
|
|
19
12
|
checkAuthor(versionData: Version): AuthorCheckResult;
|
|
20
|
-
/**
|
|
21
|
-
* Extract author information from package version data
|
|
22
|
-
*/
|
|
23
13
|
private _extractAuthors;
|
|
24
|
-
/**
|
|
25
|
-
* Normalize author data to AuthorInfo format
|
|
26
|
-
*/
|
|
27
14
|
private _normalizeAuthor;
|
|
28
|
-
/**
|
|
29
|
-
* Check a single author against all blocking rules
|
|
30
|
-
*/
|
|
31
15
|
private _checkSingleAuthor;
|
|
32
|
-
/**
|
|
33
|
-
* Get summary of configuration
|
|
34
|
-
*/
|
|
35
16
|
getSummary(): {
|
|
36
17
|
enabled: boolean;
|
|
37
18
|
blockedAuthors: number;
|
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AuthorChecker = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* Region to TLD mapping for email domain filtering
|
|
6
|
-
*/
|
|
7
4
|
const REGION_DOMAINS = {
|
|
8
5
|
'ru': ['.ru', 'yandex.ru', 'mail.ru', 'rambler.ru', 'ya.ru', 'bk.ru', 'list.ru'],
|
|
9
6
|
'cn': ['.cn', 'qq.com', '163.com', '126.com', 'sina.com', 'sohu.com'],
|
|
10
7
|
'by': ['.by', 'tut.by', 'mail.by'],
|
|
11
|
-
'kp': ['.kp'],
|
|
12
|
-
'ir': ['.ir'],
|
|
13
|
-
'sy': ['.sy'],
|
|
14
|
-
'cu': ['.cu'],
|
|
15
|
-
'sd': ['.sd'],
|
|
8
|
+
'kp': ['.kp'],
|
|
9
|
+
'ir': ['.ir'],
|
|
10
|
+
'sy': ['.sy'],
|
|
11
|
+
'cu': ['.cu'],
|
|
12
|
+
'sd': ['.sd'],
|
|
16
13
|
};
|
|
17
|
-
/**
|
|
18
|
-
* AuthorChecker validates package authors and maintainers
|
|
19
|
-
* against configured security policies.
|
|
20
|
-
*/
|
|
21
14
|
class AuthorChecker {
|
|
22
15
|
constructor(config) {
|
|
23
16
|
this.config = config;
|
|
@@ -34,9 +27,6 @@ class AuthorChecker {
|
|
|
34
27
|
this.regionDomainMap.set(region.toLowerCase(), new Set(domains));
|
|
35
28
|
});
|
|
36
29
|
}
|
|
37
|
-
/**
|
|
38
|
-
* Check if package author/maintainer is allowed
|
|
39
|
-
*/
|
|
40
30
|
checkAuthor(versionData) {
|
|
41
31
|
var _a;
|
|
42
32
|
if (!((_a = this.config) === null || _a === void 0 ? void 0 : _a.enabled)) {
|
|
@@ -61,9 +51,6 @@ class AuthorChecker {
|
|
|
61
51
|
}
|
|
62
52
|
return { allowed: true };
|
|
63
53
|
}
|
|
64
|
-
/**
|
|
65
|
-
* Extract author information from package version data
|
|
66
|
-
*/
|
|
67
54
|
_extractAuthors(versionData) {
|
|
68
55
|
const authors = [];
|
|
69
56
|
if (versionData.author) {
|
|
@@ -81,9 +68,6 @@ class AuthorChecker {
|
|
|
81
68
|
}
|
|
82
69
|
return authors;
|
|
83
70
|
}
|
|
84
|
-
/**
|
|
85
|
-
* Normalize author data to AuthorInfo format
|
|
86
|
-
*/
|
|
87
71
|
_normalizeAuthor(author) {
|
|
88
72
|
if (typeof author === 'string') {
|
|
89
73
|
const emailMatch = author.match(/<([^>]+)>/);
|
|
@@ -99,11 +83,7 @@ class AuthorChecker {
|
|
|
99
83
|
url: author.url,
|
|
100
84
|
};
|
|
101
85
|
}
|
|
102
|
-
/**
|
|
103
|
-
* Check a single author against all blocking rules
|
|
104
|
-
*/
|
|
105
86
|
_checkSingleAuthor(author) {
|
|
106
|
-
// Check blocked author names
|
|
107
87
|
if (author.name) {
|
|
108
88
|
const normalizedName = author.name.toLowerCase().trim();
|
|
109
89
|
if (this.blockedAuthors.has(normalizedName)) {
|
|
@@ -125,7 +105,6 @@ class AuthorChecker {
|
|
|
125
105
|
}
|
|
126
106
|
}
|
|
127
107
|
}
|
|
128
|
-
// Check blocked emails
|
|
129
108
|
if (author.email) {
|
|
130
109
|
const normalizedEmail = author.email.toLowerCase().trim();
|
|
131
110
|
if (this.blockedEmails.has(normalizedEmail)) {
|
|
@@ -146,7 +125,6 @@ class AuthorChecker {
|
|
|
146
125
|
};
|
|
147
126
|
}
|
|
148
127
|
}
|
|
149
|
-
// Check blocked email domains
|
|
150
128
|
for (const domain of this.blockedEmailDomains) {
|
|
151
129
|
if (normalizedEmail.endsWith(domain) || normalizedEmail.includes(`@${domain}`)) {
|
|
152
130
|
return {
|
|
@@ -157,7 +135,6 @@ class AuthorChecker {
|
|
|
157
135
|
};
|
|
158
136
|
}
|
|
159
137
|
}
|
|
160
|
-
// Check blocked regions
|
|
161
138
|
for (const [region, domains] of this.regionDomainMap.entries()) {
|
|
162
139
|
for (const domain of domains) {
|
|
163
140
|
if (normalizedEmail.endsWith(domain) ||
|
|
@@ -175,9 +152,6 @@ class AuthorChecker {
|
|
|
175
152
|
}
|
|
176
153
|
return { allowed: true };
|
|
177
154
|
}
|
|
178
|
-
/**
|
|
179
|
-
* Get summary of configuration
|
|
180
|
-
*/
|
|
181
155
|
getSummary() {
|
|
182
156
|
var _a;
|
|
183
157
|
return {
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import { CVECheckConfig, CVECheckResult } from '../types';
|
|
2
|
-
/**
|
|
3
|
-
* CVE Checker that integrates with vulnerability databases
|
|
4
|
-
*/
|
|
5
2
|
export declare class CVEChecker {
|
|
6
3
|
private config;
|
|
7
4
|
private cache;
|
|
@@ -10,49 +7,15 @@ export declare class CVEChecker {
|
|
|
10
7
|
private saveTimer;
|
|
11
8
|
private readonly SAVE_DEBOUNCE_MS;
|
|
12
9
|
constructor(config?: CVECheckConfig);
|
|
13
|
-
/**
|
|
14
|
-
* Check if a package version has known vulnerabilities
|
|
15
|
-
*/
|
|
16
10
|
checkPackage(packageName: string, version: string): Promise<CVECheckResult>;
|
|
17
|
-
/**
|
|
18
|
-
* Fetch vulnerabilities from configured databases
|
|
19
|
-
*/
|
|
20
11
|
private fetchVulnerabilities;
|
|
21
|
-
/**
|
|
22
|
-
* Fetch vulnerabilities from OSV (Open Source Vulnerabilities) database
|
|
23
|
-
* https://osv.dev/
|
|
24
|
-
*/
|
|
25
12
|
private fetchFromOSV;
|
|
26
|
-
/**
|
|
27
|
-
* Sleep utility for retry delays
|
|
28
|
-
*/
|
|
29
13
|
private sleep;
|
|
30
|
-
/**
|
|
31
|
-
* Map OSV severity to our severity levels
|
|
32
|
-
*/
|
|
33
14
|
private mapOSVSeverity;
|
|
34
|
-
/**
|
|
35
|
-
* Check if cache is stale
|
|
36
|
-
*/
|
|
37
15
|
private isCacheStale;
|
|
38
|
-
/**
|
|
39
|
-
* Load cache from disk
|
|
40
|
-
*/
|
|
41
16
|
private loadCache;
|
|
42
|
-
/**
|
|
43
|
-
* Debounced save cache to reduce I/O operations
|
|
44
|
-
*/
|
|
45
17
|
private debouncedSaveCache;
|
|
46
|
-
/**
|
|
47
|
-
* Save cache to disk immediately
|
|
48
|
-
*/
|
|
49
18
|
private saveCache;
|
|
50
|
-
/**
|
|
51
|
-
* Clear cache
|
|
52
|
-
*/
|
|
53
19
|
clearCache(): void;
|
|
54
|
-
/**
|
|
55
|
-
* Cleanup resources
|
|
56
|
-
*/
|
|
57
20
|
destroy(): void;
|
|
58
21
|
}
|