@objectstack/core 0.9.0 → 0.9.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.
Files changed (91) hide show
  1. package/{ENHANCED_FEATURES.md → ADVANCED_FEATURES.md} +13 -13
  2. package/CHANGELOG.md +15 -0
  3. package/PHASE2_IMPLEMENTATION.md +388 -0
  4. package/README.md +60 -11
  5. package/REFACTORING_SUMMARY.md +40 -0
  6. package/dist/api-registry-plugin.test.js +20 -20
  7. package/dist/dependency-resolver.d.ts +62 -0
  8. package/dist/dependency-resolver.d.ts.map +1 -0
  9. package/dist/dependency-resolver.js +317 -0
  10. package/dist/dependency-resolver.test.d.ts +2 -0
  11. package/dist/dependency-resolver.test.d.ts.map +1 -0
  12. package/dist/dependency-resolver.test.js +241 -0
  13. package/dist/health-monitor.d.ts +65 -0
  14. package/dist/health-monitor.d.ts.map +1 -0
  15. package/dist/health-monitor.js +269 -0
  16. package/dist/health-monitor.test.d.ts +2 -0
  17. package/dist/health-monitor.test.d.ts.map +1 -0
  18. package/dist/health-monitor.test.js +68 -0
  19. package/dist/hot-reload.d.ts +79 -0
  20. package/dist/hot-reload.d.ts.map +1 -0
  21. package/dist/hot-reload.js +313 -0
  22. package/dist/index.d.ts +4 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +5 -1
  25. package/dist/kernel-base.d.ts +2 -2
  26. package/dist/kernel-base.js +2 -2
  27. package/dist/kernel.d.ts +79 -31
  28. package/dist/kernel.d.ts.map +1 -1
  29. package/dist/kernel.js +383 -73
  30. package/dist/kernel.test.js +373 -122
  31. package/dist/lite-kernel.d.ts +55 -0
  32. package/dist/lite-kernel.d.ts.map +1 -0
  33. package/dist/lite-kernel.js +112 -0
  34. package/dist/lite-kernel.test.d.ts +2 -0
  35. package/dist/lite-kernel.test.d.ts.map +1 -0
  36. package/dist/lite-kernel.test.js +161 -0
  37. package/dist/logger.d.ts +3 -2
  38. package/dist/logger.d.ts.map +1 -1
  39. package/dist/logger.js +61 -18
  40. package/dist/plugin-loader.d.ts +11 -0
  41. package/dist/plugin-loader.d.ts.map +1 -1
  42. package/dist/plugin-loader.js +34 -10
  43. package/dist/plugin-loader.test.js +9 -0
  44. package/dist/security/index.d.ts +3 -0
  45. package/dist/security/index.d.ts.map +1 -1
  46. package/dist/security/index.js +4 -0
  47. package/dist/security/permission-manager.d.ts +96 -0
  48. package/dist/security/permission-manager.d.ts.map +1 -0
  49. package/dist/security/permission-manager.js +235 -0
  50. package/dist/security/permission-manager.test.d.ts +2 -0
  51. package/dist/security/permission-manager.test.d.ts.map +1 -0
  52. package/dist/security/permission-manager.test.js +220 -0
  53. package/dist/security/plugin-signature-verifier.js +3 -3
  54. package/dist/security/sandbox-runtime.d.ts +115 -0
  55. package/dist/security/sandbox-runtime.d.ts.map +1 -0
  56. package/dist/security/sandbox-runtime.js +310 -0
  57. package/dist/security/security-scanner.d.ts +92 -0
  58. package/dist/security/security-scanner.d.ts.map +1 -0
  59. package/dist/security/security-scanner.js +273 -0
  60. package/examples/{enhanced-kernel-example.ts → kernel-features-example.ts} +6 -6
  61. package/examples/phase2-integration.ts +355 -0
  62. package/package.json +2 -2
  63. package/src/api-registry-plugin.test.ts +20 -20
  64. package/src/dependency-resolver.test.ts +287 -0
  65. package/src/dependency-resolver.ts +388 -0
  66. package/src/health-monitor.test.ts +81 -0
  67. package/src/health-monitor.ts +316 -0
  68. package/src/hot-reload.ts +388 -0
  69. package/src/index.ts +6 -1
  70. package/src/kernel-base.ts +2 -2
  71. package/src/kernel.test.ts +469 -134
  72. package/src/kernel.ts +464 -78
  73. package/src/lite-kernel.test.ts +200 -0
  74. package/src/lite-kernel.ts +135 -0
  75. package/src/logger.ts +64 -18
  76. package/src/plugin-loader.test.ts +10 -1
  77. package/src/plugin-loader.ts +42 -13
  78. package/src/security/index.ts +19 -0
  79. package/src/security/permission-manager.test.ts +256 -0
  80. package/src/security/permission-manager.ts +336 -0
  81. package/src/security/plugin-signature-verifier.ts +3 -3
  82. package/src/security/sandbox-runtime.ts +432 -0
  83. package/src/security/security-scanner.ts +365 -0
  84. package/dist/enhanced-kernel.d.ts +0 -103
  85. package/dist/enhanced-kernel.d.ts.map +0 -1
  86. package/dist/enhanced-kernel.js +0 -403
  87. package/dist/enhanced-kernel.test.d.ts +0 -2
  88. package/dist/enhanced-kernel.test.d.ts.map +0 -1
  89. package/dist/enhanced-kernel.test.js +0 -412
  90. package/src/enhanced-kernel.test.ts +0 -535
  91. package/src/enhanced-kernel.ts +0 -496
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-runtime.d.ts","sourceRoot":"","sources":["../../src/security/sandbox-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACd,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,GAAG,EAAE;QACH,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,WAAW,EAAE;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC;IAChB,aAAa,EAAE,aAAa,CAAC;CAC9B;AAED;;;;;GAKG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,MAAM,CAAe;IAG7B,OAAO,CAAC,SAAS,CAAqC;IAGtD,OAAO,CAAC,mBAAmB,CAAqC;gBAEpD,MAAM,EAAE,YAAY;IAIhC;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,cAAc;IA+BtE;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IActC;;OAEG;IACH,mBAAmB,CACjB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,KAAK,EACpD,YAAY,CAAC,EAAE,MAAM,GACpB;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;IA0BxC;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAiDvB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAwD1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAkB1B;;OAEG;IACH,OAAO,CAAC,cAAc;IAsBtB;;OAEG;IACH,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG;QACrC,YAAY,EAAE,OAAO,CAAC;QACtB,UAAU,EAAE,MAAM,EAAE,CAAA;KACrB;IAiCD;;OAEG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAK7D;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAS/B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAQ9B;;;;;;;OAOG;IACH,OAAO,CAAC,mBAAmB;IAiC3B;;OAEG;IACH,eAAe,IAAI,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC;IAI9C;;OAEG;IACH,QAAQ,IAAI,IAAI;CAUjB"}
@@ -0,0 +1,310 @@
1
+ /**
2
+ * Plugin Sandbox Runtime
3
+ *
4
+ * Provides isolated execution environments for plugins with resource limits
5
+ * and access controls
6
+ */
7
+ export class PluginSandboxRuntime {
8
+ constructor(logger) {
9
+ // Active sandboxes (pluginId -> context)
10
+ this.sandboxes = new Map();
11
+ // Resource monitoring intervals
12
+ this.monitoringIntervals = new Map();
13
+ this.logger = logger.child({ component: 'SandboxRuntime' });
14
+ }
15
+ /**
16
+ * Create a sandbox for a plugin
17
+ */
18
+ createSandbox(pluginId, config) {
19
+ if (this.sandboxes.has(pluginId)) {
20
+ throw new Error(`Sandbox already exists for plugin: ${pluginId}`);
21
+ }
22
+ const context = {
23
+ pluginId,
24
+ config,
25
+ startTime: new Date(),
26
+ resourceUsage: {
27
+ memory: { current: 0, peak: 0, limit: config.memory?.maxHeap },
28
+ cpu: { current: 0, average: 0, limit: config.cpu?.maxCpuPercent },
29
+ connections: { current: 0, limit: config.network?.maxConnections },
30
+ },
31
+ };
32
+ this.sandboxes.set(pluginId, context);
33
+ // Start resource monitoring
34
+ this.startResourceMonitoring(pluginId);
35
+ this.logger.info('Sandbox created', {
36
+ pluginId,
37
+ level: config.level,
38
+ memoryLimit: config.memory?.maxHeap,
39
+ cpuLimit: config.cpu?.maxCpuPercent
40
+ });
41
+ return context;
42
+ }
43
+ /**
44
+ * Destroy a sandbox
45
+ */
46
+ destroySandbox(pluginId) {
47
+ const context = this.sandboxes.get(pluginId);
48
+ if (!context) {
49
+ return;
50
+ }
51
+ // Stop monitoring
52
+ this.stopResourceMonitoring(pluginId);
53
+ this.sandboxes.delete(pluginId);
54
+ this.logger.info('Sandbox destroyed', { pluginId });
55
+ }
56
+ /**
57
+ * Check if resource access is allowed
58
+ */
59
+ checkResourceAccess(pluginId, resourceType, resourcePath) {
60
+ const context = this.sandboxes.get(pluginId);
61
+ if (!context) {
62
+ return { allowed: false, reason: 'Sandbox not found' };
63
+ }
64
+ const { config } = context;
65
+ switch (resourceType) {
66
+ case 'file':
67
+ return this.checkFileAccess(config, resourcePath);
68
+ case 'network':
69
+ return this.checkNetworkAccess(config, resourcePath);
70
+ case 'process':
71
+ return this.checkProcessAccess(config);
72
+ case 'env':
73
+ return this.checkEnvAccess(config, resourcePath);
74
+ default:
75
+ return { allowed: false, reason: 'Unknown resource type' };
76
+ }
77
+ }
78
+ /**
79
+ * Check file system access
80
+ * WARNING: Uses simple prefix matching. For production, use proper path
81
+ * resolution with path.resolve() and path.normalize() to prevent traversal.
82
+ */
83
+ checkFileAccess(config, path) {
84
+ if (config.level === 'none') {
85
+ return { allowed: true };
86
+ }
87
+ if (!config.filesystem) {
88
+ return { allowed: false, reason: 'File system access not configured' };
89
+ }
90
+ // If no path specified, check general access
91
+ if (!path) {
92
+ return { allowed: config.filesystem.mode !== 'none' };
93
+ }
94
+ // TODO: Use path.resolve() and path.normalize() for production
95
+ // Check allowed paths
96
+ const allowedPaths = config.filesystem.allowedPaths || [];
97
+ const isAllowed = allowedPaths.some(allowed => {
98
+ // Simple prefix matching - vulnerable to traversal attacks
99
+ // TODO: Use proper path resolution
100
+ return path.startsWith(allowed);
101
+ });
102
+ if (allowedPaths.length > 0 && !isAllowed) {
103
+ return {
104
+ allowed: false,
105
+ reason: `Path not in allowed list: ${path}`
106
+ };
107
+ }
108
+ // Check denied paths
109
+ const deniedPaths = config.filesystem.deniedPaths || [];
110
+ const isDenied = deniedPaths.some(denied => {
111
+ return path.startsWith(denied);
112
+ });
113
+ if (isDenied) {
114
+ return {
115
+ allowed: false,
116
+ reason: `Path is explicitly denied: ${path}`
117
+ };
118
+ }
119
+ return { allowed: true };
120
+ }
121
+ /**
122
+ * Check network access
123
+ * WARNING: Uses simple string matching. For production, use proper URL
124
+ * parsing with new URL() and check hostname property.
125
+ */
126
+ checkNetworkAccess(config, url) {
127
+ if (config.level === 'none') {
128
+ return { allowed: true };
129
+ }
130
+ if (!config.network) {
131
+ return { allowed: false, reason: 'Network access not configured' };
132
+ }
133
+ // Check if network access is enabled
134
+ if (config.network.mode === 'none') {
135
+ return { allowed: false, reason: 'Network access disabled' };
136
+ }
137
+ // If no URL specified, check general access
138
+ if (!url) {
139
+ return { allowed: config.network.mode !== 'none' };
140
+ }
141
+ // TODO: Use new URL() and check hostname property for production
142
+ // Check allowed hosts
143
+ const allowedHosts = config.network.allowedHosts || [];
144
+ if (allowedHosts.length > 0) {
145
+ const isAllowed = allowedHosts.some(host => {
146
+ // Simple string matching - vulnerable to bypass
147
+ // TODO: Use proper URL parsing
148
+ return url.includes(host);
149
+ });
150
+ if (!isAllowed) {
151
+ return {
152
+ allowed: false,
153
+ reason: `Host not in allowed list: ${url}`
154
+ };
155
+ }
156
+ }
157
+ // Check denied hosts
158
+ const deniedHosts = config.network.deniedHosts || [];
159
+ const isDenied = deniedHosts.some(host => {
160
+ return url.includes(host);
161
+ });
162
+ if (isDenied) {
163
+ return {
164
+ allowed: false,
165
+ reason: `Host is blocked: ${url}`
166
+ };
167
+ }
168
+ return { allowed: true };
169
+ }
170
+ /**
171
+ * Check process spawning access
172
+ */
173
+ checkProcessAccess(config) {
174
+ if (config.level === 'none') {
175
+ return { allowed: true };
176
+ }
177
+ if (!config.process) {
178
+ return { allowed: false, reason: 'Process access not configured' };
179
+ }
180
+ if (!config.process.allowSpawn) {
181
+ return { allowed: false, reason: 'Process spawning not allowed' };
182
+ }
183
+ return { allowed: true };
184
+ }
185
+ /**
186
+ * Check environment variable access
187
+ */
188
+ checkEnvAccess(config, varName) {
189
+ if (config.level === 'none') {
190
+ return { allowed: true };
191
+ }
192
+ if (!config.process) {
193
+ return { allowed: false, reason: 'Environment access not configured' };
194
+ }
195
+ // If no variable specified, check general access
196
+ if (!varName) {
197
+ return { allowed: true };
198
+ }
199
+ // For now, allow all env access if process is configured
200
+ // In a real implementation, would check specific allowed vars
201
+ return { allowed: true };
202
+ }
203
+ /**
204
+ * Check resource limits
205
+ */
206
+ checkResourceLimits(pluginId) {
207
+ const context = this.sandboxes.get(pluginId);
208
+ if (!context) {
209
+ return { withinLimits: true, violations: [] };
210
+ }
211
+ const violations = [];
212
+ const { resourceUsage, config } = context;
213
+ // Check memory limit
214
+ if (config.memory?.maxHeap &&
215
+ resourceUsage.memory.current > config.memory.maxHeap) {
216
+ violations.push(`Memory limit exceeded: ${resourceUsage.memory.current} > ${config.memory.maxHeap}`);
217
+ }
218
+ // Check CPU limit (would need runtime config)
219
+ if (config.runtime?.resourceLimits?.maxCpu &&
220
+ resourceUsage.cpu.current > config.runtime.resourceLimits.maxCpu) {
221
+ violations.push(`CPU limit exceeded: ${resourceUsage.cpu.current}% > ${config.runtime.resourceLimits.maxCpu}%`);
222
+ }
223
+ // Check connection limit
224
+ if (config.network?.maxConnections &&
225
+ resourceUsage.connections.current > config.network.maxConnections) {
226
+ violations.push(`Connection limit exceeded: ${resourceUsage.connections.current} > ${config.network.maxConnections}`);
227
+ }
228
+ return {
229
+ withinLimits: violations.length === 0,
230
+ violations,
231
+ };
232
+ }
233
+ /**
234
+ * Get resource usage for a plugin
235
+ */
236
+ getResourceUsage(pluginId) {
237
+ const context = this.sandboxes.get(pluginId);
238
+ return context?.resourceUsage;
239
+ }
240
+ /**
241
+ * Start monitoring resource usage
242
+ */
243
+ startResourceMonitoring(pluginId) {
244
+ // Monitor every 5 seconds
245
+ const interval = setInterval(() => {
246
+ this.updateResourceUsage(pluginId);
247
+ }, 5000);
248
+ this.monitoringIntervals.set(pluginId, interval);
249
+ }
250
+ /**
251
+ * Stop monitoring resource usage
252
+ */
253
+ stopResourceMonitoring(pluginId) {
254
+ const interval = this.monitoringIntervals.get(pluginId);
255
+ if (interval) {
256
+ clearInterval(interval);
257
+ this.monitoringIntervals.delete(pluginId);
258
+ }
259
+ }
260
+ /**
261
+ * Update resource usage statistics
262
+ *
263
+ * NOTE: Currently uses global process.memoryUsage() which tracks the entire
264
+ * Node.js process, not individual plugins. For production, implement proper
265
+ * per-plugin tracking using V8 heap snapshots or allocation tracking at
266
+ * plugin boundaries.
267
+ */
268
+ updateResourceUsage(pluginId) {
269
+ const context = this.sandboxes.get(pluginId);
270
+ if (!context) {
271
+ return;
272
+ }
273
+ // In a real implementation, this would collect actual metrics
274
+ // For now, this is a placeholder structure
275
+ // Update memory usage (global process memory - not per-plugin)
276
+ // TODO: Implement per-plugin memory tracking
277
+ const memoryUsage = process.memoryUsage();
278
+ context.resourceUsage.memory.current = memoryUsage.heapUsed;
279
+ context.resourceUsage.memory.peak = Math.max(context.resourceUsage.memory.peak, memoryUsage.heapUsed);
280
+ // Update CPU usage (would use process.cpuUsage() or similar)
281
+ // This is a placeholder - real implementation would track per-plugin CPU
282
+ // TODO: Implement per-plugin CPU tracking
283
+ context.resourceUsage.cpu.current = 0;
284
+ // Check for violations
285
+ const { withinLimits, violations } = this.checkResourceLimits(pluginId);
286
+ if (!withinLimits) {
287
+ this.logger.warn('Resource limit violations detected', {
288
+ pluginId,
289
+ violations
290
+ });
291
+ }
292
+ }
293
+ /**
294
+ * Get all active sandboxes
295
+ */
296
+ getAllSandboxes() {
297
+ return new Map(this.sandboxes);
298
+ }
299
+ /**
300
+ * Shutdown sandbox runtime
301
+ */
302
+ shutdown() {
303
+ // Stop all monitoring
304
+ for (const pluginId of this.monitoringIntervals.keys()) {
305
+ this.stopResourceMonitoring(pluginId);
306
+ }
307
+ this.sandboxes.clear();
308
+ this.logger.info('Sandbox runtime shutdown complete');
309
+ }
310
+ }
@@ -0,0 +1,92 @@
1
+ import type { SecurityVulnerability, SecurityScanResult } from '@objectstack/spec/system';
2
+ import type { ObjectLogger } from '../logger.js';
3
+ /**
4
+ * Scan Target
5
+ */
6
+ export interface ScanTarget {
7
+ pluginId: string;
8
+ version: string;
9
+ files?: string[];
10
+ dependencies?: Record<string, string>;
11
+ }
12
+ /**
13
+ * Security Issue
14
+ */
15
+ export interface SecurityIssue {
16
+ id: string;
17
+ severity: 'critical' | 'high' | 'medium' | 'low' | 'info';
18
+ category: 'vulnerability' | 'malware' | 'license' | 'code-quality' | 'configuration';
19
+ title: string;
20
+ description: string;
21
+ location?: {
22
+ file?: string;
23
+ line?: number;
24
+ column?: number;
25
+ };
26
+ remediation?: string;
27
+ cve?: string;
28
+ cvss?: number;
29
+ }
30
+ /**
31
+ * Plugin Security Scanner
32
+ *
33
+ * Scans plugins for security vulnerabilities, malware, and license issues
34
+ */
35
+ export declare class PluginSecurityScanner {
36
+ private logger;
37
+ private vulnerabilityDb;
38
+ private scanResults;
39
+ private passThreshold;
40
+ constructor(logger: ObjectLogger, config?: {
41
+ passThreshold?: number;
42
+ });
43
+ /**
44
+ * Perform a comprehensive security scan on a plugin
45
+ */
46
+ scan(target: ScanTarget): Promise<SecurityScanResult>;
47
+ /**
48
+ * Scan code for vulnerabilities
49
+ */
50
+ private scanCode;
51
+ /**
52
+ * Scan dependencies for known vulnerabilities
53
+ */
54
+ private scanDependencies;
55
+ /**
56
+ * Scan for malware patterns
57
+ */
58
+ private scanMalware;
59
+ /**
60
+ * Check license compliance
61
+ */
62
+ private scanLicenses;
63
+ /**
64
+ * Check configuration security
65
+ */
66
+ private scanConfiguration;
67
+ /**
68
+ * Calculate security score based on issues
69
+ */
70
+ private calculateSecurityScore;
71
+ /**
72
+ * Add a vulnerability to the database
73
+ */
74
+ addVulnerability(packageName: string, version: string, vulnerability: SecurityVulnerability): void;
75
+ /**
76
+ * Get scan result from cache
77
+ */
78
+ getScanResult(pluginId: string, version: string): SecurityScanResult | undefined;
79
+ /**
80
+ * Clear scan results cache
81
+ */
82
+ clearCache(): void;
83
+ /**
84
+ * Update vulnerability database from external source
85
+ */
86
+ updateVulnerabilityDatabase(): Promise<void>;
87
+ /**
88
+ * Shutdown security scanner
89
+ */
90
+ shutdown(): void;
91
+ }
92
+ //# sourceMappingURL=security-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-scanner.d.ts","sourceRoot":"","sources":["../../src/security/security-scanner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;IAC1D,QAAQ,EAAE,eAAe,GAAG,SAAS,GAAG,SAAS,GAAG,cAAc,GAAG,eAAe,CAAC;IACrF,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE;QACT,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,MAAM,CAAe;IAG7B,OAAO,CAAC,eAAe,CAA4C;IAGnE,OAAO,CAAC,WAAW,CAAyC;IAE5D,OAAO,CAAC,aAAa,CAAc;gBAEvB,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE;IAOrE;;OAEG;IACG,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA8E3D;;OAEG;YACW,QAAQ;IAmBtB;;OAEG;YACW,gBAAgB;IAyC9B;;OAEG;YACW,WAAW;IAmBzB;;OAEG;YACW,YAAY;IAsB1B;;OAEG;YACW,iBAAiB;IAkB/B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA6B9B;;OAEG;IACH,gBAAgB,CACd,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,qBAAqB,GACnC,IAAI;IAWP;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAIhF;;OAEG;IACH,UAAU,IAAI,IAAI;IAKlB;;OAEG;IACG,2BAA2B,IAAI,OAAO,CAAC,IAAI,CAAC;IAclD;;OAEG;IACH,QAAQ,IAAI,IAAI;CAMjB"}
@@ -0,0 +1,273 @@
1
+ /**
2
+ * Plugin Security Scanner
3
+ *
4
+ * Scans plugins for security vulnerabilities, malware, and license issues
5
+ */
6
+ export class PluginSecurityScanner {
7
+ constructor(logger, config) {
8
+ // Known vulnerabilities database (CVE cache)
9
+ this.vulnerabilityDb = new Map();
10
+ // Scan results cache
11
+ this.scanResults = new Map();
12
+ this.passThreshold = 70;
13
+ this.logger = logger.child({ component: 'SecurityScanner' });
14
+ if (config?.passThreshold !== undefined) {
15
+ this.passThreshold = config.passThreshold;
16
+ }
17
+ }
18
+ /**
19
+ * Perform a comprehensive security scan on a plugin
20
+ */
21
+ async scan(target) {
22
+ this.logger.info('Starting security scan', {
23
+ pluginId: target.pluginId,
24
+ version: target.version
25
+ });
26
+ const issues = [];
27
+ try {
28
+ // 1. Scan for code vulnerabilities
29
+ const codeIssues = await this.scanCode(target);
30
+ issues.push(...codeIssues);
31
+ // 2. Scan dependencies for known vulnerabilities
32
+ const depIssues = await this.scanDependencies(target);
33
+ issues.push(...depIssues);
34
+ // 3. Scan for malware patterns
35
+ const malwareIssues = await this.scanMalware(target);
36
+ issues.push(...malwareIssues);
37
+ // 4. Check license compliance
38
+ const licenseIssues = await this.scanLicenses(target);
39
+ issues.push(...licenseIssues);
40
+ // 5. Check configuration security
41
+ const configIssues = await this.scanConfiguration(target);
42
+ issues.push(...configIssues);
43
+ // Calculate security score (0-100, higher is better)
44
+ const score = this.calculateSecurityScore(issues);
45
+ const result = {
46
+ timestamp: new Date().toISOString(),
47
+ scanner: { name: 'ObjectStack Security Scanner', version: '1.0.0' },
48
+ status: score >= this.passThreshold ? 'passed' : 'failed',
49
+ vulnerabilities: issues.map(issue => ({
50
+ id: issue.id,
51
+ severity: issue.severity,
52
+ category: issue.category,
53
+ title: issue.title,
54
+ description: issue.description,
55
+ location: issue.location ? `${issue.location.file}:${issue.location.line}` : undefined,
56
+ remediation: issue.remediation,
57
+ affectedVersions: [],
58
+ exploitAvailable: false,
59
+ patchAvailable: false,
60
+ })),
61
+ summary: {
62
+ totalVulnerabilities: issues.length,
63
+ criticalCount: issues.filter(i => i.severity === 'critical').length,
64
+ highCount: issues.filter(i => i.severity === 'high').length,
65
+ mediumCount: issues.filter(i => i.severity === 'medium').length,
66
+ lowCount: issues.filter(i => i.severity === 'low').length,
67
+ infoCount: issues.filter(i => i.severity === 'info').length,
68
+ },
69
+ };
70
+ this.scanResults.set(`${target.pluginId}:${target.version}`, result);
71
+ this.logger.info('Security scan complete', {
72
+ pluginId: target.pluginId,
73
+ score,
74
+ status: result.status,
75
+ summary: result.summary
76
+ });
77
+ return result;
78
+ }
79
+ catch (error) {
80
+ this.logger.error('Security scan failed', {
81
+ pluginId: target.pluginId,
82
+ error
83
+ });
84
+ throw error;
85
+ }
86
+ }
87
+ /**
88
+ * Scan code for vulnerabilities
89
+ */
90
+ async scanCode(target) {
91
+ const issues = [];
92
+ // In a real implementation, this would:
93
+ // - Parse code with AST (e.g., using @typescript-eslint/parser)
94
+ // - Check for dangerous patterns (eval, Function constructor, etc.)
95
+ // - Check for XSS vulnerabilities
96
+ // - Check for SQL injection patterns
97
+ // - Check for insecure crypto usage
98
+ // - Check for path traversal vulnerabilities
99
+ this.logger.debug('Code scan complete', {
100
+ pluginId: target.pluginId,
101
+ issuesFound: issues.length
102
+ });
103
+ return issues;
104
+ }
105
+ /**
106
+ * Scan dependencies for known vulnerabilities
107
+ */
108
+ async scanDependencies(target) {
109
+ const issues = [];
110
+ if (!target.dependencies) {
111
+ return issues;
112
+ }
113
+ // In a real implementation, this would:
114
+ // - Query npm audit API
115
+ // - Check GitHub Advisory Database
116
+ // - Check Snyk vulnerability database
117
+ // - Check OSV (Open Source Vulnerabilities)
118
+ for (const [depName, version] of Object.entries(target.dependencies)) {
119
+ const vulnKey = `${depName}@${version}`;
120
+ const vulnerability = this.vulnerabilityDb.get(vulnKey);
121
+ if (vulnerability) {
122
+ issues.push({
123
+ id: `vuln-${vulnerability.cve || depName}`,
124
+ severity: vulnerability.severity,
125
+ category: 'vulnerability',
126
+ title: `Vulnerable dependency: ${depName}`,
127
+ description: `${depName}@${version} has known security vulnerabilities`,
128
+ remediation: vulnerability.fixedIn
129
+ ? `Upgrade to ${vulnerability.fixedIn.join(' or ')}`
130
+ : 'No fix available',
131
+ cve: vulnerability.cve,
132
+ });
133
+ }
134
+ }
135
+ this.logger.debug('Dependency scan complete', {
136
+ pluginId: target.pluginId,
137
+ dependencies: Object.keys(target.dependencies).length,
138
+ vulnerabilities: issues.length
139
+ });
140
+ return issues;
141
+ }
142
+ /**
143
+ * Scan for malware patterns
144
+ */
145
+ async scanMalware(target) {
146
+ const issues = [];
147
+ // In a real implementation, this would:
148
+ // - Check for obfuscated code
149
+ // - Check for suspicious network activity patterns
150
+ // - Check for crypto mining patterns
151
+ // - Check for data exfiltration patterns
152
+ // - Use ML-based malware detection
153
+ // - Check file hashes against known malware databases
154
+ this.logger.debug('Malware scan complete', {
155
+ pluginId: target.pluginId,
156
+ issuesFound: issues.length
157
+ });
158
+ return issues;
159
+ }
160
+ /**
161
+ * Check license compliance
162
+ */
163
+ async scanLicenses(target) {
164
+ const issues = [];
165
+ if (!target.dependencies) {
166
+ return issues;
167
+ }
168
+ // In a real implementation, this would:
169
+ // - Check license compatibility
170
+ // - Detect GPL contamination
171
+ // - Flag proprietary dependencies
172
+ // - Check for missing licenses
173
+ // - Verify SPDX identifiers
174
+ this.logger.debug('License scan complete', {
175
+ pluginId: target.pluginId,
176
+ issuesFound: issues.length
177
+ });
178
+ return issues;
179
+ }
180
+ /**
181
+ * Check configuration security
182
+ */
183
+ async scanConfiguration(target) {
184
+ const issues = [];
185
+ // In a real implementation, this would:
186
+ // - Check for hardcoded secrets
187
+ // - Check for weak permissions
188
+ // - Check for insecure defaults
189
+ // - Check for missing security headers
190
+ // - Check CSP policies
191
+ this.logger.debug('Configuration scan complete', {
192
+ pluginId: target.pluginId,
193
+ issuesFound: issues.length
194
+ });
195
+ return issues;
196
+ }
197
+ /**
198
+ * Calculate security score based on issues
199
+ */
200
+ calculateSecurityScore(issues) {
201
+ // Start with perfect score
202
+ let score = 100;
203
+ // Deduct points based on severity
204
+ for (const issue of issues) {
205
+ switch (issue.severity) {
206
+ case 'critical':
207
+ score -= 20;
208
+ break;
209
+ case 'high':
210
+ score -= 10;
211
+ break;
212
+ case 'medium':
213
+ score -= 5;
214
+ break;
215
+ case 'low':
216
+ score -= 2;
217
+ break;
218
+ case 'info':
219
+ score -= 0;
220
+ break;
221
+ }
222
+ }
223
+ // Ensure score doesn't go below 0
224
+ return Math.max(0, score);
225
+ }
226
+ /**
227
+ * Add a vulnerability to the database
228
+ */
229
+ addVulnerability(packageName, version, vulnerability) {
230
+ const key = `${packageName}@${version}`;
231
+ this.vulnerabilityDb.set(key, vulnerability);
232
+ this.logger.debug('Vulnerability added to database', {
233
+ package: packageName,
234
+ version,
235
+ cve: vulnerability.cve
236
+ });
237
+ }
238
+ /**
239
+ * Get scan result from cache
240
+ */
241
+ getScanResult(pluginId, version) {
242
+ return this.scanResults.get(`${pluginId}:${version}`);
243
+ }
244
+ /**
245
+ * Clear scan results cache
246
+ */
247
+ clearCache() {
248
+ this.scanResults.clear();
249
+ this.logger.debug('Scan results cache cleared');
250
+ }
251
+ /**
252
+ * Update vulnerability database from external source
253
+ */
254
+ async updateVulnerabilityDatabase() {
255
+ this.logger.info('Updating vulnerability database');
256
+ // In a real implementation, this would:
257
+ // - Fetch from GitHub Advisory Database
258
+ // - Fetch from npm audit
259
+ // - Fetch from NVD (National Vulnerability Database)
260
+ // - Parse and cache vulnerability data
261
+ this.logger.info('Vulnerability database updated', {
262
+ entries: this.vulnerabilityDb.size
263
+ });
264
+ }
265
+ /**
266
+ * Shutdown security scanner
267
+ */
268
+ shutdown() {
269
+ this.vulnerabilityDb.clear();
270
+ this.scanResults.clear();
271
+ this.logger.info('Security scanner shutdown complete');
272
+ }
273
+ }