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.
- package/README.md +1 -0
- package/build/index.d.ts +1 -54
- package/build/index.js +51 -170
- package/build/lib/author-checker.d.ts +0 -19
- package/build/lib/author-checker.js +5 -31
- package/build/lib/cve-checker.d.ts +5 -31
- package/build/lib/cve-checker.js +29 -42
- 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 +12 -0
- package/build/lib/lru-cache.js +46 -0
- package/build/lib/metrics.d.ts +1 -36
- package/build/lib/metrics.js +4 -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 +5 -3
package/build/lib/metrics.js
CHANGED
|
@@ -36,34 +36,29 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.MetricsCollector = void 0;
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
|
-
/**
|
|
40
|
-
* Metrics collector for tracking security events
|
|
41
|
-
*/
|
|
42
39
|
class MetricsCollector {
|
|
43
40
|
constructor(config) {
|
|
44
41
|
this.metricsBuffer = [];
|
|
45
42
|
this.bufferSize = 100;
|
|
43
|
+
this.maxBufferSize = 500;
|
|
46
44
|
this.config = {
|
|
47
45
|
enabled: (config === null || config === void 0 ? void 0 : config.enabled) || false,
|
|
48
46
|
output: (config === null || config === void 0 ? void 0 : config.output) || 'stdout',
|
|
49
47
|
filePath: (config === null || config === void 0 ? void 0 : config.filePath) || './security-metrics.json',
|
|
50
48
|
};
|
|
51
49
|
}
|
|
52
|
-
/**
|
|
53
|
-
* Record a security event
|
|
54
|
-
*/
|
|
55
50
|
record(event) {
|
|
56
51
|
if (!this.config.enabled) {
|
|
57
52
|
return;
|
|
58
53
|
}
|
|
54
|
+
if (this.metricsBuffer.length >= this.maxBufferSize) {
|
|
55
|
+
this.flush();
|
|
56
|
+
}
|
|
59
57
|
this.metricsBuffer.push(event);
|
|
60
58
|
if (this.metricsBuffer.length >= this.bufferSize) {
|
|
61
59
|
this.flush();
|
|
62
60
|
}
|
|
63
61
|
}
|
|
64
|
-
/**
|
|
65
|
-
* Record a block event
|
|
66
|
-
*/
|
|
67
62
|
recordBlock(packageName, version, reason, metadata) {
|
|
68
63
|
this.record({
|
|
69
64
|
timestamp: new Date().toISOString(),
|
|
@@ -74,9 +69,6 @@ class MetricsCollector {
|
|
|
74
69
|
metadata,
|
|
75
70
|
});
|
|
76
71
|
}
|
|
77
|
-
/**
|
|
78
|
-
* Record a fallback event
|
|
79
|
-
*/
|
|
80
72
|
recordFallback(packageName, fromVersion, toVersion, reason) {
|
|
81
73
|
this.record({
|
|
82
74
|
timestamp: new Date().toISOString(),
|
|
@@ -87,9 +79,6 @@ class MetricsCollector {
|
|
|
87
79
|
metadata: { toVersion },
|
|
88
80
|
});
|
|
89
81
|
}
|
|
90
|
-
/**
|
|
91
|
-
* Record a publish rejection
|
|
92
|
-
*/
|
|
93
82
|
recordPublishRejected(packageName, version, reason) {
|
|
94
83
|
this.record({
|
|
95
84
|
timestamp: new Date().toISOString(),
|
|
@@ -99,9 +88,6 @@ class MetricsCollector {
|
|
|
99
88
|
reason,
|
|
100
89
|
});
|
|
101
90
|
}
|
|
102
|
-
/**
|
|
103
|
-
* Record a CVE detection
|
|
104
|
-
*/
|
|
105
91
|
recordCVE(packageName, version, cveId, severity) {
|
|
106
92
|
this.record({
|
|
107
93
|
timestamp: new Date().toISOString(),
|
|
@@ -112,9 +98,6 @@ class MetricsCollector {
|
|
|
112
98
|
metadata: { cveId, severity },
|
|
113
99
|
});
|
|
114
100
|
}
|
|
115
|
-
/**
|
|
116
|
-
* Record a license block
|
|
117
|
-
*/
|
|
118
101
|
recordLicenseBlock(packageName, version, license, reason) {
|
|
119
102
|
this.record({
|
|
120
103
|
timestamp: new Date().toISOString(),
|
|
@@ -125,9 +108,6 @@ class MetricsCollector {
|
|
|
125
108
|
metadata: { license },
|
|
126
109
|
});
|
|
127
110
|
}
|
|
128
|
-
/**
|
|
129
|
-
* Record a package-too-new event
|
|
130
|
-
*/
|
|
131
111
|
recordPackageTooNew(packageName, version, reason, metadata) {
|
|
132
112
|
this.record({
|
|
133
113
|
timestamp: new Date().toISOString(),
|
|
@@ -138,9 +118,6 @@ class MetricsCollector {
|
|
|
138
118
|
metadata,
|
|
139
119
|
});
|
|
140
120
|
}
|
|
141
|
-
/**
|
|
142
|
-
* Flush metrics buffer to output
|
|
143
|
-
*/
|
|
144
121
|
flush() {
|
|
145
122
|
if (this.metricsBuffer.length === 0) {
|
|
146
123
|
return;
|
|
@@ -155,18 +132,13 @@ class MetricsCollector {
|
|
|
155
132
|
}
|
|
156
133
|
this.metricsBuffer = [];
|
|
157
134
|
}
|
|
158
|
-
/**
|
|
159
|
-
* Write metrics to file
|
|
160
|
-
*/
|
|
161
135
|
writeToFile(metrics) {
|
|
162
136
|
try {
|
|
163
137
|
const filePath = this.config.filePath;
|
|
164
138
|
const dir = path.dirname(filePath);
|
|
165
|
-
// Ensure directory exists
|
|
166
139
|
if (!fs.existsSync(dir)) {
|
|
167
140
|
fs.mkdirSync(dir, { recursive: true });
|
|
168
141
|
}
|
|
169
|
-
// Append to file
|
|
170
142
|
const content = metrics.map(m => JSON.stringify(m)).join('\n') + '\n';
|
|
171
143
|
fs.appendFileSync(filePath, content, 'utf8');
|
|
172
144
|
}
|
|
@@ -175,9 +147,6 @@ class MetricsCollector {
|
|
|
175
147
|
console.error('[Security Filter] Failed to write metrics:', message);
|
|
176
148
|
}
|
|
177
149
|
}
|
|
178
|
-
/**
|
|
179
|
-
* Get metrics summary
|
|
180
|
-
*/
|
|
181
150
|
getSummary() {
|
|
182
151
|
const summary = {
|
|
183
152
|
block: 0,
|
|
@@ -193,9 +162,6 @@ class MetricsCollector {
|
|
|
193
162
|
});
|
|
194
163
|
return summary;
|
|
195
164
|
}
|
|
196
|
-
/**
|
|
197
|
-
* Cleanup - flush remaining metrics
|
|
198
|
-
*/
|
|
199
165
|
cleanup() {
|
|
200
166
|
this.flush();
|
|
201
167
|
}
|
|
@@ -1,45 +1,23 @@
|
|
|
1
1
|
import { PackageAgeConfig } from '../types';
|
|
2
2
|
import { Package } from '@verdaccio/types';
|
|
3
|
-
/**
|
|
4
|
-
* Package age checker to protect against newly created malicious packages
|
|
5
|
-
*/
|
|
6
3
|
export declare class PackageAgeChecker {
|
|
7
4
|
private config;
|
|
8
5
|
constructor(config?: PackageAgeConfig);
|
|
9
|
-
/**
|
|
10
|
-
* Check if a package is old enough to be trusted
|
|
11
|
-
*/
|
|
12
6
|
checkPackageAge(packageData: Package): {
|
|
13
7
|
allowed: boolean;
|
|
14
8
|
reason?: string;
|
|
15
9
|
ageDays?: number;
|
|
16
10
|
warnOnly?: boolean;
|
|
17
11
|
};
|
|
18
|
-
/**
|
|
19
|
-
* Check if a specific version is old enough to be trusted
|
|
20
|
-
*/
|
|
21
12
|
checkVersionAge(packageData: Package, version: string): {
|
|
22
13
|
allowed: boolean;
|
|
23
14
|
reason?: string;
|
|
24
15
|
ageDays?: number;
|
|
25
16
|
warnOnly?: boolean;
|
|
26
17
|
};
|
|
27
|
-
/**
|
|
28
|
-
* Get package creation date from time field
|
|
29
|
-
* The 'created' field in package.time represents when the package was first published
|
|
30
|
-
*/
|
|
31
18
|
private getPackageCreationDate;
|
|
32
|
-
/**
|
|
33
|
-
* Get version publish date from time field
|
|
34
|
-
*/
|
|
35
19
|
private getVersionPublishDate;
|
|
36
|
-
/**
|
|
37
|
-
* Calculate age in days from a date
|
|
38
|
-
*/
|
|
39
20
|
private calculateAgeDays;
|
|
40
|
-
/**
|
|
41
|
-
* Get summary of configuration
|
|
42
|
-
*/
|
|
43
21
|
getSummary(): {
|
|
44
22
|
enabled: boolean;
|
|
45
23
|
minPackageAgeDays: number;
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PackageAgeChecker = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* Package age checker to protect against newly created malicious packages
|
|
6
|
-
*/
|
|
7
4
|
class PackageAgeChecker {
|
|
8
5
|
constructor(config) {
|
|
9
6
|
var _a, _b, _c;
|
|
@@ -14,17 +11,12 @@ class PackageAgeChecker {
|
|
|
14
11
|
warnOnly: (_c = config === null || config === void 0 ? void 0 : config.warnOnly) !== null && _c !== void 0 ? _c : false,
|
|
15
12
|
};
|
|
16
13
|
}
|
|
17
|
-
/**
|
|
18
|
-
* Check if a package is old enough to be trusted
|
|
19
|
-
*/
|
|
20
14
|
checkPackageAge(packageData) {
|
|
21
15
|
if (!this.config.enabled) {
|
|
22
16
|
return { allowed: true };
|
|
23
17
|
}
|
|
24
|
-
// Get package creation date from time field
|
|
25
18
|
const createdDate = this.getPackageCreationDate(packageData);
|
|
26
19
|
if (!createdDate) {
|
|
27
|
-
// If we can't determine creation date, allow but warn
|
|
28
20
|
return {
|
|
29
21
|
allowed: true,
|
|
30
22
|
reason: 'Cannot determine package creation date',
|
|
@@ -42,17 +34,12 @@ class PackageAgeChecker {
|
|
|
42
34
|
}
|
|
43
35
|
return { allowed: true, ageDays };
|
|
44
36
|
}
|
|
45
|
-
/**
|
|
46
|
-
* Check if a specific version is old enough to be trusted
|
|
47
|
-
*/
|
|
48
37
|
checkVersionAge(packageData, version) {
|
|
49
38
|
if (!this.config.enabled || !this.config.minVersionAgeDays) {
|
|
50
39
|
return { allowed: true };
|
|
51
40
|
}
|
|
52
|
-
// Get version publish date from time field
|
|
53
41
|
const versionDate = this.getVersionPublishDate(packageData, version);
|
|
54
42
|
if (!versionDate) {
|
|
55
|
-
// If we can't determine version date, allow but warn
|
|
56
43
|
return {
|
|
57
44
|
allowed: true,
|
|
58
45
|
reason: `Cannot determine version ${version} publish date`,
|
|
@@ -70,22 +57,16 @@ class PackageAgeChecker {
|
|
|
70
57
|
}
|
|
71
58
|
return { allowed: true, ageDays };
|
|
72
59
|
}
|
|
73
|
-
/**
|
|
74
|
-
* Get package creation date from time field
|
|
75
|
-
* The 'created' field in package.time represents when the package was first published
|
|
76
|
-
*/
|
|
77
60
|
getPackageCreationDate(packageData) {
|
|
78
61
|
if (!packageData.time) {
|
|
79
62
|
return null;
|
|
80
63
|
}
|
|
81
|
-
// Try to get 'created' field first
|
|
82
64
|
if ('created' in packageData.time) {
|
|
83
65
|
const created = packageData.time.created;
|
|
84
66
|
if (typeof created === 'string') {
|
|
85
67
|
return new Date(created);
|
|
86
68
|
}
|
|
87
69
|
}
|
|
88
|
-
// Fallback: find the oldest version
|
|
89
70
|
const versions = Object.keys(packageData.time).filter(key => key !== 'modified' && key !== 'created');
|
|
90
71
|
if (versions.length === 0) {
|
|
91
72
|
return null;
|
|
@@ -99,12 +80,8 @@ class PackageAgeChecker {
|
|
|
99
80
|
if (dates.length === 0) {
|
|
100
81
|
return null;
|
|
101
82
|
}
|
|
102
|
-
// Return the oldest date
|
|
103
83
|
return new Date(Math.min(...dates.map(d => d.getTime())));
|
|
104
84
|
}
|
|
105
|
-
/**
|
|
106
|
-
* Get version publish date from time field
|
|
107
|
-
*/
|
|
108
85
|
getVersionPublishDate(packageData, version) {
|
|
109
86
|
if (!packageData.time || !(version in packageData.time)) {
|
|
110
87
|
return null;
|
|
@@ -115,17 +92,11 @@ class PackageAgeChecker {
|
|
|
115
92
|
}
|
|
116
93
|
return null;
|
|
117
94
|
}
|
|
118
|
-
/**
|
|
119
|
-
* Calculate age in days from a date
|
|
120
|
-
*/
|
|
121
95
|
calculateAgeDays(date) {
|
|
122
96
|
const now = new Date();
|
|
123
97
|
const diffMs = now.getTime() - date.getTime();
|
|
124
98
|
return Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
125
99
|
}
|
|
126
|
-
/**
|
|
127
|
-
* Get summary of configuration
|
|
128
|
-
*/
|
|
129
100
|
getSummary() {
|
|
130
101
|
return {
|
|
131
102
|
enabled: this.config.enabled,
|
|
@@ -1,45 +1,19 @@
|
|
|
1
1
|
import { WhitelistConfig } from '../types';
|
|
2
|
-
/**
|
|
3
|
-
* Whitelist checker for package validation
|
|
4
|
-
*/
|
|
5
2
|
export declare class WhitelistChecker {
|
|
6
3
|
private config;
|
|
7
4
|
constructor(config?: WhitelistConfig);
|
|
8
|
-
/**
|
|
9
|
-
* Check if a package is whitelisted
|
|
10
|
-
*/
|
|
11
5
|
isWhitelisted(packageName: string, version?: string): {
|
|
12
6
|
allowed: boolean;
|
|
13
7
|
reason?: string;
|
|
14
8
|
};
|
|
15
|
-
/**
|
|
16
|
-
* Check if package name matches any whitelist pattern
|
|
17
|
-
*/
|
|
18
9
|
private matchesPattern;
|
|
19
|
-
/**
|
|
20
|
-
* Add package to whitelist
|
|
21
|
-
*/
|
|
22
10
|
addPackage(packageName: string, versionRange?: string): void;
|
|
23
|
-
/**
|
|
24
|
-
* Remove package from whitelist
|
|
25
|
-
*/
|
|
26
11
|
removePackage(packageName: string): void;
|
|
27
|
-
/**
|
|
28
|
-
* Add pattern to whitelist
|
|
29
|
-
*/
|
|
30
12
|
addPattern(pattern: string): void;
|
|
31
|
-
/**
|
|
32
|
-
* Check if package meets auto-approve criteria
|
|
33
|
-
* Note: This is a placeholder. Real implementation would require
|
|
34
|
-
* integration with npm registry API to fetch download stats, etc.
|
|
35
|
-
*/
|
|
36
13
|
meetsAutoApproveCriteria(_packageName: string): Promise<{
|
|
37
14
|
approved: boolean;
|
|
38
15
|
reason?: string;
|
|
39
16
|
}>;
|
|
40
|
-
/**
|
|
41
|
-
* Get whitelist summary
|
|
42
|
-
*/
|
|
43
17
|
getSummary(): {
|
|
44
18
|
totalPackages: number;
|
|
45
19
|
totalPatterns: number;
|
|
@@ -44,9 +44,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
exports.WhitelistChecker = void 0;
|
|
46
46
|
const semver = __importStar(require("semver"));
|
|
47
|
-
/**
|
|
48
|
-
* Whitelist checker for package validation
|
|
49
|
-
*/
|
|
50
47
|
class WhitelistChecker {
|
|
51
48
|
constructor(config) {
|
|
52
49
|
this.config = {
|
|
@@ -56,13 +53,8 @@ class WhitelistChecker {
|
|
|
56
53
|
autoApprove: config === null || config === void 0 ? void 0 : config.autoApprove,
|
|
57
54
|
};
|
|
58
55
|
}
|
|
59
|
-
/**
|
|
60
|
-
* Check if a package is whitelisted
|
|
61
|
-
*/
|
|
62
56
|
isWhitelisted(packageName, version) {
|
|
63
|
-
// Check exact package name match
|
|
64
57
|
if (this.config.packages.includes(packageName)) {
|
|
65
|
-
// If version is specified, check version constraint
|
|
66
58
|
if (version && this.config.versions && packageName in this.config.versions) {
|
|
67
59
|
const versionRange = this.config.versions[packageName];
|
|
68
60
|
if (!semver.satisfies(version, versionRange)) {
|
|
@@ -74,19 +66,14 @@ class WhitelistChecker {
|
|
|
74
66
|
}
|
|
75
67
|
return { allowed: true };
|
|
76
68
|
}
|
|
77
|
-
// Check pattern match
|
|
78
69
|
if (this.matchesPattern(packageName)) {
|
|
79
70
|
return { allowed: true };
|
|
80
71
|
}
|
|
81
|
-
// Not in whitelist
|
|
82
72
|
return {
|
|
83
73
|
allowed: false,
|
|
84
74
|
reason: 'Package is not in whitelist',
|
|
85
75
|
};
|
|
86
76
|
}
|
|
87
|
-
/**
|
|
88
|
-
* Check if package name matches any whitelist pattern
|
|
89
|
-
*/
|
|
90
77
|
matchesPattern(packageName) {
|
|
91
78
|
return this.config.patterns.some(pattern => {
|
|
92
79
|
try {
|
|
@@ -99,9 +86,6 @@ class WhitelistChecker {
|
|
|
99
86
|
}
|
|
100
87
|
});
|
|
101
88
|
}
|
|
102
|
-
/**
|
|
103
|
-
* Add package to whitelist
|
|
104
|
-
*/
|
|
105
89
|
addPackage(packageName, versionRange) {
|
|
106
90
|
if (!this.config.packages.includes(packageName)) {
|
|
107
91
|
this.config.packages.push(packageName);
|
|
@@ -111,9 +95,6 @@ class WhitelistChecker {
|
|
|
111
95
|
this.config.versions[packageName] = versionRange;
|
|
112
96
|
}
|
|
113
97
|
}
|
|
114
|
-
/**
|
|
115
|
-
* Remove package from whitelist
|
|
116
|
-
*/
|
|
117
98
|
removePackage(packageName) {
|
|
118
99
|
const index = this.config.packages.indexOf(packageName);
|
|
119
100
|
if (index > -1) {
|
|
@@ -123,40 +104,22 @@ class WhitelistChecker {
|
|
|
123
104
|
delete this.config.versions[packageName];
|
|
124
105
|
}
|
|
125
106
|
}
|
|
126
|
-
/**
|
|
127
|
-
* Add pattern to whitelist
|
|
128
|
-
*/
|
|
129
107
|
addPattern(pattern) {
|
|
130
108
|
if (!this.config.patterns.includes(pattern)) {
|
|
131
109
|
this.config.patterns.push(pattern);
|
|
132
110
|
}
|
|
133
111
|
}
|
|
134
|
-
/**
|
|
135
|
-
* Check if package meets auto-approve criteria
|
|
136
|
-
* Note: This is a placeholder. Real implementation would require
|
|
137
|
-
* integration with npm registry API to fetch download stats, etc.
|
|
138
|
-
*/
|
|
139
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
140
112
|
meetsAutoApproveCriteria(_packageName) {
|
|
141
113
|
return __awaiter(this, void 0, void 0, function* () {
|
|
142
114
|
if (!this.config.autoApprove) {
|
|
143
115
|
return { approved: false, reason: 'Auto-approve not configured' };
|
|
144
116
|
}
|
|
145
|
-
// Placeholder for npm registry API integration
|
|
146
|
-
// In a real implementation, you would:
|
|
147
|
-
// 1. Fetch package info from npm registry
|
|
148
|
-
// 2. Check weekly downloads against minDownloads
|
|
149
|
-
// 3. Check GitHub stars against minStars
|
|
150
|
-
// 4. Check if publisher is verified
|
|
151
117
|
return {
|
|
152
118
|
approved: false,
|
|
153
119
|
reason: 'Auto-approve requires npm registry API integration (not implemented)',
|
|
154
120
|
};
|
|
155
121
|
});
|
|
156
122
|
}
|
|
157
|
-
/**
|
|
158
|
-
* Get whitelist summary
|
|
159
|
-
*/
|
|
160
123
|
getSummary() {
|
|
161
124
|
return {
|
|
162
125
|
totalPackages: this.config.packages.length,
|
package/build/types.d.ts
CHANGED
|
@@ -90,11 +90,8 @@ export interface SecurityRules {
|
|
|
90
90
|
versionRangeRules: VersionRangeRule[];
|
|
91
91
|
}
|
|
92
92
|
export interface VersionData extends Version {
|
|
93
|
-
/** Internal flag for fallback versions */
|
|
94
93
|
_fallback?: boolean;
|
|
95
|
-
/** Original version this is falling back from */
|
|
96
94
|
_fallbackFrom?: string;
|
|
97
|
-
/** Reason for fallback */
|
|
98
95
|
_fallbackReason?: string;
|
|
99
96
|
}
|
|
100
97
|
export interface FilterResult {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "verdaccio-security-filter",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
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",
|
|
@@ -67,7 +67,9 @@
|
|
|
67
67
|
},
|
|
68
68
|
"homepage": "https://github.com/ponomarenko/verdaccio-security-filter#readme",
|
|
69
69
|
"files": [
|
|
70
|
-
"build
|
|
71
|
-
"
|
|
70
|
+
"build/**/*.js",
|
|
71
|
+
"build/**/*.d.ts",
|
|
72
|
+
"README.md",
|
|
73
|
+
"LICENSE"
|
|
72
74
|
]
|
|
73
75
|
}
|