@objectstack/core 4.0.4 → 4.0.5

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 (75) hide show
  1. package/README.md +95 -10
  2. package/dist/index.cjs +169 -507
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +24 -223
  5. package/dist/index.d.ts +24 -223
  6. package/dist/index.js +175 -505
  7. package/dist/index.js.map +1 -1
  8. package/dist/logger.cjs +177 -0
  9. package/dist/logger.cjs.map +1 -0
  10. package/dist/logger.d.cts +26 -0
  11. package/dist/logger.d.ts +26 -0
  12. package/dist/logger.js +158 -0
  13. package/dist/logger.js.map +1 -0
  14. package/package.json +36 -15
  15. package/.turbo/turbo-build.log +0 -22
  16. package/ADVANCED_FEATURES.md +0 -380
  17. package/API_REGISTRY.md +0 -392
  18. package/CHANGELOG.md +0 -472
  19. package/PHASE2_IMPLEMENTATION.md +0 -388
  20. package/REFACTORING_SUMMARY.md +0 -40
  21. package/examples/api-registry-example.ts +0 -559
  22. package/examples/kernel-features-example.ts +0 -311
  23. package/examples/phase2-integration.ts +0 -357
  24. package/src/api-registry-plugin.test.ts +0 -393
  25. package/src/api-registry-plugin.ts +0 -89
  26. package/src/api-registry.test.ts +0 -1089
  27. package/src/api-registry.ts +0 -739
  28. package/src/contracts/data-engine.ts +0 -57
  29. package/src/contracts/http-server.ts +0 -151
  30. package/src/contracts/logger.ts +0 -72
  31. package/src/dependency-resolver.test.ts +0 -287
  32. package/src/dependency-resolver.ts +0 -390
  33. package/src/fallbacks/fallbacks.test.ts +0 -281
  34. package/src/fallbacks/index.ts +0 -26
  35. package/src/fallbacks/memory-cache.ts +0 -34
  36. package/src/fallbacks/memory-i18n.ts +0 -112
  37. package/src/fallbacks/memory-job.ts +0 -23
  38. package/src/fallbacks/memory-metadata.ts +0 -50
  39. package/src/fallbacks/memory-queue.ts +0 -28
  40. package/src/health-monitor.test.ts +0 -81
  41. package/src/health-monitor.ts +0 -318
  42. package/src/hot-reload.ts +0 -382
  43. package/src/index.ts +0 -50
  44. package/src/kernel-base.ts +0 -273
  45. package/src/kernel.test.ts +0 -624
  46. package/src/kernel.ts +0 -631
  47. package/src/lite-kernel.test.ts +0 -248
  48. package/src/lite-kernel.ts +0 -137
  49. package/src/logger.test.ts +0 -116
  50. package/src/logger.ts +0 -355
  51. package/src/namespace-resolver.test.ts +0 -130
  52. package/src/namespace-resolver.ts +0 -188
  53. package/src/package-manager.test.ts +0 -225
  54. package/src/package-manager.ts +0 -428
  55. package/src/plugin-loader.test.ts +0 -421
  56. package/src/plugin-loader.ts +0 -484
  57. package/src/qa/adapter.ts +0 -16
  58. package/src/qa/http-adapter.ts +0 -116
  59. package/src/qa/index.ts +0 -5
  60. package/src/qa/runner.ts +0 -189
  61. package/src/security/index.ts +0 -50
  62. package/src/security/permission-manager.test.ts +0 -256
  63. package/src/security/permission-manager.ts +0 -338
  64. package/src/security/plugin-config-validator.test.ts +0 -276
  65. package/src/security/plugin-config-validator.ts +0 -193
  66. package/src/security/plugin-permission-enforcer.test.ts +0 -251
  67. package/src/security/plugin-permission-enforcer.ts +0 -436
  68. package/src/security/plugin-signature-verifier.ts +0 -403
  69. package/src/security/sandbox-runtime.ts +0 -462
  70. package/src/security/security-scanner.ts +0 -367
  71. package/src/types.ts +0 -120
  72. package/src/utils/env.test.ts +0 -62
  73. package/src/utils/env.ts +0 -53
  74. package/tsconfig.json +0 -10
  75. package/vitest.config.ts +0 -10
@@ -1,436 +0,0 @@
1
- // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
-
3
- import type { Logger } from '@objectstack/spec/contracts';
4
- import type { PluginCapability } from '@objectstack/spec/kernel';
5
- import type { PluginContext } from '../types.js';
6
-
7
- /**
8
- * Plugin Permissions
9
- * Defines what actions a plugin is allowed to perform
10
- */
11
- export interface PluginPermissions {
12
- canAccessService(serviceName: string): boolean;
13
- canTriggerHook(hookName: string): boolean;
14
- canReadFile(path: string): boolean;
15
- canWriteFile(path: string): boolean;
16
- canNetworkRequest(url: string): boolean;
17
- }
18
-
19
- /**
20
- * Permission Check Result
21
- */
22
- export interface PermissionCheckResult {
23
- allowed: boolean;
24
- reason?: string;
25
- capability?: string;
26
- }
27
-
28
- /**
29
- * Plugin Permission Enforcer
30
- *
31
- * Implements capability-based security model to enforce:
32
- * 1. Service access control - which services a plugin can use
33
- * 2. Hook restrictions - which hooks a plugin can trigger
34
- * 3. File system permissions - what files a plugin can read/write
35
- * 4. Network permissions - what URLs a plugin can access
36
- *
37
- * Architecture:
38
- * - Uses capability declarations from plugin manifest
39
- * - Checks permissions before allowing operations
40
- * - Logs all permission denials for security audit
41
- * - Supports allowlist and denylist patterns
42
- *
43
- * Security Model:
44
- * - Principle of least privilege - plugins get minimal permissions
45
- * - Explicit declaration - all capabilities must be declared
46
- * - Runtime enforcement - checks happen at operation time
47
- * - Audit trail - all denials are logged
48
- *
49
- * Usage:
50
- * ```typescript
51
- * const enforcer = new PluginPermissionEnforcer(logger);
52
- * enforcer.registerPluginPermissions(pluginName, capabilities);
53
- * enforcer.enforceServiceAccess(pluginName, 'database');
54
- * ```
55
- */
56
- export class PluginPermissionEnforcer {
57
- private logger: Logger;
58
- private permissionRegistry: Map<string, PluginPermissions> = new Map();
59
- private capabilityRegistry: Map<string, PluginCapability[]> = new Map();
60
-
61
- constructor(logger: Logger) {
62
- this.logger = logger;
63
- }
64
-
65
- /**
66
- * Register plugin capabilities and build permission set
67
- *
68
- * @param pluginName - Plugin identifier
69
- * @param capabilities - Array of capability declarations
70
- */
71
- registerPluginPermissions(pluginName: string, capabilities: PluginCapability[]): void {
72
- this.capabilityRegistry.set(pluginName, capabilities);
73
-
74
- const permissions: PluginPermissions = {
75
- canAccessService: (service) => this.checkServiceAccess(capabilities, service),
76
- canTriggerHook: (hook) => this.checkHookAccess(capabilities, hook),
77
- canReadFile: (path) => this.checkFileRead(capabilities, path),
78
- canWriteFile: (path) => this.checkFileWrite(capabilities, path),
79
- canNetworkRequest: (url) => this.checkNetworkAccess(capabilities, url),
80
- };
81
-
82
- this.permissionRegistry.set(pluginName, permissions);
83
-
84
- this.logger.info(`Permissions registered for plugin: ${pluginName}`, {
85
- plugin: pluginName,
86
- capabilityCount: capabilities.length,
87
- });
88
- }
89
-
90
- /**
91
- * Enforce service access permission
92
- *
93
- * @param pluginName - Plugin requesting access
94
- * @param serviceName - Service to access
95
- * @throws Error if permission denied
96
- */
97
- enforceServiceAccess(pluginName: string, serviceName: string): void {
98
- const result = this.checkPermission(pluginName, (perms) => perms.canAccessService(serviceName));
99
-
100
- if (!result.allowed) {
101
- const error = `Permission denied: Plugin ${pluginName} cannot access service ${serviceName}`;
102
- this.logger.warn(error, {
103
- plugin: pluginName,
104
- service: serviceName,
105
- reason: result.reason,
106
- });
107
- throw new Error(error);
108
- }
109
-
110
- this.logger.debug(`Service access granted: ${pluginName} -> ${serviceName}`);
111
- }
112
-
113
- /**
114
- * Enforce hook trigger permission
115
- *
116
- * @param pluginName - Plugin requesting access
117
- * @param hookName - Hook to trigger
118
- * @throws Error if permission denied
119
- */
120
- enforceHookTrigger(pluginName: string, hookName: string): void {
121
- const result = this.checkPermission(pluginName, (perms) => perms.canTriggerHook(hookName));
122
-
123
- if (!result.allowed) {
124
- const error = `Permission denied: Plugin ${pluginName} cannot trigger hook ${hookName}`;
125
- this.logger.warn(error, {
126
- plugin: pluginName,
127
- hook: hookName,
128
- reason: result.reason,
129
- });
130
- throw new Error(error);
131
- }
132
-
133
- this.logger.debug(`Hook trigger granted: ${pluginName} -> ${hookName}`);
134
- }
135
-
136
- /**
137
- * Enforce file read permission
138
- *
139
- * @param pluginName - Plugin requesting access
140
- * @param path - File path to read
141
- * @throws Error if permission denied
142
- */
143
- enforceFileRead(pluginName: string, path: string): void {
144
- const result = this.checkPermission(pluginName, (perms) => perms.canReadFile(path));
145
-
146
- if (!result.allowed) {
147
- const error = `Permission denied: Plugin ${pluginName} cannot read file ${path}`;
148
- this.logger.warn(error, {
149
- plugin: pluginName,
150
- path,
151
- reason: result.reason,
152
- });
153
- throw new Error(error);
154
- }
155
-
156
- this.logger.debug(`File read granted: ${pluginName} -> ${path}`);
157
- }
158
-
159
- /**
160
- * Enforce file write permission
161
- *
162
- * @param pluginName - Plugin requesting access
163
- * @param path - File path to write
164
- * @throws Error if permission denied
165
- */
166
- enforceFileWrite(pluginName: string, path: string): void {
167
- const result = this.checkPermission(pluginName, (perms) => perms.canWriteFile(path));
168
-
169
- if (!result.allowed) {
170
- const error = `Permission denied: Plugin ${pluginName} cannot write file ${path}`;
171
- this.logger.warn(error, {
172
- plugin: pluginName,
173
- path,
174
- reason: result.reason,
175
- });
176
- throw new Error(error);
177
- }
178
-
179
- this.logger.debug(`File write granted: ${pluginName} -> ${path}`);
180
- }
181
-
182
- /**
183
- * Enforce network request permission
184
- *
185
- * @param pluginName - Plugin requesting access
186
- * @param url - URL to access
187
- * @throws Error if permission denied
188
- */
189
- enforceNetworkRequest(pluginName: string, url: string): void {
190
- const result = this.checkPermission(pluginName, (perms) => perms.canNetworkRequest(url));
191
-
192
- if (!result.allowed) {
193
- const error = `Permission denied: Plugin ${pluginName} cannot access URL ${url}`;
194
- this.logger.warn(error, {
195
- plugin: pluginName,
196
- url,
197
- reason: result.reason,
198
- });
199
- throw new Error(error);
200
- }
201
-
202
- this.logger.debug(`Network request granted: ${pluginName} -> ${url}`);
203
- }
204
-
205
- /**
206
- * Get plugin capabilities
207
- *
208
- * @param pluginName - Plugin identifier
209
- * @returns Array of capabilities or undefined
210
- */
211
- getPluginCapabilities(pluginName: string): PluginCapability[] | undefined {
212
- return this.capabilityRegistry.get(pluginName);
213
- }
214
-
215
- /**
216
- * Get plugin permissions
217
- *
218
- * @param pluginName - Plugin identifier
219
- * @returns Permissions object or undefined
220
- */
221
- getPluginPermissions(pluginName: string): PluginPermissions | undefined {
222
- return this.permissionRegistry.get(pluginName);
223
- }
224
-
225
- /**
226
- * Revoke all permissions for a plugin
227
- *
228
- * @param pluginName - Plugin identifier
229
- */
230
- revokePermissions(pluginName: string): void {
231
- this.permissionRegistry.delete(pluginName);
232
- this.capabilityRegistry.delete(pluginName);
233
- this.logger.warn(`Permissions revoked for plugin: ${pluginName}`);
234
- }
235
-
236
- // Private methods
237
-
238
- private checkPermission(
239
- pluginName: string,
240
- check: (perms: PluginPermissions) => boolean
241
- ): PermissionCheckResult {
242
- const permissions = this.permissionRegistry.get(pluginName);
243
-
244
- if (!permissions) {
245
- return {
246
- allowed: false,
247
- reason: 'Plugin permissions not registered',
248
- };
249
- }
250
-
251
- const allowed = check(permissions);
252
-
253
- return {
254
- allowed,
255
- reason: allowed ? undefined : 'No matching capability found',
256
- };
257
- }
258
-
259
- private checkServiceAccess(capabilities: PluginCapability[], serviceName: string): boolean {
260
- // Check if plugin has capability to access this service
261
- return capabilities.some(cap => {
262
- const protocolId = cap.protocol.id;
263
-
264
- // Check for wildcard service access
265
- if (protocolId.includes('protocol.service.all')) {
266
- return true;
267
- }
268
-
269
- // Check for specific service protocol
270
- if (protocolId.includes(`protocol.service.${serviceName}`)) {
271
- return true;
272
- }
273
-
274
- // Check for service category match
275
- const serviceCategory = serviceName.split('.')[0];
276
- if (protocolId.includes(`protocol.service.${serviceCategory}`)) {
277
- return true;
278
- }
279
-
280
- return false;
281
- });
282
- }
283
-
284
- private checkHookAccess(capabilities: PluginCapability[], hookName: string): boolean {
285
- // Check if plugin has capability to trigger this hook
286
- return capabilities.some(cap => {
287
- const protocolId = cap.protocol.id;
288
-
289
- // Check for wildcard hook access
290
- if (protocolId.includes('protocol.hook.all')) {
291
- return true;
292
- }
293
-
294
- // Check for specific hook protocol
295
- if (protocolId.includes(`protocol.hook.${hookName}`)) {
296
- return true;
297
- }
298
-
299
- // Check for hook category match
300
- const hookCategory = hookName.split(':')[0];
301
- if (protocolId.includes(`protocol.hook.${hookCategory}`)) {
302
- return true;
303
- }
304
-
305
- return false;
306
- });
307
- }
308
-
309
- private matchGlob(pattern: string, str: string): boolean {
310
- const regexStr = pattern
311
- .split('**')
312
- .map(segment => {
313
- const escaped = segment.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
314
- return escaped.replace(/\*/g, '[^/]*');
315
- })
316
- .join('.*');
317
- return new RegExp(`^${regexStr}$`).test(str);
318
- }
319
-
320
- private checkFileRead(capabilities: PluginCapability[], path: string): boolean {
321
- // Check if plugin has capability to read this file
322
- return capabilities.some(cap => {
323
- const protocolId = cap.protocol.id;
324
-
325
- // Check for file read capability
326
- if (protocolId.includes('protocol.filesystem.read')) {
327
- const paths = cap.metadata?.paths;
328
- if (!Array.isArray(paths) || paths.length === 0) {
329
- return true;
330
- }
331
- return paths.some(p => typeof p === 'string' && this.matchGlob(p, path));
332
- }
333
-
334
- return false;
335
- });
336
- }
337
-
338
- private checkFileWrite(capabilities: PluginCapability[], path: string): boolean {
339
- // Check if plugin has capability to write this file
340
- return capabilities.some(cap => {
341
- const protocolId = cap.protocol.id;
342
-
343
- // Check for file write capability
344
- if (protocolId.includes('protocol.filesystem.write')) {
345
- const paths = cap.metadata?.paths;
346
- if (!Array.isArray(paths) || paths.length === 0) {
347
- return true;
348
- }
349
- return paths.some(p => typeof p === 'string' && this.matchGlob(p, path));
350
- }
351
-
352
- return false;
353
- });
354
- }
355
-
356
- private checkNetworkAccess(capabilities: PluginCapability[], url: string): boolean {
357
- // Check if plugin has capability to access this URL
358
- return capabilities.some(cap => {
359
- const protocolId = cap.protocol.id;
360
-
361
- // Check for network capability
362
- if (protocolId.includes('protocol.network')) {
363
- const hosts = cap.metadata?.hosts;
364
- if (!Array.isArray(hosts) || hosts.length === 0) {
365
- return true;
366
- }
367
- return hosts.some(h => typeof h === 'string' && this.matchGlob(h, url));
368
- }
369
-
370
- return false;
371
- });
372
- }
373
- }
374
-
375
- /**
376
- * Secure Plugin Context
377
- * Wraps PluginContext with permission checks
378
- */
379
- export class SecurePluginContext implements PluginContext {
380
- constructor(
381
- private pluginName: string,
382
- private permissionEnforcer: PluginPermissionEnforcer,
383
- private baseContext: PluginContext
384
- ) {}
385
-
386
- registerService(name: string, service: any): void {
387
- // No permission check for service registration (handled during init)
388
- this.baseContext.registerService(name, service);
389
- }
390
-
391
- getService<T>(name: string): T {
392
- // Check permission before accessing service
393
- this.permissionEnforcer.enforceServiceAccess(this.pluginName, name);
394
- return this.baseContext.getService<T>(name);
395
- }
396
-
397
- replaceService<T>(name: string, implementation: T): void {
398
- // Check permission before replacing service
399
- this.permissionEnforcer.enforceServiceAccess(this.pluginName, name);
400
- this.baseContext.replaceService(name, implementation);
401
- }
402
-
403
- getServices(): Map<string, any> {
404
- // Return all services (no permission check for listing)
405
- return this.baseContext.getServices();
406
- }
407
-
408
- hook(name: string, handler: (...args: any[]) => void | Promise<void>): void {
409
- // No permission check for registering hooks (handled during init)
410
- this.baseContext.hook(name, handler);
411
- }
412
-
413
- async trigger(name: string, ...args: any[]): Promise<void> {
414
- // Check permission before triggering hook
415
- this.permissionEnforcer.enforceHookTrigger(this.pluginName, name);
416
- await this.baseContext.trigger(name, ...args);
417
- }
418
-
419
- get logger() {
420
- return this.baseContext.logger;
421
- }
422
-
423
- getKernel() {
424
- return this.baseContext.getKernel();
425
- }
426
- }
427
-
428
- /**
429
- * Create a plugin permission enforcer
430
- *
431
- * @param logger - Logger instance
432
- * @returns Plugin permission enforcer
433
- */
434
- export function createPluginPermissionEnforcer(logger: Logger): PluginPermissionEnforcer {
435
- return new PluginPermissionEnforcer(logger);
436
- }