@objectstack/core 4.0.3 → 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 -465
  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,403 +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 { PluginMetadata } from '../plugin-loader.js';
5
-
6
- // Conditionally import crypto for Node.js environments
7
- let cryptoModule: typeof import('crypto') | null = null;
8
-
9
-
10
- /**
11
- * Plugin Signature Configuration
12
- * Controls how plugin signatures are verified
13
- */
14
- export interface PluginSignatureConfig {
15
- /**
16
- * Map of publisher IDs to their trusted public keys
17
- * Format: { 'com.objectstack': '-----BEGIN PUBLIC KEY-----...' }
18
- */
19
- trustedPublicKeys: Map<string, string>;
20
-
21
- /**
22
- * Signature algorithm to use
23
- * - RS256: RSA with SHA-256
24
- * - ES256: ECDSA with SHA-256
25
- */
26
- algorithm: 'RS256' | 'ES256';
27
-
28
- /**
29
- * Strict mode: reject plugins without signatures
30
- * - true: All plugins must be signed
31
- * - false: Unsigned plugins are allowed with warning
32
- */
33
- strictMode: boolean;
34
-
35
- /**
36
- * Allow self-signed plugins in development
37
- */
38
- allowSelfSigned?: boolean;
39
- }
40
-
41
- /**
42
- * Plugin Signature Verification Result
43
- */
44
- export interface SignatureVerificationResult {
45
- verified: boolean;
46
- error?: string;
47
- publisherId?: string;
48
- algorithm?: string;
49
- signedAt?: Date;
50
- }
51
-
52
- /**
53
- * Plugin Signature Verifier
54
- *
55
- * Implements cryptographic verification of plugin signatures to ensure:
56
- * 1. Plugin integrity - code hasn't been tampered with
57
- * 2. Publisher authenticity - plugin comes from trusted source
58
- * 3. Non-repudiation - publisher cannot deny signing
59
- *
60
- * Architecture:
61
- * - Uses Node.js crypto module for signature verification
62
- * - Supports RSA (RS256) and ECDSA (ES256) algorithms
63
- * - Verifies against trusted public key registry
64
- * - Computes hash of plugin code for integrity check
65
- *
66
- * Security Model:
67
- * - Public keys are pre-registered and trusted
68
- * - Plugin signature is verified before loading
69
- * - Strict mode rejects unsigned plugins
70
- * - Development mode allows self-signed plugins
71
- */
72
- export class PluginSignatureVerifier {
73
- private config: PluginSignatureConfig;
74
- private logger: Logger;
75
-
76
- constructor(config: PluginSignatureConfig, logger: Logger) {
77
- this.config = config;
78
- this.logger = logger;
79
-
80
- this.validateConfig();
81
- }
82
-
83
- /**
84
- * Verify plugin signature
85
- *
86
- * @param plugin - Plugin metadata with signature
87
- * @returns Verification result
88
- * @throws Error if verification fails in strict mode
89
- */
90
- async verifyPluginSignature(plugin: PluginMetadata): Promise<SignatureVerificationResult> {
91
- // Handle unsigned plugins
92
- if (!plugin.signature) {
93
- return this.handleUnsignedPlugin(plugin);
94
- }
95
-
96
- try {
97
- // 1. Extract publisher ID from plugin name (reverse domain notation)
98
- const publisherId = this.extractPublisherId(plugin.name);
99
-
100
- // 2. Get trusted public key for publisher
101
- const publicKey = this.config.trustedPublicKeys.get(publisherId);
102
- if (!publicKey) {
103
- const error = `No trusted public key for publisher: ${publisherId}`;
104
- this.logger.warn(error, { plugin: plugin.name, publisherId });
105
-
106
- if (this.config.strictMode && !this.config.allowSelfSigned) {
107
- throw new Error(error);
108
- }
109
-
110
- return {
111
- verified: false,
112
- error,
113
- publisherId,
114
- };
115
- }
116
-
117
- // 3. Compute plugin code hash
118
- const pluginHash = this.computePluginHash(plugin);
119
-
120
- // 4. Verify signature using crypto module
121
- const isValid = await this.verifyCryptoSignature(
122
- pluginHash,
123
- plugin.signature,
124
- publicKey
125
- );
126
-
127
- if (!isValid) {
128
- const error = `Signature verification failed for plugin: ${plugin.name}`;
129
- this.logger.error(error, undefined, { plugin: plugin.name, publisherId });
130
- throw new Error(error);
131
- }
132
-
133
- this.logger.info(`✅ Plugin signature verified: ${plugin.name}`, {
134
- plugin: plugin.name,
135
- publisherId,
136
- algorithm: this.config.algorithm,
137
- });
138
-
139
- return {
140
- verified: true,
141
- publisherId,
142
- algorithm: this.config.algorithm,
143
- };
144
-
145
- } catch (error) {
146
- this.logger.error(`Signature verification error: ${plugin.name}`, error as Error);
147
-
148
- if (this.config.strictMode) {
149
- throw error;
150
- }
151
-
152
- return {
153
- verified: false,
154
- error: (error as Error).message,
155
- };
156
- }
157
- }
158
-
159
- /**
160
- * Register a trusted public key for a publisher
161
- */
162
- registerPublicKey(publisherId: string, publicKey: string): void {
163
- this.config.trustedPublicKeys.set(publisherId, publicKey);
164
- this.logger.info(`Trusted public key registered for: ${publisherId}`);
165
- }
166
-
167
- /**
168
- * Remove a trusted public key
169
- */
170
- revokePublicKey(publisherId: string): void {
171
- this.config.trustedPublicKeys.delete(publisherId);
172
- this.logger.warn(`Public key revoked for: ${publisherId}`);
173
- }
174
-
175
- /**
176
- * Get list of trusted publishers
177
- */
178
- getTrustedPublishers(): string[] {
179
- return Array.from(this.config.trustedPublicKeys.keys());
180
- }
181
-
182
- // Private methods
183
-
184
- private handleUnsignedPlugin(plugin: PluginMetadata): SignatureVerificationResult {
185
- if (this.config.strictMode) {
186
- const error = `Plugin missing signature (strict mode): ${plugin.name}`;
187
- this.logger.error(error, undefined, { plugin: plugin.name });
188
- throw new Error(error);
189
- }
190
-
191
- this.logger.warn(`⚠️ Plugin not signed: ${plugin.name}`, {
192
- plugin: plugin.name,
193
- recommendation: 'Consider signing plugins for production environments',
194
- });
195
-
196
- return {
197
- verified: false,
198
- error: 'Plugin not signed',
199
- };
200
- }
201
-
202
- private extractPublisherId(pluginName: string): string {
203
- // Extract publisher from reverse domain notation
204
- // Example: "com.objectstack.engine.objectql" -> "com.objectstack"
205
- const parts = pluginName.split('.');
206
-
207
- if (parts.length < 2) {
208
- throw new Error(`Invalid plugin name format: ${pluginName} (expected reverse domain notation)`);
209
- }
210
-
211
- // Return first two parts (domain reversed)
212
- return `${parts[0]}.${parts[1]}`;
213
- }
214
-
215
- private computePluginHash(plugin: PluginMetadata): string {
216
- // In browser environment, use SubtleCrypto
217
- if (typeof (globalThis as any).window !== 'undefined') {
218
- return this.computePluginHashBrowser(plugin);
219
- }
220
-
221
- // In Node.js environment, use crypto module
222
- return this.computePluginHashNode(plugin);
223
- }
224
-
225
- private computePluginHashNode(plugin: PluginMetadata): string {
226
- // Use pre-loaded crypto module
227
- if (!cryptoModule) {
228
- this.logger.warn('crypto module not available, using fallback hash');
229
- return this.computePluginHashFallback(plugin);
230
- }
231
-
232
- // Compute hash of plugin code
233
- const pluginCode = this.serializePluginCode(plugin);
234
- return cryptoModule.createHash('sha256').update(pluginCode).digest('hex');
235
- }
236
-
237
- private computePluginHashBrowser(plugin: PluginMetadata): string {
238
- // Browser environment - use simple hash for now
239
- // In production, should use SubtleCrypto for proper cryptographic hash
240
- this.logger.debug('Using browser hash (SubtleCrypto integration pending)');
241
- return this.computePluginHashFallback(plugin);
242
- }
243
-
244
- private computePluginHashFallback(plugin: PluginMetadata): string {
245
- // Simple hash fallback (not cryptographically secure)
246
- const pluginCode = this.serializePluginCode(plugin);
247
- let hash = 0;
248
-
249
- for (let i = 0; i < pluginCode.length; i++) {
250
- const char = pluginCode.charCodeAt(i);
251
- hash = ((hash << 5) - hash) + char;
252
- hash = hash & hash; // Convert to 32-bit integer
253
- }
254
-
255
- return hash.toString(16);
256
- }
257
-
258
- private serializePluginCode(plugin: PluginMetadata): string {
259
- // Serialize plugin code for hashing
260
- // Include init, start, destroy functions
261
- const parts: string[] = [
262
- plugin.name,
263
- plugin.version,
264
- plugin.init.toString(),
265
- ];
266
-
267
- if (plugin.start) {
268
- parts.push(plugin.start.toString());
269
- }
270
-
271
- if (plugin.destroy) {
272
- parts.push(plugin.destroy.toString());
273
- }
274
-
275
- return parts.join('|');
276
- }
277
-
278
- private async verifyCryptoSignature(
279
- data: string,
280
- signature: string,
281
- publicKey: string
282
- ): Promise<boolean> {
283
- // In browser environment, use SubtleCrypto
284
- if (typeof (globalThis as any).window !== 'undefined') {
285
- return this.verifyCryptoSignatureBrowser(data, signature, publicKey);
286
- }
287
-
288
- // In Node.js environment, use crypto module
289
- return this.verifyCryptoSignatureNode(data, signature, publicKey);
290
- }
291
-
292
- private async verifyCryptoSignatureNode(
293
- data: string,
294
- signature: string,
295
- publicKey: string
296
- ): Promise<boolean> {
297
- if (!cryptoModule) {
298
- try {
299
- // @ts-ignore
300
- cryptoModule = await import('crypto');
301
- } catch (e) {
302
- // ignore
303
- }
304
- }
305
-
306
- if (!cryptoModule) {
307
- this.logger.error('Crypto module not available for signature verification');
308
- return false;
309
- }
310
-
311
- try {
312
- // Create verify object based on algorithm
313
- if (this.config.algorithm === 'ES256') {
314
- // ECDSA verification - requires lowercase 'sha256'
315
- const verify = cryptoModule.createVerify('sha256');
316
- verify.update(data);
317
- return verify.verify(
318
- {
319
- key: publicKey,
320
- format: 'pem',
321
- type: 'spki',
322
- },
323
- signature,
324
- 'base64'
325
- );
326
- } else {
327
- // RSA verification (RS256)
328
- const verify = cryptoModule.createVerify('RSA-SHA256');
329
- verify.update(data);
330
- return verify.verify(publicKey, signature, 'base64');
331
- }
332
- } catch (error) {
333
- this.logger.error('Signature verification failed', error as Error);
334
- return false;
335
- }
336
- }
337
-
338
- private async verifyCryptoSignatureBrowser(
339
- data: string,
340
- signature: string,
341
- publicKey: string
342
- ): Promise<boolean> {
343
- try {
344
- const subtle = globalThis.crypto?.subtle;
345
- if (!subtle) {
346
- this.logger.error('SubtleCrypto not available in this environment');
347
- return false;
348
- }
349
-
350
- // Decode PEM public key to raw DER bytes
351
- const pemBody = publicKey
352
- .replace(/-----BEGIN PUBLIC KEY-----/, '')
353
- .replace(/-----END PUBLIC KEY-----/, '')
354
- .replace(/\s/g, '');
355
- const keyBytes = Uint8Array.from(atob(pemBody), c => c.charCodeAt(0));
356
-
357
- // Configure algorithms based on RS256 or ES256
358
- let importAlgorithm: { name: string; hash?: string; namedCurve?: string };
359
- let verifyAlgorithm: { name: string; hash?: string };
360
-
361
- if (this.config.algorithm === 'ES256') {
362
- importAlgorithm = { name: 'ECDSA', namedCurve: 'P-256' };
363
- verifyAlgorithm = { name: 'ECDSA', hash: 'SHA-256' };
364
- } else {
365
- importAlgorithm = { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' };
366
- verifyAlgorithm = { name: 'RSASSA-PKCS1-v1_5' };
367
- }
368
-
369
- const cryptoKey = await subtle.importKey(
370
- 'spki',
371
- keyBytes,
372
- importAlgorithm,
373
- false,
374
- ['verify']
375
- );
376
-
377
- // Decode base64 signature to ArrayBuffer
378
- const signatureBytes = Uint8Array.from(atob(signature), c => c.charCodeAt(0));
379
-
380
- // Encode data to ArrayBuffer
381
- const dataBytes = new TextEncoder().encode(data);
382
-
383
- return await subtle.verify(verifyAlgorithm, cryptoKey, signatureBytes, dataBytes);
384
- } catch (error) {
385
- this.logger.error('Browser signature verification failed', error as Error);
386
- return false;
387
- }
388
- }
389
-
390
- private validateConfig(): void {
391
- if (!this.config.trustedPublicKeys || this.config.trustedPublicKeys.size === 0) {
392
- this.logger.warn('No trusted public keys configured - all signatures will fail');
393
- }
394
-
395
- if (!this.config.algorithm) {
396
- throw new Error('Signature algorithm must be specified');
397
- }
398
-
399
- if (!['RS256', 'ES256'].includes(this.config.algorithm)) {
400
- throw new Error(`Unsupported algorithm: ${this.config.algorithm}`);
401
- }
402
- }
403
- }