@jishankai/solid-cli 1.0.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/LICENSE +21 -0
- package/README.md +276 -0
- package/config/default.json +79 -0
- package/package.json +60 -0
- package/src/Orchestrator.js +482 -0
- package/src/agents/BaseAgent.js +35 -0
- package/src/agents/BlockchainAgent.js +453 -0
- package/src/agents/DeFiSecurityAgent.js +257 -0
- package/src/agents/NetworkAgent.js +341 -0
- package/src/agents/PermissionAgent.js +192 -0
- package/src/agents/PersistenceAgent.js +361 -0
- package/src/agents/ProcessAgent.js +572 -0
- package/src/agents/ResourceAgent.js +217 -0
- package/src/agents/SystemAgent.js +173 -0
- package/src/config/ConfigManager.js +446 -0
- package/src/index.js +629 -0
- package/src/llm/LLMAnalyzer.js +705 -0
- package/src/logging/Logger.js +352 -0
- package/src/report/ReportManager.js +445 -0
- package/src/report/generators/MarkdownGenerator.js +173 -0
- package/src/report/generators/PDFGenerator.js +616 -0
- package/src/report/templates/report.hbs +465 -0
- package/src/report/utils/formatter.js +426 -0
- package/src/report/utils/sanitizer.js +275 -0
- package/src/utils/commander.js +42 -0
- package/src/utils/signature.js +121 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
import { BaseAgent } from './BaseAgent.js';
|
|
2
|
+
import { executeShellCommand } from '../utils/commander.js';
|
|
3
|
+
import fs from 'fs/promises';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* BlockchainAgent - Analyzes blockchain, wallet, and DeFi security threats
|
|
8
|
+
*/
|
|
9
|
+
export class BlockchainAgent extends BaseAgent {
|
|
10
|
+
constructor() {
|
|
11
|
+
super('BlockchainAgent');
|
|
12
|
+
|
|
13
|
+
// Known wallet applications and processes
|
|
14
|
+
this.knownWallets = new Set([
|
|
15
|
+
'MetaMask', 'phantom', 'solana', 'trust-wallet', 'coinbase-wallet',
|
|
16
|
+
'ledger-live', 'trezor-bridge', 'exodus', 'atomic-wallet', 'myetherwallet',
|
|
17
|
+
'metamask', 'phantom-wallet', 'brave-wallet', 'rainbow', 'argent',
|
|
18
|
+
'gnosis-safe', 'imtoken', 'tokenpocket', 'mathwallet', 'safepal'
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
// Known DeFi platforms and protocols
|
|
22
|
+
this.knownDeFi = new Set([
|
|
23
|
+
'uniswap', 'sushiswap', 'pancakeswap', 'curve', 'balancer',
|
|
24
|
+
'compound', 'aave', 'makerdao', 'yearn-finance', 'lido',
|
|
25
|
+
'opensea', 'rarible', 'superrare', 'foundation', 'zora',
|
|
26
|
+
'1inch', '0x', 'paraswap', 'dydx', 'perpetual-protocol',
|
|
27
|
+
'synthetix', 'uma', 'kyber', 'bancor', 'thorchain'
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
// Suspicious blockchain-related processes
|
|
31
|
+
this.suspiciousProcesses = new Set([
|
|
32
|
+
'crypto-miner', 'coin-miner', 'xmr-miner', 'eth-miner',
|
|
33
|
+
'bitcoin-miner', 'crypto-hijack', 'blockchain-malware',
|
|
34
|
+
'wallet-stealer', 'seed-phrase', 'private-key', 'mnemonic'
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
// Common wallet file patterns
|
|
38
|
+
this.walletFilePatterns = [
|
|
39
|
+
/wallet\.json$/i,
|
|
40
|
+
/keystore.*\.json$/i,
|
|
41
|
+
/.*_private.*\.key$/i,
|
|
42
|
+
/.*_mnemonic.*\.txt$/i,
|
|
43
|
+
/.*_seed.*\.txt$/i,
|
|
44
|
+
/.*_secret.*\.txt$/i,
|
|
45
|
+
/metamask.*\.json$/i,
|
|
46
|
+
/phantom.*\.json$/i
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
// Browser extension paths
|
|
50
|
+
this.browserExtensionPaths = [
|
|
51
|
+
'/Library/Application Support/Google/Chrome/Default/Extensions',
|
|
52
|
+
'/Library/Application Support/BraveSoftware/Brave-Browser/Default/Extensions',
|
|
53
|
+
'/Library/Application Support/Microsoft Edge/Default/Extensions',
|
|
54
|
+
'/Library/Application Support/Firefox/Profiles',
|
|
55
|
+
'~/Library/Application Support/Google/Chrome/Default/Extensions',
|
|
56
|
+
'~/Library/Application Support/BraveSoftware/Brave-Browser/Default/Extensions',
|
|
57
|
+
'~/Library/Application Support/Microsoft Edge/Default/Extensions',
|
|
58
|
+
'~/Library/Application Support/Firefox/Profiles'
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async analyze() {
|
|
63
|
+
const findings = [];
|
|
64
|
+
|
|
65
|
+
// 1. Check for wallet processes
|
|
66
|
+
findings.push(...await this.checkWalletProcesses());
|
|
67
|
+
|
|
68
|
+
// 2. Scan for wallet files
|
|
69
|
+
findings.push(...await this.scanWalletFiles());
|
|
70
|
+
|
|
71
|
+
// 3. Check browser extensions
|
|
72
|
+
findings.push(...await this.checkBrowserExtensions());
|
|
73
|
+
|
|
74
|
+
// 4. Analyze network connections for DeFi/blockchain
|
|
75
|
+
findings.push(...await this.analyzeBlockchainNetwork());
|
|
76
|
+
|
|
77
|
+
// 5. Check for mining processes
|
|
78
|
+
findings.push(...await this.checkMiningProcesses());
|
|
79
|
+
|
|
80
|
+
// 6. Scan for suspicious wallet configurations
|
|
81
|
+
findings.push(...await this.checkWalletConfigurations());
|
|
82
|
+
|
|
83
|
+
this.results = {
|
|
84
|
+
agent: this.name,
|
|
85
|
+
timestamp: new Date().toISOString(),
|
|
86
|
+
findings,
|
|
87
|
+
overallRisk: this.calculateOverallRisk(findings)
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return this.results;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Check for running wallet processes
|
|
95
|
+
*/
|
|
96
|
+
async checkWalletProcesses() {
|
|
97
|
+
const findings = [];
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
const psOutput = await executeShellCommand('ps -axo pid,ppid,user,comm');
|
|
101
|
+
const lines = psOutput.split('\n').slice(1);
|
|
102
|
+
|
|
103
|
+
for (const line of lines) {
|
|
104
|
+
if (!line.trim()) continue;
|
|
105
|
+
|
|
106
|
+
const parts = line.trim().split(/\s+/);
|
|
107
|
+
if (parts.length >= 4) {
|
|
108
|
+
const pid = parseInt(parts[0]);
|
|
109
|
+
const user = parts[2];
|
|
110
|
+
const command = parts.slice(3).join(' ').toLowerCase();
|
|
111
|
+
|
|
112
|
+
// Check for known wallet processes
|
|
113
|
+
for (const wallet of this.knownWallets) {
|
|
114
|
+
if (command.includes(wallet.toLowerCase())) {
|
|
115
|
+
findings.push({
|
|
116
|
+
type: 'wallet_process',
|
|
117
|
+
pid,
|
|
118
|
+
name: wallet,
|
|
119
|
+
command: parts.slice(3).join(' '),
|
|
120
|
+
user,
|
|
121
|
+
risk: 'low',
|
|
122
|
+
description: `Known wallet process detected: ${wallet}`
|
|
123
|
+
});
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Check for suspicious processes
|
|
129
|
+
for (const suspicious of this.suspiciousProcesses) {
|
|
130
|
+
if (command.includes(suspicious)) {
|
|
131
|
+
findings.push({
|
|
132
|
+
type: 'suspicious_blockchain_process',
|
|
133
|
+
pid,
|
|
134
|
+
name: suspicious,
|
|
135
|
+
command: parts.slice(3).join(' '),
|
|
136
|
+
user,
|
|
137
|
+
risk: 'high',
|
|
138
|
+
description: `Suspicious blockchain process detected: ${suspicious}`
|
|
139
|
+
});
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.error('Error checking wallet processes:', error.message);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return findings;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Scan for wallet files on the system (SECURITY: Only metadata, no content reading)
|
|
154
|
+
*/
|
|
155
|
+
async scanWalletFiles() {
|
|
156
|
+
const findings = [];
|
|
157
|
+
const searchPaths = [
|
|
158
|
+
'~/Downloads',
|
|
159
|
+
'/tmp'
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
for (const searchPath of searchPaths) {
|
|
163
|
+
try {
|
|
164
|
+
const findOutput = await executeShellCommand(
|
|
165
|
+
`find "${searchPath.replace('~', '/Users')}" -type f -name "*.json" -o -name "*.key" 2>/dev/null | head -10`
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const files = findOutput.split('\n').filter(f => f.trim());
|
|
169
|
+
|
|
170
|
+
for (const file of files) {
|
|
171
|
+
// Check if file matches wallet patterns
|
|
172
|
+
for (const pattern of this.walletFilePatterns) {
|
|
173
|
+
if (pattern.test(path.basename(file))) {
|
|
174
|
+
try {
|
|
175
|
+
const stats = await fs.stat(file);
|
|
176
|
+
findings.push({
|
|
177
|
+
type: 'wallet_file',
|
|
178
|
+
path: file,
|
|
179
|
+
size: stats.size,
|
|
180
|
+
modified: stats.mtime.toISOString(),
|
|
181
|
+
pattern: pattern.source,
|
|
182
|
+
risk: 'medium',
|
|
183
|
+
description: `Potential wallet file found: ${path.basename(file)} (metadata only)`,
|
|
184
|
+
securityNote: 'Content not read for privacy protection'
|
|
185
|
+
});
|
|
186
|
+
} catch (error) {
|
|
187
|
+
// File might not be accessible
|
|
188
|
+
findings.push({
|
|
189
|
+
type: 'wallet_file',
|
|
190
|
+
path: file,
|
|
191
|
+
pattern: pattern.source,
|
|
192
|
+
risk: 'medium',
|
|
193
|
+
description: `Potential wallet file (inaccessible): ${path.basename(file)}`,
|
|
194
|
+
securityNote: 'Content not read for privacy protection'
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
} catch (error) {
|
|
202
|
+
// Ignore search errors for paths that might not exist
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return findings;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Check browser extensions for wallet-related extensions
|
|
211
|
+
*/
|
|
212
|
+
async checkBrowserExtensions() {
|
|
213
|
+
const findings = [];
|
|
214
|
+
|
|
215
|
+
for (const extPath of this.browserExtensionPaths) {
|
|
216
|
+
try {
|
|
217
|
+
const expandedPath = extPath.replace('~', '/Users');
|
|
218
|
+
|
|
219
|
+
if (await this.pathExists(expandedPath)) {
|
|
220
|
+
const findOutput = await executeShellCommand(
|
|
221
|
+
`find "${expandedPath}" -name "manifest.json" 2>/dev/null | head -20`
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const manifests = findOutput.split('\n').filter(m => m.trim());
|
|
225
|
+
|
|
226
|
+
for (const manifest of manifests) {
|
|
227
|
+
try {
|
|
228
|
+
const content = await fs.readFile(manifest, 'utf8');
|
|
229
|
+
const manifestData = JSON.parse(content);
|
|
230
|
+
|
|
231
|
+
// Check for wallet-related extensions
|
|
232
|
+
const name = (manifestData.name || '').toLowerCase();
|
|
233
|
+
const description = (manifestData.description || '').toLowerCase();
|
|
234
|
+
|
|
235
|
+
for (const wallet of this.knownWallets) {
|
|
236
|
+
if (name.includes(wallet.toLowerCase()) ||
|
|
237
|
+
description.includes(wallet.toLowerCase())) {
|
|
238
|
+
findings.push({
|
|
239
|
+
type: 'wallet_extension',
|
|
240
|
+
name: manifestData.name,
|
|
241
|
+
path: manifest,
|
|
242
|
+
version: manifestData.version,
|
|
243
|
+
risk: 'low',
|
|
244
|
+
description: `Wallet extension detected: ${manifestData.name}`
|
|
245
|
+
});
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Check for suspicious extensions
|
|
251
|
+
if (name.includes('crypto') || name.includes('blockchain') ||
|
|
252
|
+
name.includes('wallet') || name.includes('defi')) {
|
|
253
|
+
if (!this.knownWallets.has(name)) {
|
|
254
|
+
findings.push({
|
|
255
|
+
type: 'suspicious_blockchain_extension',
|
|
256
|
+
name: manifestData.name,
|
|
257
|
+
path: manifest,
|
|
258
|
+
version: manifestData.version,
|
|
259
|
+
risk: 'medium',
|
|
260
|
+
description: `Unknown blockchain-related extension: ${manifestData.name}`
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
} catch (error) {
|
|
265
|
+
// Skip invalid manifests
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
} catch (error) {
|
|
270
|
+
// Ignore extension path errors
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return findings;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Analyze network connections for blockchain/DeFi activity
|
|
279
|
+
*/
|
|
280
|
+
async analyzeBlockchainNetwork() {
|
|
281
|
+
const findings = [];
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
const netstatOutput = await executeShellCommand('netstat -an | grep -E "(ESTABLISHED|LISTEN)"');
|
|
285
|
+
const lines = netstatOutput.split('\n');
|
|
286
|
+
|
|
287
|
+
const blockchainDomains = [
|
|
288
|
+
'etherscan.io', 'bscscan.com', 'polygonscan.com', 'arbiscan.io',
|
|
289
|
+
'uniswap.org', 'sushi.com', 'pancakeswap.finance', 'curve.fi',
|
|
290
|
+
'compound.finance', 'aave.com', 'makerdao.com', 'yearn.finance',
|
|
291
|
+
'opensea.io', 'rarible.com', '1inch.io', '0x.org',
|
|
292
|
+
'metamask.io', 'phantom.app', 'solana.com', 'ethereum.org',
|
|
293
|
+
'infura.io', 'alchemy.com', 'quicknode.com', 'moralis.io'
|
|
294
|
+
];
|
|
295
|
+
|
|
296
|
+
for (const line of lines) {
|
|
297
|
+
if (!line.trim()) continue;
|
|
298
|
+
|
|
299
|
+
const parts = line.trim().split(/\s+/);
|
|
300
|
+
if (parts.length >= 5) {
|
|
301
|
+
const address = parts[4];
|
|
302
|
+
|
|
303
|
+
// Extract domain from address if present
|
|
304
|
+
if (address.includes(':') && !address.startsWith('127.') && !address.startsWith('::1')) {
|
|
305
|
+
const hostPort = address.split(':');
|
|
306
|
+
if (hostPort.length >= 2) {
|
|
307
|
+
const host = hostPort[0];
|
|
308
|
+
|
|
309
|
+
for (const domain of blockchainDomains) {
|
|
310
|
+
if (host.includes(domain) || host.endsWith(domain)) {
|
|
311
|
+
findings.push({
|
|
312
|
+
type: 'blockchain_network_connection',
|
|
313
|
+
host,
|
|
314
|
+
address,
|
|
315
|
+
protocol: parts[0],
|
|
316
|
+
risk: 'low',
|
|
317
|
+
description: `Blockchain/DeFi network connection: ${host}`
|
|
318
|
+
});
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
} catch (error) {
|
|
327
|
+
console.error('Error analyzing blockchain network:', error.message);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return findings;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Check for cryptocurrency mining processes
|
|
335
|
+
*/
|
|
336
|
+
async checkMiningProcesses() {
|
|
337
|
+
const findings = [];
|
|
338
|
+
|
|
339
|
+
try {
|
|
340
|
+
// Check CPU usage for potential mining
|
|
341
|
+
const topOutput = await executeShellCommand('top -l 1 -n 10');
|
|
342
|
+
const lines = topOutput.split('\n');
|
|
343
|
+
|
|
344
|
+
const miningKeywords = [
|
|
345
|
+
'miner', 'mining', 'xmr', 'monero', 'eth', 'ethereum',
|
|
346
|
+
'btc', 'bitcoin', 'crypto', 'hash', 'pool'
|
|
347
|
+
];
|
|
348
|
+
|
|
349
|
+
for (const line of lines) {
|
|
350
|
+
const lowerLine = line.toLowerCase();
|
|
351
|
+
|
|
352
|
+
for (const keyword of miningKeywords) {
|
|
353
|
+
if (lowerLine.includes(keyword)) {
|
|
354
|
+
// Check if process has high CPU usage
|
|
355
|
+
const cpuMatch = line.match(/(\d+\.\d+)%/);
|
|
356
|
+
const cpuUsage = cpuMatch ? parseFloat(cpuMatch[1]) : 0;
|
|
357
|
+
|
|
358
|
+
if (cpuUsage > 10) { // High CPU usage threshold
|
|
359
|
+
findings.push({
|
|
360
|
+
type: 'crypto_mining_process',
|
|
361
|
+
process: line.trim(),
|
|
362
|
+
cpuUsage,
|
|
363
|
+
risk: 'high',
|
|
364
|
+
description: `Potential crypto mining process with ${cpuUsage}% CPU usage`
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Check for known mining executables
|
|
373
|
+
const findOutput = await executeShellCommand(
|
|
374
|
+
'find /Users /tmp /var/tmp -name "*miner*" -o -name "*xmr*" -o -name "*eth*" 2>/dev/null | head -10'
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
const miningFiles = findOutput.split('\n').filter(f => f.trim());
|
|
378
|
+
|
|
379
|
+
for (const file of miningFiles) {
|
|
380
|
+
findings.push({
|
|
381
|
+
type: 'mining_executable',
|
|
382
|
+
path: file,
|
|
383
|
+
risk: 'high',
|
|
384
|
+
description: `Potential mining executable found: ${path.basename(file)}`
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
} catch (error) {
|
|
389
|
+
console.error('Error checking mining processes:', error.message);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return findings;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Check for suspicious wallet configurations (SECURITY: No sensitive content reading)
|
|
397
|
+
*/
|
|
398
|
+
async checkWalletConfigurations() {
|
|
399
|
+
const findings = [];
|
|
400
|
+
|
|
401
|
+
// SECURITY: Do NOT read environment variables to prevent private key exposure
|
|
402
|
+
// Instead, check for suspicious process arguments that might indicate key compromise
|
|
403
|
+
try {
|
|
404
|
+
const psOutput = await executeShellCommand('ps -axo command');
|
|
405
|
+
const lines = psOutput.split('\n');
|
|
406
|
+
|
|
407
|
+
const suspiciousArgs = [
|
|
408
|
+
/--private-key/i,
|
|
409
|
+
/--mnemonic/i,
|
|
410
|
+
/--seed/i,
|
|
411
|
+
/private.*key.*=/i,
|
|
412
|
+
/seed.*=/i,
|
|
413
|
+
/mnemonic.*=/i
|
|
414
|
+
];
|
|
415
|
+
|
|
416
|
+
for (const line of lines) {
|
|
417
|
+
if (!line.trim()) continue;
|
|
418
|
+
|
|
419
|
+
for (const pattern of suspiciousArgs) {
|
|
420
|
+
if (pattern.test(line)) {
|
|
421
|
+
// Sanitize line to not include actual keys
|
|
422
|
+
const sanitized = line.replace(/(private-key|mnemonic|seed|private.*key).*=[a-zA-Z0-9+/]+/gi, '$1=***REDACTED***');
|
|
423
|
+
|
|
424
|
+
findings.push({
|
|
425
|
+
type: 'sensitive_process_args',
|
|
426
|
+
command: sanitized,
|
|
427
|
+
risk: 'high',
|
|
428
|
+
description: `Process with potential sensitive arguments detected`,
|
|
429
|
+
securityNote: 'Sensitive content redacted for privacy'
|
|
430
|
+
});
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
} catch (error) {
|
|
436
|
+
console.error('Error checking wallet configurations:', error.message);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return findings;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Helper function to check if a path exists
|
|
444
|
+
*/
|
|
445
|
+
async pathExists(path) {
|
|
446
|
+
try {
|
|
447
|
+
await fs.access(path);
|
|
448
|
+
return true;
|
|
449
|
+
} catch {
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { BaseAgent } from './BaseAgent.js';
|
|
2
|
+
import { executeShellCommand } from '../utils/commander.js';
|
|
3
|
+
import fs from 'fs/promises';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* DeFiSecurityAgent - Specialized agent for DeFi protocol security analysis (PRIVACY PROTECTED)
|
|
8
|
+
*/
|
|
9
|
+
export class DeFiSecurityAgent extends BaseAgent {
|
|
10
|
+
constructor() {
|
|
11
|
+
super('DeFiSecurityAgent');
|
|
12
|
+
|
|
13
|
+
// Known DeFi protocol domains
|
|
14
|
+
this.defiDomains = new Set([
|
|
15
|
+
'uniswap.org', 'sushi.com', 'pancakeswap.finance', 'curve.fi',
|
|
16
|
+
'balancer.finance', 'compound.finance', 'aave.com', 'makerdao.com',
|
|
17
|
+
'yearn.finance', 'lido.fi', 'opensea.io', 'rarible.com',
|
|
18
|
+
'1inch.io', '0x.org', 'paraswap.io', 'dydx.exchange'
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
// Suspicious DeFi scam indicators (filename only, not content)
|
|
22
|
+
this.scamIndicators = new Set([
|
|
23
|
+
'rugpull', 'honeypot', 'exit-scam', 'drain-wallet',
|
|
24
|
+
'token-swap', 'airdrop-claim', 'claim-free-tokens',
|
|
25
|
+
'connect-wallet', 'approve-token', 'unlimited-approval'
|
|
26
|
+
]);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async analyze() {
|
|
30
|
+
const findings = [];
|
|
31
|
+
|
|
32
|
+
// 1. Check browser history (metadata only)
|
|
33
|
+
findings.push(...await this.checkBrowserHistory());
|
|
34
|
+
|
|
35
|
+
// 2. Scan for DeFi-related downloads (filename only)
|
|
36
|
+
findings.push(...await this.checkDefiDownloads());
|
|
37
|
+
|
|
38
|
+
// 3. Check clipboard status (no content reading)
|
|
39
|
+
findings.push(...await this.checkClipboard());
|
|
40
|
+
|
|
41
|
+
// 4. Check for DeFi scam indicators (processes only)
|
|
42
|
+
findings.push(...await this.checkDefiScams());
|
|
43
|
+
|
|
44
|
+
// 5. Analyze network for DeFi connections
|
|
45
|
+
findings.push(...await this.analyzeDefiNetwork());
|
|
46
|
+
|
|
47
|
+
this.results = {
|
|
48
|
+
agent: this.name,
|
|
49
|
+
timestamp: new Date().toISOString(),
|
|
50
|
+
findings,
|
|
51
|
+
overallRisk: this.calculateOverallRisk(findings)
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return this.results;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check browser history (metadata only, no content reading)
|
|
59
|
+
*/
|
|
60
|
+
async checkBrowserHistory() {
|
|
61
|
+
const findings = [];
|
|
62
|
+
|
|
63
|
+
// Skip browser history scanning for privacy protection
|
|
64
|
+
findings.push({
|
|
65
|
+
type: 'browser_history_privacy_notice',
|
|
66
|
+
risk: 'info',
|
|
67
|
+
description: 'Browser history scanning disabled for privacy protection'
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return findings;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Scan for DeFi-related downloads (filename only, no content reading)
|
|
75
|
+
*/
|
|
76
|
+
async checkDefiDownloads() {
|
|
77
|
+
const findings = [];
|
|
78
|
+
|
|
79
|
+
const downloadPaths = [
|
|
80
|
+
'~/Downloads',
|
|
81
|
+
'/tmp'
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
for (const downloadPath of downloadPaths) {
|
|
85
|
+
try {
|
|
86
|
+
const expandedPath = downloadPath.replace('~', '/Users');
|
|
87
|
+
|
|
88
|
+
if (await this.pathExists(expandedPath)) {
|
|
89
|
+
const findOutput = await executeShellCommand(
|
|
90
|
+
`find "${expandedPath}" -name "*.html" -o -name "*.js" 2>/dev/null | head -5`
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const files = findOutput.split('\n').filter(f => f.trim());
|
|
94
|
+
|
|
95
|
+
for (const file of files) {
|
|
96
|
+
try {
|
|
97
|
+
// SECURITY: Only check filename, not content
|
|
98
|
+
const stats = await fs.stat(file);
|
|
99
|
+
const filename = path.basename(file).toLowerCase();
|
|
100
|
+
|
|
101
|
+
// Check filename for scam indicators
|
|
102
|
+
for (const scam of this.scamIndicators) {
|
|
103
|
+
if (filename.includes(scam)) {
|
|
104
|
+
findings.push({
|
|
105
|
+
type: 'defi_scam_download',
|
|
106
|
+
path: this.sanitizePath(file),
|
|
107
|
+
indicator: scam,
|
|
108
|
+
size: stats.size,
|
|
109
|
+
risk: 'high',
|
|
110
|
+
description: `Suspicious DeFi file detected: ${path.basename(file)}`,
|
|
111
|
+
securityNote: 'Content not read for privacy protection'
|
|
112
|
+
});
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
} catch (error) {
|
|
118
|
+
// Skip files that can't be accessed
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
} catch (error) {
|
|
123
|
+
// Ignore download path errors
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return findings;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Check clipboard status (no content reading)
|
|
132
|
+
*/
|
|
133
|
+
async checkClipboard() {
|
|
134
|
+
const findings = [];
|
|
135
|
+
|
|
136
|
+
// SECURITY: Do NOT read actual clipboard content
|
|
137
|
+
findings.push({
|
|
138
|
+
type: 'clipboard_security_notice',
|
|
139
|
+
risk: 'info',
|
|
140
|
+
description: 'Clipboard access disabled for privacy protection',
|
|
141
|
+
securityNote: 'Content not read to protect sensitive data'
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
return findings;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Check for DeFi scam indicators (processes only)
|
|
149
|
+
*/
|
|
150
|
+
async checkDefiScams() {
|
|
151
|
+
const findings = [];
|
|
152
|
+
|
|
153
|
+
// Check running processes for scam indicators
|
|
154
|
+
try {
|
|
155
|
+
const psOutput = await executeShellCommand('ps -axo pid,ppid,user,comm');
|
|
156
|
+
const lines = psOutput.split('\n');
|
|
157
|
+
|
|
158
|
+
for (const line of lines) {
|
|
159
|
+
if (!line.trim()) continue;
|
|
160
|
+
|
|
161
|
+
const parts = line.trim().split(/\s+/);
|
|
162
|
+
if (parts.length >= 4) {
|
|
163
|
+
const pid = parseInt(parts[0]);
|
|
164
|
+
const user = parts[2];
|
|
165
|
+
const command = parts.slice(3).join(' ').toLowerCase();
|
|
166
|
+
|
|
167
|
+
for (const scam of this.scamIndicators) {
|
|
168
|
+
if (command.includes(scam)) {
|
|
169
|
+
findings.push({
|
|
170
|
+
type: 'defi_scam_process',
|
|
171
|
+
pid,
|
|
172
|
+
command: parts.slice(3).join(' '),
|
|
173
|
+
user,
|
|
174
|
+
indicator: scam,
|
|
175
|
+
risk: 'high',
|
|
176
|
+
description: `Process with DeFi scam indicator: ${scam}`
|
|
177
|
+
});
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
} catch (error) {
|
|
184
|
+
console.error('Error checking DeFi scams:', error.message);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return findings;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Analyze network for DeFi connections
|
|
192
|
+
*/
|
|
193
|
+
async analyzeDefiNetwork() {
|
|
194
|
+
const findings = [];
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
const netstatOutput = await executeShellCommand('netstat -an | grep -E "(ESTABLISHED|LISTEN)"');
|
|
198
|
+
const lines = netstatOutput.split('\n');
|
|
199
|
+
|
|
200
|
+
for (const line of lines) {
|
|
201
|
+
if (!line.trim()) continue;
|
|
202
|
+
|
|
203
|
+
const parts = line.trim().split(/\s+/);
|
|
204
|
+
if (parts.length >= 5) {
|
|
205
|
+
const address = parts[4];
|
|
206
|
+
|
|
207
|
+
// Extract domain from address if present
|
|
208
|
+
if (address.includes(':') && !address.startsWith('127.') && !address.startsWith('::1')) {
|
|
209
|
+
const hostPort = address.split(':');
|
|
210
|
+
if (hostPort.length >= 2) {
|
|
211
|
+
const host = hostPort[0];
|
|
212
|
+
|
|
213
|
+
// Check for DeFi domain connections
|
|
214
|
+
for (const domain of this.defiDomains) {
|
|
215
|
+
if (host.includes(domain) || host.endsWith(domain)) {
|
|
216
|
+
findings.push({
|
|
217
|
+
type: 'defi_network_connection',
|
|
218
|
+
host,
|
|
219
|
+
address,
|
|
220
|
+
protocol: parts[0],
|
|
221
|
+
risk: 'low',
|
|
222
|
+
description: `DeFi protocol connection: ${host}`
|
|
223
|
+
});
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
} catch (error) {
|
|
232
|
+
console.error('Error analyzing DeFi network:', error.message);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return findings;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Helper function to check if a path exists
|
|
240
|
+
*/
|
|
241
|
+
async pathExists(path) {
|
|
242
|
+
try {
|
|
243
|
+
await fs.access(path);
|
|
244
|
+
return true;
|
|
245
|
+
} catch {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Sanitize path to protect privacy
|
|
252
|
+
*/
|
|
253
|
+
sanitizePath(path) {
|
|
254
|
+
if (!path) return path;
|
|
255
|
+
return path.replace(/\/Users\/[^\/]+/g, '/Users/***REDACTED***');
|
|
256
|
+
}
|
|
257
|
+
}
|