@devilsdev/rag-pipeline-utils 2.1.12 → 2.2.0
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/.ragrc.schema.json +218 -0
- package/README.md +292 -32
- package/bin/cli.js +8 -14
- package/dist/index.d.ts +237 -0
- package/dist/index.mjs +22 -7
- package/package.json +17 -13
- package/src/ai/adaptive-retrieval.js +191 -0
- package/src/ai/federated-learning.js +214 -0
- package/src/ai/federation/federated-learning-coordinator.js +345 -0
- package/src/ai/federation.js +76 -0
- package/src/ai/index-fixed.js +509 -0
- package/src/ai/index.js +33 -0
- package/src/ai/model-trainer.js +217 -0
- package/src/ai/model-training.js +205 -0
- package/src/ai/module-template.js +37 -0
- package/src/ai/multimodal/multi-modal-processor.js +345 -0
- package/src/ai/multimodal-processing.js +268 -0
- package/src/ai/multimodal.js +71 -0
- package/src/ai/orchestrator.js +70 -0
- package/src/ai/retrieval/adaptive-retrieval-engine.js +398 -0
- package/src/ai/retrieval-engine.js +69 -0
- package/src/ai/training/model-training-orchestrator.js +228 -0
- package/src/ci/workflow-analyzer.js +71 -0
- package/src/cli/commands/ai-ml.js +967 -0
- package/src/cli/commands/docs.js +228 -0
- package/src/cli/commands/dx.js +538 -0
- package/src/cli/commands/plugin-hub.js +875 -0
- package/src/cli/doctor-command.js +639 -0
- package/src/cli/enhanced-cli-commands.js +945 -0
- package/src/cli/interactive-wizard.js +997 -0
- package/src/cli/plugin-marketplace-commands.js +869 -0
- package/src/cli.js +17 -0
- package/src/config/enhanced-ragrc-schema.js +632 -0
- package/src/config/load-config.js +54 -0
- package/src/config/load-plugin-config.js +69 -0
- package/src/config/normalize-config.js +105 -0
- package/src/config/schema-validator.js +315 -0
- package/src/config/validate-plugin-schema.js +35 -0
- package/src/config/validate-schema.js +113 -0
- package/src/contracts/plugin-contracts.js +33 -0
- package/src/core/create-pipeline.js +99 -0
- package/src/core/dependency-injection.js +242 -0
- package/src/core/observability/event-logger.js +241 -0
- package/src/core/observability/instrumented-pipeline.js +609 -0
- package/src/core/observability/metrics.js +497 -0
- package/src/core/observability/tracing.js +781 -0
- package/src/core/performance/benchmark.js +516 -0
- package/src/core/performance/parallel-processor.js +240 -0
- package/src/core/performance/streaming-safeguards.js +611 -0
- package/src/core/pipeline-factory.js +60 -0
- package/src/core/plugin-contracts.js +89 -0
- package/src/core/plugin-marketplace/plugin-metadata.js +485 -0
- package/src/core/plugin-marketplace/plugin-publisher.js +574 -0
- package/src/core/plugin-marketplace/plugin-registry-format.js +419 -0
- package/src/core/plugin-marketplace/version-resolver.js +61 -0
- package/src/core/plugin-registry.js +265 -0
- package/src/dag/dag-engine.js +954 -0
- package/src/dag/dag-node.js +76 -0
- package/src/dag/enhanced-dag-engine.js +822 -0
- package/src/dx/index.js +40 -0
- package/src/dx/integration-templates.js +665 -0
- package/src/dx/performance-profiler.js +968 -0
- package/src/dx/realtime-debugger.js +755 -0
- package/src/dx/visual-pipeline-builder.js +578 -0
- package/src/ecosystem/plugin-analytics-dashboard.js +778 -0
- package/src/ecosystem/plugin-certification.js +580 -0
- package/src/ecosystem/plugin-hub.js +821 -0
- package/src/enterprise/audit-logging.js +967 -0
- package/src/enterprise/data-governance.js +408 -0
- package/src/enterprise/multi-tenancy.js +496 -0
- package/src/enterprise/sso-integration.js +903 -0
- package/src/evaluate/evaluator.js +52 -0
- package/src/evaluate/scoring.js +91 -0
- package/src/index.js +67 -0
- package/src/ingest.js +59 -0
- package/src/loader/csv-loader.js +38 -0
- package/src/loader/directory-loader.js +48 -0
- package/src/loader/html-loader.js +62 -0
- package/src/loader/markdown-loader.js +67 -0
- package/src/mocks/openai-embedder.js +119 -0
- package/src/mocks/openai-llm.js +77 -0
- package/src/mocks/pdf-loader.js +21 -0
- package/src/mocks/pinecone-retriever.js +106 -0
- package/src/observability/audit-logger.js +758 -0
- package/src/observability/dashboard-config.json +675 -0
- package/src/observability/event-logger.js +310 -0
- package/src/observability/metrics.js +691 -0
- package/src/observability/security-monitor.js +830 -0
- package/src/observability/slo-monitor.js +319 -0
- package/src/observability/telemetry.js +686 -0
- package/src/pipeline/pipeline.js +18 -0
- package/src/plugins/sample-embedder.js +53 -0
- package/src/plugins/sample-llm.js +30 -0
- package/src/plugins/sample-loader.js +24 -0
- package/src/plugins/sample-retriever.js +84 -0
- package/src/query.js +117 -0
- package/src/reranker/llm-reranker.js +85 -0
- package/src/scripts/script-utilities.js +36 -0
- package/src/security/plugin-sandbox.js +586 -0
- package/src/security/plugin-signature-verifier.js +414 -0
- package/src/security/validator.js +36 -0
- package/src/utils/ci/diagnostic-reporter.js +57 -0
- package/src/utils/logger.js +211 -0
- package/src/utils/performance-monitor.js +561 -0
- package/src/utils/plugin-scaffolder.js +447 -0
- package/src/utils/retry-policy.js +443 -0
- package/src/utils/retry.js +107 -0
- package/src/utils/safe-expression-evaluator.js +579 -0
- package/src/utils/structured-logger.js +361 -0
- package/src/utils/validate-plugin-contract.js +42 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Plugin Signature Verifier
|
|
3
|
+
* Verifies Ed25519 digital signatures for plugin manifests to ensure authenticity
|
|
4
|
+
* Enforces fail-closed verification for production security
|
|
5
|
+
*
|
|
6
|
+
* @author DevilsDev Team
|
|
7
|
+
* @since 2.1.8
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const crypto = require('crypto');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Plugin signature verification with Ed25519
|
|
16
|
+
*/
|
|
17
|
+
class PluginSignatureVerifier {
|
|
18
|
+
constructor(options = {}) {
|
|
19
|
+
this.enabled = options.enabled !== false;
|
|
20
|
+
this.trustStore = options.trustStore || new Map();
|
|
21
|
+
this.auditLogger =
|
|
22
|
+
options.auditLogger || this._defaultAuditLogger.bind(this);
|
|
23
|
+
this.failClosed = options.failClosed !== false; // Default to fail-closed
|
|
24
|
+
|
|
25
|
+
// Load trusted public keys
|
|
26
|
+
this._loadTrustedKeys(options.trustedKeysPath);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Load trusted public keys from configuration
|
|
31
|
+
* @param {string} keysPath - Path to trusted keys file
|
|
32
|
+
*/
|
|
33
|
+
_loadTrustedKeys(keysPath) {
|
|
34
|
+
if (!keysPath) {
|
|
35
|
+
// Default trusted keys for the DevilsDev organization
|
|
36
|
+
this.trustStore.set('devilsdev-official', {
|
|
37
|
+
publicKey: 'your-ed25519-public-key-here',
|
|
38
|
+
name: 'DevilsDev Official Plugins',
|
|
39
|
+
verified: true,
|
|
40
|
+
});
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
if (fs.existsSync(keysPath)) {
|
|
46
|
+
const keysData = fs.readFileSync(keysPath, 'utf8');
|
|
47
|
+
const trustedKeys = JSON.parse(keysData);
|
|
48
|
+
|
|
49
|
+
for (const [keyId, keyInfo] of Object.entries(trustedKeys)) {
|
|
50
|
+
if (keyInfo.publicKey && keyInfo.name) {
|
|
51
|
+
this.trustStore.set(keyId, keyInfo);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
if (this.failClosed) {
|
|
57
|
+
throw new Error(`Failed to load trusted keys: ${error.message}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Verify plugin signature
|
|
64
|
+
* @param {object} manifest - Plugin manifest
|
|
65
|
+
* @param {string} signature - Base64 encoded signature
|
|
66
|
+
* @param {string} signerId - Signer identifier
|
|
67
|
+
* @returns {Promise<object>} Verification result
|
|
68
|
+
*/
|
|
69
|
+
async verifyPluginSignature(manifest, signature, signerId) {
|
|
70
|
+
const verificationResult = {
|
|
71
|
+
verified: false,
|
|
72
|
+
signerId,
|
|
73
|
+
timestamp: new Date().toISOString(),
|
|
74
|
+
error: null,
|
|
75
|
+
auditTrail: [],
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
if (!this.enabled) {
|
|
80
|
+
verificationResult.error = 'Signature verification disabled';
|
|
81
|
+
this._auditVerification(verificationResult, manifest);
|
|
82
|
+
|
|
83
|
+
if (this.failClosed) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
'Plugin signature verification is disabled in fail-closed mode',
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
return verificationResult;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Validate inputs
|
|
92
|
+
if (!manifest || typeof manifest !== 'object') {
|
|
93
|
+
throw new Error('Invalid manifest: must be an object');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (!signature || typeof signature !== 'string') {
|
|
97
|
+
throw new Error('Invalid signature: must be a non-empty string');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!signerId || typeof signerId !== 'string') {
|
|
101
|
+
throw new Error('Invalid signerId: must be a non-empty string');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Check if signer is trusted
|
|
105
|
+
const trustedKey = this.trustStore.get(signerId);
|
|
106
|
+
if (!trustedKey) {
|
|
107
|
+
throw new Error(`Untrusted signer: ${signerId}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Canonicalize manifest for verification
|
|
111
|
+
const canonicalManifest = this._canonicalizeManifest(manifest);
|
|
112
|
+
const manifestData = Buffer.from(
|
|
113
|
+
JSON.stringify(canonicalManifest),
|
|
114
|
+
'utf8',
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
// Decode signature
|
|
118
|
+
let signatureBuffer;
|
|
119
|
+
try {
|
|
120
|
+
signatureBuffer = Buffer.from(signature, 'base64');
|
|
121
|
+
} catch (error) {
|
|
122
|
+
throw new Error(`Invalid signature encoding: ${error.message}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Verify signature using Web Crypto API or Node.js crypto
|
|
126
|
+
const isValid = await this._verifyEd25519Signature(
|
|
127
|
+
manifestData,
|
|
128
|
+
signatureBuffer,
|
|
129
|
+
trustedKey.publicKey,
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
if (!isValid) {
|
|
133
|
+
throw new Error('Signature verification failed');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
verificationResult.verified = true;
|
|
137
|
+
verificationResult.auditTrail.push({
|
|
138
|
+
action: 'signature_verified',
|
|
139
|
+
signerId,
|
|
140
|
+
timestamp: new Date().toISOString(),
|
|
141
|
+
manifestHash: this._hashManifest(canonicalManifest),
|
|
142
|
+
});
|
|
143
|
+
} catch (error) {
|
|
144
|
+
verificationResult.error = error.message;
|
|
145
|
+
verificationResult.auditTrail.push({
|
|
146
|
+
action: 'verification_failed',
|
|
147
|
+
signerId,
|
|
148
|
+
timestamp: new Date().toISOString(),
|
|
149
|
+
error: error.message,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
if (this.failClosed) {
|
|
153
|
+
this._auditVerification(verificationResult, manifest);
|
|
154
|
+
throw new Error(
|
|
155
|
+
`Plugin signature verification failed: ${error.message}`,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
this._auditVerification(verificationResult, manifest);
|
|
161
|
+
return verificationResult;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Verify Ed25519 signature
|
|
166
|
+
* @param {Buffer} data - Data that was signed
|
|
167
|
+
* @param {Buffer} signature - Signature bytes
|
|
168
|
+
* @param {string} publicKeyHex - Public key in hex format
|
|
169
|
+
* @returns {Promise<boolean>} Verification result
|
|
170
|
+
*/
|
|
171
|
+
async _verifyEd25519Signature(data, signature, publicKeyHex) {
|
|
172
|
+
try {
|
|
173
|
+
// Convert hex public key to buffer
|
|
174
|
+
const publicKeyBuffer = Buffer.from(publicKeyHex, 'hex');
|
|
175
|
+
|
|
176
|
+
// Use Node.js crypto for Ed25519 verification
|
|
177
|
+
const verify = crypto.createVerify('Ed25519');
|
|
178
|
+
verify.update(data);
|
|
179
|
+
|
|
180
|
+
// Create public key object
|
|
181
|
+
const publicKeyObject = crypto.createPublicKey({
|
|
182
|
+
key: publicKeyBuffer,
|
|
183
|
+
format: 'der',
|
|
184
|
+
type: 'spki',
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
return verify.verify(publicKeyObject, signature);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
// Fallback to Web Crypto API if available
|
|
190
|
+
if (typeof crypto.subtle !== 'undefined') {
|
|
191
|
+
try {
|
|
192
|
+
const publicKeyBuffer = Buffer.from(publicKeyHex, 'hex');
|
|
193
|
+
const publicKey = await crypto.subtle.importKey(
|
|
194
|
+
'raw',
|
|
195
|
+
publicKeyBuffer,
|
|
196
|
+
{
|
|
197
|
+
name: 'Ed25519',
|
|
198
|
+
namedCurve: 'Ed25519',
|
|
199
|
+
},
|
|
200
|
+
false,
|
|
201
|
+
['verify'],
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
return await crypto.subtle.verify(
|
|
205
|
+
'Ed25519',
|
|
206
|
+
publicKey,
|
|
207
|
+
signature,
|
|
208
|
+
data,
|
|
209
|
+
);
|
|
210
|
+
} catch (webCryptoError) {
|
|
211
|
+
throw new Error(`Ed25519 verification failed: ${error.message}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
throw new Error(`Ed25519 verification failed: ${error.message}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Canonicalize manifest for consistent signing
|
|
221
|
+
* @param {object} manifest - Plugin manifest
|
|
222
|
+
* @returns {object} Canonicalized manifest
|
|
223
|
+
*/
|
|
224
|
+
_canonicalizeManifest(manifest) {
|
|
225
|
+
// Remove signature-related fields and sort keys
|
|
226
|
+
const canonical = JSON.parse(JSON.stringify(manifest));
|
|
227
|
+
delete canonical.signature;
|
|
228
|
+
delete canonical.signerId;
|
|
229
|
+
delete canonical.signedAt;
|
|
230
|
+
|
|
231
|
+
// Sort object keys recursively for deterministic serialization
|
|
232
|
+
return this._sortObjectKeys(canonical);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Sort object keys recursively
|
|
237
|
+
* @param {any} obj - Object to sort
|
|
238
|
+
* @returns {any} Object with sorted keys
|
|
239
|
+
*/
|
|
240
|
+
_sortObjectKeys(obj) {
|
|
241
|
+
if (obj === null || typeof obj !== 'object') {
|
|
242
|
+
return obj;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (Array.isArray(obj)) {
|
|
246
|
+
return obj.map((item) => this._sortObjectKeys(item));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const sorted = {};
|
|
250
|
+
const keys = Object.keys(obj).sort();
|
|
251
|
+
|
|
252
|
+
for (const key of keys) {
|
|
253
|
+
sorted[key] = this._sortObjectKeys(obj[key]);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return sorted;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Hash manifest for audit trail
|
|
261
|
+
* @param {object} manifest - Canonicalized manifest
|
|
262
|
+
* @returns {string} SHA-256 hash
|
|
263
|
+
*/
|
|
264
|
+
_hashManifest(manifest) {
|
|
265
|
+
const manifestData = JSON.stringify(manifest);
|
|
266
|
+
return crypto.createHash('sha256').update(manifestData).digest('hex');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Default audit logger
|
|
271
|
+
* @param {object} result - Verification result
|
|
272
|
+
* @param {object} manifest - Plugin manifest
|
|
273
|
+
*/
|
|
274
|
+
_defaultAuditLogger(result, manifest) {
|
|
275
|
+
const auditEntry = {
|
|
276
|
+
timestamp: result.timestamp,
|
|
277
|
+
action: 'plugin_signature_verification',
|
|
278
|
+
pluginId: manifest.name || 'unknown',
|
|
279
|
+
pluginVersion: manifest.version || 'unknown',
|
|
280
|
+
signerId: result.signerId,
|
|
281
|
+
verified: result.verified,
|
|
282
|
+
error: result.error,
|
|
283
|
+
auditTrail: result.auditTrail,
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
// In production, this should write to secure audit log
|
|
287
|
+
if (process.env.NODE_ENV === 'production') {
|
|
288
|
+
console.warn('[AUDIT]', JSON.stringify(auditEntry));
|
|
289
|
+
} else {
|
|
290
|
+
console.log('[AUDIT]', auditEntry);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Audit verification attempt
|
|
296
|
+
* @param {object} result - Verification result
|
|
297
|
+
* @param {object} manifest - Plugin manifest
|
|
298
|
+
*/
|
|
299
|
+
_auditVerification(result, manifest) {
|
|
300
|
+
try {
|
|
301
|
+
this.auditLogger(result, manifest);
|
|
302
|
+
} catch (error) {
|
|
303
|
+
// Audit logging failure should not break verification
|
|
304
|
+
console.error('Audit logging failed:', error.message);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Add trusted signer
|
|
310
|
+
* @param {string} signerId - Signer identifier
|
|
311
|
+
* @param {string} publicKey - Ed25519 public key in hex
|
|
312
|
+
* @param {string} name - Human-readable name
|
|
313
|
+
* @param {object} metadata - Additional metadata
|
|
314
|
+
*/
|
|
315
|
+
addTrustedSigner(signerId, publicKey, name, metadata = {}) {
|
|
316
|
+
if (!signerId || !publicKey || !name) {
|
|
317
|
+
throw new Error('signerId, publicKey, and name are required');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Validate public key format
|
|
321
|
+
if (!/^[0-9a-fA-F]{64}$/.test(publicKey)) {
|
|
322
|
+
throw new Error('Public key must be 64 hex characters (32 bytes)');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
this.trustStore.set(signerId, {
|
|
326
|
+
publicKey: publicKey.toLowerCase(),
|
|
327
|
+
name,
|
|
328
|
+
verified: metadata.verified || false,
|
|
329
|
+
addedAt: new Date().toISOString(),
|
|
330
|
+
...metadata,
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
this.auditLogger(
|
|
334
|
+
{
|
|
335
|
+
timestamp: new Date().toISOString(),
|
|
336
|
+
action: 'trusted_signer_added',
|
|
337
|
+
signerId,
|
|
338
|
+
name,
|
|
339
|
+
verified: true,
|
|
340
|
+
auditTrail: [
|
|
341
|
+
{
|
|
342
|
+
action: 'signer_added',
|
|
343
|
+
signerId,
|
|
344
|
+
timestamp: new Date().toISOString(),
|
|
345
|
+
},
|
|
346
|
+
],
|
|
347
|
+
},
|
|
348
|
+
{ name: 'trust_store_update' },
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Remove trusted signer
|
|
354
|
+
* @param {string} signerId - Signer identifier
|
|
355
|
+
*/
|
|
356
|
+
removeTrustedSigner(signerId) {
|
|
357
|
+
if (!signerId) {
|
|
358
|
+
throw new Error('signerId is required');
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const removed = this.trustStore.delete(signerId);
|
|
362
|
+
|
|
363
|
+
if (removed) {
|
|
364
|
+
this.auditLogger(
|
|
365
|
+
{
|
|
366
|
+
timestamp: new Date().toISOString(),
|
|
367
|
+
action: 'trusted_signer_removed',
|
|
368
|
+
signerId,
|
|
369
|
+
verified: true,
|
|
370
|
+
auditTrail: [
|
|
371
|
+
{
|
|
372
|
+
action: 'signer_removed',
|
|
373
|
+
signerId,
|
|
374
|
+
timestamp: new Date().toISOString(),
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
},
|
|
378
|
+
{ name: 'trust_store_update' },
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return removed;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* List trusted signers
|
|
387
|
+
* @returns {Array} List of trusted signers
|
|
388
|
+
*/
|
|
389
|
+
listTrustedSigners() {
|
|
390
|
+
return Array.from(this.trustStore.entries()).map(([signerId, info]) => ({
|
|
391
|
+
signerId,
|
|
392
|
+
name: info.name,
|
|
393
|
+
verified: info.verified,
|
|
394
|
+
addedAt: info.addedAt,
|
|
395
|
+
}));
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Create plugin signature verifier instance
|
|
401
|
+
* @param {object} options - Configuration options
|
|
402
|
+
* @returns {PluginSignatureVerifier} Verifier instance
|
|
403
|
+
*/
|
|
404
|
+
function createPluginVerifier(options = {}) {
|
|
405
|
+
return new PluginSignatureVerifier(options);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
module.exports = {
|
|
409
|
+
PluginSignatureVerifier,
|
|
410
|
+
createPluginVerifier,
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
// CommonJS/ESM interop
|
|
414
|
+
module.exports.default = module.exports;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function validateInput(input, opts = {}) {
|
|
4
|
+
const maxLength = opts.maxLength || 20000;
|
|
5
|
+
const inputStr = String(input);
|
|
6
|
+
|
|
7
|
+
const reasons = [];
|
|
8
|
+
|
|
9
|
+
// Check length
|
|
10
|
+
if (inputStr.length > maxLength) {
|
|
11
|
+
reasons.push(`Input exceeds maximum length of ${maxLength} characters`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Disallowed tokens (case-insensitive)
|
|
15
|
+
const disallowedTokens = ['```', '${', '</script', '--', ';', '||', '&&'];
|
|
16
|
+
const lowerInput = inputStr.toLowerCase();
|
|
17
|
+
|
|
18
|
+
for (const token of disallowedTokens) {
|
|
19
|
+
if (lowerInput.includes(token.toLowerCase())) {
|
|
20
|
+
reasons.push(`Input contains disallowed token: ${token}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const valid = reasons.length === 0;
|
|
25
|
+
const sanitized = inputStr.substring(0, maxLength);
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
valid,
|
|
29
|
+
reasons,
|
|
30
|
+
sanitized,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// CJS+ESM interop pattern
|
|
35
|
+
module.exports = { validateInput };
|
|
36
|
+
module.exports.default = module.exports;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version: 1.0.0
|
|
3
|
+
* Description: Enhanced CI diagnostics for plugin mock verification
|
|
4
|
+
* Author: Ali Kahwaji
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const path = require('path');
|
|
8
|
+
// eslint-disable-line global-require
|
|
9
|
+
const chalk = require('chalk');
|
|
10
|
+
// eslint-disable-line global-require
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Pretty prints CI validation errors and guidance
|
|
14
|
+
* @param {Array<{ file: string, missing: string[] }>} failures
|
|
15
|
+
*/
|
|
16
|
+
function reportPluginValidationFailures(failures) {
|
|
17
|
+
if (!failures.length) return;
|
|
18
|
+
|
|
19
|
+
console.log(`\n${chalk.redBright.bold('❌ Plugin Mock Validation Failed')}`);
|
|
20
|
+
// eslint-disable-line no-console
|
|
21
|
+
|
|
22
|
+
failures.forEach(({ file, missing }) => {
|
|
23
|
+
const fileName = path.basename(file);
|
|
24
|
+
console.log(
|
|
25
|
+
// eslint-disable-line no-console
|
|
26
|
+
`${chalk.red(` ✖ ${fileName}`)} ${chalk.gray(`missing: ${missing.join(', ')}`)}`,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
// Add method-level fix hints
|
|
30
|
+
missing.forEach((method) => {
|
|
31
|
+
const pluginType = fileName.split('.')[0].split('-')[1] || 'unknown';
|
|
32
|
+
console.error(
|
|
33
|
+
` → Fix: implement method ${chalk.cyan(`${method}()`)} for plugin _type ${chalk.yellow(pluginType)}`,
|
|
34
|
+
);
|
|
35
|
+
// eslint-disable-line no-console
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
console.log(`\n${chalk.yellowBright('Recommended Fixes:')}`);
|
|
40
|
+
// eslint-disable-line no-console
|
|
41
|
+
console.log(
|
|
42
|
+
' • Implement all required methods defined in /src/core/plugin-contracts.js',
|
|
43
|
+
);
|
|
44
|
+
// eslint-disable-line no-console
|
|
45
|
+
console.log(` • Re-run: ${chalk.cyan('npm run ci')} to confirm validation`);
|
|
46
|
+
// eslint-disable-line no-console
|
|
47
|
+
console.log(
|
|
48
|
+
` • Optionally use: ${chalk.cyan('scripts/repair-fixtures.js')} to auto-repair mocks\n`,
|
|
49
|
+
);
|
|
50
|
+
// eslint-disable-line no-console
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Default export
|
|
54
|
+
|
|
55
|
+
module.exports = {
|
|
56
|
+
reportPluginValidationFailures,
|
|
57
|
+
};
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version: 0.2.0
|
|
3
|
+
* Path: /src/utils/logger.js
|
|
4
|
+
* Description: Centralized structured logger with correlation IDs and enterprise features
|
|
5
|
+
* Author: Ali Kahwaji
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const pino = require('pino'); // eslint-disable-line global-require
|
|
9
|
+
const crypto = require('crypto'); // eslint-disable-line global-require
|
|
10
|
+
|
|
11
|
+
// Determine log level with environment override
|
|
12
|
+
const getLogLevel = () => {
|
|
13
|
+
if (process.env.LOG_LEVEL) {
|
|
14
|
+
const level = process.env.LOG_LEVEL.toLowerCase();
|
|
15
|
+
const validLevels = [
|
|
16
|
+
'trace',
|
|
17
|
+
'debug',
|
|
18
|
+
'info',
|
|
19
|
+
'warn',
|
|
20
|
+
'error',
|
|
21
|
+
'fatal',
|
|
22
|
+
'silent',
|
|
23
|
+
];
|
|
24
|
+
if (validLevels.includes(level)) {
|
|
25
|
+
return level;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return process.env.NODE_ENV === 'test' ? 'silent' : 'info';
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Determine if pretty printing should be enabled
|
|
32
|
+
const shouldUsePrettyPrint = () => {
|
|
33
|
+
// Enable pretty print in development or when explicitly requested
|
|
34
|
+
return (
|
|
35
|
+
process.env.NODE_ENV === 'development' || process.env.LOG_PRETTY === 'true'
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create a structured JSON logger instance with correlation IDs and enterprise features.
|
|
41
|
+
* Supports distributed tracing, tenant isolation, and security audit logging.
|
|
42
|
+
*/
|
|
43
|
+
const baseLogger = pino({
|
|
44
|
+
name: 'rag-pipeline-utils',
|
|
45
|
+
level: getLogLevel(),
|
|
46
|
+
formatters: {
|
|
47
|
+
level: (label) => {
|
|
48
|
+
return { level: label };
|
|
49
|
+
},
|
|
50
|
+
log: (object) => {
|
|
51
|
+
// Add correlation ID if available
|
|
52
|
+
if (object.correlationId) {
|
|
53
|
+
object.correlationId = object.correlationId;
|
|
54
|
+
}
|
|
55
|
+
return object;
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
// Pretty print in development or when LOG_PRETTY=true
|
|
59
|
+
...(shouldUsePrettyPrint() && {
|
|
60
|
+
transport: {
|
|
61
|
+
target: 'pino-pretty',
|
|
62
|
+
options: {
|
|
63
|
+
colorize: true,
|
|
64
|
+
translateTime: 'HH:MM:ss',
|
|
65
|
+
ignore: 'pid,hostname',
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
}),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Enhanced logger with correlation ID support and structured context
|
|
73
|
+
*/
|
|
74
|
+
class StructuredLogger {
|
|
75
|
+
constructor(baseLogger) {
|
|
76
|
+
this.logger = baseLogger;
|
|
77
|
+
this.correlationId = null;
|
|
78
|
+
this.context = {};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Create a child logger with correlation ID and context
|
|
83
|
+
*/
|
|
84
|
+
child(context = {}) {
|
|
85
|
+
const correlationId = context.correlationId || crypto.randomUUID();
|
|
86
|
+
const childLogger = new StructuredLogger(
|
|
87
|
+
this.logger.child({
|
|
88
|
+
correlationId,
|
|
89
|
+
...context,
|
|
90
|
+
}),
|
|
91
|
+
);
|
|
92
|
+
childLogger.correlationId = correlationId;
|
|
93
|
+
childLogger.context = { ...this.context, ...context };
|
|
94
|
+
return childLogger;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Set correlation ID for distributed tracing
|
|
99
|
+
*/
|
|
100
|
+
setCorrelationId(correlationId) {
|
|
101
|
+
this.correlationId = correlationId;
|
|
102
|
+
return this;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Add persistent context to all log messages
|
|
107
|
+
*/
|
|
108
|
+
setContext(context) {
|
|
109
|
+
this.context = { ...this.context, ...context };
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Log with automatic correlation ID and context injection
|
|
115
|
+
*/
|
|
116
|
+
_log(level, message, meta = {}) {
|
|
117
|
+
const logData = {
|
|
118
|
+
...this.context,
|
|
119
|
+
correlationId: this.correlationId || crypto.randomUUID(),
|
|
120
|
+
timestamp: new Date().toISOString(),
|
|
121
|
+
...meta,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
this.logger[level](logData, message);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
info(message, meta) {
|
|
128
|
+
this._log('info', message, meta);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
error(message, meta) {
|
|
132
|
+
this._log('error', message, meta);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
warn(message, meta) {
|
|
136
|
+
this._log('warn', message, meta);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
debug(message, meta) {
|
|
140
|
+
this._log('debug', message, meta);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Security audit logging with enhanced metadata
|
|
145
|
+
*/
|
|
146
|
+
audit(action, meta = {}) {
|
|
147
|
+
this._log('info', `AUDIT: ${action}`, {
|
|
148
|
+
...meta,
|
|
149
|
+
audit: true,
|
|
150
|
+
action,
|
|
151
|
+
severity: meta.severity || 'medium',
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Performance logging with timing information
|
|
157
|
+
*/
|
|
158
|
+
performance(operation, duration, meta = {}) {
|
|
159
|
+
this._log('info', `PERFORMANCE: ${operation}`, {
|
|
160
|
+
...meta,
|
|
161
|
+
performance: true,
|
|
162
|
+
operation,
|
|
163
|
+
duration,
|
|
164
|
+
unit: 'ms',
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Tenant-specific logging for multi-tenant applications
|
|
170
|
+
*/
|
|
171
|
+
tenant(tenantId, message, meta = {}) {
|
|
172
|
+
this._log('info', message, {
|
|
173
|
+
...meta,
|
|
174
|
+
tenantId,
|
|
175
|
+
tenant: true,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Create default logger instance
|
|
181
|
+
const logger = new StructuredLogger(baseLogger);
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Create a logger with correlation ID for request tracing
|
|
185
|
+
*/
|
|
186
|
+
function createLogger(correlationId, context = {}) {
|
|
187
|
+
return logger.child({ correlationId, ...context });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Middleware function for Express.js to add correlation IDs to requests
|
|
192
|
+
*/
|
|
193
|
+
function correlationMiddleware(req, res, next) {
|
|
194
|
+
const correlationId = req.headers['x-correlation-id'] || crypto.randomUUID();
|
|
195
|
+
req.correlationId = correlationId;
|
|
196
|
+
req.logger = createLogger(correlationId, {
|
|
197
|
+
method: req.method,
|
|
198
|
+
url: req.url,
|
|
199
|
+
userAgent: req.headers['user-agent'],
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
res.setHeader('x-correlation-id', correlationId);
|
|
203
|
+
next();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
module.exports = {
|
|
207
|
+
logger,
|
|
208
|
+
createLogger,
|
|
209
|
+
correlationMiddleware,
|
|
210
|
+
StructuredLogger,
|
|
211
|
+
};
|