verdaccio-security-filter 1.0.6

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.
@@ -0,0 +1,202 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.MetricsCollector = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ /**
40
+ * Metrics collector for tracking security events
41
+ */
42
+ class MetricsCollector {
43
+ constructor(config) {
44
+ this.metricsBuffer = [];
45
+ this.bufferSize = 100;
46
+ this.config = {
47
+ enabled: (config === null || config === void 0 ? void 0 : config.enabled) || false,
48
+ output: (config === null || config === void 0 ? void 0 : config.output) || 'stdout',
49
+ filePath: (config === null || config === void 0 ? void 0 : config.filePath) || './security-metrics.json',
50
+ };
51
+ }
52
+ /**
53
+ * Record a security event
54
+ */
55
+ record(event) {
56
+ if (!this.config.enabled) {
57
+ return;
58
+ }
59
+ this.metricsBuffer.push(event);
60
+ if (this.metricsBuffer.length >= this.bufferSize) {
61
+ this.flush();
62
+ }
63
+ }
64
+ /**
65
+ * Record a block event
66
+ */
67
+ recordBlock(packageName, version, reason, metadata) {
68
+ this.record({
69
+ timestamp: new Date().toISOString(),
70
+ event: 'block',
71
+ packageName,
72
+ version,
73
+ reason,
74
+ metadata,
75
+ });
76
+ }
77
+ /**
78
+ * Record a fallback event
79
+ */
80
+ recordFallback(packageName, fromVersion, toVersion, reason) {
81
+ this.record({
82
+ timestamp: new Date().toISOString(),
83
+ event: 'fallback',
84
+ packageName,
85
+ version: fromVersion,
86
+ reason,
87
+ metadata: { toVersion },
88
+ });
89
+ }
90
+ /**
91
+ * Record a publish rejection
92
+ */
93
+ recordPublishRejected(packageName, version, reason) {
94
+ this.record({
95
+ timestamp: new Date().toISOString(),
96
+ event: 'publish_rejected',
97
+ packageName,
98
+ version,
99
+ reason,
100
+ });
101
+ }
102
+ /**
103
+ * Record a CVE detection
104
+ */
105
+ recordCVE(packageName, version, cveId, severity) {
106
+ this.record({
107
+ timestamp: new Date().toISOString(),
108
+ event: 'cve_detected',
109
+ packageName,
110
+ version,
111
+ reason: `CVE ${cveId} (${severity})`,
112
+ metadata: { cveId, severity },
113
+ });
114
+ }
115
+ /**
116
+ * Record a license block
117
+ */
118
+ recordLicenseBlock(packageName, version, license, reason) {
119
+ this.record({
120
+ timestamp: new Date().toISOString(),
121
+ event: 'license_blocked',
122
+ packageName,
123
+ version,
124
+ reason,
125
+ metadata: { license },
126
+ });
127
+ }
128
+ /**
129
+ * Record a package-too-new event
130
+ */
131
+ recordPackageTooNew(packageName, version, reason, metadata) {
132
+ this.record({
133
+ timestamp: new Date().toISOString(),
134
+ event: 'package_too_new',
135
+ packageName,
136
+ version,
137
+ reason,
138
+ metadata,
139
+ });
140
+ }
141
+ /**
142
+ * Flush metrics buffer to output
143
+ */
144
+ flush() {
145
+ if (this.metricsBuffer.length === 0) {
146
+ return;
147
+ }
148
+ if (this.config.output === 'stdout') {
149
+ this.metricsBuffer.forEach(metric => {
150
+ console.log(JSON.stringify(metric));
151
+ });
152
+ }
153
+ else if (this.config.output === 'file' && this.config.filePath) {
154
+ this.writeToFile(this.metricsBuffer);
155
+ }
156
+ this.metricsBuffer = [];
157
+ }
158
+ /**
159
+ * Write metrics to file
160
+ */
161
+ writeToFile(metrics) {
162
+ try {
163
+ const filePath = this.config.filePath;
164
+ const dir = path.dirname(filePath);
165
+ // Ensure directory exists
166
+ if (!fs.existsSync(dir)) {
167
+ fs.mkdirSync(dir, { recursive: true });
168
+ }
169
+ // Append to file
170
+ const content = metrics.map(m => JSON.stringify(m)).join('\n') + '\n';
171
+ fs.appendFileSync(filePath, content, 'utf8');
172
+ }
173
+ catch (error) {
174
+ const message = error instanceof Error ? error.message : String(error);
175
+ console.error('[Security Filter] Failed to write metrics:', message);
176
+ }
177
+ }
178
+ /**
179
+ * Get metrics summary
180
+ */
181
+ getSummary() {
182
+ const summary = {
183
+ block: 0,
184
+ fallback: 0,
185
+ publish_rejected: 0,
186
+ cve_detected: 0,
187
+ license_blocked: 0,
188
+ package_too_new: 0,
189
+ };
190
+ this.metricsBuffer.forEach(metric => {
191
+ summary[metric.event] = (summary[metric.event] || 0) + 1;
192
+ });
193
+ return summary;
194
+ }
195
+ /**
196
+ * Cleanup - flush remaining metrics
197
+ */
198
+ cleanup() {
199
+ this.flush();
200
+ }
201
+ }
202
+ exports.MetricsCollector = MetricsCollector;
@@ -0,0 +1,49 @@
1
+ import { PackageAgeConfig } from '../types';
2
+ import { Package } from '@verdaccio/types';
3
+ /**
4
+ * Package age checker to protect against newly created malicious packages
5
+ */
6
+ export declare class PackageAgeChecker {
7
+ private config;
8
+ constructor(config?: PackageAgeConfig);
9
+ /**
10
+ * Check if a package is old enough to be trusted
11
+ */
12
+ checkPackageAge(packageData: Package): {
13
+ allowed: boolean;
14
+ reason?: string;
15
+ ageDays?: number;
16
+ warnOnly?: boolean;
17
+ };
18
+ /**
19
+ * Check if a specific version is old enough to be trusted
20
+ */
21
+ checkVersionAge(packageData: Package, version: string): {
22
+ allowed: boolean;
23
+ reason?: string;
24
+ ageDays?: number;
25
+ warnOnly?: boolean;
26
+ };
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
+ private getPackageCreationDate;
32
+ /**
33
+ * Get version publish date from time field
34
+ */
35
+ private getVersionPublishDate;
36
+ /**
37
+ * Calculate age in days from a date
38
+ */
39
+ private calculateAgeDays;
40
+ /**
41
+ * Get summary of configuration
42
+ */
43
+ getSummary(): {
44
+ enabled: boolean;
45
+ minPackageAgeDays: number;
46
+ minVersionAgeDays?: number;
47
+ warnOnly: boolean;
48
+ };
49
+ }
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PackageAgeChecker = void 0;
4
+ /**
5
+ * Package age checker to protect against newly created malicious packages
6
+ */
7
+ class PackageAgeChecker {
8
+ constructor(config) {
9
+ var _a, _b, _c;
10
+ this.config = {
11
+ enabled: (_a = config === null || config === void 0 ? void 0 : config.enabled) !== null && _a !== void 0 ? _a : false,
12
+ minPackageAgeDays: (_b = config === null || config === void 0 ? void 0 : config.minPackageAgeDays) !== null && _b !== void 0 ? _b : 0,
13
+ minVersionAgeDays: config === null || config === void 0 ? void 0 : config.minVersionAgeDays,
14
+ warnOnly: (_c = config === null || config === void 0 ? void 0 : config.warnOnly) !== null && _c !== void 0 ? _c : false,
15
+ };
16
+ }
17
+ /**
18
+ * Check if a package is old enough to be trusted
19
+ */
20
+ checkPackageAge(packageData) {
21
+ if (!this.config.enabled) {
22
+ return { allowed: true };
23
+ }
24
+ // Get package creation date from time field
25
+ const createdDate = this.getPackageCreationDate(packageData);
26
+ if (!createdDate) {
27
+ // If we can't determine creation date, allow but warn
28
+ return {
29
+ allowed: true,
30
+ reason: 'Cannot determine package creation date',
31
+ };
32
+ }
33
+ const ageDays = this.calculateAgeDays(createdDate);
34
+ if (ageDays < this.config.minPackageAgeDays) {
35
+ const warnOnlyValue = this.config.warnOnly || false;
36
+ return {
37
+ allowed: warnOnlyValue,
38
+ reason: `Package is only ${ageDays} days old (minimum: ${this.config.minPackageAgeDays} days)`,
39
+ ageDays,
40
+ warnOnly: warnOnlyValue,
41
+ };
42
+ }
43
+ return { allowed: true, ageDays };
44
+ }
45
+ /**
46
+ * Check if a specific version is old enough to be trusted
47
+ */
48
+ checkVersionAge(packageData, version) {
49
+ if (!this.config.enabled || !this.config.minVersionAgeDays) {
50
+ return { allowed: true };
51
+ }
52
+ // Get version publish date from time field
53
+ const versionDate = this.getVersionPublishDate(packageData, version);
54
+ if (!versionDate) {
55
+ // If we can't determine version date, allow but warn
56
+ return {
57
+ allowed: true,
58
+ reason: `Cannot determine version ${version} publish date`,
59
+ };
60
+ }
61
+ const ageDays = this.calculateAgeDays(versionDate);
62
+ if (ageDays < this.config.minVersionAgeDays) {
63
+ const warnOnlyValue = this.config.warnOnly || false;
64
+ return {
65
+ allowed: warnOnlyValue,
66
+ reason: `Version ${version} is only ${ageDays} days old (minimum: ${this.config.minVersionAgeDays} days)`,
67
+ ageDays,
68
+ warnOnly: warnOnlyValue,
69
+ };
70
+ }
71
+ return { allowed: true, ageDays };
72
+ }
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
+ getPackageCreationDate(packageData) {
78
+ if (!packageData.time) {
79
+ return null;
80
+ }
81
+ // Try to get 'created' field first
82
+ if ('created' in packageData.time) {
83
+ const created = packageData.time.created;
84
+ if (typeof created === 'string') {
85
+ return new Date(created);
86
+ }
87
+ }
88
+ // Fallback: find the oldest version
89
+ const versions = Object.keys(packageData.time).filter(key => key !== 'modified' && key !== 'created');
90
+ if (versions.length === 0) {
91
+ return null;
92
+ }
93
+ const dates = versions
94
+ .map(v => {
95
+ const timeValue = packageData.time[v];
96
+ return typeof timeValue === 'string' ? new Date(timeValue) : null;
97
+ })
98
+ .filter((d) => d !== null);
99
+ if (dates.length === 0) {
100
+ return null;
101
+ }
102
+ // Return the oldest date
103
+ return new Date(Math.min(...dates.map(d => d.getTime())));
104
+ }
105
+ /**
106
+ * Get version publish date from time field
107
+ */
108
+ getVersionPublishDate(packageData, version) {
109
+ if (!packageData.time || !(version in packageData.time)) {
110
+ return null;
111
+ }
112
+ const timeValue = packageData.time[version];
113
+ if (typeof timeValue === 'string') {
114
+ return new Date(timeValue);
115
+ }
116
+ return null;
117
+ }
118
+ /**
119
+ * Calculate age in days from a date
120
+ */
121
+ calculateAgeDays(date) {
122
+ const now = new Date();
123
+ const diffMs = now.getTime() - date.getTime();
124
+ return Math.floor(diffMs / (1000 * 60 * 60 * 24));
125
+ }
126
+ /**
127
+ * Get summary of configuration
128
+ */
129
+ getSummary() {
130
+ return {
131
+ enabled: this.config.enabled,
132
+ minPackageAgeDays: this.config.minPackageAgeDays,
133
+ minVersionAgeDays: this.config.minVersionAgeDays,
134
+ warnOnly: this.config.warnOnly || false,
135
+ };
136
+ }
137
+ }
138
+ exports.PackageAgeChecker = PackageAgeChecker;
@@ -0,0 +1,48 @@
1
+ import { WhitelistConfig } from '../types';
2
+ /**
3
+ * Whitelist checker for package validation
4
+ */
5
+ export declare class WhitelistChecker {
6
+ private config;
7
+ constructor(config?: WhitelistConfig);
8
+ /**
9
+ * Check if a package is whitelisted
10
+ */
11
+ isWhitelisted(packageName: string, version?: string): {
12
+ allowed: boolean;
13
+ reason?: string;
14
+ };
15
+ /**
16
+ * Check if package name matches any whitelist pattern
17
+ */
18
+ private matchesPattern;
19
+ /**
20
+ * Add package to whitelist
21
+ */
22
+ addPackage(packageName: string, versionRange?: string): void;
23
+ /**
24
+ * Remove package from whitelist
25
+ */
26
+ removePackage(packageName: string): void;
27
+ /**
28
+ * Add pattern to whitelist
29
+ */
30
+ 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
+ meetsAutoApproveCriteria(_packageName: string): Promise<{
37
+ approved: boolean;
38
+ reason?: string;
39
+ }>;
40
+ /**
41
+ * Get whitelist summary
42
+ */
43
+ getSummary(): {
44
+ totalPackages: number;
45
+ totalPatterns: number;
46
+ hasAutoApprove: boolean;
47
+ };
48
+ }
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
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
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.WhitelistChecker = void 0;
46
+ const semver = __importStar(require("semver"));
47
+ /**
48
+ * Whitelist checker for package validation
49
+ */
50
+ class WhitelistChecker {
51
+ constructor(config) {
52
+ this.config = {
53
+ packages: (config === null || config === void 0 ? void 0 : config.packages) || [],
54
+ patterns: (config === null || config === void 0 ? void 0 : config.patterns) || [],
55
+ versions: (config === null || config === void 0 ? void 0 : config.versions) || {},
56
+ autoApprove: config === null || config === void 0 ? void 0 : config.autoApprove,
57
+ };
58
+ }
59
+ /**
60
+ * Check if a package is whitelisted
61
+ */
62
+ isWhitelisted(packageName, version) {
63
+ // Check exact package name match
64
+ if (this.config.packages.includes(packageName)) {
65
+ // If version is specified, check version constraint
66
+ if (version && this.config.versions && packageName in this.config.versions) {
67
+ const versionRange = this.config.versions[packageName];
68
+ if (!semver.satisfies(version, versionRange)) {
69
+ return {
70
+ allowed: false,
71
+ reason: `Version ${version} does not satisfy whitelist constraint ${versionRange}`,
72
+ };
73
+ }
74
+ }
75
+ return { allowed: true };
76
+ }
77
+ // Check pattern match
78
+ if (this.matchesPattern(packageName)) {
79
+ return { allowed: true };
80
+ }
81
+ // Not in whitelist
82
+ return {
83
+ allowed: false,
84
+ reason: 'Package is not in whitelist',
85
+ };
86
+ }
87
+ /**
88
+ * Check if package name matches any whitelist pattern
89
+ */
90
+ matchesPattern(packageName) {
91
+ return this.config.patterns.some(pattern => {
92
+ try {
93
+ const regex = new RegExp(pattern);
94
+ return regex.test(packageName);
95
+ }
96
+ catch (_a) {
97
+ console.error(`[Whitelist] Invalid pattern: ${pattern}`);
98
+ return false;
99
+ }
100
+ });
101
+ }
102
+ /**
103
+ * Add package to whitelist
104
+ */
105
+ addPackage(packageName, versionRange) {
106
+ if (!this.config.packages.includes(packageName)) {
107
+ this.config.packages.push(packageName);
108
+ }
109
+ if (versionRange) {
110
+ this.config.versions = this.config.versions || {};
111
+ this.config.versions[packageName] = versionRange;
112
+ }
113
+ }
114
+ /**
115
+ * Remove package from whitelist
116
+ */
117
+ removePackage(packageName) {
118
+ const index = this.config.packages.indexOf(packageName);
119
+ if (index > -1) {
120
+ this.config.packages.splice(index, 1);
121
+ }
122
+ if (this.config.versions && packageName in this.config.versions) {
123
+ delete this.config.versions[packageName];
124
+ }
125
+ }
126
+ /**
127
+ * Add pattern to whitelist
128
+ */
129
+ addPattern(pattern) {
130
+ if (!this.config.patterns.includes(pattern)) {
131
+ this.config.patterns.push(pattern);
132
+ }
133
+ }
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
+ meetsAutoApproveCriteria(_packageName) {
141
+ return __awaiter(this, void 0, void 0, function* () {
142
+ if (!this.config.autoApprove) {
143
+ return { approved: false, reason: 'Auto-approve not configured' };
144
+ }
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
+ return {
152
+ approved: false,
153
+ reason: 'Auto-approve requires npm registry API integration (not implemented)',
154
+ };
155
+ });
156
+ }
157
+ /**
158
+ * Get whitelist summary
159
+ */
160
+ getSummary() {
161
+ return {
162
+ totalPackages: this.config.packages.length,
163
+ totalPatterns: this.config.patterns.length,
164
+ hasAutoApprove: !!this.config.autoApprove,
165
+ };
166
+ }
167
+ }
168
+ exports.WhitelistChecker = WhitelistChecker;