@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,572 @@
|
|
|
1
|
+
import { BaseAgent } from './BaseAgent.js';
|
|
2
|
+
import { executeShellCommand } from '../utils/commander.js';
|
|
3
|
+
import { getSignatureAssessment } from '../utils/signature.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ProcessAgent - Analyzes running processes for anomalies
|
|
7
|
+
*/
|
|
8
|
+
export class ProcessAgent extends BaseAgent {
|
|
9
|
+
constructor() {
|
|
10
|
+
super('ProcessAgent');
|
|
11
|
+
this.systemPaths = [
|
|
12
|
+
// System directories
|
|
13
|
+
'/Applications',
|
|
14
|
+
'/System',
|
|
15
|
+
'/System/Applications',
|
|
16
|
+
'/System/Library',
|
|
17
|
+
'/System/Library/CoreServices',
|
|
18
|
+
'/System/Library/Frameworks',
|
|
19
|
+
'/Library',
|
|
20
|
+
'/Library/Apple',
|
|
21
|
+
'/Library/Application Support',
|
|
22
|
+
'/Library/Frameworks',
|
|
23
|
+
'/Library/PreferencePanes',
|
|
24
|
+
'/Library/LaunchAgents',
|
|
25
|
+
'/Library/LaunchDaemons',
|
|
26
|
+
|
|
27
|
+
// Standard Unix paths
|
|
28
|
+
'/bin',
|
|
29
|
+
'/sbin',
|
|
30
|
+
'/usr/bin',
|
|
31
|
+
'/usr/sbin',
|
|
32
|
+
'/usr/lib',
|
|
33
|
+
'/usr/libexec',
|
|
34
|
+
'/usr/local/bin',
|
|
35
|
+
'/usr/local/sbin',
|
|
36
|
+
'/usr/local/lib',
|
|
37
|
+
'/usr/local/share',
|
|
38
|
+
'/opt/homebrew/bin',
|
|
39
|
+
'/opt/local/bin',
|
|
40
|
+
|
|
41
|
+
// Developer tools
|
|
42
|
+
'/usr/bin/code',
|
|
43
|
+
'/Applications/Visual Studio Code.app',
|
|
44
|
+
'/Applications/Xcode.app',
|
|
45
|
+
'/Applications/Atom.app',
|
|
46
|
+
'/Applications/Sublime Text.app',
|
|
47
|
+
'/Developer',
|
|
48
|
+
'/Xcode',
|
|
49
|
+
|
|
50
|
+
// Common application directories
|
|
51
|
+
'/Applications/Microsoft Office',
|
|
52
|
+
'/Applications/Adobe',
|
|
53
|
+
'/Applications/Google Chrome.app',
|
|
54
|
+
'/Applications/Google Chrome',
|
|
55
|
+
'/Applications/Safari.app',
|
|
56
|
+
'/Applications/Firefox.app',
|
|
57
|
+
'/Applications/Opera.app',
|
|
58
|
+
'/Applications/Slack.app',
|
|
59
|
+
'/Applications/Discord.app',
|
|
60
|
+
'/Applications/Zoom.app',
|
|
61
|
+
'/Applications/Teams.app',
|
|
62
|
+
'/Applications/Skype.app',
|
|
63
|
+
'/Applications/Dropbox.app',
|
|
64
|
+
'/Applications/Spotify.app',
|
|
65
|
+
'/Applications/VLC.app',
|
|
66
|
+
'/Applications/QuickTime Player.app',
|
|
67
|
+
'/Applications/iTunes.app',
|
|
68
|
+
'/Applications/Preview.app',
|
|
69
|
+
'/Applications/TextEdit.app',
|
|
70
|
+
'/Applications/Activity Monitor.app',
|
|
71
|
+
'/Applications/Console.app',
|
|
72
|
+
'/Applications/System Preferences.app',
|
|
73
|
+
'/Applications/System Information.app',
|
|
74
|
+
'/Applications/Utilities',
|
|
75
|
+
'/Applications/Terminal.app',
|
|
76
|
+
'/Applications/iTerm.app',
|
|
77
|
+
|
|
78
|
+
// Homebrew paths
|
|
79
|
+
'/opt/homebrew',
|
|
80
|
+
'/usr/local/Cellar',
|
|
81
|
+
'/usr/local/Caskroom',
|
|
82
|
+
|
|
83
|
+
// Node.js and npm
|
|
84
|
+
'/usr/local/bin/node',
|
|
85
|
+
'/usr/local/bin/npm',
|
|
86
|
+
'/opt/homebrew/bin/node',
|
|
87
|
+
'/opt/homebrew/bin/npm',
|
|
88
|
+
|
|
89
|
+
// Python paths
|
|
90
|
+
'/usr/bin/python',
|
|
91
|
+
'/usr/bin/python3',
|
|
92
|
+
'/usr/local/bin/python',
|
|
93
|
+
'/usr/local/bin/python3',
|
|
94
|
+
'/opt/homebrew/bin/python',
|
|
95
|
+
'/opt/homebrew/bin/python3',
|
|
96
|
+
|
|
97
|
+
// Git
|
|
98
|
+
'/usr/bin/git',
|
|
99
|
+
'/usr/local/bin/git',
|
|
100
|
+
'/opt/homebrew/bin/git',
|
|
101
|
+
|
|
102
|
+
// Shell paths
|
|
103
|
+
'/bin/bash',
|
|
104
|
+
'/bin/zsh',
|
|
105
|
+
'/bin/fish',
|
|
106
|
+
'/bin/tcsh',
|
|
107
|
+
'/usr/local/bin/bash',
|
|
108
|
+
'/usr/local/bin/zsh',
|
|
109
|
+
'/opt/homebrew/bin/bash',
|
|
110
|
+
'/opt/homebrew/bin/zsh',
|
|
111
|
+
|
|
112
|
+
// macOS specific
|
|
113
|
+
'/System/Library/PrivateFrameworks',
|
|
114
|
+
'/System/Library/CoreServices/Finder.app',
|
|
115
|
+
'/System/Library/CoreServices/Dock.app',
|
|
116
|
+
'/System/Library/CoreServices/Menu Extras',
|
|
117
|
+
'/System/Library/CoreServices/Spotlight.app'
|
|
118
|
+
];
|
|
119
|
+
this.systemProcessNames = [
|
|
120
|
+
// Core system processes
|
|
121
|
+
'kernel_task', 'launchd', 'loginwindow',
|
|
122
|
+
|
|
123
|
+
// UI and desktop
|
|
124
|
+
'Finder', 'SystemUIServer', 'WindowServer', 'Dock',
|
|
125
|
+
'UserNotificationCenter', 'NotificationCenter', 'Spotlight',
|
|
126
|
+
|
|
127
|
+
// System services
|
|
128
|
+
'cfprefsd', 'mds', 'mdworker', 'mds_stores', 'distnoted',
|
|
129
|
+
'notifyd', 'powerd', 'tccd', 'locationd', 'opendirectoryd',
|
|
130
|
+
'syslogd', 'logd', 'mDNSResponder', 'configd', 'systemstats',
|
|
131
|
+
'coreaudiod', 'bluetoothd', 'airportd', 'securityd', 'warmd',
|
|
132
|
+
'hidd', 'fseventsd', 'pbs', 'pboard',
|
|
133
|
+
|
|
134
|
+
// Network services
|
|
135
|
+
'networkd', 'wifid', 'socketfilterfw',
|
|
136
|
+
|
|
137
|
+
// Graphics and display
|
|
138
|
+
'WindowManager', 'coreservicesd', 'SkyLight',
|
|
139
|
+
|
|
140
|
+
// File system
|
|
141
|
+
'diskarbitrationd', 'filecoordinationd',
|
|
142
|
+
|
|
143
|
+
// Security
|
|
144
|
+
'authd', 'trustd', 'amfid',
|
|
145
|
+
|
|
146
|
+
// Development tools
|
|
147
|
+
'node', 'python', 'python3', 'git', 'ssh',
|
|
148
|
+
'bash', 'zsh', 'fish', 'tcsh',
|
|
149
|
+
|
|
150
|
+
// Common applications
|
|
151
|
+
'Chrome', 'Safari', 'firefox', 'VSCode', 'code',
|
|
152
|
+
'iTerm', 'Terminal',
|
|
153
|
+
|
|
154
|
+
// System utilities
|
|
155
|
+
'Activity Monitor', 'Preview', 'Console'
|
|
156
|
+
];
|
|
157
|
+
this.trustedSystemCommands = new Set([
|
|
158
|
+
// Core system processes
|
|
159
|
+
'launchd', 'kernel_task', 'kernelinit', 'kextd', 'kerneld',
|
|
160
|
+
|
|
161
|
+
// macOS system services
|
|
162
|
+
'WindowServer', 'SystemUIServer', 'Dock', 'Finder', 'loginwindow',
|
|
163
|
+
'UserNotificationCenter', 'Spotlight', 'NotificationCenter',
|
|
164
|
+
|
|
165
|
+
// Background services (daemons)
|
|
166
|
+
'mds', 'mdworker', 'mds_stores', 'distnoted', 'notifyd', 'powerd',
|
|
167
|
+
'cfprefsd', 'usernoted', 'tccd', 'locationd', 'opendirectoryd',
|
|
168
|
+
'syslogd', 'logd', 'mDNSResponder', 'configd', 'systemstats',
|
|
169
|
+
'coreaudiod', 'bluetoothd', 'airportd', 'securityd', 'warmd',
|
|
170
|
+
'hidd', 'cmiodalassistants', 'launchservicesd', 'iconservicesagent',
|
|
171
|
+
'lskdd', 'lsd', 'fseventsd', 'pbs', 'pboard', 'pasteboardd',
|
|
172
|
+
|
|
173
|
+
// Network and connectivity
|
|
174
|
+
'networkd', 'wifid', 'socketfilterfw', 'natd', 'pppd',
|
|
175
|
+
'racoon', 'racoonctl', 'vpnd', 'netbiosd',
|
|
176
|
+
|
|
177
|
+
// Graphics and display
|
|
178
|
+
'WindowManager', 'coreservicesd', 'SkyLight', 'HIToolbox',
|
|
179
|
+
'CGSession', 'ScreenSaverEngine', 'SystemPreferences',
|
|
180
|
+
|
|
181
|
+
// File system and storage
|
|
182
|
+
'fsapfs', 'hfs_mount', 'autodiskmount', 'diskarbitrationd',
|
|
183
|
+
'diskmanagementd', 'filecoordinationd', 'synthesisd',
|
|
184
|
+
|
|
185
|
+
// Security and authentication
|
|
186
|
+
'authd', 'authorizationhost', 'ocspd', 'trustd', 'securityd',
|
|
187
|
+
'codesign', 'taskgated', 'sandboxd', 'amfid',
|
|
188
|
+
|
|
189
|
+
// Development tools (commonly installed)
|
|
190
|
+
'node', 'npm', 'npx', 'yarn', 'pnpm', 'pnpx',
|
|
191
|
+
'pip', 'pip3', 'python', 'python3', 'pip3',
|
|
192
|
+
'git', 'git-credential-manager', 'git-gui', 'gitk',
|
|
193
|
+
'ssh', 'ssh-agent', 'ssh-add', 'scp', 'sftp', 'rsync',
|
|
194
|
+
'curl', 'wget', 'http', 'https', 'ftp',
|
|
195
|
+
'brew', 'ruby', 'perl', 'java', 'javac', 'gradle', 'maven',
|
|
196
|
+
'docker', 'docker-compose', 'kubectl', 'helm', 'minikube',
|
|
197
|
+
'make', 'cmake', 'gcc', 'clang', 'clang++',
|
|
198
|
+
'go', 'gorun', 'gobuild', 'rust', 'rustc', 'cargo',
|
|
199
|
+
'php', 'composer', 'laravel', 'symfony',
|
|
200
|
+
'typescript', 'ts-node', 'tsc', 'tsx',
|
|
201
|
+
'eslint', 'prettier', 'jest', 'mocha', 'chai',
|
|
202
|
+
'webpack', 'vite', 'parcel', 'rollup',
|
|
203
|
+
'nodemon', 'pm2', 'forever', 'supervisor',
|
|
204
|
+
'redis-server', 'redis-cli', 'mongod', 'mongo', 'mysql',
|
|
205
|
+
'psql', 'postgres', 'sqlite3', 'mongo-express',
|
|
206
|
+
'nginx', 'apache2', 'httpd', 'caddy', 'traefik',
|
|
207
|
+
|
|
208
|
+
// Common applications
|
|
209
|
+
'Chrome', 'chromium', 'Safari', 'firefox', 'Opera',
|
|
210
|
+
'Slack', 'Discord', 'Zoom', 'Teams', 'Skype',
|
|
211
|
+
'VSCode', 'code', 'Xcode', 'Atom', 'Sublime_Text',
|
|
212
|
+
'iTerm', 'Terminal', 'bash', 'zsh', 'fish', 'tcsh',
|
|
213
|
+
|
|
214
|
+
// macOS utilities
|
|
215
|
+
'Activity Monitor', 'Preview', 'TextEdit', 'QuickLook',
|
|
216
|
+
'ArchiveUtility', 'DiskUtility', 'Keychain Access',
|
|
217
|
+
'System Information', 'Console', 'Automator',
|
|
218
|
+
|
|
219
|
+
// Third-party common software
|
|
220
|
+
'Dropbox', 'GoogleDrive', 'OneDrive', 'Box',
|
|
221
|
+
'Spotify', 'VLC', 'QuickTimePlayer', 'iTunes',
|
|
222
|
+
'Microsoft Word', 'Microsoft Excel', 'Microsoft PowerPoint',
|
|
223
|
+
'Adobe Acrobat', 'Adobe Reader', 'Photoshop',
|
|
224
|
+
|
|
225
|
+
// System maintenance
|
|
226
|
+
'periodic', 'daily', 'weekly', 'monthly', 'launchctl',
|
|
227
|
+
'systemsetup', 'softwareupdate', 'pmset', 'caffeinate',
|
|
228
|
+
|
|
229
|
+
// Input and peripherals
|
|
230
|
+
'IOHIDSystem', 'IOHIDEventDriver', 'USBAgent',
|
|
231
|
+
'BluetoothUIServer', 'AudioComponentRegistrar',
|
|
232
|
+
|
|
233
|
+
// Time and sync
|
|
234
|
+
'timed', 'clockd', 'ntpd', 'networktime',
|
|
235
|
+
|
|
236
|
+
// Print and scanning
|
|
237
|
+
'cupsd', 'cups-browsed', 'hpmud', 'ImageCaptureExtension',
|
|
238
|
+
|
|
239
|
+
// Accessibility
|
|
240
|
+
'VoiceOver', 'AXUIServer', 'accessibilityd',
|
|
241
|
+
|
|
242
|
+
// Backup and recovery
|
|
243
|
+
'TimeMachine', 'backupd', 'tmutil', 'rsync',
|
|
244
|
+
|
|
245
|
+
// Additional macOS system daemons
|
|
246
|
+
'mediaremoted', 'watchdogd', 'kernelmanagerd', 'thermalmonitord',
|
|
247
|
+
'apsd', 'apsrelayd', 'applepushserviced', 'com.apple.CommCenter',
|
|
248
|
+
'commcenter', 'commcenterd', 'mobileassetd', 'assetcache',
|
|
249
|
+
'assetcachingd', 'cacheserverd', 'cached', 'cachecheck',
|
|
250
|
+
'logind', 'logindisplay', 'loginwindow', 'screensharingd',
|
|
251
|
+
'remoted', 'remotepairingd', 'remotepairingtool',
|
|
252
|
+
'sharingd', 'screencaptured', 'screenshotd',
|
|
253
|
+
'corebrightnessd', 'backlightd', 'brightnessd',
|
|
254
|
+
'controlcenterd', 'controlcenter', 'spotlightd',
|
|
255
|
+
'searchpartyd', 'searchindexer', 'mds_stores',
|
|
256
|
+
'useractivityd', 'useractivityagent', 'useractivitymonitor',
|
|
257
|
+
'timed', 'timed_sync', 'networkd', 'networkd_privileged',
|
|
258
|
+
'wifid', 'wirelessprovisioningd', 'wirelessproxd',
|
|
259
|
+
'bluetoothd', 'bluetoothaudiod', 'bluetoothUIServer',
|
|
260
|
+
'audioaccessoryd', 'audioaccessoryd',
|
|
261
|
+
'coreaudiod', 'coreaudiohelperd', 'coreaudioaopd',
|
|
262
|
+
'hidd', 'hidd_helper', 'hidd_system',
|
|
263
|
+
'universalaccessd', 'accessibilityd', 'AXUIServer',
|
|
264
|
+
'voiceover', 'voiceoverd',
|
|
265
|
+
'distnoted', 'distributednotificationcenter',
|
|
266
|
+
'nsurlsessiond', 'nsurlstoraged', 'webkitnetworkprocess',
|
|
267
|
+
'webkitwebcontentprocess', 'webkitpluginprocess',
|
|
268
|
+
'plugind', 'pluginmanagerd',
|
|
269
|
+
'launchservicesd', 'lsd', 'lskdd',
|
|
270
|
+
'iconservicesagent', 'iconservicesd',
|
|
271
|
+
'pasteboardd', 'pboard', 'pbs',
|
|
272
|
+
'cfprefsd', 'preferencesd', 'systempreferencesd',
|
|
273
|
+
'tccd', 'tccutil', 'privacyd',
|
|
274
|
+
'locationd', 'locationservicesd', 'geod',
|
|
275
|
+
'compassd', 'magnetometerd', 'accelerometerd',
|
|
276
|
+
'gyroscoped', 'barometerd',
|
|
277
|
+
'fseventsd', 'fsapfs', 'filecoordinationd',
|
|
278
|
+
'synthesisd', 'syncdefaultsd', 'syncservicesd',
|
|
279
|
+
'diskarbitrationd', 'diskmanagementd', 'diskimagesd',
|
|
280
|
+
'hdiutil', 'hdid', 'hdihelperd',
|
|
281
|
+
'authd', 'authorizationhost', 'authtrampoline',
|
|
282
|
+
'securityd', 'securityagent', 'securityhelperd',
|
|
283
|
+
'codesign', 'codesign_allocate', 'taskgated',
|
|
284
|
+
'amfid', 'amfite', 'applemobilefileintegrity',
|
|
285
|
+
'sandboxd', 'sandboxd_helper', 'seatbeltd',
|
|
286
|
+
'trustd', 'trustevaluationagent',
|
|
287
|
+
'ocspd', 'ocsp_helperd',
|
|
288
|
+
'certificateauthorityd', 'certificated',
|
|
289
|
+
'keychaind', 'keychainaccesshelperd',
|
|
290
|
+
'smartcardservicesd', 'tokend',
|
|
291
|
+
'biometrickitd', 'touchid', 'faced',
|
|
292
|
+
'corecrypto', 'corecryptod',
|
|
293
|
+
'kernelmanagerd', 'kextd', 'kextcache',
|
|
294
|
+
'systemstats', 'systemstatsd',
|
|
295
|
+
'powerd', 'powermanagementd', 'pmset',
|
|
296
|
+
'thermalmonitord', 'thermalmonitord_helper',
|
|
297
|
+
'warmd', 'warmd_helper',
|
|
298
|
+
'configd', 'configd_helper',
|
|
299
|
+
'networksetup', 'networksetup_helper',
|
|
300
|
+
'scutil', 'scutil_helper',
|
|
301
|
+
'ifconfig', 'ifconfig_helper',
|
|
302
|
+
'netstat', 'netstat_helper',
|
|
303
|
+
'ping', 'ping_helper',
|
|
304
|
+
'traceroute', 'traceroute_helper',
|
|
305
|
+
'nslookup', 'nslookup_helper',
|
|
306
|
+
'dig', 'dig_helper',
|
|
307
|
+
'host', 'host_helper'
|
|
308
|
+
]);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async analyze() {
|
|
312
|
+
const processes = await this.getProcessDetails();
|
|
313
|
+
let findings = this.analyzeProcesses(processes);
|
|
314
|
+
|
|
315
|
+
// Reduce false positives: apply signature/Gatekeeper trust to downgrade or drop weak-signal findings.
|
|
316
|
+
findings = await this.applyTrustHeuristics(findings);
|
|
317
|
+
|
|
318
|
+
this.results = {
|
|
319
|
+
agent: this.name,
|
|
320
|
+
timestamp: new Date().toISOString(),
|
|
321
|
+
totalProcesses: processes.length,
|
|
322
|
+
findings,
|
|
323
|
+
overallRisk: this.calculateOverallRisk(findings)
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
return this.results;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Get detailed process information
|
|
331
|
+
*/
|
|
332
|
+
async getProcessDetails() {
|
|
333
|
+
// Get process list with parent relationships
|
|
334
|
+
const psOutput = await executeShellCommand('ps -axo pid,ppid,user,comm');
|
|
335
|
+
const lines = psOutput.split('\n').slice(1); // Skip header
|
|
336
|
+
|
|
337
|
+
const processes = [];
|
|
338
|
+
|
|
339
|
+
for (const line of lines) {
|
|
340
|
+
if (!line.trim()) continue;
|
|
341
|
+
|
|
342
|
+
const parts = line.trim().split(/\s+/);
|
|
343
|
+
if (parts.length >= 4) {
|
|
344
|
+
const pid = parseInt(parts[0]);
|
|
345
|
+
const ppid = parseInt(parts[1]);
|
|
346
|
+
const user = parts[2];
|
|
347
|
+
const command = parts.slice(3).join(' ');
|
|
348
|
+
|
|
349
|
+
// Try to get executable path with multiple fallbacks
|
|
350
|
+
const fullPath = await this.getExecutablePath(pid, command);
|
|
351
|
+
|
|
352
|
+
processes.push({
|
|
353
|
+
pid,
|
|
354
|
+
ppid,
|
|
355
|
+
user,
|
|
356
|
+
name: command.split('/').pop().split(' ')[0],
|
|
357
|
+
command,
|
|
358
|
+
path: fullPath
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return processes;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Resolve executable path with fallbacks to reduce false positives
|
|
368
|
+
*/
|
|
369
|
+
async getExecutablePath(pid, commandFallback) {
|
|
370
|
+
// Use ps-based lookups only to avoid macOS privacy prompts from lsof
|
|
371
|
+
try {
|
|
372
|
+
const cmdOutput = await executeShellCommand(
|
|
373
|
+
`ps -p ${pid} -o command= 2>/dev/null`,
|
|
374
|
+
{ quiet: true }
|
|
375
|
+
);
|
|
376
|
+
if (cmdOutput) {
|
|
377
|
+
const candidate = cmdOutput.trim().split(' ')[0];
|
|
378
|
+
if (candidate.startsWith('/')) return candidate;
|
|
379
|
+
}
|
|
380
|
+
} catch (error) {
|
|
381
|
+
// ignore
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
try {
|
|
385
|
+
const commOutput = await executeShellCommand(
|
|
386
|
+
`ps -p ${pid} -o comm= 2>/dev/null`,
|
|
387
|
+
{ quiet: true }
|
|
388
|
+
);
|
|
389
|
+
if (commOutput) {
|
|
390
|
+
const candidate = commOutput.trim().split(' ')[0];
|
|
391
|
+
if (candidate.startsWith('/')) return candidate;
|
|
392
|
+
}
|
|
393
|
+
} catch (error) {
|
|
394
|
+
// ignore
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Fallback to the short command we already have
|
|
398
|
+
return commandFallback;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Analyze processes for suspicious patterns
|
|
403
|
+
*/
|
|
404
|
+
analyzeProcesses(processes) {
|
|
405
|
+
const findings = [];
|
|
406
|
+
|
|
407
|
+
for (const proc of processes) {
|
|
408
|
+
const risks = [];
|
|
409
|
+
let riskLevel = 'low';
|
|
410
|
+
const hasAbsolutePath = proc.path && proc.path.startsWith('/');
|
|
411
|
+
const isTrustedPath = hasAbsolutePath && this.systemPaths.some(sysPath => proc.path.startsWith(sysPath));
|
|
412
|
+
const isTrustedCommand = this.trustedSystemCommands.has(proc.name);
|
|
413
|
+
const isUserPath = hasAbsolutePath && proc.path.includes('/Users/');
|
|
414
|
+
|
|
415
|
+
// Check 1: System process name but running from user directory
|
|
416
|
+
if (hasAbsolutePath && this.systemProcessNames.includes(proc.name)) {
|
|
417
|
+
if (isUserPath) {
|
|
418
|
+
risks.push('System process name running from user directory');
|
|
419
|
+
riskLevel = 'high';
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Check 2: Process name doesn't match path
|
|
424
|
+
const pathBasename = hasAbsolutePath ? proc.path.split('/').pop().split(' ')[0] : '';
|
|
425
|
+
if (hasAbsolutePath && pathBasename && proc.name !== pathBasename) {
|
|
426
|
+
risks.push(`Process name mismatch (name: ${proc.name}, path: ${pathBasename})`);
|
|
427
|
+
riskLevel = 'medium';
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Check 3: Non-system path execution
|
|
431
|
+
if (hasAbsolutePath && !isTrustedPath && proc.path !== proc.command) {
|
|
432
|
+
risks.push('Running from non-standard location');
|
|
433
|
+
|
|
434
|
+
// Elevate risk if in hidden directory
|
|
435
|
+
if (proc.path.includes('/.')) {
|
|
436
|
+
risks.push('Running from hidden directory');
|
|
437
|
+
riskLevel = 'high';
|
|
438
|
+
} else if (isUserPath) {
|
|
439
|
+
riskLevel = 'medium';
|
|
440
|
+
} else {
|
|
441
|
+
// keep as low unless combined with other risks
|
|
442
|
+
riskLevel = riskLevel === 'low' ? 'medium' : riskLevel;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Check 4: Suspicious parent process
|
|
447
|
+
const parent = processes.find(p => p.pid === proc.ppid);
|
|
448
|
+
if (parent && hasAbsolutePath && !isTrustedPath) {
|
|
449
|
+
if (parent.name === 'bash' || parent.name === 'sh' || parent.name === 'python') {
|
|
450
|
+
risks.push(`Spawned by shell: ${parent.name}`);
|
|
451
|
+
riskLevel = 'medium';
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Check 5: Root/elevated processes from user paths
|
|
456
|
+
if (hasAbsolutePath && (proc.user === 'root' || proc.user === '_coreaudiod') && proc.path.includes('/Users/')) {
|
|
457
|
+
risks.push('Elevated privileges from user directory');
|
|
458
|
+
riskLevel = 'high';
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Check 6: Hidden or obfuscated names
|
|
462
|
+
if (proc.name.startsWith('.') && !isTrustedCommand) {
|
|
463
|
+
risks.push('Hidden process name (starts with dot)');
|
|
464
|
+
riskLevel = 'high';
|
|
465
|
+
} else if (proc.name.match(/^[a-z]{1,2}[0-9]{6,}$/) && !isTrustedCommand) {
|
|
466
|
+
// Pattern like "ab123456" - likely obfuscated
|
|
467
|
+
risks.push('Suspicious obfuscated process name pattern');
|
|
468
|
+
riskLevel = 'high';
|
|
469
|
+
} else if (proc.name.match(/^[0-9a-f]{16,}$/i) && !isTrustedCommand) {
|
|
470
|
+
// Hex-like pattern - likely obfuscated
|
|
471
|
+
risks.push('Suspicious hex-like process name pattern');
|
|
472
|
+
riskLevel = 'high';
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// If we only have a trusted command name and no absolute path, avoid flagging
|
|
476
|
+
if (!hasAbsolutePath && isTrustedCommand && risks.length === 0) {
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Only report if there are meaningful risks (high risk or multiple signals)
|
|
481
|
+
const shouldReport = risks.length > 1 || riskLevel === 'high';
|
|
482
|
+
if (shouldReport) {
|
|
483
|
+
findings.push({
|
|
484
|
+
type: 'suspicious_process',
|
|
485
|
+
pid: proc.pid,
|
|
486
|
+
name: proc.name,
|
|
487
|
+
path: proc.path,
|
|
488
|
+
user: proc.user,
|
|
489
|
+
ppid: proc.ppid,
|
|
490
|
+
parentName: parent?.name || 'unknown',
|
|
491
|
+
risks,
|
|
492
|
+
risk: riskLevel,
|
|
493
|
+
description: `Process ${proc.name} (${proc.pid}): ${risks.join(', ')}`
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return findings;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Apply code-signing/Gatekeeper trust to reduce false positives.
|
|
503
|
+
*
|
|
504
|
+
* Strategy:
|
|
505
|
+
* - Only evaluate findings already considered "report-worthy".
|
|
506
|
+
* - If an executable is Gatekeeper-accepted and not showing strong malware signals
|
|
507
|
+
* (hidden dir, impersonation, elevated-from-user-home, obfuscation), downgrade/drop.
|
|
508
|
+
*
|
|
509
|
+
* @param {Array} findings
|
|
510
|
+
* @returns {Promise<Array>}
|
|
511
|
+
*/
|
|
512
|
+
async applyTrustHeuristics(findings) {
|
|
513
|
+
const signatureCache = new Map();
|
|
514
|
+
|
|
515
|
+
const enriched = await Promise.all(findings.map(async (finding) => {
|
|
516
|
+
const assessment = await getSignatureAssessment(finding.path, signatureCache);
|
|
517
|
+
|
|
518
|
+
// Attach trust metadata for transparency (useful in reports)
|
|
519
|
+
const trust = {
|
|
520
|
+
spctlAccepted: assessment.spctlAccepted,
|
|
521
|
+
teamIdentifier: assessment.teamIdentifier,
|
|
522
|
+
signedByApple: assessment.signedByApple,
|
|
523
|
+
signedByDeveloperId: assessment.signedByDeveloperId
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
const strongSignals = [
|
|
527
|
+
'System process name running from user directory',
|
|
528
|
+
'Running from hidden directory',
|
|
529
|
+
'Elevated privileges from user directory',
|
|
530
|
+
'Hidden process name (starts with dot)',
|
|
531
|
+
'Suspicious obfuscated process name pattern',
|
|
532
|
+
'Suspicious hex-like process name pattern'
|
|
533
|
+
];
|
|
534
|
+
|
|
535
|
+
const hasStrongSignal = (finding.risks || []).some(r => strongSignals.some(s => r.includes(s)));
|
|
536
|
+
|
|
537
|
+
// Apple-signed binaries in system locations with only weak signals should be dropped outright
|
|
538
|
+
const isAppleSystem = assessment.signedByApple && finding.path && (
|
|
539
|
+
finding.path.startsWith('/System') || finding.path.startsWith('/usr/libexec') || finding.path.startsWith('/usr/sbin')
|
|
540
|
+
);
|
|
541
|
+
if (isAppleSystem && !hasStrongSignal) {
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// If Gatekeeper accepts it and we only have weak heuristics, downgrade.
|
|
546
|
+
if (assessment.spctlAccepted && !hasStrongSignal) {
|
|
547
|
+
// Downgrade medium to low; keep high as-is.
|
|
548
|
+
if (finding.risk === 'medium') {
|
|
549
|
+
return {
|
|
550
|
+
...finding,
|
|
551
|
+
risk: 'low',
|
|
552
|
+
trust,
|
|
553
|
+
securityNote: 'Gatekeeper accepted (spctl). Downgraded to reduce false positives.'
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
return {
|
|
558
|
+
...finding,
|
|
559
|
+
trust
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
return {
|
|
564
|
+
...finding,
|
|
565
|
+
trust
|
|
566
|
+
};
|
|
567
|
+
}));
|
|
568
|
+
|
|
569
|
+
// Drop low-risk findings entirely to reduce noise (they were only medium before trust evaluation).
|
|
570
|
+
return enriched.filter(f => f && f.risk !== 'low');
|
|
571
|
+
}
|
|
572
|
+
}
|