verdaccio-security-filter 1.3.10 → 1.3.12

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/README.md CHANGED
@@ -36,17 +36,15 @@ This plugin implements **two independent security layers** for complete protecti
36
36
 
37
37
  #### 🔒 Version Management
38
38
  - **Version Range Blocking** - Block vulnerable versions using semver ranges
39
- - **Block Strategy**: Completely remove versions from registry
40
- - **Fallback Strategy**: Transparently redirect to safe versions
39
+ - Matching versions are hidden from metadata and their tarballs return 403, so npm resolves to the highest allowed version
41
40
  - **Exact Version Blocking** - Block specific package@version combinations
42
41
  - **Semver Support** - Full semver syntax (`^`, `~`, `>`, `>=`, `<`, `<=`, `x`, `*`)
43
42
 
44
43
  #### 🛡️ CVE & Vulnerability Scanning
45
44
  - **OSV Database Integration** - Automatic vulnerability scanning via [OSV API](https://osv.dev/)
46
45
  - **Severity Filtering** - Filter by severity: `low`, `medium`, `high`, `critical`
47
- - **Auto-Block Vulnerable Packages** - Automatically block packages with known CVEs
48
- - **Caching System** - Configurable cache with update intervals
49
- - **Multiple Database Support** - Ready for OSV, Snyk, GitHub Advisory
46
+ - **Auto-Block Vulnerable Versions** - Remove versions with known CVEs from metadata
47
+ - **Caching System** - One OSV lookup per package, cached per package with a configurable refresh interval
50
48
 
51
49
  #### ⚖️ License Compliance
52
50
  - **License Filtering** - Enforce license policies with allowed/blocked lists
@@ -62,7 +60,7 @@ This plugin implements **two independent security layers** for complete protecti
62
60
 
63
61
  #### 📊 Monitoring & Observability
64
62
  - **Enhanced Logging** - Configurable log levels (`debug`, `info`, `warn`, `error`)
65
- - **Metrics Collection** - Track security events (blocks, fallbacks, CVEs, license violations)
63
+ - **Metrics Collection** - Track security events (blocks with their reason, author blocks)
66
64
  - **Security Audit Trail** - Detailed logging of all security decisions
67
65
  - **Customizable Output** - Log to stdout or file in JSON format
68
66
 
@@ -83,9 +81,7 @@ This plugin implements **two independent security layers** for complete protecti
83
81
  - Prevents blocked packages from being installed as dependencies
84
82
  - Intercepts requests to `/:package/-/:filename.tgz` routes
85
83
  - Returns 403 Forbidden with detailed error messages
86
- - **Size Limits** - Enforce minimum and maximum package sizes
87
84
  - **Metadata Validation** - Verify package integrity and detect dangerous characters
88
- - **Checksum Enforcement** - Ensure package integrity
89
85
 
90
86
  #### ⚙️ Reliability & Error Handling
91
87
  - **Fail-Safe/Fail-Closed Configuration** - Choose error handling strategy
@@ -274,7 +270,6 @@ filters:
274
270
  enabled: true
275
271
  databases:
276
272
  - osv
277
- - github
278
273
  severity:
279
274
  - high
280
275
  - critical
@@ -304,8 +299,7 @@ filters:
304
299
  versionRangeRules:
305
300
  - package: lodash
306
301
  range: "<4.17.21"
307
- strategy: fallback
308
- fallbackVersion: "4.17.21"
302
+ strategy: block
309
303
  reason: "CVE-2021-23337: Command injection"
310
304
 
311
305
  - package: minimist
@@ -379,7 +373,7 @@ filters:
379
373
 
380
374
  cveCheck:
381
375
  enabled: true
382
- databases: [osv, github, snyk]
376
+ databases: [osv]
383
377
  severity: [low, medium, high, critical] # Block ALL
384
378
  autoBlock: true
385
379
  updateInterval: 6 # Check every 6 hours
@@ -389,9 +383,6 @@ filters:
389
383
  blocked: [GPL-3.0, AGPL-3.0, LGPL-3.0]
390
384
  requireLicense: true
391
385
 
392
- minPackageSize: 10000 # 10KB minimum
393
- maxPackageSize: 10485760 # 10MB maximum
394
-
395
386
  logger:
396
387
  level: debug # Log everything
397
388
  enabled: true
@@ -419,22 +410,21 @@ middlewares:
419
410
  | `blockedPatterns` | `string[]` | `[]` | Regex patterns for package names |
420
411
  | `allowedScopes` | `string[]` | `[]` | Allowed package scopes |
421
412
  | `blockedScopes` | `string[]` | `[]` | Blocked package scopes |
422
- | `minPackageSize` | `number` | `0` | Minimum package size in bytes |
423
- | `maxPackageSize` | `number` | `104857600` | Maximum package size (100MB) |
424
- | `enforceChecksum` | `boolean` | `true` | Enforce checksum validation |
425
413
 
426
414
  ### CVE Check Options
427
415
 
428
416
  | Option | Type | Default | Description |
429
417
  |--------|------|---------|-------------|
430
418
  | `cveCheck.enabled` | `boolean` | `false` | Enable CVE scanning |
431
- | `cveCheck.databases` | `string[]` | `['osv']` | Databases: `osv`, `snyk`, `github` |
419
+ | `cveCheck.databases` | `string[]` | `['osv']` | Vulnerability databases. Only `osv` is implemented; it already includes GitHub Security Advisories |
432
420
  | `cveCheck.severity` | `string[]` | `['high', 'critical']` | Severity levels to check |
433
421
  | `cveCheck.autoBlock` | `boolean` | `false` | Remove vulnerable versions from metadata (whole package blocked if every version is vulnerable) |
434
- | `cveCheck.updateInterval` | `number` | `24` | Cache update interval (hours) |
422
+ | `cveCheck.updateInterval` | `number` | `24` | How long a package's vulnerability data is cached (hours) |
435
423
  | `cveCheck.cacheDir` | `string` | `./.security-cache` | Cache directory path |
436
424
 
437
- > **Note on `autoBlock` and dist-tags:** only the vulnerable versions are removed; unaffected versions stay installable. Any dist-tag (including `latest`) that points to a removed version is dropped and **not** re-pointed to another version. If `latest` is vulnerable, `npm install <pkg>` without a version or range will fail - install an explicit safe version or range instead (e.g. `npm install <pkg>@^1.2.0`).
425
+ > **Note on removed versions and dist-tags:** whenever a check removes versions (CVE `autoBlock`, license, author, age, version rules, `whitelist.versions`), the other versions stay installable. The plugin drops every dist-tag that points to a removed version. Verdaccio then points `latest` at the highest version that is left (verified with Verdaccio 6.2), so `npm install <pkg>` keeps working. Other dist-tags that pointed to a removed version are gone.
426
+
427
+ > **When OSV is unreachable:** a lookup gets one retry with a 5 second timeout each. After a failure the same package is not looked up again for 60 seconds. Previously cached data, even if older than `updateInterval`, keeps being used. With no cached data, `errorHandling.onCveCheckError` decides: `fail-open` serves the package unchecked, `fail-closed` blocks it.
438
428
 
439
429
  ### License Options
440
430
 
@@ -442,7 +432,9 @@ middlewares:
442
432
  |--------|------|---------|-------------|
443
433
  | `licenses.allowed` | `string[]` | `[]` | Allowed license list |
444
434
  | `licenses.blocked` | `string[]` | `[]` | Blocked license list |
445
- | `licenses.requireLicense` | `boolean` | `false` | Require license field |
435
+ | `licenses.requireLicense` | `boolean` | `true` | Reject versions without a license field |
436
+
437
+ The license check runs when `licenses.allowed` is not empty. Every version is checked: versions with a rejected license are removed from the metadata, and the package is blocked as a whole only when no version is left.
446
438
 
447
439
  ### Whitelist Options
448
440
 
@@ -491,13 +483,14 @@ middlewares:
491
483
  | `authorFilter.requireVerifiedEmail` | `boolean` | `false` | Require author email information |
492
484
  | `authorFilter.warnOnly` | `boolean` | `false` | Only warn, don't block |
493
485
 
486
+ Every version is checked against the author rules: versions published by a blocked author are removed from the metadata, and the package is blocked as a whole only when no version is left.
487
+
494
488
  ### Error Handling Options
495
489
 
496
490
  | Option | Type | Default | Description |
497
491
  |--------|------|---------|-------------|
498
492
  | `errorHandling.onFilterError` | `'fail-open' \| 'fail-closed'` | `'fail-open'` | Strategy when filter errors occur |
499
- | `errorHandling.onCveCheckError` | `'fail-open' \| 'fail-closed'` | `'fail-open'` | Strategy when CVE check fails |
500
- | `errorHandling.onLicenseCheckError` | `'fail-open' \| 'fail-closed'` | `'fail-open'` | Strategy when license check fails |
493
+ | `errorHandling.onCveCheckError` | `'fail-open' \| 'fail-closed'` | `'fail-open'` | Strategy when the vulnerability lookup fails and no cached data exists |
501
494
 
502
495
  **Error Handling Strategies:**
503
496
  - **fail-open** (Default): Allow packages through on errors (prioritizes availability)
@@ -509,19 +502,16 @@ middlewares:
509
502
  errorHandling:
510
503
  onFilterError: fail-closed
511
504
  onCveCheckError: fail-closed
512
- onLicenseCheckError: fail-closed
513
505
 
514
506
  # Development: Availability-first approach
515
507
  errorHandling:
516
508
  onFilterError: fail-open
517
509
  onCveCheckError: fail-open
518
- onLicenseCheckError: fail-open
519
510
 
520
- # Balanced: Block only on license failures
511
+ # Balanced: block only when vulnerability data is unavailable
521
512
  errorHandling:
522
513
  onFilterError: fail-open
523
- onCveCheckError: fail-open
524
- onLicenseCheckError: fail-closed
514
+ onCveCheckError: fail-closed
525
515
  ```
526
516
 
527
517
  ### Version Range Rules
@@ -530,8 +520,7 @@ errorHandling:
530
520
  {
531
521
  package: string; // Package name
532
522
  range: string; // Semver range
533
- strategy: 'block' | 'fallback';
534
- fallbackVersion?: string; // Required for fallback strategy
523
+ strategy: 'block'; // 'fallback' is deprecated and behaves like 'block'
535
524
  reason?: string; // Explanation for blocking
536
525
  }
537
526
  ```
@@ -586,12 +575,7 @@ npm run build -- --watch
586
575
 
587
576
  When metrics are enabled, the plugin tracks:
588
577
 
589
- - `block` - Packages blocked by version/pattern/scope rules
590
- - `fallback` - Versions redirected to safe alternatives
591
- - `publish_rejected` - Publish attempts rejected
592
- - `cve_detected` - CVE vulnerabilities found
593
- - `license_blocked` - Packages blocked by license rules
594
- - `package_too_new` - Packages/versions blocked due to age restrictions
578
+ - `block` - Packages or versions blocked by rules, CVE, license or age checks; the `reason` field says which
595
579
  - `author_blocked` - Packages blocked due to author/region filtering
596
580
 
597
581
  Example metrics output:
@@ -621,17 +605,18 @@ versionRangeRules:
621
605
  reason: "CVE-2021-23337: Command injection vulnerability"
622
606
  ```
623
607
 
624
- ### 2. Transparent Security Patches (Fallback)
608
+ ### 2. Steer Installs to a Patched Version
625
609
 
626
610
  ```yaml
627
611
  versionRangeRules:
628
612
  - package: axios
629
613
  range: "0.21.1"
630
- strategy: fallback
631
- fallbackVersion: "0.21.4"
614
+ strategy: block
632
615
  reason: "SSRF vulnerability fix"
633
616
  ```
634
617
 
618
+ The blocked version disappears from the package metadata, so a range such as `axios@^0.21.0` resolves to the newest allowed version. A lockfile pinned to the blocked version fails with 403 until it is updated.
619
+
635
620
  ### 3. Enforce Corporate License Policy
636
621
 
637
622
  ```yaml
@@ -665,7 +650,7 @@ whitelist:
665
650
  ```yaml
666
651
  cveCheck:
667
652
  enabled: true
668
- databases: [osv, github]
653
+ databases: [osv]
669
654
  severity: [high, critical]
670
655
  autoBlock: true
671
656
  updateInterval: 12
package/build/index.d.ts CHANGED
@@ -23,6 +23,7 @@ export default class SecurityFilterPlugin implements IPluginMiddleware<SecurityC
23
23
  register_middlewares(app: Application, _auth: IBasicAuth<SecurityConfig>, _storage: IStorageManager<SecurityConfig>): void;
24
24
  private _blockedPackage;
25
25
  private _removeVersions;
26
+ private _rejectedVersions;
26
27
  filter_metadata(packageInfo: Package): Promise<Package>;
27
28
  private _versionBlockReason;
28
29
  private _checkPackageBlock;
package/build/index.js CHANGED
@@ -64,7 +64,6 @@ class SecurityFilterPlugin {
64
64
  this.config.errorHandling = {
65
65
  onFilterError: 'fail-open',
66
66
  onCveCheckError: 'fail-open',
67
- onLicenseCheckError: 'fail-open',
68
67
  };
69
68
  }
70
69
  this.logger = new logger_1.SecurityLogger(options.logger, this.config.logger);
@@ -77,11 +76,8 @@ class SecurityFilterPlugin {
77
76
  this.securityRules = {
78
77
  blockedVersions: this.config.blockedVersions || [],
79
78
  blockedPatterns: this.config.blockedPatterns || [],
80
- minPackageSize: this.config.minPackageSize || 0,
81
- maxPackageSize: this.config.maxPackageSize || 100 * 1024 * 1024,
82
79
  allowedScopes: this.config.allowedScopes || [],
83
80
  blockedScopes: this.config.blockedScopes || [],
84
- enforceChecksum: this.config.enforceChecksum !== false,
85
81
  versionRangeRules: this._parseVersionRangeRules(this.config.versionRangeRules || [])
86
82
  };
87
83
  this.blockedPatternRegexes = this.securityRules.blockedPatterns.flatMap(pattern => {
@@ -310,9 +306,23 @@ class SecurityFilterPlugin {
310
306
  }
311
307
  return Object.assign(Object.assign({}, pkg), { versions, 'dist-tags': distTags });
312
308
  }
309
+ _rejectedVersions(pkg, check) {
310
+ var _a;
311
+ const all = Object.entries(pkg.versions || {});
312
+ const results = all
313
+ .map(([version, data]) => [version, check(data)])
314
+ .filter(([, result]) => !result.allowed);
315
+ const latest = results.find(([version]) => { var _a; return version === ((_a = pkg['dist-tags']) === null || _a === void 0 ? void 0 : _a.latest); });
316
+ return {
317
+ results,
318
+ versions: results.map(([version]) => version),
319
+ first: (_a = (latest || results[0])) === null || _a === void 0 ? void 0 : _a[1],
320
+ all: all.length > 0 && results.length === all.length,
321
+ };
322
+ }
313
323
  filter_metadata(packageInfo) {
314
324
  return __awaiter(this, void 0, void 0, function* () {
315
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
325
+ var _a, _b, _c, _d, _e;
316
326
  let pkg = packageInfo;
317
327
  const packageName = pkg.name;
318
328
  this.logger.debug(`[filter_metadata] --> Processing: ${packageName}`);
@@ -323,9 +333,10 @@ class SecurityFilterPlugin {
323
333
  this.metrics.recordBlock(packageName, '*', blockResult.reason);
324
334
  return this._blockedPackage(pkg, blockResult.reason, { rules: this._getAppliedRules(packageName) });
325
335
  }
326
- if (this.securityRules.blockedVersions.length > 0 || this.securityRules.versionRangeRules.length > 0) {
327
- const ruleBlocked = Object.keys(pkg.versions || {})
328
- .filter(version => this._versionBlockReason(packageName, version) !== null);
336
+ const whitelistMode = this.config.mode === 'whitelist';
337
+ if (whitelistMode || this.securityRules.blockedVersions.length > 0 || this.securityRules.versionRangeRules.length > 0) {
338
+ const ruleBlocked = Object.keys(pkg.versions || {}).filter(version => this._versionBlockReason(packageName, version) !== null ||
339
+ (whitelistMode && !this.whitelistChecker.isWhitelisted(packageName, version).allowed));
329
340
  if (ruleBlocked.length > 0) {
330
341
  this.logger.warn(`[filter_metadata] RULE FILTERED: ${packageName}: ${ruleBlocked.join(', ')}`);
331
342
  pkg = this._removeVersions(pkg, ruleBlocked);
@@ -364,19 +375,22 @@ class SecurityFilterPlugin {
364
375
  }
365
376
  }
366
377
  if (this.config.licenses && this.config.licenses.allowed && this.config.licenses.allowed.length > 0) {
367
- const latestVersion = (_c = pkg['dist-tags']) === null || _c === void 0 ? void 0 : _c.latest;
368
- if (latestVersion && ((_d = pkg.versions) === null || _d === void 0 ? void 0 : _d[latestVersion])) {
369
- const versionData = pkg.versions[latestVersion];
370
- const licenseResult = this.licenseChecker.checkLicense(versionData);
371
- if (!licenseResult.allowed) {
372
- const reason = licenseResult.reason || 'License not allowed';
373
- this.logger.warn(`[filter_metadata] LICENSE BLOCKED: ${packageName} - ${reason}`);
374
- this.metrics.recordBlock(packageName, '*', reason);
375
- return this._blockedPackage(pkg, reason, { license: licenseResult.license });
378
+ const rejected = this._rejectedVersions(pkg, data => this.licenseChecker.checkLicense(data));
379
+ if (rejected.all) {
380
+ const reason = rejected.first.reason || 'License not allowed';
381
+ this.logger.warn(`[filter_metadata] LICENSE BLOCKED: ${packageName} - ${reason}`);
382
+ this.metrics.recordBlock(packageName, '*', reason);
383
+ return this._blockedPackage(pkg, reason, { license: rejected.first.license });
384
+ }
385
+ if (rejected.versions.length > 0) {
386
+ this.logger.warn(`[filter_metadata] LICENSE FILTERED: ${packageName}: ${rejected.versions.join(', ')}`);
387
+ for (const [version, result] of rejected.results) {
388
+ this.metrics.recordBlock(packageName, version, result.reason || 'License not allowed');
376
389
  }
390
+ pkg = this._removeVersions(pkg, rejected.versions);
377
391
  }
378
392
  }
379
- if ((_e = this.config.packageAge) === null || _e === void 0 ? void 0 : _e.enabled) {
393
+ if ((_c = this.config.packageAge) === null || _c === void 0 ? void 0 : _c.enabled) {
380
394
  const ageResult = this.packageAgeChecker.checkPackageAge(pkg);
381
395
  if (!ageResult.allowed && !ageResult.warnOnly) {
382
396
  const reason = ageResult.reason || 'Package too new';
@@ -403,33 +417,38 @@ class SecurityFilterPlugin {
403
417
  pkg = this._removeVersions(pkg, removedVersions);
404
418
  }
405
419
  }
406
- if ((_f = this.config.authorFilter) === null || _f === void 0 ? void 0 : _f.enabled) {
407
- const latestVersion = (_g = pkg['dist-tags']) === null || _g === void 0 ? void 0 : _g.latest;
408
- if (latestVersion && ((_h = pkg.versions) === null || _h === void 0 ? void 0 : _h[latestVersion])) {
409
- const versionData = pkg.versions[latestVersion];
410
- const authorResult = this.authorChecker.checkAuthor(versionData);
411
- if (!authorResult.allowed && !this.config.authorFilter.warnOnly) {
412
- const reason = authorResult.reason || 'Author not allowed';
413
- this.logger.warn(`[filter_metadata] AUTHOR BLOCKED: ${packageName} - ${reason}`);
420
+ if ((_d = this.config.authorFilter) === null || _d === void 0 ? void 0 : _d.enabled) {
421
+ const rejected = this._rejectedVersions(pkg, data => this.authorChecker.checkAuthor(data));
422
+ if (rejected.versions.length > 0 && this.config.authorFilter.warnOnly) {
423
+ this.logger.warn(`[filter_metadata] AUTHOR WARNING: ${packageName}: ${rejected.versions.join(', ')} - ${rejected.first.reason} (warn-only mode)`);
424
+ }
425
+ else if (rejected.versions.length > 0) {
426
+ for (const [version, result] of rejected.results) {
414
427
  this.metrics.record({
415
428
  timestamp: new Date().toISOString(),
416
429
  event: 'author_blocked',
417
430
  packageName,
418
- version: latestVersion,
419
- reason,
431
+ version: rejected.all ? '*' : version,
432
+ reason: result.reason || 'Author not allowed',
420
433
  metadata: {
421
- blockedBy: authorResult.blockedBy,
422
- authorInfo: authorResult.authorInfo,
434
+ blockedBy: result.blockedBy,
435
+ authorInfo: result.authorInfo,
423
436
  },
424
437
  });
438
+ if (rejected.all) {
439
+ break;
440
+ }
441
+ }
442
+ if (rejected.all) {
443
+ const reason = rejected.first.reason || 'Author not allowed';
444
+ this.logger.warn(`[filter_metadata] AUTHOR BLOCKED: ${packageName} - ${reason}`);
425
445
  return this._blockedPackage(pkg, reason, {
426
- blockedBy: authorResult.blockedBy,
427
- authorInfo: authorResult.authorInfo,
446
+ blockedBy: rejected.first.blockedBy,
447
+ authorInfo: rejected.first.authorInfo,
428
448
  });
429
449
  }
430
- else if (!authorResult.allowed && this.config.authorFilter.warnOnly) {
431
- this.logger.warn(`[filter_metadata] AUTHOR WARNING: ${packageName} - ${authorResult.reason} (warn-only mode)`);
432
- }
450
+ this.logger.warn(`[filter_metadata] AUTHOR FILTERED: ${packageName}: ${rejected.versions.join(', ')}`);
451
+ pkg = this._removeVersions(pkg, rejected.versions);
433
452
  }
434
453
  }
435
454
  this.logger.debug(`[filter_metadata] [OK] ALLOWED: ${packageName}`);
@@ -437,7 +456,7 @@ class SecurityFilterPlugin {
437
456
  }
438
457
  catch (error) {
439
458
  this.logger.error(`[filter_metadata] Error processing ${packageName}: ${error.message}`);
440
- const errorStrategy = ((_j = this.config.errorHandling) === null || _j === void 0 ? void 0 : _j.onFilterError) || 'fail-open';
459
+ const errorStrategy = ((_e = this.config.errorHandling) === null || _e === void 0 ? void 0 : _e.onFilterError) || 'fail-open';
441
460
  if (errorStrategy === 'fail-closed') {
442
461
  this.logger.warn(`[filter_metadata] Error handling: fail-closed - BLOCKING ${packageName}`);
443
462
  this.metrics.recordBlock(packageName, '*', `Error during processing: ${error.message}`);
@@ -9,9 +9,7 @@ export declare class MetricsCollector {
9
9
  constructor(config?: MetricsConfig);
10
10
  record(event: MetricsData): void;
11
11
  recordBlock(packageName: string, version: string, reason: string, metadata?: Record<string, any>): void;
12
- recordFallback(packageName: string, fromVersion: string, toVersion: string, reason: string): void;
13
12
  recordPublishRejected(packageName: string, version: string, reason: string): void;
14
- recordCVE(packageName: string, version: string, cveId: string, severity: string): void;
15
13
  recordLicenseBlock(packageName: string, version: string, license: string, reason: string): void;
16
14
  recordPackageTooNew(packageName: string, version: string, reason: string, metadata?: Record<string, any>): void;
17
15
  flush(): void;
@@ -75,16 +75,6 @@ class MetricsCollector {
75
75
  metadata,
76
76
  });
77
77
  }
78
- recordFallback(packageName, fromVersion, toVersion, reason) {
79
- this.record({
80
- timestamp: new Date().toISOString(),
81
- event: 'fallback',
82
- packageName,
83
- version: fromVersion,
84
- reason,
85
- metadata: { toVersion },
86
- });
87
- }
88
78
  recordPublishRejected(packageName, version, reason) {
89
79
  this.record({
90
80
  timestamp: new Date().toISOString(),
@@ -94,16 +84,6 @@ class MetricsCollector {
94
84
  reason,
95
85
  });
96
86
  }
97
- recordCVE(packageName, version, cveId, severity) {
98
- this.record({
99
- timestamp: new Date().toISOString(),
100
- event: 'cve_detected',
101
- packageName,
102
- version,
103
- reason: `CVE ${cveId} (${severity})`,
104
- metadata: { cveId, severity },
105
- });
106
- }
107
87
  recordLicenseBlock(packageName, version, license, reason) {
108
88
  this.record({
109
89
  timestamp: new Date().toISOString(),
@@ -12,13 +12,8 @@ export declare class WhitelistChecker {
12
12
  addPackage(packageName: string, versionRange?: string): void;
13
13
  removePackage(packageName: string): void;
14
14
  addPattern(pattern: string): void;
15
- meetsAutoApproveCriteria(_packageName: string): Promise<{
16
- approved: boolean;
17
- reason?: string;
18
- }>;
19
15
  getSummary(): {
20
16
  totalPackages: number;
21
17
  totalPatterns: number;
22
- hasAutoApprove: boolean;
23
18
  };
24
19
  }
@@ -32,15 +32,6 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
- return new (P || (P = Promise))(function (resolve, reject) {
38
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
- step((generator = generator.apply(thisArg, _arguments || [])).next());
42
- });
43
- };
44
35
  Object.defineProperty(exports, "__esModule", { value: true });
45
36
  exports.WhitelistChecker = void 0;
46
37
  const semver = __importStar(require("semver"));
@@ -51,7 +42,6 @@ class WhitelistChecker {
51
42
  packages: (config === null || config === void 0 ? void 0 : config.packages) || [],
52
43
  patterns: (config === null || config === void 0 ? void 0 : config.patterns) || [],
53
44
  versions: (config === null || config === void 0 ? void 0 : config.versions) || {},
54
- autoApprove: config === null || config === void 0 ? void 0 : config.autoApprove,
55
45
  };
56
46
  }
57
47
  isWhitelisted(packageName, version) {
@@ -115,22 +105,10 @@ class WhitelistChecker {
115
105
  this.config.patterns.push(pattern);
116
106
  }
117
107
  }
118
- meetsAutoApproveCriteria(_packageName) {
119
- return __awaiter(this, void 0, void 0, function* () {
120
- if (!this.config.autoApprove) {
121
- return { approved: false, reason: 'Auto-approve not configured' };
122
- }
123
- return {
124
- approved: false,
125
- reason: 'Auto-approve requires npm registry API integration (not implemented)',
126
- };
127
- });
128
- }
129
108
  getSummary() {
130
109
  return {
131
110
  totalPackages: this.config.packages.length,
132
111
  totalPatterns: this.config.patterns.length,
133
- hasAutoApprove: !!this.config.autoApprove,
134
112
  };
135
113
  }
136
114
  }
package/build/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Config, Version } from '@verdaccio/types';
1
+ import { Config } from '@verdaccio/types';
2
2
  export interface VersionRangeRule {
3
3
  package: string;
4
4
  range: string;
@@ -8,7 +8,7 @@ export interface VersionRangeRule {
8
8
  }
9
9
  export interface CVECheckConfig {
10
10
  enabled: boolean;
11
- databases: ('osv' | 'snyk' | 'github')[];
11
+ databases: 'osv'[];
12
12
  severity: ('low' | 'medium' | 'high' | 'critical')[];
13
13
  autoBlock: boolean;
14
14
  updateInterval?: number;
@@ -18,11 +18,6 @@ export interface WhitelistConfig {
18
18
  packages: string[];
19
19
  patterns: string[];
20
20
  versions?: Record<string, string>;
21
- autoApprove?: {
22
- minDownloads?: number;
23
- minStars?: number;
24
- verifiedPublisher?: boolean;
25
- };
26
21
  }
27
22
  export interface LicenseConfig {
28
23
  allowed: string[];
@@ -59,11 +54,8 @@ export interface AuthorFilterConfig {
59
54
  export interface SecurityConfig extends Config {
60
55
  blockedVersions?: string[];
61
56
  blockedPatterns?: string[];
62
- minPackageSize?: number;
63
- maxPackageSize?: number;
64
57
  allowedScopes?: string[];
65
58
  blockedScopes?: string[];
66
- enforceChecksum?: boolean;
67
59
  versionRangeRules?: VersionRangeRule[];
68
60
  mode?: 'blacklist' | 'whitelist';
69
61
  whitelist?: WhitelistConfig;
@@ -76,32 +68,15 @@ export interface SecurityConfig extends Config {
76
68
  errorHandling?: {
77
69
  onFilterError?: 'fail-open' | 'fail-closed';
78
70
  onCveCheckError?: 'fail-open' | 'fail-closed';
79
- onLicenseCheckError?: 'fail-open' | 'fail-closed';
80
71
  };
81
72
  }
82
73
  export interface SecurityRules {
83
74
  blockedVersions: string[];
84
75
  blockedPatterns: string[];
85
- minPackageSize: number;
86
- maxPackageSize: number;
87
76
  allowedScopes: string[];
88
77
  blockedScopes: string[];
89
- enforceChecksum: boolean;
90
78
  versionRangeRules: VersionRangeRule[];
91
79
  }
92
- export interface VersionData extends Version {
93
- _fallback?: boolean;
94
- _fallbackFrom?: string;
95
- _fallbackReason?: string;
96
- }
97
- export interface FilterResult {
98
- versions: Record<string, VersionData>;
99
- blockedVersions: string[];
100
- fallbacksApplied: Array<{
101
- original: string;
102
- fallback: string;
103
- }>;
104
- }
105
80
  export interface CVEVulnerability {
106
81
  id: string;
107
82
  severity: 'low' | 'medium' | 'high' | 'critical';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "verdaccio-security-filter",
3
- "version": "1.3.10",
3
+ "version": "1.3.12",
4
4
  "description": "Advanced security filter plugin for Verdaccio with version range blocking and fallback strategies",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -28,6 +28,7 @@
28
28
  "test": "jest",
29
29
  "test:watch": "jest --watch",
30
30
  "test:coverage": "jest --coverage",
31
+ "test:e2e": "npm run build && jest --testMatch \"**/*.e2e.ts\" --coverage=false",
31
32
  "debug:verdaccio": "node --inspect-brk $(which verdaccio)",
32
33
  "debug:build": "npm run build && npm link",
33
34
  "prepack": "npm run build",
@@ -55,7 +56,7 @@
55
56
  },
56
57
  "overrides": {
57
58
  "qs": "6.16.0",
58
- "@cypress/request": "latest"
59
+ "@cypress/request": "3.0.10"
59
60
  },
60
61
  "author": {
61
62
  "name": "Vitaliy Ponomarenko",