verdaccio-security-filter 1.3.0 → 1.3.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.
@@ -1,47 +1,21 @@
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;
8
5
  private lastUpdate;
6
+ private readonly MAX_CACHE_SIZE;
7
+ private saveTimer;
8
+ private readonly SAVE_DEBOUNCE_MS;
9
9
  constructor(config?: CVECheckConfig);
10
- /**
11
- * Check if a package version has known vulnerabilities
12
- */
13
10
  checkPackage(packageName: string, version: string): Promise<CVECheckResult>;
14
- /**
15
- * Fetch vulnerabilities from configured databases
16
- */
17
11
  private fetchVulnerabilities;
18
- /**
19
- * Fetch vulnerabilities from OSV (Open Source Vulnerabilities) database
20
- * https://osv.dev/
21
- */
22
12
  private fetchFromOSV;
23
- /**
24
- * Sleep utility for retry delays
25
- */
26
13
  private sleep;
27
- /**
28
- * Map OSV severity to our severity levels
29
- */
30
14
  private mapOSVSeverity;
31
- /**
32
- * Check if cache is stale
33
- */
34
15
  private isCacheStale;
35
- /**
36
- * Load cache from disk
37
- */
38
16
  private loadCache;
39
- /**
40
- * Save cache to disk
41
- */
17
+ private debouncedSaveCache;
42
18
  private saveCache;
43
- /**
44
- * Clear cache
45
- */
46
19
  clearCache(): void;
20
+ destroy(): void;
47
21
  }
@@ -45,13 +45,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
45
45
  exports.CVEChecker = void 0;
46
46
  const fs = __importStar(require("fs"));
47
47
  const path = __importStar(require("path"));
48
- /**
49
- * CVE Checker that integrates with vulnerability databases
50
- */
48
+ const lru_cache_1 = require("./lru-cache");
51
49
  class CVEChecker {
52
50
  constructor(config) {
53
- this.cache = new Map();
54
51
  this.lastUpdate = 0;
52
+ this.MAX_CACHE_SIZE = 1000;
53
+ this.saveTimer = null;
54
+ this.SAVE_DEBOUNCE_MS = 5000;
55
55
  this.config = {
56
56
  enabled: (config === null || config === void 0 ? void 0 : config.enabled) || false,
57
57
  databases: (config === null || config === void 0 ? void 0 : config.databases) || ['osv'],
@@ -60,13 +60,11 @@ class CVEChecker {
60
60
  updateInterval: (config === null || config === void 0 ? void 0 : config.updateInterval) || 24,
61
61
  cacheDir: (config === null || config === void 0 ? void 0 : config.cacheDir) || './.security-cache',
62
62
  };
63
+ this.cache = new lru_cache_1.LRUCache(this.MAX_CACHE_SIZE);
63
64
  if (this.config.enabled) {
64
65
  this.loadCache();
65
66
  }
66
67
  }
67
- /**
68
- * Check if a package version has known vulnerabilities
69
- */
70
68
  checkPackage(packageName, version) {
71
69
  return __awaiter(this, void 0, void 0, function* () {
72
70
  if (!this.config.enabled) {
@@ -79,13 +77,10 @@ class CVEChecker {
79
77
  };
80
78
  }
81
79
  const cacheKey = `${packageName}@${version}`;
82
- // Check cache first
83
80
  if (this.cache.has(cacheKey) && !this.isCacheStale()) {
84
81
  return this.cache.get(cacheKey);
85
82
  }
86
- // Fetch from databases
87
83
  const vulnerabilities = yield this.fetchVulnerabilities(packageName, version);
88
- // Filter by configured severity
89
84
  const filteredVulnerabilities = vulnerabilities.filter(vuln => this.config.severity.includes(vuln.severity));
90
85
  const result = {
91
86
  package: packageName,
@@ -94,15 +89,11 @@ class CVEChecker {
94
89
  isVulnerable: filteredVulnerabilities.length > 0,
95
90
  checkedAt: new Date().toISOString(),
96
91
  };
97
- // Cache result
98
92
  this.cache.set(cacheKey, result);
99
- this.saveCache();
93
+ this.debouncedSaveCache();
100
94
  return result;
101
95
  });
102
96
  }
103
- /**
104
- * Fetch vulnerabilities from configured databases
105
- */
106
97
  fetchVulnerabilities(packageName, version) {
107
98
  return __awaiter(this, void 0, void 0, function* () {
108
99
  const vulnerabilities = [];
@@ -113,10 +104,8 @@ class CVEChecker {
113
104
  vulnerabilities.push(...yield this.fetchFromOSV(packageName, version));
114
105
  break;
115
106
  case 'snyk':
116
- // Snyk integration would go here (requires API key)
117
107
  break;
118
108
  case 'github':
119
- // GitHub Advisory Database integration would go here
120
109
  break;
121
110
  }
122
111
  }
@@ -128,10 +117,6 @@ class CVEChecker {
128
117
  return vulnerabilities;
129
118
  });
130
119
  }
131
- /**
132
- * Fetch vulnerabilities from OSV (Open Source Vulnerabilities) database
133
- * https://osv.dev/
134
- */
135
120
  fetchFromOSV(packageName, version) {
136
121
  return __awaiter(this, void 0, void 0, function* () {
137
122
  const maxRetries = 3;
@@ -199,20 +184,13 @@ class CVEChecker {
199
184
  return [];
200
185
  });
201
186
  }
202
- /**
203
- * Sleep utility for retry delays
204
- */
205
187
  sleep(ms) {
206
188
  return new Promise(resolve => setTimeout(resolve, ms));
207
189
  }
208
- /**
209
- * Map OSV severity to our severity levels
210
- */
211
190
  mapOSVSeverity(osvSeverity) {
212
191
  if (!osvSeverity || osvSeverity.length === 0) {
213
192
  return 'medium';
214
193
  }
215
- // OSV uses CVSS scores
216
194
  const severity = osvSeverity[0];
217
195
  if (severity.type === 'CVSS_V3') {
218
196
  const score = parseFloat(severity.score);
@@ -226,24 +204,22 @@ class CVEChecker {
226
204
  }
227
205
  return 'medium';
228
206
  }
229
- /**
230
- * Check if cache is stale
231
- */
232
207
  isCacheStale() {
233
208
  const now = Date.now();
234
209
  const updateIntervalMs = this.config.updateInterval * 60 * 60 * 1000;
235
210
  return now - this.lastUpdate > updateIntervalMs;
236
211
  }
237
- /**
238
- * Load cache from disk
239
- */
240
212
  loadCache() {
241
213
  try {
242
214
  const cacheFile = path.join(this.config.cacheDir, 'cve-cache.json');
243
215
  if (fs.existsSync(cacheFile)) {
244
216
  const data = fs.readFileSync(cacheFile, 'utf8');
245
217
  const parsed = JSON.parse(data);
246
- this.cache = new Map(Object.entries(parsed.cache));
218
+ const entries = Object.entries(parsed.cache);
219
+ const sortedEntries = entries.slice(-this.MAX_CACHE_SIZE);
220
+ sortedEntries.forEach(([key, value]) => {
221
+ this.cache.set(key, value);
222
+ });
247
223
  this.lastUpdate = parsed.lastUpdate || 0;
248
224
  }
249
225
  }
@@ -252,9 +228,15 @@ class CVEChecker {
252
228
  console.error('[CVE Checker] Failed to load cache:', message);
253
229
  }
254
230
  }
255
- /**
256
- * Save cache to disk
257
- */
231
+ debouncedSaveCache() {
232
+ if (this.saveTimer) {
233
+ clearTimeout(this.saveTimer);
234
+ }
235
+ this.saveTimer = setTimeout(() => {
236
+ this.saveCache();
237
+ this.saveTimer = null;
238
+ }, this.SAVE_DEBOUNCE_MS);
239
+ }
258
240
  saveCache() {
259
241
  try {
260
242
  const cacheFile = path.join(this.config.cacheDir, 'cve-cache.json');
@@ -263,7 +245,7 @@ class CVEChecker {
263
245
  fs.mkdirSync(dir, { recursive: true });
264
246
  }
265
247
  const data = {
266
- cache: Object.fromEntries(this.cache),
248
+ cache: Object.fromEntries(this.cache.entries()),
267
249
  lastUpdate: Date.now(),
268
250
  };
269
251
  fs.writeFileSync(cacheFile, JSON.stringify(data, null, 2), 'utf8');
@@ -274,13 +256,18 @@ class CVEChecker {
274
256
  console.error('[CVE Checker] Failed to save cache:', message);
275
257
  }
276
258
  }
277
- /**
278
- * Clear cache
279
- */
280
259
  clearCache() {
281
260
  this.cache.clear();
282
261
  this.lastUpdate = 0;
283
262
  this.saveCache();
284
263
  }
264
+ destroy() {
265
+ if (this.saveTimer) {
266
+ clearTimeout(this.saveTimer);
267
+ this.saveTimer = null;
268
+ }
269
+ this.saveCache();
270
+ this.cache.clear();
271
+ }
285
272
  }
286
273
  exports.CVEChecker = CVEChecker;
@@ -1,37 +1,16 @@
1
1
  import { LicenseConfig } from '../types';
2
2
  import { Version } from '@verdaccio/types';
3
- /**
4
- * License checker for validating package licenses
5
- */
6
3
  export declare class LicenseChecker {
7
4
  private config;
8
5
  constructor(config?: LicenseConfig);
9
- /**
10
- * Check if a package version's license is allowed
11
- */
12
6
  checkLicense(versionData: Version): {
13
7
  allowed: boolean;
14
8
  reason?: string;
15
9
  license?: string;
16
10
  };
17
- /**
18
- * Extract license from version data
19
- */
20
11
  private extractLicense;
21
- /**
22
- * Check if license is blocked
23
- */
24
12
  private isLicenseBlocked;
25
- /**
26
- * Check if license is allowed
27
- */
28
13
  private isLicenseAllowed;
29
- /**
30
- * Get common open source licenses
31
- */
32
14
  static getCommonOpenSourceLicenses(): string[];
33
- /**
34
- * Get common copyleft licenses
35
- */
36
15
  static getCopyleftLicenses(): string[];
37
16
  }
@@ -1,9 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LicenseChecker = void 0;
4
- /**
5
- * License checker for validating package licenses
6
- */
7
4
  class LicenseChecker {
8
5
  constructor(config) {
9
6
  this.config = {
@@ -12,12 +9,8 @@ class LicenseChecker {
12
9
  requireLicense: (config === null || config === void 0 ? void 0 : config.requireLicense) !== false,
13
10
  };
14
11
  }
15
- /**
16
- * Check if a package version's license is allowed
17
- */
18
12
  checkLicense(versionData) {
19
13
  const license = this.extractLicense(versionData);
20
- // Check if license is missing
21
14
  if (!license || license === 'UNLICENSED' || license === '') {
22
15
  if (this.config.requireLicense) {
23
16
  return {
@@ -28,7 +21,6 @@ class LicenseChecker {
28
21
  }
29
22
  return { allowed: true };
30
23
  }
31
- // Check blocked list
32
24
  if (this.isLicenseBlocked(license)) {
33
25
  return {
34
26
  allowed: false,
@@ -36,7 +28,6 @@ class LicenseChecker {
36
28
  license,
37
29
  };
38
30
  }
39
- // Check allowed list (if specified)
40
31
  if (this.config.allowed.length > 0 && !this.isLicenseAllowed(license)) {
41
32
  return {
42
33
  allowed: false,
@@ -46,54 +37,36 @@ class LicenseChecker {
46
37
  }
47
38
  return { allowed: true, license };
48
39
  }
49
- /**
50
- * Extract license from version data
51
- */
52
40
  extractLicense(versionData) {
53
41
  if (!versionData.license) {
54
42
  return null;
55
43
  }
56
- // Handle string license
57
44
  if (typeof versionData.license === 'string') {
58
45
  return versionData.license;
59
46
  }
60
- // Handle SPDX license object
61
47
  if (typeof versionData.license === 'object' && 'type' in versionData.license) {
62
48
  return versionData.license.type;
63
49
  }
64
50
  return null;
65
51
  }
66
- /**
67
- * Check if license is blocked
68
- */
69
52
  isLicenseBlocked(license) {
70
53
  return this.config.blocked.some(blocked => {
71
- // Exact match
72
54
  if (blocked === license) {
73
55
  return true;
74
56
  }
75
- // Handle SPDX expressions (e.g., "MIT OR Apache-2.0")
76
57
  const licenseTokens = license.split(/\s+(?:OR|AND)\s+/i);
77
58
  return licenseTokens.some(token => token.trim() === blocked);
78
59
  });
79
60
  }
80
- /**
81
- * Check if license is allowed
82
- */
83
61
  isLicenseAllowed(license) {
84
62
  return this.config.allowed.some(allowed => {
85
- // Exact match
86
63
  if (allowed === license) {
87
64
  return true;
88
65
  }
89
- // Handle SPDX expressions (e.g., "MIT OR Apache-2.0")
90
66
  const licenseTokens = license.split(/\s+(?:OR|AND)\s+/i);
91
67
  return licenseTokens.some(token => token.trim() === allowed);
92
68
  });
93
69
  }
94
- /**
95
- * Get common open source licenses
96
- */
97
70
  static getCommonOpenSourceLicenses() {
98
71
  return [
99
72
  'MIT',
@@ -106,9 +79,6 @@ class LicenseChecker {
106
79
  'Unlicense',
107
80
  ];
108
81
  }
109
- /**
110
- * Get common copyleft licenses
111
- */
112
82
  static getCopyleftLicenses() {
113
83
  return [
114
84
  'GPL-2.0',
@@ -1,8 +1,5 @@
1
1
  import { Logger } from '@verdaccio/types';
2
2
  import { LoggerConfig } from '../types';
3
- /**
4
- * Enhanced logger wrapper with configurable log levels and formatting
5
- */
6
3
  export declare class SecurityLogger {
7
4
  private verdaccioLogger;
8
5
  private config;
@@ -15,28 +12,10 @@ export declare class SecurityLogger {
15
12
  info(message: string, ...args: any[]): void;
16
13
  warn(message: string, ...args: any[]): void;
17
14
  error(message: string, ...args: any[]): void;
18
- /**
19
- * Log package blocking event
20
- */
21
15
  logBlock(packageName: string, version: string, reason: string): void;
22
- /**
23
- * Log fallback event
24
- */
25
16
  logFallback(packageName: string, fromVersion: string, toVersion: string, reason?: string): void;
26
- /**
27
- * Log CVE detection
28
- */
29
17
  logCVE(packageName: string, version: string, cveId: string, severity: string): void;
30
- /**
31
- * Log license violation
32
- */
33
18
  logLicense(packageName: string, version: string, license: string, reason: string): void;
34
- /**
35
- * Log publish rejection
36
- */
37
19
  logPublishRejected(packageName: string, version: string, reason: string): void;
38
- /**
39
- * Log initialization
40
- */
41
20
  logInit(features: string[]): void;
42
21
  }
@@ -1,9 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SecurityLogger = void 0;
4
- /**
5
- * Enhanced logger wrapper with configurable log levels and formatting
6
- */
7
4
  class SecurityLogger {
8
5
  constructor(verdaccioLogger, config) {
9
6
  this.prefix = '[Security Filter]';
@@ -54,40 +51,22 @@ class SecurityLogger {
54
51
  this.verdaccioLogger.error(this.formatMessage(message), ...args);
55
52
  }
56
53
  }
57
- /**
58
- * Log package blocking event
59
- */
60
54
  logBlock(packageName, version, reason) {
61
55
  this.warn(`Blocked ${packageName}@${version} - ${reason}`);
62
56
  }
63
- /**
64
- * Log fallback event
65
- */
66
57
  logFallback(packageName, fromVersion, toVersion, reason) {
67
58
  const reasonText = reason ? ` (${reason})` : '';
68
59
  this.info(`Fallback applied: ${packageName}@${fromVersion} -> ${toVersion}${reasonText}`);
69
60
  }
70
- /**
71
- * Log CVE detection
72
- */
73
61
  logCVE(packageName, version, cveId, severity) {
74
62
  this.warn(`CVE detected: ${cveId} [${severity}] in ${packageName}@${version}`);
75
63
  }
76
- /**
77
- * Log license violation
78
- */
79
64
  logLicense(packageName, version, license, reason) {
80
65
  this.warn(`License violation: ${packageName}@${version} has ${license} - ${reason}`);
81
66
  }
82
- /**
83
- * Log publish rejection
84
- */
85
67
  logPublishRejected(packageName, version, reason) {
86
68
  this.warn(`Publish rejected: ${packageName}@${version} - ${reason}`);
87
69
  }
88
- /**
89
- * Log initialization
90
- */
91
70
  logInit(features) {
92
71
  this.info(`Plugin initialized with features: ${features.join(', ')}`);
93
72
  }
@@ -0,0 +1,12 @@
1
+ export declare class LRUCache<K, V> {
2
+ private cache;
3
+ private readonly maxSize;
4
+ constructor(maxSize?: number);
5
+ get(key: K): V | undefined;
6
+ set(key: K, value: V): void;
7
+ has(key: K): boolean;
8
+ delete(key: K): boolean;
9
+ clear(): void;
10
+ get size(): number;
11
+ entries(): IterableIterator<[K, V]>;
12
+ }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LRUCache = void 0;
4
+ class LRUCache {
5
+ constructor(maxSize = 1000) {
6
+ this.cache = new Map();
7
+ this.maxSize = maxSize;
8
+ }
9
+ get(key) {
10
+ if (!this.cache.has(key)) {
11
+ return undefined;
12
+ }
13
+ const value = this.cache.get(key);
14
+ this.cache.delete(key);
15
+ this.cache.set(key, value);
16
+ return value;
17
+ }
18
+ set(key, value) {
19
+ if (this.cache.has(key)) {
20
+ this.cache.delete(key);
21
+ }
22
+ else if (this.cache.size >= this.maxSize) {
23
+ const firstKey = this.cache.keys().next().value;
24
+ if (firstKey !== undefined) {
25
+ this.cache.delete(firstKey);
26
+ }
27
+ }
28
+ this.cache.set(key, value);
29
+ }
30
+ has(key) {
31
+ return this.cache.has(key);
32
+ }
33
+ delete(key) {
34
+ return this.cache.delete(key);
35
+ }
36
+ clear() {
37
+ this.cache.clear();
38
+ }
39
+ get size() {
40
+ return this.cache.size;
41
+ }
42
+ entries() {
43
+ return this.cache.entries();
44
+ }
45
+ }
46
+ exports.LRUCache = LRUCache;
@@ -1,54 +1,19 @@
1
1
  import { MetricsConfig, MetricsData } from '../types';
2
- /**
3
- * Metrics collector for tracking security events
4
- */
5
2
  export declare class MetricsCollector {
6
3
  private config;
7
4
  private metricsBuffer;
8
5
  private readonly bufferSize;
6
+ private readonly maxBufferSize;
9
7
  constructor(config?: MetricsConfig);
10
- /**
11
- * Record a security event
12
- */
13
8
  record(event: MetricsData): void;
14
- /**
15
- * Record a block event
16
- */
17
9
  recordBlock(packageName: string, version: string, reason: string, metadata?: Record<string, any>): void;
18
- /**
19
- * Record a fallback event
20
- */
21
10
  recordFallback(packageName: string, fromVersion: string, toVersion: string, reason: string): void;
22
- /**
23
- * Record a publish rejection
24
- */
25
11
  recordPublishRejected(packageName: string, version: string, reason: string): void;
26
- /**
27
- * Record a CVE detection
28
- */
29
12
  recordCVE(packageName: string, version: string, cveId: string, severity: string): void;
30
- /**
31
- * Record a license block
32
- */
33
13
  recordLicenseBlock(packageName: string, version: string, license: string, reason: string): void;
34
- /**
35
- * Record a package-too-new event
36
- */
37
14
  recordPackageTooNew(packageName: string, version: string, reason: string, metadata?: Record<string, any>): void;
38
- /**
39
- * Flush metrics buffer to output
40
- */
41
15
  flush(): void;
42
- /**
43
- * Write metrics to file
44
- */
45
16
  private writeToFile;
46
- /**
47
- * Get metrics summary
48
- */
49
17
  getSummary(): Record<string, number>;
50
- /**
51
- * Cleanup - flush remaining metrics
52
- */
53
18
  cleanup(): void;
54
19
  }