erosolar-cli 2.1.239 → 2.1.241
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/dist/capabilities/index.d.ts +1 -0
- package/dist/capabilities/index.d.ts.map +1 -1
- package/dist/capabilities/index.js +1 -0
- package/dist/capabilities/index.js.map +1 -1
- package/dist/capabilities/integrityCapability.d.ts +13 -0
- package/dist/capabilities/integrityCapability.d.ts.map +1 -0
- package/dist/capabilities/integrityCapability.js +20 -0
- package/dist/capabilities/integrityCapability.js.map +1 -0
- package/dist/core/agentOrchestrator.d.ts.map +1 -1
- package/dist/core/agentOrchestrator.js +12 -2
- package/dist/core/agentOrchestrator.js.map +1 -1
- package/dist/core/integrityVerification.d.ts +250 -0
- package/dist/core/integrityVerification.d.ts.map +1 -0
- package/dist/core/integrityVerification.js +616 -0
- package/dist/core/integrityVerification.js.map +1 -0
- package/dist/plugins/tools/integrity/integrityPlugin.d.ts +3 -0
- package/dist/plugins/tools/integrity/integrityPlugin.d.ts.map +1 -0
- package/dist/plugins/tools/integrity/integrityPlugin.js +14 -0
- package/dist/plugins/tools/integrity/integrityPlugin.js.map +1 -0
- package/dist/plugins/tools/nodeDefaults.d.ts.map +1 -1
- package/dist/plugins/tools/nodeDefaults.js +2 -0
- package/dist/plugins/tools/nodeDefaults.js.map +1 -1
- package/dist/tools/integrityTools.d.ts +18 -0
- package/dist/tools/integrityTools.d.ts.map +1 -0
- package/dist/tools/integrityTools.js +1172 -0
- package/dist/tools/integrityTools.js.map +1 -0
- package/dist/ui/UnifiedUIRenderer.d.ts +11 -0
- package/dist/ui/UnifiedUIRenderer.d.ts.map +1 -1
- package/dist/ui/UnifiedUIRenderer.js +61 -4
- package/dist/ui/UnifiedUIRenderer.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,1172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integrity Verification Tools
|
|
3
|
+
*
|
|
4
|
+
* CLI tool suite for cryptographic integrity verification and
|
|
5
|
+
* legal evidence preservation for pro se litigation.
|
|
6
|
+
*
|
|
7
|
+
* Tools:
|
|
8
|
+
* - IntegrityChain: Create and manage hash chains
|
|
9
|
+
* - EvidenceCollect: Capture and hash evidence
|
|
10
|
+
* - IntegrityVerify: Verify chain/record integrity
|
|
11
|
+
* - ExhibitGenerate: Create legal exhibits with proofs
|
|
12
|
+
* - FraudDocument: Document fraud indicators
|
|
13
|
+
* - CryptoHash: General hashing operations
|
|
14
|
+
* - CryptoSign: Digital signatures
|
|
15
|
+
*/
|
|
16
|
+
import * as fs from 'node:fs/promises';
|
|
17
|
+
import * as fsSync from 'node:fs';
|
|
18
|
+
import * as path from 'node:path';
|
|
19
|
+
import * as crypto from 'node:crypto';
|
|
20
|
+
import * as https from 'node:https';
|
|
21
|
+
import * as http from 'node:http';
|
|
22
|
+
import { IntegrityVerificationEngine, TECH_COMPANY_TARGETS, FRAUD_TYPES, LEGAL_CITATIONS, hashFile, hashString, documentFraudIndicator, } from '../core/integrityVerification.js';
|
|
23
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
24
|
+
// TOOL SUITE FACTORY
|
|
25
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
26
|
+
export function createIntegrityTools(workingDir = process.cwd()) {
|
|
27
|
+
// Storage directory for integrity data
|
|
28
|
+
const integrityDir = path.join(workingDir, '.erosolar', 'integrity');
|
|
29
|
+
// Key storage
|
|
30
|
+
let keyPair;
|
|
31
|
+
const keyPath = path.join(integrityDir, 'keys');
|
|
32
|
+
// Load or generate keys
|
|
33
|
+
const loadKeys = async () => {
|
|
34
|
+
if (keyPair)
|
|
35
|
+
return keyPair;
|
|
36
|
+
try {
|
|
37
|
+
await fs.mkdir(keyPath, { recursive: true });
|
|
38
|
+
const privKeyPath = path.join(keyPath, 'private.pem');
|
|
39
|
+
const pubKeyPath = path.join(keyPath, 'public.pem');
|
|
40
|
+
if (fsSync.existsSync(privKeyPath) && fsSync.existsSync(pubKeyPath)) {
|
|
41
|
+
keyPair = {
|
|
42
|
+
privateKey: await fs.readFile(privKeyPath, 'utf8'),
|
|
43
|
+
publicKey: await fs.readFile(pubKeyPath, 'utf8'),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
keyPair = IntegrityVerificationEngine.generateKeyPair();
|
|
48
|
+
await fs.writeFile(privKeyPath, keyPair.privateKey, { mode: 0o600 });
|
|
49
|
+
await fs.writeFile(pubKeyPath, keyPair.publicKey);
|
|
50
|
+
}
|
|
51
|
+
return keyPair;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
// Generate ephemeral keys if persistence fails
|
|
55
|
+
keyPair = IntegrityVerificationEngine.generateKeyPair();
|
|
56
|
+
return keyPair;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
// Engine instance cache
|
|
60
|
+
let engineInstance = null;
|
|
61
|
+
const getEngine = async () => {
|
|
62
|
+
if (engineInstance)
|
|
63
|
+
return engineInstance;
|
|
64
|
+
await fs.mkdir(integrityDir, { recursive: true });
|
|
65
|
+
const keys = await loadKeys();
|
|
66
|
+
engineInstance = new IntegrityVerificationEngine({
|
|
67
|
+
storageDir: integrityDir,
|
|
68
|
+
algorithm: 'sha256',
|
|
69
|
+
keyPair: keys,
|
|
70
|
+
});
|
|
71
|
+
return engineInstance;
|
|
72
|
+
};
|
|
73
|
+
return {
|
|
74
|
+
id: 'integrity',
|
|
75
|
+
description: 'Cryptographic integrity verification and legal evidence preservation',
|
|
76
|
+
tools: [
|
|
77
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
78
|
+
// Chain Management
|
|
79
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
80
|
+
{
|
|
81
|
+
name: 'IntegrityChain',
|
|
82
|
+
description: `Create and manage cryptographic hash chains for evidence preservation.
|
|
83
|
+
|
|
84
|
+
Operations:
|
|
85
|
+
- create: Initialize a new evidence chain
|
|
86
|
+
- load: Load an existing chain
|
|
87
|
+
- verify: Verify chain integrity
|
|
88
|
+
- export: Export chain with proofs
|
|
89
|
+
- list: List all chains
|
|
90
|
+
|
|
91
|
+
Hash chains provide tamper-evident logging with:
|
|
92
|
+
- SHA-256/384/512 hashing
|
|
93
|
+
- Chain linkage verification
|
|
94
|
+
- Merkle tree proofs
|
|
95
|
+
- Digital signatures`,
|
|
96
|
+
parameters: {
|
|
97
|
+
type: 'object',
|
|
98
|
+
required: ['operation'],
|
|
99
|
+
properties: {
|
|
100
|
+
operation: {
|
|
101
|
+
type: 'string',
|
|
102
|
+
enum: ['create', 'load', 'verify', 'export', 'list'],
|
|
103
|
+
description: 'Operation to perform',
|
|
104
|
+
},
|
|
105
|
+
chainId: {
|
|
106
|
+
type: 'string',
|
|
107
|
+
description: 'Chain identifier (for load/verify/export)',
|
|
108
|
+
},
|
|
109
|
+
name: {
|
|
110
|
+
type: 'string',
|
|
111
|
+
description: 'Human-readable name for new chain',
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
handler: async (args) => {
|
|
116
|
+
const operation = args['operation'];
|
|
117
|
+
const chainId = args['chainId'];
|
|
118
|
+
const engine = await getEngine();
|
|
119
|
+
switch (operation) {
|
|
120
|
+
case 'create': {
|
|
121
|
+
const chain = await engine.createChain(chainId);
|
|
122
|
+
return JSON.stringify({
|
|
123
|
+
success: true,
|
|
124
|
+
operation: 'create',
|
|
125
|
+
chain: {
|
|
126
|
+
id: chain.id,
|
|
127
|
+
created: chain.created,
|
|
128
|
+
algorithm: chain.algorithm,
|
|
129
|
+
recordCount: 0,
|
|
130
|
+
},
|
|
131
|
+
message: `Evidence chain created: ${chain.id}`,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
case 'load': {
|
|
135
|
+
if (!chainId) {
|
|
136
|
+
return JSON.stringify({ success: false, error: 'chainId required for load' });
|
|
137
|
+
}
|
|
138
|
+
const chain = await engine.loadChain(chainId);
|
|
139
|
+
if (!chain) {
|
|
140
|
+
return JSON.stringify({ success: false, error: `Chain not found: ${chainId}` });
|
|
141
|
+
}
|
|
142
|
+
return JSON.stringify({
|
|
143
|
+
success: true,
|
|
144
|
+
operation: 'load',
|
|
145
|
+
chain: {
|
|
146
|
+
id: chain.id,
|
|
147
|
+
created: chain.created,
|
|
148
|
+
lastModified: chain.lastModified,
|
|
149
|
+
algorithm: chain.algorithm,
|
|
150
|
+
recordCount: chain.records.length,
|
|
151
|
+
merkleRoot: chain.merkleTree.root,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
case 'verify': {
|
|
156
|
+
if (!chainId) {
|
|
157
|
+
return JSON.stringify({ success: false, error: 'chainId required for verify' });
|
|
158
|
+
}
|
|
159
|
+
await engine.loadChain(chainId);
|
|
160
|
+
const result = engine.verifyChainIntegrity(chainId);
|
|
161
|
+
return JSON.stringify({
|
|
162
|
+
success: true,
|
|
163
|
+
operation: 'verify',
|
|
164
|
+
chainId,
|
|
165
|
+
integrity: {
|
|
166
|
+
valid: result.valid,
|
|
167
|
+
verifiedRecords: result.verifiedRecords,
|
|
168
|
+
errors: result.errors,
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
case 'export': {
|
|
173
|
+
if (!chainId) {
|
|
174
|
+
return JSON.stringify({ success: false, error: 'chainId required for export' });
|
|
175
|
+
}
|
|
176
|
+
await engine.loadChain(chainId);
|
|
177
|
+
const bundle = await engine.exportChainBundle(chainId);
|
|
178
|
+
return JSON.stringify({
|
|
179
|
+
success: true,
|
|
180
|
+
operation: 'export',
|
|
181
|
+
bundle: {
|
|
182
|
+
chainId: bundle.chain.id,
|
|
183
|
+
recordCount: bundle.chain.records.length,
|
|
184
|
+
merkleRoot: bundle.chain.merkleTree.root,
|
|
185
|
+
verification: bundle.verification,
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
case 'list': {
|
|
190
|
+
const chainsDir = path.join(integrityDir, 'chains');
|
|
191
|
+
try {
|
|
192
|
+
const dirs = await fs.readdir(chainsDir);
|
|
193
|
+
const chains = [];
|
|
194
|
+
for (const dir of dirs) {
|
|
195
|
+
try {
|
|
196
|
+
const chainPath = path.join(chainsDir, dir, 'chain.json');
|
|
197
|
+
const data = await fs.readFile(chainPath, 'utf8');
|
|
198
|
+
const chain = JSON.parse(data);
|
|
199
|
+
chains.push({
|
|
200
|
+
id: chain.id,
|
|
201
|
+
created: chain.created,
|
|
202
|
+
lastModified: chain.lastModified,
|
|
203
|
+
recordCount: chain.records?.length || 0,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
// Skip invalid chains
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return JSON.stringify({ success: true, operation: 'list', chains });
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return JSON.stringify({ success: true, operation: 'list', chains: [] });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
default:
|
|
217
|
+
return JSON.stringify({ success: false, error: `Unknown operation: ${operation}` });
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
222
|
+
// Evidence Collection
|
|
223
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
224
|
+
{
|
|
225
|
+
name: 'EvidenceCollect',
|
|
226
|
+
description: `Collect and cryptographically hash evidence for legal preservation.
|
|
227
|
+
|
|
228
|
+
Supports collecting:
|
|
229
|
+
- Files (documents, images, binaries)
|
|
230
|
+
- URLs (web pages, API responses)
|
|
231
|
+
- Text content (messages, logs)
|
|
232
|
+
- Screenshots
|
|
233
|
+
- Network traffic captures
|
|
234
|
+
|
|
235
|
+
Each piece of evidence is:
|
|
236
|
+
- Hashed with SHA-256
|
|
237
|
+
- Linked to previous evidence (chain)
|
|
238
|
+
- Timestamped
|
|
239
|
+
- Digitally signed
|
|
240
|
+
- Stored with full metadata
|
|
241
|
+
|
|
242
|
+
Target entities (auto-populated info available):
|
|
243
|
+
- apple, google, meta, microsoft, amazon`,
|
|
244
|
+
parameters: {
|
|
245
|
+
type: 'object',
|
|
246
|
+
required: ['chainId', 'evidenceType', 'source'],
|
|
247
|
+
properties: {
|
|
248
|
+
chainId: {
|
|
249
|
+
type: 'string',
|
|
250
|
+
description: 'Evidence chain to add to',
|
|
251
|
+
},
|
|
252
|
+
evidenceType: {
|
|
253
|
+
type: 'string',
|
|
254
|
+
enum: [
|
|
255
|
+
'communication', 'transaction', 'terms_violation', 'privacy_breach',
|
|
256
|
+
'data_manipulation', 'api_response', 'screenshot', 'document',
|
|
257
|
+
'metadata', 'network_traffic', 'audit_log', 'configuration',
|
|
258
|
+
'binary_artifact', 'fraud_indicator',
|
|
259
|
+
],
|
|
260
|
+
description: 'Type of evidence being collected',
|
|
261
|
+
},
|
|
262
|
+
source: {
|
|
263
|
+
type: 'string',
|
|
264
|
+
description: 'Source: file path, URL, or "inline" for direct content',
|
|
265
|
+
},
|
|
266
|
+
content: {
|
|
267
|
+
type: 'string',
|
|
268
|
+
description: 'Direct content (when source is "inline")',
|
|
269
|
+
},
|
|
270
|
+
target: {
|
|
271
|
+
type: 'string',
|
|
272
|
+
description: 'Target entity (apple, google, meta, microsoft, amazon) or custom name',
|
|
273
|
+
},
|
|
274
|
+
targetProduct: {
|
|
275
|
+
type: 'string',
|
|
276
|
+
description: 'Specific product (e.g., "iOS", "Gmail", "Facebook")',
|
|
277
|
+
},
|
|
278
|
+
legalBasis: {
|
|
279
|
+
type: 'string',
|
|
280
|
+
description: 'Legal basis for evidence (e.g., "FTC Act Section 5")',
|
|
281
|
+
},
|
|
282
|
+
jurisdiction: {
|
|
283
|
+
type: 'string',
|
|
284
|
+
description: 'Legal jurisdiction (e.g., "Federal", "California")',
|
|
285
|
+
},
|
|
286
|
+
caseReference: {
|
|
287
|
+
type: 'string',
|
|
288
|
+
description: 'Case reference number if applicable',
|
|
289
|
+
},
|
|
290
|
+
collector: {
|
|
291
|
+
type: 'string',
|
|
292
|
+
description: 'Name of person collecting evidence',
|
|
293
|
+
},
|
|
294
|
+
notes: {
|
|
295
|
+
type: 'string',
|
|
296
|
+
description: 'Additional notes about the evidence',
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
handler: async (args) => {
|
|
301
|
+
const chainId = args['chainId'];
|
|
302
|
+
const evidenceType = args['evidenceType'];
|
|
303
|
+
const source = args['source'];
|
|
304
|
+
const content = args['content'];
|
|
305
|
+
const targetName = args['target'];
|
|
306
|
+
const targetProduct = args['targetProduct'];
|
|
307
|
+
const legalBasis = args['legalBasis'] || 'Investigation';
|
|
308
|
+
const jurisdiction = args['jurisdiction'] || 'Federal';
|
|
309
|
+
const caseReference = args['caseReference'];
|
|
310
|
+
const collector = args['collector'] || 'erosolar-cli';
|
|
311
|
+
const notes = args['notes'];
|
|
312
|
+
const engine = await getEngine();
|
|
313
|
+
await engine.loadChain(chainId);
|
|
314
|
+
// Build target entity
|
|
315
|
+
let targetEntity;
|
|
316
|
+
const companyInfo = targetName ? TECH_COMPANY_TARGETS[targetName.toLowerCase()] : undefined;
|
|
317
|
+
if (companyInfo) {
|
|
318
|
+
targetEntity = {
|
|
319
|
+
name: companyInfo.company,
|
|
320
|
+
type: 'corporation',
|
|
321
|
+
identifiers: {
|
|
322
|
+
legalName: companyInfo.corporateInfo.legalName,
|
|
323
|
+
headquarters: companyInfo.corporateInfo.headquarters,
|
|
324
|
+
secFilings: companyInfo.corporateInfo.secFilings || '',
|
|
325
|
+
incorporationState: companyInfo.corporateInfo.incorporationState || '',
|
|
326
|
+
},
|
|
327
|
+
};
|
|
328
|
+
if (targetProduct) {
|
|
329
|
+
const product = companyInfo.products.find(p => p.name.toLowerCase() === targetProduct.toLowerCase());
|
|
330
|
+
if (product) {
|
|
331
|
+
targetEntity = {
|
|
332
|
+
name: product.name,
|
|
333
|
+
type: 'product',
|
|
334
|
+
identifiers: {
|
|
335
|
+
productType: product.type,
|
|
336
|
+
dataTypes: product.dataTypes.join(', '),
|
|
337
|
+
},
|
|
338
|
+
parent: companyInfo.company,
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
targetEntity = {
|
|
345
|
+
name: targetName || 'Unknown',
|
|
346
|
+
type: 'corporation',
|
|
347
|
+
identifiers: {},
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
// Get content based on source
|
|
351
|
+
let evidenceContent;
|
|
352
|
+
let mimeType;
|
|
353
|
+
let captureMethod;
|
|
354
|
+
let sourceUrl;
|
|
355
|
+
if (source === 'inline') {
|
|
356
|
+
if (!content) {
|
|
357
|
+
return JSON.stringify({ success: false, error: 'Content required for inline source' });
|
|
358
|
+
}
|
|
359
|
+
evidenceContent = content;
|
|
360
|
+
captureMethod = 'direct_input';
|
|
361
|
+
}
|
|
362
|
+
else if (source.startsWith('http://') || source.startsWith('https://')) {
|
|
363
|
+
// Fetch URL content
|
|
364
|
+
sourceUrl = source;
|
|
365
|
+
try {
|
|
366
|
+
evidenceContent = await fetchUrl(source);
|
|
367
|
+
captureMethod = 'url_fetch';
|
|
368
|
+
mimeType = 'text/html';
|
|
369
|
+
}
|
|
370
|
+
catch (error) {
|
|
371
|
+
return JSON.stringify({
|
|
372
|
+
success: false,
|
|
373
|
+
error: `Failed to fetch URL: ${error instanceof Error ? error.message : String(error)}`,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
// Read file
|
|
379
|
+
try {
|
|
380
|
+
evidenceContent = await fs.readFile(source);
|
|
381
|
+
captureMethod = 'file_read';
|
|
382
|
+
mimeType = getMimeType(source);
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
return JSON.stringify({
|
|
386
|
+
success: false,
|
|
387
|
+
error: `Failed to read file: ${error instanceof Error ? error.message : String(error)}`,
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
const record = await engine.collectEvidence({
|
|
392
|
+
chainId,
|
|
393
|
+
target: targetEntity,
|
|
394
|
+
evidenceType,
|
|
395
|
+
content: evidenceContent,
|
|
396
|
+
mimeType,
|
|
397
|
+
sourceUrl,
|
|
398
|
+
captureMethod,
|
|
399
|
+
legalBasis,
|
|
400
|
+
jurisdiction,
|
|
401
|
+
caseReference,
|
|
402
|
+
collector,
|
|
403
|
+
notes,
|
|
404
|
+
});
|
|
405
|
+
return JSON.stringify({
|
|
406
|
+
success: true,
|
|
407
|
+
record: {
|
|
408
|
+
id: record.id,
|
|
409
|
+
timestamp: record.timestamp,
|
|
410
|
+
contentHash: record.contentHash,
|
|
411
|
+
previousHash: record.previousHash,
|
|
412
|
+
merkleRoot: record.merkleRoot,
|
|
413
|
+
signed: !!record.signature,
|
|
414
|
+
target: record.metadata.target.name,
|
|
415
|
+
evidenceType: record.metadata.evidenceType,
|
|
416
|
+
},
|
|
417
|
+
message: `Evidence collected and hashed: ${record.contentHash.slice(0, 16)}...`,
|
|
418
|
+
});
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
422
|
+
// Integrity Verification
|
|
423
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
424
|
+
{
|
|
425
|
+
name: 'IntegrityVerify',
|
|
426
|
+
description: `Verify cryptographic integrity of evidence chains and records.
|
|
427
|
+
|
|
428
|
+
Verification includes:
|
|
429
|
+
- Content hash verification
|
|
430
|
+
- Chain linkage verification
|
|
431
|
+
- Merkle tree proof verification
|
|
432
|
+
- Digital signature verification
|
|
433
|
+
- Timestamp validation
|
|
434
|
+
|
|
435
|
+
Returns detailed verification report suitable for court submission.`,
|
|
436
|
+
parameters: {
|
|
437
|
+
type: 'object',
|
|
438
|
+
required: ['chainId'],
|
|
439
|
+
properties: {
|
|
440
|
+
chainId: {
|
|
441
|
+
type: 'string',
|
|
442
|
+
description: 'Chain to verify',
|
|
443
|
+
},
|
|
444
|
+
recordId: {
|
|
445
|
+
type: 'string',
|
|
446
|
+
description: 'Specific record to verify (optional)',
|
|
447
|
+
},
|
|
448
|
+
generateProof: {
|
|
449
|
+
type: 'boolean',
|
|
450
|
+
description: 'Generate Merkle proof for record',
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
},
|
|
454
|
+
handler: async (args) => {
|
|
455
|
+
const chainId = args['chainId'];
|
|
456
|
+
const recordId = args['recordId'];
|
|
457
|
+
const generateProof = args['generateProof'];
|
|
458
|
+
const engine = await getEngine();
|
|
459
|
+
await engine.loadChain(chainId);
|
|
460
|
+
const result = engine.verifyChainIntegrity(chainId);
|
|
461
|
+
const response = {
|
|
462
|
+
success: true,
|
|
463
|
+
chainId,
|
|
464
|
+
verification: {
|
|
465
|
+
valid: result.valid,
|
|
466
|
+
verifiedRecords: result.verifiedRecords,
|
|
467
|
+
errors: result.errors,
|
|
468
|
+
timestamp: new Date().toISOString(),
|
|
469
|
+
},
|
|
470
|
+
};
|
|
471
|
+
if (recordId && generateProof) {
|
|
472
|
+
const proof = engine.generateMerkleProof(chainId, recordId);
|
|
473
|
+
if (proof) {
|
|
474
|
+
const proofValid = engine.verifyMerkleProof(proof);
|
|
475
|
+
response['merkleProof'] = {
|
|
476
|
+
recordId: proof.recordId,
|
|
477
|
+
leafHash: proof.leafHash,
|
|
478
|
+
rootHash: proof.rootHash,
|
|
479
|
+
siblingCount: proof.siblings.length,
|
|
480
|
+
verified: proofValid,
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return JSON.stringify(response);
|
|
485
|
+
},
|
|
486
|
+
},
|
|
487
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
488
|
+
// Legal Exhibit Generation
|
|
489
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
490
|
+
{
|
|
491
|
+
name: 'ExhibitGenerate',
|
|
492
|
+
description: `Generate legal exhibits with cryptographic integrity proofs.
|
|
493
|
+
|
|
494
|
+
Creates court-ready exhibits including:
|
|
495
|
+
- Exhibit metadata and numbering
|
|
496
|
+
- Full chain of custody documentation
|
|
497
|
+
- Merkle proofs for each piece of evidence
|
|
498
|
+
- Digital signatures and timestamps
|
|
499
|
+
- Integrity verification summary
|
|
500
|
+
|
|
501
|
+
Output formats: JSON, HTML (for printing)`,
|
|
502
|
+
parameters: {
|
|
503
|
+
type: 'object',
|
|
504
|
+
required: ['chainId', 'exhibitNumber', 'title'],
|
|
505
|
+
properties: {
|
|
506
|
+
chainId: {
|
|
507
|
+
type: 'string',
|
|
508
|
+
description: 'Evidence chain to generate exhibit from',
|
|
509
|
+
},
|
|
510
|
+
exhibitNumber: {
|
|
511
|
+
type: 'string',
|
|
512
|
+
description: 'Exhibit number (e.g., "A", "1", "Plaintiff-1")',
|
|
513
|
+
},
|
|
514
|
+
title: {
|
|
515
|
+
type: 'string',
|
|
516
|
+
description: 'Exhibit title',
|
|
517
|
+
},
|
|
518
|
+
description: {
|
|
519
|
+
type: 'string',
|
|
520
|
+
description: 'Exhibit description',
|
|
521
|
+
},
|
|
522
|
+
recordIds: {
|
|
523
|
+
type: 'array',
|
|
524
|
+
items: { type: 'string' },
|
|
525
|
+
description: 'Specific record IDs to include (default: all)',
|
|
526
|
+
},
|
|
527
|
+
format: {
|
|
528
|
+
type: 'string',
|
|
529
|
+
enum: ['json', 'html'],
|
|
530
|
+
description: 'Output format',
|
|
531
|
+
},
|
|
532
|
+
outputPath: {
|
|
533
|
+
type: 'string',
|
|
534
|
+
description: 'Path to write exhibit file',
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
},
|
|
538
|
+
handler: async (args) => {
|
|
539
|
+
const chainId = args['chainId'];
|
|
540
|
+
const exhibitNumber = args['exhibitNumber'];
|
|
541
|
+
const title = args['title'];
|
|
542
|
+
const description = args['description'] || '';
|
|
543
|
+
const recordIds = args['recordIds'];
|
|
544
|
+
const format = args['format'] || 'json';
|
|
545
|
+
const outputPath = args['outputPath'];
|
|
546
|
+
const engine = await getEngine();
|
|
547
|
+
await engine.loadChain(chainId);
|
|
548
|
+
const exhibit = await engine.generateExhibit({
|
|
549
|
+
chainId,
|
|
550
|
+
exhibitNumber,
|
|
551
|
+
title,
|
|
552
|
+
description,
|
|
553
|
+
recordIds,
|
|
554
|
+
format,
|
|
555
|
+
});
|
|
556
|
+
let output;
|
|
557
|
+
if (format === 'html') {
|
|
558
|
+
output = generateHtmlExhibit(exhibit);
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
output = JSON.stringify(exhibit, null, 2);
|
|
562
|
+
}
|
|
563
|
+
if (outputPath) {
|
|
564
|
+
await fs.writeFile(outputPath, output);
|
|
565
|
+
}
|
|
566
|
+
return JSON.stringify({
|
|
567
|
+
success: true,
|
|
568
|
+
exhibit: {
|
|
569
|
+
exhibitNumber: exhibit.exhibitNumber,
|
|
570
|
+
title: exhibit.title,
|
|
571
|
+
chainId: exhibit.chainId,
|
|
572
|
+
recordCount: exhibit.recordIds.length,
|
|
573
|
+
merkleRoot: exhibit.integrityProof.rootHash,
|
|
574
|
+
custodyEntries: exhibit.chainOfCustody.length,
|
|
575
|
+
generated: exhibit.generated,
|
|
576
|
+
format,
|
|
577
|
+
outputPath: outputPath || null,
|
|
578
|
+
},
|
|
579
|
+
message: `Legal exhibit ${exhibitNumber} generated with ${exhibit.recordIds.length} evidence records`,
|
|
580
|
+
});
|
|
581
|
+
},
|
|
582
|
+
},
|
|
583
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
584
|
+
// Fraud Documentation
|
|
585
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
586
|
+
{
|
|
587
|
+
name: 'FraudDocument',
|
|
588
|
+
description: `Document fraud indicators with legal citations.
|
|
589
|
+
|
|
590
|
+
Pre-defined fraud types:
|
|
591
|
+
- DATA_MANIPULATION: Unauthorized modification of user data
|
|
592
|
+
- PRIVACY_VIOLATION: Collection/use beyond stated purposes
|
|
593
|
+
- TERMS_VIOLATION: Provider breach of ToS
|
|
594
|
+
- MISREPRESENTATION: False/misleading statements
|
|
595
|
+
- ANTICOMPETITIVE: Anti-competitive practices
|
|
596
|
+
- SECURITY_NEGLIGENCE: Failure to protect data
|
|
597
|
+
- BILLING_FRAUD: Unauthorized charges
|
|
598
|
+
- CONSENT_VIOLATION: Action without consent
|
|
599
|
+
- DISCRIMINATION: Algorithmic discrimination
|
|
600
|
+
- CENSORSHIP: Improper content moderation
|
|
601
|
+
|
|
602
|
+
Auto-suggests relevant legal citations (FTC Act, CCPA, CFAA, etc.)`,
|
|
603
|
+
parameters: {
|
|
604
|
+
type: 'object',
|
|
605
|
+
required: ['chainId', 'fraudType', 'description'],
|
|
606
|
+
properties: {
|
|
607
|
+
chainId: {
|
|
608
|
+
type: 'string',
|
|
609
|
+
description: 'Evidence chain to add fraud indicator to',
|
|
610
|
+
},
|
|
611
|
+
fraudType: {
|
|
612
|
+
type: 'string',
|
|
613
|
+
enum: Object.keys(FRAUD_TYPES),
|
|
614
|
+
description: 'Type of fraud',
|
|
615
|
+
},
|
|
616
|
+
description: {
|
|
617
|
+
type: 'string',
|
|
618
|
+
description: 'Detailed description of the fraud',
|
|
619
|
+
},
|
|
620
|
+
severity: {
|
|
621
|
+
type: 'string',
|
|
622
|
+
enum: ['low', 'medium', 'high', 'critical'],
|
|
623
|
+
description: 'Severity level',
|
|
624
|
+
},
|
|
625
|
+
evidenceRecordIds: {
|
|
626
|
+
type: 'array',
|
|
627
|
+
items: { type: 'string' },
|
|
628
|
+
description: 'IDs of evidence records supporting this fraud indicator',
|
|
629
|
+
},
|
|
630
|
+
legalCitation: {
|
|
631
|
+
type: 'string',
|
|
632
|
+
description: 'Specific legal citation (auto-suggested if not provided)',
|
|
633
|
+
},
|
|
634
|
+
},
|
|
635
|
+
},
|
|
636
|
+
handler: async (args) => {
|
|
637
|
+
const chainId = args['chainId'];
|
|
638
|
+
const fraudType = args['fraudType'];
|
|
639
|
+
const description = args['description'];
|
|
640
|
+
const severity = args['severity'] || 'medium';
|
|
641
|
+
const evidenceRecordIds = args['evidenceRecordIds'] || [];
|
|
642
|
+
let legalCitation = args['legalCitation'];
|
|
643
|
+
// Auto-suggest legal citation based on fraud type
|
|
644
|
+
if (!legalCitation) {
|
|
645
|
+
const citationMap = {
|
|
646
|
+
DATA_MANIPULATION: LEGAL_CITATIONS.COMPUTER_FRAUD,
|
|
647
|
+
PRIVACY_VIOLATION: LEGAL_CITATIONS.CCPA,
|
|
648
|
+
TERMS_VIOLATION: LEGAL_CITATIONS.FTC_ACT_SECTION_5,
|
|
649
|
+
MISREPRESENTATION: LEGAL_CITATIONS.LANHAM_ACT,
|
|
650
|
+
ANTICOMPETITIVE: LEGAL_CITATIONS.SHERMAN_ACT,
|
|
651
|
+
SECURITY_NEGLIGENCE: LEGAL_CITATIONS.FTC_ACT_SECTION_5,
|
|
652
|
+
BILLING_FRAUD: LEGAL_CITATIONS.WIRE_FRAUD,
|
|
653
|
+
CONSENT_VIOLATION: LEGAL_CITATIONS.ECPA,
|
|
654
|
+
DISCRIMINATION: LEGAL_CITATIONS.FTC_ACT_SECTION_5,
|
|
655
|
+
CENSORSHIP: LEGAL_CITATIONS.FTC_ACT_SECTION_5,
|
|
656
|
+
};
|
|
657
|
+
legalCitation = citationMap[fraudType] || LEGAL_CITATIONS.FTC_ACT_SECTION_5;
|
|
658
|
+
}
|
|
659
|
+
const indicator = documentFraudIndicator({
|
|
660
|
+
type: fraudType,
|
|
661
|
+
description,
|
|
662
|
+
severity,
|
|
663
|
+
evidenceRefs: evidenceRecordIds,
|
|
664
|
+
legalCitation,
|
|
665
|
+
});
|
|
666
|
+
// Store as evidence
|
|
667
|
+
const engine = await getEngine();
|
|
668
|
+
await engine.loadChain(chainId);
|
|
669
|
+
const record = await engine.collectEvidence({
|
|
670
|
+
chainId,
|
|
671
|
+
target: {
|
|
672
|
+
name: 'Fraud Indicator',
|
|
673
|
+
type: 'system',
|
|
674
|
+
identifiers: { type: fraudType },
|
|
675
|
+
},
|
|
676
|
+
evidenceType: 'fraud_indicator',
|
|
677
|
+
content: JSON.stringify(indicator),
|
|
678
|
+
mimeType: 'application/json',
|
|
679
|
+
captureMethod: 'fraud_documentation',
|
|
680
|
+
legalBasis: legalCitation,
|
|
681
|
+
jurisdiction: 'Federal',
|
|
682
|
+
collector: 'erosolar-cli',
|
|
683
|
+
notes: `Fraud type: ${FRAUD_TYPES[fraudType]}`,
|
|
684
|
+
});
|
|
685
|
+
return JSON.stringify({
|
|
686
|
+
success: true,
|
|
687
|
+
fraudIndicator: {
|
|
688
|
+
recordId: record.id,
|
|
689
|
+
type: fraudType,
|
|
690
|
+
typeDescription: FRAUD_TYPES[fraudType],
|
|
691
|
+
severity,
|
|
692
|
+
legalCitation,
|
|
693
|
+
evidenceRefs: evidenceRecordIds,
|
|
694
|
+
contentHash: record.contentHash,
|
|
695
|
+
},
|
|
696
|
+
suggestedCitations: Object.entries(LEGAL_CITATIONS)
|
|
697
|
+
.filter(([key]) => key.includes('FTC') || key.includes('FRAUD') ||
|
|
698
|
+
key.includes('PRIVACY') || key.includes('COMPUTER'))
|
|
699
|
+
.map(([key, value]) => ({ code: key, citation: value })),
|
|
700
|
+
});
|
|
701
|
+
},
|
|
702
|
+
},
|
|
703
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
704
|
+
// Cryptographic Hashing
|
|
705
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
706
|
+
{
|
|
707
|
+
name: 'CryptoHash',
|
|
708
|
+
description: `Compute cryptographic hashes for integrity verification.
|
|
709
|
+
|
|
710
|
+
Algorithms: md5, sha1, sha256, sha384, sha512, sha3-256, sha3-512
|
|
711
|
+
|
|
712
|
+
Operations:
|
|
713
|
+
- hash: Compute hash of content
|
|
714
|
+
- verify: Verify content against expected hash
|
|
715
|
+
- file: Hash a file
|
|
716
|
+
- compare: Compare two hashes`,
|
|
717
|
+
parameters: {
|
|
718
|
+
type: 'object',
|
|
719
|
+
required: ['operation'],
|
|
720
|
+
properties: {
|
|
721
|
+
operation: {
|
|
722
|
+
type: 'string',
|
|
723
|
+
enum: ['hash', 'verify', 'file', 'compare'],
|
|
724
|
+
description: 'Operation to perform',
|
|
725
|
+
},
|
|
726
|
+
content: {
|
|
727
|
+
type: 'string',
|
|
728
|
+
description: 'Content to hash (for hash/verify)',
|
|
729
|
+
},
|
|
730
|
+
filePath: {
|
|
731
|
+
type: 'string',
|
|
732
|
+
description: 'File path (for file operation)',
|
|
733
|
+
},
|
|
734
|
+
algorithm: {
|
|
735
|
+
type: 'string',
|
|
736
|
+
enum: ['md5', 'sha1', 'sha256', 'sha384', 'sha512', 'sha3-256', 'sha3-512'],
|
|
737
|
+
description: 'Hash algorithm (default: sha256)',
|
|
738
|
+
},
|
|
739
|
+
expectedHash: {
|
|
740
|
+
type: 'string',
|
|
741
|
+
description: 'Expected hash value (for verify)',
|
|
742
|
+
},
|
|
743
|
+
hash1: {
|
|
744
|
+
type: 'string',
|
|
745
|
+
description: 'First hash (for compare)',
|
|
746
|
+
},
|
|
747
|
+
hash2: {
|
|
748
|
+
type: 'string',
|
|
749
|
+
description: 'Second hash (for compare)',
|
|
750
|
+
},
|
|
751
|
+
},
|
|
752
|
+
},
|
|
753
|
+
handler: async (args) => {
|
|
754
|
+
const operation = args['operation'];
|
|
755
|
+
const content = args['content'];
|
|
756
|
+
const filePath = args['filePath'];
|
|
757
|
+
const algorithm = args['algorithm'] || 'sha256';
|
|
758
|
+
const expectedHash = args['expectedHash'];
|
|
759
|
+
const hash1 = args['hash1'];
|
|
760
|
+
const hash2 = args['hash2'];
|
|
761
|
+
switch (operation) {
|
|
762
|
+
case 'hash': {
|
|
763
|
+
if (!content) {
|
|
764
|
+
return JSON.stringify({ success: false, error: 'Content required' });
|
|
765
|
+
}
|
|
766
|
+
const hash = hashString(content, algorithm);
|
|
767
|
+
return JSON.stringify({
|
|
768
|
+
success: true,
|
|
769
|
+
operation: 'hash',
|
|
770
|
+
algorithm,
|
|
771
|
+
hash,
|
|
772
|
+
inputLength: content.length,
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
case 'verify': {
|
|
776
|
+
if (!content || !expectedHash) {
|
|
777
|
+
return JSON.stringify({ success: false, error: 'Content and expectedHash required' });
|
|
778
|
+
}
|
|
779
|
+
const actualHash = hashString(content, algorithm);
|
|
780
|
+
const match = actualHash === expectedHash.toLowerCase();
|
|
781
|
+
return JSON.stringify({
|
|
782
|
+
success: true,
|
|
783
|
+
operation: 'verify',
|
|
784
|
+
algorithm,
|
|
785
|
+
match,
|
|
786
|
+
actualHash,
|
|
787
|
+
expectedHash: expectedHash.toLowerCase(),
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
case 'file': {
|
|
791
|
+
if (!filePath) {
|
|
792
|
+
return JSON.stringify({ success: false, error: 'filePath required' });
|
|
793
|
+
}
|
|
794
|
+
try {
|
|
795
|
+
const hash = hashFile(filePath, algorithm);
|
|
796
|
+
const stats = await fs.stat(filePath);
|
|
797
|
+
return JSON.stringify({
|
|
798
|
+
success: true,
|
|
799
|
+
operation: 'file',
|
|
800
|
+
algorithm,
|
|
801
|
+
hash,
|
|
802
|
+
filePath,
|
|
803
|
+
fileSize: stats.size,
|
|
804
|
+
modified: stats.mtime.toISOString(),
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
catch (error) {
|
|
808
|
+
return JSON.stringify({
|
|
809
|
+
success: false,
|
|
810
|
+
error: `Failed to hash file: ${error instanceof Error ? error.message : String(error)}`,
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
case 'compare': {
|
|
815
|
+
if (!hash1 || !hash2) {
|
|
816
|
+
return JSON.stringify({ success: false, error: 'hash1 and hash2 required' });
|
|
817
|
+
}
|
|
818
|
+
const match = hash1.toLowerCase() === hash2.toLowerCase();
|
|
819
|
+
return JSON.stringify({
|
|
820
|
+
success: true,
|
|
821
|
+
operation: 'compare',
|
|
822
|
+
match,
|
|
823
|
+
hash1: hash1.toLowerCase(),
|
|
824
|
+
hash2: hash2.toLowerCase(),
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
default:
|
|
828
|
+
return JSON.stringify({ success: false, error: `Unknown operation: ${operation}` });
|
|
829
|
+
}
|
|
830
|
+
},
|
|
831
|
+
},
|
|
832
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
833
|
+
// Digital Signatures
|
|
834
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
835
|
+
{
|
|
836
|
+
name: 'CryptoSign',
|
|
837
|
+
description: `Digital signature operations for evidence authentication.
|
|
838
|
+
|
|
839
|
+
Operations:
|
|
840
|
+
- sign: Sign content with private key
|
|
841
|
+
- verify: Verify signature with public key
|
|
842
|
+
- generateKeys: Generate new RSA key pair
|
|
843
|
+
- exportPublicKey: Export public key for verification`,
|
|
844
|
+
parameters: {
|
|
845
|
+
type: 'object',
|
|
846
|
+
required: ['operation'],
|
|
847
|
+
properties: {
|
|
848
|
+
operation: {
|
|
849
|
+
type: 'string',
|
|
850
|
+
enum: ['sign', 'verify', 'generateKeys', 'exportPublicKey'],
|
|
851
|
+
description: 'Operation to perform',
|
|
852
|
+
},
|
|
853
|
+
content: {
|
|
854
|
+
type: 'string',
|
|
855
|
+
description: 'Content to sign or verify',
|
|
856
|
+
},
|
|
857
|
+
signature: {
|
|
858
|
+
type: 'string',
|
|
859
|
+
description: 'Signature to verify',
|
|
860
|
+
},
|
|
861
|
+
publicKey: {
|
|
862
|
+
type: 'string',
|
|
863
|
+
description: 'Public key for verification (uses stored key if not provided)',
|
|
864
|
+
},
|
|
865
|
+
},
|
|
866
|
+
},
|
|
867
|
+
handler: async (args) => {
|
|
868
|
+
const operation = args['operation'];
|
|
869
|
+
const content = args['content'];
|
|
870
|
+
const signature = args['signature'];
|
|
871
|
+
const publicKeyArg = args['publicKey'];
|
|
872
|
+
const keys = await loadKeys();
|
|
873
|
+
switch (operation) {
|
|
874
|
+
case 'sign': {
|
|
875
|
+
if (!content) {
|
|
876
|
+
return JSON.stringify({ success: false, error: 'Content required' });
|
|
877
|
+
}
|
|
878
|
+
const privateKey = crypto.createPrivateKey(keys.privateKey);
|
|
879
|
+
const sign = crypto.createSign('SHA256');
|
|
880
|
+
sign.update(content);
|
|
881
|
+
const sig = sign.sign(privateKey, 'hex');
|
|
882
|
+
return JSON.stringify({
|
|
883
|
+
success: true,
|
|
884
|
+
operation: 'sign',
|
|
885
|
+
signature: sig,
|
|
886
|
+
algorithm: 'SHA256withRSA',
|
|
887
|
+
contentHash: hashString(content),
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
case 'verify': {
|
|
891
|
+
if (!content || !signature) {
|
|
892
|
+
return JSON.stringify({ success: false, error: 'Content and signature required' });
|
|
893
|
+
}
|
|
894
|
+
const publicKey = crypto.createPublicKey(publicKeyArg || keys.publicKey);
|
|
895
|
+
const verify = crypto.createVerify('SHA256');
|
|
896
|
+
verify.update(content);
|
|
897
|
+
const valid = verify.verify(publicKey, signature, 'hex');
|
|
898
|
+
return JSON.stringify({
|
|
899
|
+
success: true,
|
|
900
|
+
operation: 'verify',
|
|
901
|
+
valid,
|
|
902
|
+
algorithm: 'SHA256withRSA',
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
case 'generateKeys': {
|
|
906
|
+
const newKeys = IntegrityVerificationEngine.generateKeyPair();
|
|
907
|
+
// Don't overwrite existing keys, just return new ones
|
|
908
|
+
return JSON.stringify({
|
|
909
|
+
success: true,
|
|
910
|
+
operation: 'generateKeys',
|
|
911
|
+
publicKey: newKeys.publicKey,
|
|
912
|
+
privateKey: '[GENERATED - store securely]',
|
|
913
|
+
algorithm: 'RSA-4096',
|
|
914
|
+
note: 'Keys generated but not stored. Use exportPublicKey to get stored public key.',
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
case 'exportPublicKey': {
|
|
918
|
+
return JSON.stringify({
|
|
919
|
+
success: true,
|
|
920
|
+
operation: 'exportPublicKey',
|
|
921
|
+
publicKey: keys.publicKey,
|
|
922
|
+
algorithm: 'RSA-4096',
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
default:
|
|
926
|
+
return JSON.stringify({ success: false, error: `Unknown operation: ${operation}` });
|
|
927
|
+
}
|
|
928
|
+
},
|
|
929
|
+
},
|
|
930
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
931
|
+
// Tech Company Info
|
|
932
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
933
|
+
{
|
|
934
|
+
name: 'TechCompanyInfo',
|
|
935
|
+
description: `Get corporate and product information for major tech companies.
|
|
936
|
+
|
|
937
|
+
Provides:
|
|
938
|
+
- Legal entity names
|
|
939
|
+
- Headquarters addresses
|
|
940
|
+
- SEC filing information (CIK)
|
|
941
|
+
- Product/service listings with data types collected
|
|
942
|
+
- Registered agent information
|
|
943
|
+
|
|
944
|
+
Useful for correctly naming defendants in legal filings.`,
|
|
945
|
+
parameters: {
|
|
946
|
+
type: 'object',
|
|
947
|
+
required: ['company'],
|
|
948
|
+
properties: {
|
|
949
|
+
company: {
|
|
950
|
+
type: 'string',
|
|
951
|
+
enum: ['apple', 'google', 'meta', 'microsoft', 'amazon', 'all'],
|
|
952
|
+
description: 'Company to get info for',
|
|
953
|
+
},
|
|
954
|
+
includeProducts: {
|
|
955
|
+
type: 'boolean',
|
|
956
|
+
description: 'Include product details',
|
|
957
|
+
},
|
|
958
|
+
},
|
|
959
|
+
},
|
|
960
|
+
handler: async (args) => {
|
|
961
|
+
const company = args['company'];
|
|
962
|
+
const includeProducts = args['includeProducts'];
|
|
963
|
+
if (company === 'all') {
|
|
964
|
+
const summary = Object.entries(TECH_COMPANY_TARGETS).map(([key, info]) => ({
|
|
965
|
+
id: key,
|
|
966
|
+
company: info.company,
|
|
967
|
+
legalName: info.corporateInfo.legalName,
|
|
968
|
+
headquarters: info.corporateInfo.headquarters,
|
|
969
|
+
productCount: info.products.length,
|
|
970
|
+
}));
|
|
971
|
+
return JSON.stringify({ success: true, companies: summary });
|
|
972
|
+
}
|
|
973
|
+
const info = TECH_COMPANY_TARGETS[company.toLowerCase()];
|
|
974
|
+
if (!info) {
|
|
975
|
+
return JSON.stringify({
|
|
976
|
+
success: false,
|
|
977
|
+
error: `Unknown company: ${company}`,
|
|
978
|
+
available: Object.keys(TECH_COMPANY_TARGETS),
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
const response = {
|
|
982
|
+
success: true,
|
|
983
|
+
company: info.company,
|
|
984
|
+
corporateInfo: info.corporateInfo,
|
|
985
|
+
};
|
|
986
|
+
if (includeProducts) {
|
|
987
|
+
response['products'] = info.products;
|
|
988
|
+
}
|
|
989
|
+
else {
|
|
990
|
+
response['productCount'] = info.products.length;
|
|
991
|
+
response['productNames'] = info.products.map(p => p.name);
|
|
992
|
+
}
|
|
993
|
+
return JSON.stringify(response);
|
|
994
|
+
},
|
|
995
|
+
},
|
|
996
|
+
],
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1000
|
+
// HELPER FUNCTIONS
|
|
1001
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1002
|
+
async function fetchUrl(url) {
|
|
1003
|
+
return new Promise((resolve, reject) => {
|
|
1004
|
+
const protocol = url.startsWith('https') ? https : http;
|
|
1005
|
+
const request = protocol.get(url, { timeout: 30000 }, (response) => {
|
|
1006
|
+
if (response.statusCode && response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
|
1007
|
+
fetchUrl(response.headers.location).then(resolve).catch(reject);
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
let data = '';
|
|
1011
|
+
response.on('data', chunk => data += chunk);
|
|
1012
|
+
response.on('end', () => resolve(data));
|
|
1013
|
+
response.on('error', reject);
|
|
1014
|
+
});
|
|
1015
|
+
request.on('error', reject);
|
|
1016
|
+
request.on('timeout', () => {
|
|
1017
|
+
request.destroy();
|
|
1018
|
+
reject(new Error('Request timeout'));
|
|
1019
|
+
});
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
function getMimeType(filePath) {
|
|
1023
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
1024
|
+
const mimeTypes = {
|
|
1025
|
+
'.txt': 'text/plain',
|
|
1026
|
+
'.html': 'text/html',
|
|
1027
|
+
'.htm': 'text/html',
|
|
1028
|
+
'.json': 'application/json',
|
|
1029
|
+
'.xml': 'application/xml',
|
|
1030
|
+
'.pdf': 'application/pdf',
|
|
1031
|
+
'.png': 'image/png',
|
|
1032
|
+
'.jpg': 'image/jpeg',
|
|
1033
|
+
'.jpeg': 'image/jpeg',
|
|
1034
|
+
'.gif': 'image/gif',
|
|
1035
|
+
'.webp': 'image/webp',
|
|
1036
|
+
'.svg': 'image/svg+xml',
|
|
1037
|
+
'.mp4': 'video/mp4',
|
|
1038
|
+
'.webm': 'video/webm',
|
|
1039
|
+
'.mp3': 'audio/mpeg',
|
|
1040
|
+
'.wav': 'audio/wav',
|
|
1041
|
+
'.zip': 'application/zip',
|
|
1042
|
+
'.gz': 'application/gzip',
|
|
1043
|
+
'.tar': 'application/x-tar',
|
|
1044
|
+
'.doc': 'application/msword',
|
|
1045
|
+
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
1046
|
+
'.xls': 'application/vnd.ms-excel',
|
|
1047
|
+
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
1048
|
+
'.ppt': 'application/vnd.ms-powerpoint',
|
|
1049
|
+
'.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
1050
|
+
};
|
|
1051
|
+
return mimeTypes[ext] || 'application/octet-stream';
|
|
1052
|
+
}
|
|
1053
|
+
function generateHtmlExhibit(exhibit) {
|
|
1054
|
+
return `<!DOCTYPE html>
|
|
1055
|
+
<html lang="en">
|
|
1056
|
+
<head>
|
|
1057
|
+
<meta charset="UTF-8">
|
|
1058
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1059
|
+
<title>Exhibit ${exhibit.exhibitNumber} - ${exhibit.title}</title>
|
|
1060
|
+
<style>
|
|
1061
|
+
body { font-family: 'Times New Roman', serif; max-width: 8.5in; margin: 0 auto; padding: 1in; }
|
|
1062
|
+
h1 { text-align: center; border-bottom: 2px solid black; padding-bottom: 10px; }
|
|
1063
|
+
h2 { border-bottom: 1px solid #666; padding-bottom: 5px; margin-top: 2em; }
|
|
1064
|
+
.exhibit-header { text-align: center; margin-bottom: 2em; }
|
|
1065
|
+
.exhibit-number { font-size: 1.5em; font-weight: bold; }
|
|
1066
|
+
.metadata { background: #f5f5f5; padding: 15px; margin: 1em 0; border: 1px solid #ddd; }
|
|
1067
|
+
.metadata dt { font-weight: bold; }
|
|
1068
|
+
.metadata dd { margin-left: 20px; margin-bottom: 10px; }
|
|
1069
|
+
.hash { font-family: monospace; background: #eee; padding: 2px 5px; word-break: break-all; }
|
|
1070
|
+
.custody-table { width: 100%; border-collapse: collapse; margin: 1em 0; }
|
|
1071
|
+
.custody-table th, .custody-table td { border: 1px solid #333; padding: 8px; text-align: left; }
|
|
1072
|
+
.custody-table th { background: #f0f0f0; }
|
|
1073
|
+
.proof-section { margin: 1em 0; padding: 10px; background: #f9f9f9; border-left: 3px solid #333; }
|
|
1074
|
+
.certification { margin-top: 3em; border-top: 2px solid black; padding-top: 1em; }
|
|
1075
|
+
.signature-line { margin-top: 3em; border-top: 1px solid black; width: 50%; }
|
|
1076
|
+
@media print { body { padding: 0; } }
|
|
1077
|
+
</style>
|
|
1078
|
+
</head>
|
|
1079
|
+
<body>
|
|
1080
|
+
<div class="exhibit-header">
|
|
1081
|
+
<div class="exhibit-number">EXHIBIT ${exhibit.exhibitNumber}</div>
|
|
1082
|
+
<h1>${exhibit.title}</h1>
|
|
1083
|
+
<p>${exhibit.description}</p>
|
|
1084
|
+
</div>
|
|
1085
|
+
|
|
1086
|
+
<h2>Evidence Summary</h2>
|
|
1087
|
+
<dl class="metadata">
|
|
1088
|
+
<dt>Chain ID:</dt>
|
|
1089
|
+
<dd class="hash">${exhibit.chainId}</dd>
|
|
1090
|
+
<dt>Record Count:</dt>
|
|
1091
|
+
<dd>${exhibit.recordIds.length}</dd>
|
|
1092
|
+
<dt>Merkle Root Hash:</dt>
|
|
1093
|
+
<dd class="hash">${exhibit.integrityProof.rootHash}</dd>
|
|
1094
|
+
<dt>Hash Algorithm:</dt>
|
|
1095
|
+
<dd>${exhibit.integrityProof.algorithm.toUpperCase()}</dd>
|
|
1096
|
+
<dt>Generated:</dt>
|
|
1097
|
+
<dd>${new Date(exhibit.generated).toLocaleString()}</dd>
|
|
1098
|
+
</dl>
|
|
1099
|
+
|
|
1100
|
+
<h2>Chain of Custody</h2>
|
|
1101
|
+
<table class="custody-table">
|
|
1102
|
+
<thead>
|
|
1103
|
+
<tr>
|
|
1104
|
+
<th>Timestamp</th>
|
|
1105
|
+
<th>Action</th>
|
|
1106
|
+
<th>Actor</th>
|
|
1107
|
+
<th>Description</th>
|
|
1108
|
+
<th>Hash</th>
|
|
1109
|
+
</tr>
|
|
1110
|
+
</thead>
|
|
1111
|
+
<tbody>
|
|
1112
|
+
${exhibit.chainOfCustody.map(entry => `
|
|
1113
|
+
<tr>
|
|
1114
|
+
<td>${new Date(entry.timestamp).toLocaleString()}</td>
|
|
1115
|
+
<td>${entry.action.toUpperCase()}</td>
|
|
1116
|
+
<td>${entry.actor}</td>
|
|
1117
|
+
<td>${entry.description}</td>
|
|
1118
|
+
<td class="hash">${entry.hash.slice(0, 16)}...</td>
|
|
1119
|
+
</tr>
|
|
1120
|
+
`).join('')}
|
|
1121
|
+
</tbody>
|
|
1122
|
+
</table>
|
|
1123
|
+
|
|
1124
|
+
<h2>Cryptographic Integrity Proofs</h2>
|
|
1125
|
+
<div class="proof-section">
|
|
1126
|
+
<p><strong>Merkle Tree Root:</strong> <span class="hash">${exhibit.integrityProof.rootHash}</span></p>
|
|
1127
|
+
<p>This exhibit contains ${exhibit.integrityProof.merkleProofs.length} Merkle proofs that cryptographically verify the integrity and authenticity of each evidence record.</p>
|
|
1128
|
+
<p><strong>Verification Method:</strong> Each record's content hash is a leaf in the Merkle tree. The sibling hashes at each level can be combined to reconstruct the root hash, proving the record has not been tampered with.</p>
|
|
1129
|
+
</div>
|
|
1130
|
+
|
|
1131
|
+
${exhibit.integrityProof.signatures.length > 0 ? `
|
|
1132
|
+
<h2>Digital Signatures</h2>
|
|
1133
|
+
<table class="custody-table">
|
|
1134
|
+
<thead>
|
|
1135
|
+
<tr>
|
|
1136
|
+
<th>Signer</th>
|
|
1137
|
+
<th>Timestamp</th>
|
|
1138
|
+
<th>Algorithm</th>
|
|
1139
|
+
<th>Signature (truncated)</th>
|
|
1140
|
+
</tr>
|
|
1141
|
+
</thead>
|
|
1142
|
+
<tbody>
|
|
1143
|
+
${exhibit.integrityProof.signatures.map(sig => `
|
|
1144
|
+
<tr>
|
|
1145
|
+
<td>${sig.signer}</td>
|
|
1146
|
+
<td>${new Date(sig.timestamp).toLocaleString()}</td>
|
|
1147
|
+
<td>${sig.algorithm}</td>
|
|
1148
|
+
<td class="hash">${sig.signature.slice(0, 32)}...</td>
|
|
1149
|
+
</tr>
|
|
1150
|
+
`).join('')}
|
|
1151
|
+
</tbody>
|
|
1152
|
+
</table>
|
|
1153
|
+
` : ''}
|
|
1154
|
+
|
|
1155
|
+
<div class="certification">
|
|
1156
|
+
<h2>Certification</h2>
|
|
1157
|
+
<p>I hereby certify under penalty of perjury that the evidence contained in this exhibit is true and accurate to the best of my knowledge, and that the cryptographic hashes and integrity proofs presented herein accurately represent the state of the evidence at the time of collection.</p>
|
|
1158
|
+
<div class="signature-line">
|
|
1159
|
+
<p>Signature: _______________________</p>
|
|
1160
|
+
<p>Date: _______________________</p>
|
|
1161
|
+
<p>Printed Name: _______________________</p>
|
|
1162
|
+
</div>
|
|
1163
|
+
</div>
|
|
1164
|
+
|
|
1165
|
+
<footer style="margin-top: 3em; text-align: center; color: #666; font-size: 0.9em;">
|
|
1166
|
+
<p>Generated by erosolar-cli integrity verification system</p>
|
|
1167
|
+
<p>Exhibit ID: ${exhibit.exhibitNumber} | Chain: ${exhibit.chainId.slice(0, 8)}...</p>
|
|
1168
|
+
</footer>
|
|
1169
|
+
</body>
|
|
1170
|
+
</html>`;
|
|
1171
|
+
}
|
|
1172
|
+
//# sourceMappingURL=integrityTools.js.map
|