pentesting 0.70.11 → 0.72.8

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.
@@ -0,0 +1,3160 @@
1
+ import {
2
+ DETECTION_PATTERNS,
3
+ EXIT_CODES,
4
+ ORPHAN_PROCESS_NAMES,
5
+ PROCESS_EVENTS,
6
+ PROCESS_ICONS,
7
+ PROCESS_ROLES,
8
+ STATUS_MARKERS,
9
+ SYSTEM_LIMITS,
10
+ clearAllProcesses,
11
+ deleteProcess,
12
+ getAllProcessIds,
13
+ getAllProcesses,
14
+ getBackgroundProcessesMap,
15
+ getLimits,
16
+ getPipelineConfig,
17
+ getProcess,
18
+ getProcessEventLog,
19
+ logEvent,
20
+ setProcess
21
+ } from "./chunk-74KL4OOU.js";
22
+
23
+ // src/shared/constants/time/conversions.ts
24
+ var MS_PER_MINUTE = 6e4;
25
+
26
+ // src/shared/constants/time/timeouts.ts
27
+ var TOOL_TIMEOUTS = {
28
+ /** Default timeout for most shell commands (30 minutes) */
29
+ DEFAULT_COMMAND: 18e5,
30
+ /** Quick operations like whois, dig */
31
+ QUICK: 15e3,
32
+ /** Search operations (searchsploit, web search) */
33
+ SEARCH: 3e4,
34
+ /** Directory brute force operations (60 minutes) */
35
+ DIR_BRUTEFORCE: 36e5,
36
+ /** Port scanning operations (60 minutes) */
37
+ PORT_SCAN: 36e5,
38
+ /** Full web application scan (60 minutes) */
39
+ WEB_SCAN: 36e5
40
+ };
41
+ var CURL_MAX_TIME_SEC = 600;
42
+
43
+ // src/shared/constants/limits/display.ts
44
+ var DISPLAY_LIMITS = {
45
+ /** Max characters to show in tool input preview */
46
+ TOOL_INPUT_PREVIEW: 50,
47
+ /** Max characters after truncation indicator */
48
+ TOOL_INPUT_TRUNCATED: 30,
49
+ /** Max characters to show in tool output preview */
50
+ TOOL_OUTPUT_PREVIEW: 200,
51
+ /** Max characters to show in tool output slice (events) */
52
+ TOOL_OUTPUT_SLICE: 80,
53
+ /** Max characters to show in tool error preview (events) */
54
+ TOOL_ERROR_SLICE: 120,
55
+ /** Max characters for delegate output preview */
56
+ DELEGATE_OUTPUT_SLICE: 60,
57
+ /** Max characters for command preview in logs */
58
+ COMMAND_PREVIEW: 300,
59
+ /** Max characters for output summary in agent loop */
60
+ OUTPUT_SUMMARY: 200,
61
+ /** Max characters for error preview in logs */
62
+ ERROR_PREVIEW: 100,
63
+ /** Max characters for status thought preview */
64
+ STATUS_THOUGHT_PREVIEW: 60,
65
+ /** Max characters after truncation for status */
66
+ STATUS_THOUGHT_TRUNCATED: 40,
67
+ /** Max characters for thought snippet in events */
68
+ THOUGHT_SNIPPET: 80,
69
+ /** Max characters for retry error preview */
70
+ RETRY_ERROR_PREVIEW: 60,
71
+ /** Max characters after truncation for retry error */
72
+ RETRY_ERROR_TRUNCATED: 57,
73
+ /** Exit delay in ms */
74
+ EXIT_DELAY: 100,
75
+ /** Max characters in reasoning buffer */
76
+ REASONING_BUFFER: 5e3,
77
+ /** Max characters in reasoning history slice */
78
+ REASONING_HISTORY_SLICE: 1e3,
79
+ /** Timer update interval in ms */
80
+ TIMER_INTERVAL: 1e3,
81
+ /** Default number of compact list items to display */
82
+ COMPACT_LIST_ITEMS: 7,
83
+ /** Number of targets to preview in state summary */
84
+ TARGET_PREVIEW: 3,
85
+ /** Number of important findings to preview */
86
+ FINDING_PREVIEW: 5,
87
+ /** Number of hashes to preview */
88
+ HASH_PREVIEW: 20,
89
+ /** Number of loot items to preview */
90
+ LOOT_PREVIEW: 30,
91
+ /** Number of recent process events to show */
92
+ RECENT_EVENT_COUNT: 5,
93
+ /** Max links to preview in browser output */
94
+ LINKS_PREVIEW: 20,
95
+ /** Max endpoints to sample */
96
+ ENDPOINT_SAMPLE: 5,
97
+ /** Purpose truncation length */
98
+ PURPOSE_TRUNCATE: 27,
99
+ /** Max characters for command description in background process */
100
+ COMMAND_DESCRIPTION_PREVIEW: 80,
101
+ /** Max characters for hash preview in tool output */
102
+ HASH_PREVIEW_LENGTH: 20,
103
+ /** Max characters for loot detail preview */
104
+ LOOT_DETAIL_PREVIEW: 30,
105
+ /** Max characters for episodic memory event detail preview */
106
+ MEMORY_EVENT_PREVIEW: 50,
107
+ /** Max characters for link text preview in browser */
108
+ LINK_TEXT_PREVIEW: 100,
109
+ /** Prefix length for API key redaction in logs */
110
+ API_KEY_PREFIX: 8,
111
+ /** Prefix length for sensitive value redaction */
112
+ REDACT_PREFIX: 4,
113
+ /** Max characters for raw JSON error preview */
114
+ RAW_JSON_ERROR_PREVIEW: 500,
115
+ // ─── Memory/History Slice Sizes (§4-3 Extract Magic Values) ─────
116
+ /** Recent credentials to include in context */
117
+ RECENT_CREDENTIALS: 5,
118
+ /** Recent failures to analyze for patterns */
119
+ RECENT_FAILURES: 5,
120
+ /** Recent successes to include in summary */
121
+ RECENT_SUCCESSES: 3,
122
+ /** Recent insights to display */
123
+ RECENT_INSIGHTS: 3,
124
+ /** Recent memory events to include */
125
+ RECENT_MEMORY_EVENTS: 10,
126
+ /** Summary window - small (for quick summaries) */
127
+ SUMMARY_WINDOW_SMALL: 100,
128
+ /** Summary window - large (for detailed summaries) */
129
+ SUMMARY_WINDOW_LARGE: 500,
130
+ // ─── Evidence Display ────────────────────────────────────────
131
+ /** Max evidence items to show in finding preview */
132
+ EVIDENCE_ITEMS_PREVIEW: 3,
133
+ /** Max characters per evidence item in preview */
134
+ EVIDENCE_PREVIEW_LENGTH: 120,
135
+ /** Max characters for API error response body */
136
+ API_ERROR_PREVIEW: 500,
137
+ /** Max characters per session/storage value in auth capture */
138
+ SESSION_VALUE_PREVIEW: 300
139
+ };
140
+
141
+ // src/shared/constants/llm/config.ts
142
+ var RETRY_CONFIG = {
143
+ baseDelayMs: 2e3,
144
+ maxDelayMs: 6e4,
145
+ jitterMs: 1e3,
146
+ /** WHY Infinity: Rate limit (429) errors are transient infrastructure delays.
147
+ * Infinite fixed-interval retries allow the agent to eventually recover
148
+ * without failing the entire engagement. */
149
+ autoRetryMaxAttempts: Infinity,
150
+ autoRetryDelayMs: 1e4
151
+ // 10s fixed interval for 429/network errors
152
+ };
153
+ var config = getPipelineConfig();
154
+ var mainLlmTokens = config.llm_nodes?.["main_llm"]?.limits?.max_tokens ?? 128e3;
155
+ var strategistTokens = config.llm_nodes?.["strategist"]?.limits?.max_tokens ?? 262144;
156
+ var LLM_LIMITS = {
157
+ /** WHY 64K: non-streaming calls (orchestrator, summaries) benefit from
158
+ * generous output budgets. Don't force premature truncation. */
159
+ nonStreamMaxTokens: 65536,
160
+ /** WHY dynamic: streaming calls are the main agent loop. Override via config. */
161
+ streamMaxTokens: mainLlmTokens,
162
+ /** WHY: ~3.5 chars/token is a reasonable average for mixed English/CJK content */
163
+ charsPerTokenEstimate: 3.5,
164
+ /** WHY: 5 minutes max timeout for streaming and non-streaming responses */
165
+ fetchTimeoutMs: 3e5,
166
+ /** WHY dynamic: Total liberation for reporting and complex strategy. */
167
+ orchestratorMaxTokens: strategistTokens
168
+ };
169
+ var LLM_ERROR_TYPES = {
170
+ RATE_LIMIT: "rate_limit",
171
+ AUTH_ERROR: "auth_error",
172
+ INVALID_REQUEST: "invalid_request",
173
+ NETWORK_ERROR: "network_error",
174
+ TIMEOUT: "timeout",
175
+ UNKNOWN: "unknown"
176
+ };
177
+
178
+ // src/shared/constants/limits/agent.ts
179
+ var { agent_loop, state, tool_output, prompt } = getLimits();
180
+ var AGENT_LIMITS = {
181
+ // ─── Agent Loop ───────────────────────────────────────────────
182
+ MAX_ITERATIONS: agent_loop.max_iterations,
183
+ MAX_CONSECUTIVE_IDLE: agent_loop.max_consecutive_idle,
184
+ MAX_SESSION_FILES: agent_loop.max_session_files,
185
+ PERIODIC_REPORT_INTERVAL: agent_loop.periodic_report_every_n_turns,
186
+ // ─── Prompt ───────────────────────────────────────────────────
187
+ MAX_PROMPT_CHARS: prompt.max_chars,
188
+ // ─── Tool Output ──────────────────────────────────────────────
189
+ MAX_TOOL_OUTPUT_LENGTH: tool_output.max_chars,
190
+ // ─── SharedState Growth Limits ────────────────────────────────
191
+ MAX_FINDINGS: state.max_findings,
192
+ MAX_LOOT: state.max_loot,
193
+ MAX_ACTION_LOG: state.max_action_log,
194
+ MAX_FAILED_PATHS: state.max_failed_paths,
195
+ MAX_ATTACK_NODES: state.max_attack_nodes,
196
+ MAX_ATTACK_EDGES: state.max_attack_edges,
197
+ // ─── Fixed Infrastructure Constants (not tunable via yaml) ───
198
+ /** Infinite retry on LLM errors — rate limit recovery */
199
+ MAX_CONSECUTIVE_LLM_ERRORS: agent_loop.max_consecutive_llm_errors === "infinity" || agent_loop.max_consecutive_llm_errors === Infinity ? Infinity : Number(agent_loop.max_consecutive_llm_errors ?? Infinity),
200
+ MAX_RETRIES: agent_loop.max_retries ?? 3,
201
+ MAX_INSTALL_RETRIES: 1,
202
+ // kept as fixed internal infrastructure config
203
+ MAX_TOKENS: LLM_LIMITS.streamMaxTokens,
204
+ ID_LENGTH: 7,
205
+ ID_RADIX: 36,
206
+ BLOCKED_PATTERN_KEY_SLICE: 80,
207
+ ERROR_SEARCH_PREVIEW_SLICE: 50,
208
+ MAX_BLOCKED_BEFORE_WARN: 2
209
+ };
210
+
211
+ // src/shared/constants/limits/memory.ts
212
+ var { state: state2, archive } = getLimits();
213
+ var MEMORY_LIMITS = {
214
+ WORKING_MEMORY_MAX_ENTRIES: state2.working_memory_max,
215
+ EPISODIC_MEMORY_MAX_EVENTS: state2.episodic_memory_max,
216
+ CONSECUTIVE_FAIL_THRESHOLD: state2.consecutive_fail_threshold,
217
+ DYNAMIC_TECHNIQUES_MAX: state2.dynamic_techniques_max,
218
+ TECHNIQUE_FAILURE_DECAY: state2.technique_failure_decay,
219
+ TECHNIQUE_PRUNE_THRESHOLD: state2.technique_prune_threshold,
220
+ MAX_TURN_ENTRIES: archive.max_turn_entries,
221
+ /** Maximum unverified techniques to show in prompt (display-only, not in yaml) */
222
+ PROMPT_UNVERIFIED_TECHNIQUES: 10
223
+ };
224
+
225
+ // src/shared/constants/patterns.ts
226
+ var INPUT_PROMPT_PATTERNS = [
227
+ /\[sudo\] password/i,
228
+ /Password:/i,
229
+ /password for/i,
230
+ /Enter passphrase/i,
231
+ /Are you sure.*\(yes\/no\)/i,
232
+ /\(y\/N\)/i,
233
+ /\(Y\/n\)/i
234
+ ];
235
+
236
+ // src/shared/constants/agent.ts
237
+ var APP_NAME = "Pentest AI";
238
+ var APP_VERSION = "0.72.8";
239
+ var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
240
+ var LLM_ROLES = {
241
+ SYSTEM: "system",
242
+ USER: "user",
243
+ ASSISTANT: "assistant"
244
+ };
245
+ var INPUT_TYPES = {
246
+ TEXT: "text",
247
+ PASSWORD: "password",
248
+ SUDO_PASSWORD: "sudo_password",
249
+ SSH_PASSWORD: "ssh_password",
250
+ SSH_KEY: "ssh_key",
251
+ PASSPHRASE: "passphrase",
252
+ API_KEY: "api_key",
253
+ CREDENTIAL: "credential",
254
+ CONFIRMATION: "confirmation",
255
+ CHOICE: "choice"
256
+ };
257
+ var SENSITIVE_INPUT_TYPES = [
258
+ INPUT_TYPES.PASSWORD,
259
+ INPUT_TYPES.SUDO_PASSWORD,
260
+ INPUT_TYPES.SSH_PASSWORD,
261
+ INPUT_TYPES.PASSPHRASE,
262
+ INPUT_TYPES.API_KEY,
263
+ INPUT_TYPES.CREDENTIAL
264
+ ];
265
+ var AUXILIARY_WORK_TYPES = {
266
+ EXTRACTION: "context_extraction",
267
+ REFLECTION: "reflection",
268
+ POST_STEP: "post_step_tasks",
269
+ TURN_ARCHIVE: "turn_archive",
270
+ TURN_CYCLE: "turn_cycle"
271
+ };
272
+
273
+ // src/shared/constants/protocol/phases.ts
274
+ var PHASES = {
275
+ RECON: "recon",
276
+ VULN_ANALYSIS: "vulnerability_analysis",
277
+ EXPLOIT: "exploit",
278
+ POST_EXPLOIT: "post_exploitation",
279
+ PRIV_ESC: "privilege_escalation",
280
+ LATERAL: "lateral_movement",
281
+ PERSISTENCE: "persistence",
282
+ EXFIL: "exfiltration",
283
+ WEB: "web",
284
+ REPORT: "report",
285
+ // CTF-specific phases
286
+ PWN: "pwn",
287
+ CRYPTO: "crypto",
288
+ FORENSICS: "forensics"
289
+ };
290
+ var PHASE_TRANSITIONS = {
291
+ [PHASES.RECON]: [PHASES.VULN_ANALYSIS, PHASES.WEB, PHASES.RECON, PHASES.PWN, PHASES.CRYPTO, PHASES.FORENSICS],
292
+ [PHASES.WEB]: [PHASES.VULN_ANALYSIS, PHASES.EXPLOIT, PHASES.RECON],
293
+ [PHASES.VULN_ANALYSIS]: [PHASES.EXPLOIT, PHASES.RECON, PHASES.WEB, PHASES.VULN_ANALYSIS],
294
+ [PHASES.EXPLOIT]: [PHASES.POST_EXPLOIT, PHASES.PRIV_ESC, PHASES.VULN_ANALYSIS, PHASES.LATERAL],
295
+ [PHASES.PRIV_ESC]: [PHASES.POST_EXPLOIT, PHASES.LATERAL, PHASES.EXPLOIT],
296
+ [PHASES.LATERAL]: [PHASES.POST_EXPLOIT, PHASES.EXPLOIT, PHASES.RECON],
297
+ [PHASES.PERSISTENCE]: [PHASES.POST_EXPLOIT, PHASES.LATERAL],
298
+ [PHASES.EXFIL]: [PHASES.POST_EXPLOIT, PHASES.REPORT],
299
+ [PHASES.POST_EXPLOIT]: [PHASES.PERSISTENCE, PHASES.EXFIL, PHASES.LATERAL, PHASES.REPORT],
300
+ [PHASES.REPORT]: [],
301
+ // CTF: 각 Phase에서 exploit/report로 전환 가능; 서로 간 전환도 허용
302
+ [PHASES.PWN]: [PHASES.EXPLOIT, PHASES.REPORT, PHASES.CRYPTO, PHASES.FORENSICS],
303
+ [PHASES.CRYPTO]: [PHASES.EXPLOIT, PHASES.REPORT, PHASES.PWN, PHASES.FORENSICS],
304
+ [PHASES.FORENSICS]: [PHASES.EXPLOIT, PHASES.REPORT, PHASES.PWN, PHASES.CRYPTO]
305
+ };
306
+ var AGENT_ROLES = {
307
+ ORCHESTRATOR: "orchestrator"
308
+ };
309
+
310
+ // src/shared/constants/protocol/services.ts
311
+ var SERVICES = {
312
+ HTTP: "http",
313
+ HTTPS: "https",
314
+ SSH: "ssh",
315
+ FTP: "ftp",
316
+ SMB: "smb",
317
+ AD: "ad",
318
+ MYSQL: "mysql",
319
+ MSSQL: "mssql",
320
+ POSTGRES: "postgresql",
321
+ MONGODB: "mongodb",
322
+ REDIS: "redis",
323
+ ELASTIC: "elasticsearch",
324
+ API: "api",
325
+ // AD / Directory services
326
+ KERBEROS: "kerberos",
327
+ LDAP: "ldap",
328
+ // Network/Infrastructure services
329
+ NFS: "nfs",
330
+ RDP: "rdp",
331
+ VNC: "vnc",
332
+ // Container services
333
+ DOCKER: "docker",
334
+ KUBERNETES: "kubernetes",
335
+ // Wireless services
336
+ WIFI: "wifi",
337
+ BLUETOOTH: "bluetooth",
338
+ // ICS/SCADA services
339
+ MODBUS: "modbus",
340
+ DNP3: "dnp3",
341
+ S7COMM: "s7comm",
342
+ // Email services
343
+ SMTP: "smtp",
344
+ POP3: "pop3",
345
+ IMAP: "imap"
346
+ };
347
+ var SERVICE_CATEGORIES = {
348
+ NETWORK: "network",
349
+ WEB: "web",
350
+ DATABASE: "database",
351
+ AD: "ad",
352
+ EMAIL: "email",
353
+ REMOTE_ACCESS: "remote_access",
354
+ FILE_SHARING: "file_sharing",
355
+ CLOUD: "cloud",
356
+ CONTAINER: "container",
357
+ API: "api",
358
+ WIRELESS: "wireless",
359
+ ICS: "ics"
360
+ };
361
+ var APPROVAL_LEVELS = {
362
+ AUTO: "auto",
363
+ CONFIRM: "confirm",
364
+ REVIEW: "review",
365
+ BLOCK: "block"
366
+ };
367
+ var DANGER_LEVELS = {
368
+ PASSIVE: "passive",
369
+ ACTIVE: "active",
370
+ EXPLOIT: "exploit"
371
+ };
372
+ var CATEGORY_APPROVAL = {
373
+ [SERVICE_CATEGORIES.NETWORK]: APPROVAL_LEVELS.CONFIRM,
374
+ [SERVICE_CATEGORIES.WEB]: APPROVAL_LEVELS.CONFIRM,
375
+ [SERVICE_CATEGORIES.DATABASE]: APPROVAL_LEVELS.REVIEW,
376
+ [SERVICE_CATEGORIES.AD]: APPROVAL_LEVELS.REVIEW,
377
+ [SERVICE_CATEGORIES.EMAIL]: APPROVAL_LEVELS.CONFIRM,
378
+ [SERVICE_CATEGORIES.REMOTE_ACCESS]: APPROVAL_LEVELS.REVIEW,
379
+ [SERVICE_CATEGORIES.FILE_SHARING]: APPROVAL_LEVELS.CONFIRM,
380
+ [SERVICE_CATEGORIES.CLOUD]: APPROVAL_LEVELS.REVIEW,
381
+ [SERVICE_CATEGORIES.CONTAINER]: APPROVAL_LEVELS.REVIEW,
382
+ [SERVICE_CATEGORIES.API]: APPROVAL_LEVELS.CONFIRM,
383
+ [SERVICE_CATEGORIES.WIRELESS]: APPROVAL_LEVELS.REVIEW,
384
+ [SERVICE_CATEGORIES.ICS]: APPROVAL_LEVELS.BLOCK
385
+ };
386
+ var DANGER_LEVEL_MAP = {
387
+ [SERVICE_CATEGORIES.NETWORK]: DANGER_LEVELS.PASSIVE,
388
+ [SERVICE_CATEGORIES.WEB]: DANGER_LEVELS.ACTIVE,
389
+ [SERVICE_CATEGORIES.API]: DANGER_LEVELS.ACTIVE,
390
+ [SERVICE_CATEGORIES.EMAIL]: DANGER_LEVELS.ACTIVE,
391
+ [SERVICE_CATEGORIES.REMOTE_ACCESS]: DANGER_LEVELS.EXPLOIT,
392
+ [SERVICE_CATEGORIES.FILE_SHARING]: DANGER_LEVELS.ACTIVE,
393
+ [SERVICE_CATEGORIES.DATABASE]: DANGER_LEVELS.EXPLOIT,
394
+ [SERVICE_CATEGORIES.AD]: DANGER_LEVELS.EXPLOIT,
395
+ [SERVICE_CATEGORIES.CLOUD]: DANGER_LEVELS.EXPLOIT,
396
+ [SERVICE_CATEGORIES.CONTAINER]: DANGER_LEVELS.EXPLOIT,
397
+ [SERVICE_CATEGORIES.WIRELESS]: DANGER_LEVELS.EXPLOIT,
398
+ [SERVICE_CATEGORIES.ICS]: DANGER_LEVELS.EXPLOIT
399
+ };
400
+
401
+ // src/shared/constants/protocol/pentest.ts
402
+ var TODO_STATUSES = {
403
+ PENDING: "pending",
404
+ IN_PROGRESS: "in_progress",
405
+ DONE: "done",
406
+ SKIPPED: "skipped"
407
+ };
408
+ var LOOT_TYPES = {
409
+ CREDENTIAL: "credential",
410
+ SESSION: "session",
411
+ HASH: "hash",
412
+ TOKEN: "token",
413
+ FILE: "file",
414
+ TICKET: "ticket",
415
+ SSH_KEY: "ssh_key",
416
+ CERTIFICATE: "certificate",
417
+ API_KEY: "api_key"
418
+ };
419
+ var APPROVAL_STATUSES = {
420
+ AUTO: "auto",
421
+ USER_CONFIRMED: "user_confirmed",
422
+ USER_REVIEWED: "user_reviewed",
423
+ DENIED: "denied"
424
+ };
425
+ var SEVERITIES = {
426
+ CRITICAL: "critical",
427
+ HIGH: "high",
428
+ MEDIUM: "medium",
429
+ LOW: "low",
430
+ INFO: "info"
431
+ };
432
+ var PRIORITIES = {
433
+ HIGH: "high",
434
+ MEDIUM: "medium",
435
+ LOW: "low"
436
+ };
437
+ var NOISE_LEVELS = {
438
+ LOW: "low",
439
+ MEDIUM: "medium",
440
+ HIGH: "high"
441
+ };
442
+ var DEFAULTS = {
443
+ ENGAGEMENT_NAME: "auto-assessment",
444
+ ENGAGEMENT_CLIENT: "internal",
445
+ UNKNOWN_PHASE: "unknown",
446
+ INIT_PHASE: "init",
447
+ /** Fallback service name when port fingerprinting yielded no result */
448
+ UNKNOWN_SERVICE: "unknown",
449
+ /** Default port state when not explicitly specified */
450
+ PORT_STATE_OPEN: "open"
451
+ };
452
+ var WORDLISTS = {
453
+ ROCKYOU: "/usr/share/wordlists/rockyou.txt",
454
+ COMMON_PASSWORDS: "/usr/share/seclists/Passwords/common-passwords.txt",
455
+ PASSWORDS_10K: "/usr/share/seclists/Passwords/Common-Credentials/10k-most-common.txt",
456
+ USERNAMES: "/usr/share/seclists/Usernames/top-usernames-shortlist.txt",
457
+ DIRB_COMMON: "/usr/share/wordlists/dirb/common.txt",
458
+ RAFT_MEDIUM_DIRS: "/usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt",
459
+ SUBDOMAINS: "/usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt",
460
+ API_ENDPOINTS: "/usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt",
461
+ API_FUZZ: "/usr/share/seclists/Fuzzing/api-fuzz.txt"
462
+ };
463
+ var HASHCAT_MODES = {
464
+ MD5: "0",
465
+ SHA1: "100",
466
+ NTLM: "1000",
467
+ SHA256: "1400",
468
+ bcrypt: "3200",
469
+ WPA: "2500"
470
+ };
471
+ var NOISE_CLASSIFICATION = {
472
+ HIGH: [
473
+ "arp_spoof",
474
+ "mitm_proxy",
475
+ "packet_sniff",
476
+ "dns_spoof",
477
+ "traffic_intercept",
478
+ "nmap",
479
+ "nuclei",
480
+ "nikto"
481
+ ],
482
+ MEDIUM: [
483
+ "ffuf",
484
+ "gobuster",
485
+ "dirsearch",
486
+ "feroxbuster"
487
+ ],
488
+ LOW_VISIBILITY: [
489
+ "update_mission",
490
+ "get_state",
491
+ "update_phase",
492
+ "update_todo",
493
+ "add_target",
494
+ "add_finding",
495
+ "add_loot",
496
+ "set_scope",
497
+ "bg_status",
498
+ "bg_cleanup",
499
+ "health_check",
500
+ "ask_user"
501
+ ]
502
+ };
503
+
504
+ // src/shared/constants/tool-names.ts
505
+ var TOOL_NAMES = {
506
+ // Basic Tools
507
+ RUN_CMD: "run_cmd",
508
+ READ_FILE: "read_file",
509
+ WRITE_FILE: "write_file",
510
+ BG_PROCESS: "bg_process",
511
+ // Intelligence & Research
512
+ PARSE_NMAP: "parse_nmap",
513
+ SEARCH_CVE: "search_cve",
514
+ WEB_SEARCH: "web_search",
515
+ BROWSE_URL: "browse_url",
516
+ EXTRACT_URLS: "extract_urls",
517
+ GET_CVE_INFO: "get_cve_info",
518
+ GET_OWASP_KNOWLEDGE: "get_owasp_knowledge",
519
+ GET_WEB_ATTACK_SURFACE: "get_web_attack_surface",
520
+ FILL_FORM: "fill_form",
521
+ // Payload Engineering
522
+ PAYLOAD_MUTATE: "payload_mutate",
523
+ GET_WORDLISTS: "get_wordlists",
524
+ // High-level / Engine Tools
525
+ ADD_FINDING: "add_finding",
526
+ UPDATE_MISSION: "update_mission",
527
+ UPDATE_TODO: "update_todo",
528
+ UPDATE_PHASE: "update_phase",
529
+ HASH_CRACK: "hash_crack",
530
+ ADD_LOOT: "add_loot",
531
+ GET_STATE: "get_state",
532
+ SET_SCOPE: "set_scope",
533
+ ADD_TARGET: "add_target",
534
+ ASK_USER: "ask_user",
535
+ HELP: "help",
536
+ // Session Management (3차 Insane — long session resumption)
537
+ SAVE_SESSION_SNAPSHOT: "save_session_snapshot",
538
+ // Resource Management
539
+ BG_STATUS: "bg_status",
540
+ BG_CLEANUP: "bg_cleanup",
541
+ HEALTH_CHECK: "health_check",
542
+ // Database Specialized
543
+ SQLMAP_BASIC: "sqlmap_basic",
544
+ SQLMAP_ADVANCED: "sqlmap_advanced",
545
+ MYSQL_ENUM: "mysql_enum",
546
+ POSTGRES_ENUM: "postgres_enum",
547
+ REDIS_ENUM: "redis_enum",
548
+ DB_BRUTE: "db_brute_common",
549
+ // Network Specialized
550
+ NMAP_QUICK: "nmap_quick",
551
+ NMAP_FULL: "nmap_full",
552
+ RUSTSCAN: "rustscan_fast",
553
+ // Web Specialized
554
+ HTTP_FINGERPRINT: "http_fingerprint",
555
+ WAF_DETECT: "waf_detect",
556
+ DIRSEARCH: "dirsearch",
557
+ NUCLEI_WEB: "nuclei_web",
558
+ // AD Specialized
559
+ BLOODHOUND_COLLECT: "bloodhound_collect",
560
+ KERBEROAST: "kerberoast",
561
+ LDAP_ENUM: "ldap_enum",
562
+ // API Specialized
563
+ API_DISCOVER: "api_discover",
564
+ GRAPHQL_INTROSPECT: "graphql_introspect",
565
+ SWAGGER_PARSE: "swagger_parse",
566
+ API_FUZZ: "api_fuzz",
567
+ // Cloud Specialized
568
+ AWS_S3_CHECK: "aws_s3_check",
569
+ CLOUD_META_CHECK: "cloud_meta_check",
570
+ // Container Specialized
571
+ DOCKER_PS: "docker_ps",
572
+ KUBE_GET_PODS: "kube_get_pods",
573
+ // Service Enumeration
574
+ SMTP_ENUM: "smtp_enum",
575
+ FTP_ENUM: "ftp_enum",
576
+ SMB_ENUM: "smb_enum",
577
+ MODBUS_ENUM: "modbus_enum",
578
+ SSH_ENUM: "ssh_enum",
579
+ RDP_ENUM: "rdp_enum",
580
+ WIFI_SCAN: "wifi_scan",
581
+ // Network Attack Tools
582
+ ARP_SPOOF: "arp_spoof",
583
+ MITM_PROXY: "mitm_proxy",
584
+ PACKET_SNIFF: "packet_sniff",
585
+ DNS_SPOOF: "dns_spoof",
586
+ TRAFFIC_INTERCEPT: "traffic_intercept",
587
+ ANALYZE_PCAP: "analyze_pcap",
588
+ // IMP-3: PWN / Binary Exploitation
589
+ CHECKSEC: "checksec",
590
+ FILE_INFO: "file_info",
591
+ STRINGS_BINARY: "strings_binary",
592
+ PATTERN_CREATE: "pattern_create",
593
+ PATTERN_OFFSET: "pattern_offset",
594
+ ONE_GADGET: "one_gadget",
595
+ ROP_GADGETS: "rop_gadgets",
596
+ LTRACE_BINARY: "ltrace_binary",
597
+ STRACE_BINARY: "strace_binary",
598
+ PWN_TEMPLATE: "pwn_template",
599
+ // IMP-4: Crypto Attacks
600
+ RSA_ANALYZE: "rsa_analyze",
601
+ FREQUENCY_ANALYSIS: "frequency_analysis",
602
+ HASH_IDENTIFY: "hash_identify",
603
+ XOR_DECODE: "xor_decode",
604
+ BASE_DECODE: "base_decode",
605
+ PADDING_ORACLE_CHECK: "padding_oracle_check",
606
+ // IMP-5: Digital Forensics
607
+ BINWALK_ANALYZE: "binwalk_analyze",
608
+ STEGHIDE_EXTRACT: "steghide_extract",
609
+ ZSTEG_ANALYZE: "zsteg_analyze",
610
+ EXIF_DATA: "exif_data",
611
+ PCAP_ANALYZE: "pcap_analyze",
612
+ FOREMOST_CARVE: "foremost_carve",
613
+ VOLATILITY_ANALYZE: "volatility_analyze",
614
+ STEGSEEK: "stegseek",
615
+ // IMP-12: OSINT
616
+ WHOIS_LOOKUP: "whois_lookup",
617
+ DNS_RECON: "dns_recon",
618
+ SUBDOMAIN_ENUM: "subdomain_enum",
619
+ HARVESTER: "harvester",
620
+ SHODAN_QUERY: "shodan_query",
621
+ CERT_TRANSPARENCY: "cert_transparency",
622
+ GOOGLE_DORK: "google_dork"
623
+ };
624
+
625
+ // src/shared/constants/attack.ts
626
+ var ATTACK_TACTICS = {
627
+ INITIAL_ACCESS: "initial_access",
628
+ EXECUTION: "execution",
629
+ PERSISTENCE: "persistence",
630
+ PRIV_ESC: "privilege_escalation",
631
+ DEFENSE_EVASION: "defense_evasion",
632
+ CREDENTIAL_ACCESS: "credential_access",
633
+ DISCOVERY: "discovery",
634
+ LATERAL_MOVEMENT: "lateral_movement",
635
+ COLLECTION: "collection",
636
+ EXFILTRATION: "exfiltration",
637
+ C2: "command_and_control",
638
+ IMPACT: "impact"
639
+ };
640
+ var ATTACK_VALUE_RANK = {
641
+ HIGH: 3,
642
+ MED: 2,
643
+ LOW: 1,
644
+ NONE: 0
645
+ };
646
+ var CONFIDENCE_THRESHOLDS = {
647
+ /** ≥80: exploit confirmed, shell access, flag captured */
648
+ CONFIRMED: 80,
649
+ /** ≥50: strong but not yet proven */
650
+ PROBABLE: 50,
651
+ /** ≥25: initial discovery, weak signal */
652
+ POSSIBLE: 25,
653
+ /** <25: speculation only */
654
+ NONE: 0
655
+ };
656
+
657
+ // src/shared/constants/event-types.ts
658
+ var EVENT_TYPES = {
659
+ START: "start",
660
+ PHASE_CHANGE: "phase_change",
661
+ STATE_CHANGE: "state_change",
662
+ REASONING_START: "reasoning_start",
663
+ REASONING_DELTA: "reasoning_delta",
664
+ REASONING_END: "reasoning_end",
665
+ AI_RESPONSE: "ai_response",
666
+ TOOL_CALL: "tool_call",
667
+ TOOL_RESULT: "tool_result",
668
+ ERROR: "error",
669
+ COMPLETE: "complete",
670
+ THINK: "think",
671
+ APPROVAL_ASK: "approval_ask",
672
+ APPROVAL_RESULT: "approval_result",
673
+ DELEGATE: "delegate",
674
+ DELEGATE_RESULT: "delegate_result",
675
+ ESCALATE: "escalate",
676
+ AGENT_SWITCH: "agent_switch",
677
+ OBSERVE: "observe",
678
+ RETRY: "retry",
679
+ USAGE_UPDATE: "usage_update",
680
+ INPUT_REQUEST: "input_request",
681
+ FLAG_FOUND: "flag_found",
682
+ NOTIFICATION: "notification",
683
+ AUXILIARY_WORK_START: "auxiliary_work_start",
684
+ AUXILIARY_WORK_END: "auxiliary_work_end",
685
+ QUEUE_DRAINED: "queue_drained"
686
+ };
687
+ var COMMAND_EVENT_TYPES = {
688
+ TOOL_MISSING: "tool_missing",
689
+ TOOL_INSTALL: "tool_install",
690
+ TOOL_INSTALLED: "tool_installed",
691
+ TOOL_INSTALL_FAILED: "tool_install_failed",
692
+ TOOL_RETRY: "tool_retry",
693
+ COMMAND_START: "command_start",
694
+ COMMAND_SUCCESS: "command_success",
695
+ COMMAND_FAILED: "command_failed",
696
+ COMMAND_ERROR: "command_error",
697
+ INPUT_REQUIRED: "input_required",
698
+ COMMAND_STDOUT: "command_stdout",
699
+ PROCESS_NOTIFICATION: "process_notification"
700
+ };
701
+
702
+ // src/shared/constants/ui-commands.ts
703
+ var UI_COMMANDS = {
704
+ HELP: "help",
705
+ HELP_SHORT: "h",
706
+ CLEAR: "clear",
707
+ CLEAR_SHORT: "c",
708
+ TARGET: "target",
709
+ TARGET_SHORT: "t",
710
+ START: "start",
711
+ START_SHORT: "s",
712
+ FINDINGS: "findings",
713
+ FINDINGS_SHORT: "f",
714
+ ASSETS: "assets",
715
+ ASSETS_SHORT: "a",
716
+ LOGS: "logs",
717
+ LOGS_SHORT: "l",
718
+ CTF: "ctf",
719
+ TOR: "tor",
720
+ AUTO: "auto",
721
+ LAYOUT: "layout",
722
+ GRAPH: "graph",
723
+ GRAPH_SHORT: "g",
724
+ PATHS: "paths",
725
+ PATHS_SHORT: "p",
726
+ EXIT: "exit",
727
+ QUIT: "quit",
728
+ EXIT_SHORT: "q"
729
+ };
730
+
731
+ // src/engine/process/process-lifecycle.ts
732
+ import { spawn as spawn4 } from "child_process";
733
+ import { writeFileSync as writeFileSync3, unlinkSync } from "fs";
734
+
735
+ // src/engine/process/process-tree.ts
736
+ import { execSync } from "child_process";
737
+ function discoverChildPids(parentPid) {
738
+ try {
739
+ const result = execSync(`pgrep -P ${parentPid} 2>/dev/null || true`, {
740
+ encoding: "utf-8",
741
+ timeout: SYSTEM_LIMITS.PROCESS_OP_TIMEOUT_MS
742
+ }).trim();
743
+ if (!result) return [];
744
+ return result.split("\n").map((s) => parseInt(s.trim(), 10)).filter((n) => !isNaN(n) && n > 0);
745
+ } catch {
746
+ return [];
747
+ }
748
+ }
749
+ function discoverAllDescendants(pid, visited = /* @__PURE__ */ new Set()) {
750
+ if (visited.has(pid)) return [];
751
+ visited.add(pid);
752
+ const children = discoverChildPids(pid);
753
+ const all = [...children];
754
+ for (const child of children) {
755
+ all.push(...discoverAllDescendants(child, visited));
756
+ }
757
+ return all;
758
+ }
759
+ function isPidAlive(pid) {
760
+ try {
761
+ process.kill(pid, 0);
762
+ return true;
763
+ } catch {
764
+ return false;
765
+ }
766
+ }
767
+ async function killProcessTree(pid, childPids, waitMs = SYSTEM_LIMITS.SHUTDOWN_WAIT_MS) {
768
+ try {
769
+ process.kill(-pid, "SIGTERM");
770
+ } catch {
771
+ try {
772
+ process.kill(pid, "SIGTERM");
773
+ } catch {
774
+ }
775
+ }
776
+ for (const childPid of childPids) {
777
+ try {
778
+ process.kill(childPid, "SIGTERM");
779
+ } catch {
780
+ }
781
+ }
782
+ await new Promise((r) => setTimeout(r, waitMs));
783
+ if (isPidAlive(pid)) {
784
+ try {
785
+ process.kill(-pid, "SIGKILL");
786
+ } catch {
787
+ }
788
+ try {
789
+ process.kill(pid, "SIGKILL");
790
+ } catch {
791
+ }
792
+ }
793
+ for (const childPid of childPids) {
794
+ if (isPidAlive(childPid)) {
795
+ try {
796
+ process.kill(childPid, "SIGKILL");
797
+ } catch {
798
+ }
799
+ }
800
+ }
801
+ }
802
+ function killProcessTreeSync(pid, childPids) {
803
+ try {
804
+ process.kill(-pid, "SIGKILL");
805
+ } catch {
806
+ }
807
+ try {
808
+ process.kill(pid, "SIGKILL");
809
+ } catch {
810
+ }
811
+ for (const childPid of childPids) {
812
+ try {
813
+ process.kill(childPid, "SIGKILL");
814
+ } catch {
815
+ }
816
+ }
817
+ }
818
+
819
+ // src/engine/process/process-detector.ts
820
+ function detectProcessRole(command) {
821
+ const tags = [];
822
+ let port;
823
+ let role = PROCESS_ROLES.BACKGROUND;
824
+ let isInteractive = false;
825
+ const cmd = command.toLowerCase();
826
+ if (cmd.includes("-lvnp") || cmd.includes("-nlvp") || cmd.includes("-lp") || cmd.includes("nc") && cmd.includes("listen")) {
827
+ tags.push(PROCESS_ROLES.LISTENER);
828
+ role = PROCESS_ROLES.LISTENER;
829
+ isInteractive = true;
830
+ const portMatch = command.match(DETECTION_PATTERNS.LISTENER);
831
+ if (portMatch) port = parseInt(portMatch[1], 10);
832
+ }
833
+ if (cmd.includes("http.server") || cmd.includes("simplehttpserver") || cmd.includes("httpd") || cmd.includes("php -s")) {
834
+ tags.push(PROCESS_ROLES.SERVER);
835
+ role = PROCESS_ROLES.SERVER;
836
+ const portMatch = command.match(DETECTION_PATTERNS.HTTP_SERVER);
837
+ if (portMatch) port = parseInt(portMatch[1], 10);
838
+ if (!port) {
839
+ const genericPort = command.match(DETECTION_PATTERNS.GENERIC_PORT);
840
+ if (genericPort) port = parseInt(genericPort[1], 10);
841
+ else if (cmd.includes("http.server")) port = SYSTEM_LIMITS.WEB_PORT_RANGE.MIN;
842
+ }
843
+ }
844
+ if (cmd.includes("tcpdump") || cmd.includes("tshark")) {
845
+ tags.push(PROCESS_ROLES.SNIFFER);
846
+ role = PROCESS_ROLES.SNIFFER;
847
+ }
848
+ if (cmd.includes("arpspoof") || cmd.includes("dnsspoof") || cmd.includes("ettercap")) {
849
+ tags.push(PROCESS_ROLES.SPOOFER);
850
+ role = PROCESS_ROLES.SPOOFER;
851
+ }
852
+ if (cmd.includes("mitm") || cmd.includes("proxy") || cmd.includes("intercept")) {
853
+ tags.push(PROCESS_ROLES.PROXY);
854
+ role = PROCESS_ROLES.PROXY;
855
+ }
856
+ if (cmd.includes("callback") || cmd.includes("oob")) {
857
+ tags.push(PROCESS_ROLES.CALLBACK);
858
+ role = PROCESS_ROLES.CALLBACK;
859
+ }
860
+ if (cmd.includes("socat") && cmd.includes("listen")) {
861
+ tags.push(PROCESS_ROLES.LISTENER);
862
+ role = PROCESS_ROLES.LISTENER;
863
+ isInteractive = true;
864
+ }
865
+ if (tags.length === 0) tags.push(PROCESS_ROLES.BACKGROUND);
866
+ return { tags, port, role, isInteractive };
867
+ }
868
+ function detectConnection(stdout) {
869
+ return DETECTION_PATTERNS.CONNECTION.some((p) => p.test(stdout));
870
+ }
871
+
872
+ // src/engine/tools-base/event-emitter.ts
873
+ var globalEventEmitter = null;
874
+ var globalInputHandler = null;
875
+ function setCommandEventEmitter(emitter) {
876
+ globalEventEmitter = emitter;
877
+ }
878
+ function setInputHandler(handler) {
879
+ globalInputHandler = handler;
880
+ }
881
+ function clearInputHandler() {
882
+ globalInputHandler = null;
883
+ }
884
+ function clearCommandEventEmitter() {
885
+ globalEventEmitter = null;
886
+ }
887
+ function getEventEmitter() {
888
+ return globalEventEmitter;
889
+ }
890
+ function getInputHandler() {
891
+ return globalInputHandler;
892
+ }
893
+
894
+ // src/shared/constants/paths.ts
895
+ import path from "path";
896
+ var _dirs = null;
897
+ var _root = null;
898
+ function getRoot() {
899
+ if (_root === null) {
900
+ const ws = getPipelineConfig().workspace;
901
+ _root = ws?.root ?? ".pentesting";
902
+ }
903
+ return _root;
904
+ }
905
+ function getDirs() {
906
+ if (_dirs === null) {
907
+ const ws = getPipelineConfig().workspace;
908
+ _dirs = ws?.directories ?? {};
909
+ }
910
+ return _dirs;
911
+ }
912
+ function dir(key, fallback) {
913
+ return getDirs()[key] ?? fallback;
914
+ }
915
+ var PENTESTING_ROOT = getRoot();
916
+ var WORK_DIR = dir("workspace", `${getRoot()}/workspace`);
917
+ var MEMORY_DIR = dir("memory", `${getRoot()}/memory`);
918
+ var WORKSPACE = {
919
+ get ROOT() {
920
+ return path.resolve(getRoot());
921
+ },
922
+ get TMP() {
923
+ return path.resolve(dir("workspace", `${getRoot()}/workspace`));
924
+ },
925
+ get MEMORY() {
926
+ return path.resolve(dir("memory", `${getRoot()}/memory`));
927
+ },
928
+ get REPORTS() {
929
+ return path.resolve(dir("reports", `${getRoot()}/reports`));
930
+ },
931
+ get SESSIONS() {
932
+ return path.resolve(dir("sessions", `${getRoot()}/sessions`));
933
+ },
934
+ get LOOT() {
935
+ return path.resolve(dir("loot", `${getRoot()}/loot`));
936
+ },
937
+ get DEBUG() {
938
+ return path.resolve(dir("debug", `${getRoot()}/debug`));
939
+ },
940
+ /** DEPRECATED — kept for clearWorkspace cleanup only */
941
+ get OUTPUTS() {
942
+ return path.resolve(`${getRoot()}/outputs`);
943
+ },
944
+ /** DEPRECATED — kept for clearWorkspace cleanup only */
945
+ get JOURNAL() {
946
+ return path.resolve(`${getRoot()}/journal`);
947
+ },
948
+ /** DEPRECATED — kept for clearWorkspace cleanup only */
949
+ get ARCHIVE() {
950
+ return path.resolve(dir("archive", `${getRoot()}/archive`));
951
+ },
952
+ /** Turn insight files root: .pentesting/turns/ */
953
+ get TURNS() {
954
+ return path.resolve(dir("turns", `${getRoot()}/turns`));
955
+ },
956
+ /**
957
+ * Resolve the insight file for a specific turn: .pentesting/turns/{N}.md
958
+ * Pipeline.yaml: workspace.turn_structure → single file per turn
959
+ */
960
+ turnPath(turn) {
961
+ return path.resolve(dir("turns", `${getRoot()}/turns`), `${turn}.md`);
962
+ }
963
+ };
964
+
965
+ // src/shared/utils/command-validator/security-lists.ts
966
+ var ALLOWED_BINARIES = /* @__PURE__ */ new Set([
967
+ // Network scanning
968
+ "nmap",
969
+ "rustscan",
970
+ // Web scanning
971
+ "ffuf",
972
+ "gobuster",
973
+ "dirb",
974
+ "dirsearch",
975
+ "nikto",
976
+ "nuclei",
977
+ "whatweb",
978
+ // Vulnerability scanning
979
+ "sqlmap",
980
+ "zap",
981
+ "wpscan",
982
+ // Password attacks
983
+ "hydra",
984
+ "john",
985
+ "hashcat",
986
+ "medusa",
987
+ // Network tools
988
+ "netcat",
989
+ "nc",
990
+ "ncat",
991
+ "socat",
992
+ "curl",
993
+ "wget",
994
+ "ping",
995
+ "traceroute",
996
+ // DNS
997
+ "dig",
998
+ "nslookup",
999
+ "host",
1000
+ "dnsrecon",
1001
+ // SMB/AD
1002
+ "smbclient",
1003
+ "rpcclient",
1004
+ "crackmapexec",
1005
+ "netexec",
1006
+ "bloodhound",
1007
+ "impacket-smbclient",
1008
+ "impacket-psexec",
1009
+ "impacket-wmiexec",
1010
+ "impacket-secretsdump",
1011
+ "impacket-getTGT",
1012
+ "impacket-getST",
1013
+ "impacket-GetNPUsers",
1014
+ "impacket-GetUserSPNs",
1015
+ // Exploitation
1016
+ "metasploit",
1017
+ "msfconsole",
1018
+ "msfvenom",
1019
+ // Misc pentesting
1020
+ "whois",
1021
+ "searchsploit",
1022
+ "seclists",
1023
+ "enum4linux",
1024
+ "evil-winrm",
1025
+ // Shell utilities (safe pipe targets & general tools)
1026
+ "head",
1027
+ "tail",
1028
+ "grep",
1029
+ "egrep",
1030
+ "fgrep",
1031
+ "awk",
1032
+ "sed",
1033
+ "cut",
1034
+ "tr",
1035
+ "sort",
1036
+ "uniq",
1037
+ "wc",
1038
+ "tee",
1039
+ "xargs",
1040
+ "cat",
1041
+ "less",
1042
+ "more",
1043
+ "echo",
1044
+ "printf",
1045
+ "true",
1046
+ "false",
1047
+ "test",
1048
+ "find",
1049
+ "ls",
1050
+ "file",
1051
+ "strings",
1052
+ "xxd",
1053
+ "base64",
1054
+ "python3",
1055
+ "python",
1056
+ "perl",
1057
+ "ruby",
1058
+ "php",
1059
+ "node",
1060
+ "jq",
1061
+ "xmllint",
1062
+ // Package management (for auto-install)
1063
+ "apt-get",
1064
+ "apt",
1065
+ "dpkg",
1066
+ "pip",
1067
+ "pip3",
1068
+ // System info
1069
+ "id",
1070
+ "whoami",
1071
+ "hostname",
1072
+ "uname",
1073
+ "env",
1074
+ "printenv",
1075
+ "which",
1076
+ "type",
1077
+ "ps",
1078
+ "top",
1079
+ "free",
1080
+ "df",
1081
+ "du",
1082
+ "lsof",
1083
+ "ss",
1084
+ "netstat",
1085
+ // File operations (safe subset)
1086
+ "cp",
1087
+ "mv",
1088
+ "mkdir",
1089
+ "touch",
1090
+ "ln",
1091
+ // SSH/SCP
1092
+ "ssh",
1093
+ "scp",
1094
+ "sshpass",
1095
+ "ssh-keygen",
1096
+ // Network sniffing
1097
+ "tcpdump",
1098
+ "tshark",
1099
+ // Proxy
1100
+ "proxychains",
1101
+ "proxychains4",
1102
+ "chisel",
1103
+ // Binary analysis
1104
+ "radare2",
1105
+ "r2",
1106
+ "gdb",
1107
+ "strace",
1108
+ "ltrace",
1109
+ "objdump",
1110
+ "readelf",
1111
+ // Git
1112
+ "git",
1113
+ // Sudo
1114
+ "sudo",
1115
+ // Misc
1116
+ "sleep",
1117
+ "date",
1118
+ "md5sum",
1119
+ "sha256sum",
1120
+ "sha1sum",
1121
+ "tar",
1122
+ "gzip",
1123
+ "gunzip",
1124
+ "zip",
1125
+ "unzip",
1126
+ "openssl",
1127
+ "certutil"
1128
+ ]);
1129
+ var INJECTION_PATTERNS = [
1130
+ /\$\(/,
1131
+ // Command substitution $(...)
1132
+ /`/,
1133
+ // Backtick command substitution
1134
+ /\$\{/,
1135
+ // Variable expansion ${...}
1136
+ /\$\[/,
1137
+ // Arithmetic expansion $[...]
1138
+ /\0/,
1139
+ // Null byte injection
1140
+ /\n/,
1141
+ // Newline injection
1142
+ /\r/
1143
+ // Carriage return injection
1144
+ ];
1145
+ var SAFE_PIPE_TARGETS = /* @__PURE__ */ new Set([
1146
+ "head",
1147
+ "tail",
1148
+ "grep",
1149
+ "egrep",
1150
+ "fgrep",
1151
+ "awk",
1152
+ "sed",
1153
+ "cut",
1154
+ "tr",
1155
+ "sort",
1156
+ "uniq",
1157
+ "wc",
1158
+ "tee",
1159
+ "xargs",
1160
+ "less",
1161
+ "more",
1162
+ "cat",
1163
+ "jq",
1164
+ "xmllint",
1165
+ "strings",
1166
+ "base64",
1167
+ "xxd",
1168
+ "python3",
1169
+ "python"
1170
+ ]);
1171
+ var SAFE_REDIRECT_PATHS = [`${WORK_DIR}/`];
1172
+ var BLOCKED_BINARIES = /* @__PURE__ */ new Set([
1173
+ "rm",
1174
+ "shred",
1175
+ "wipe",
1176
+ "mkfs",
1177
+ "fdisk",
1178
+ "parted",
1179
+ "dd",
1180
+ "shutdown",
1181
+ "reboot",
1182
+ "poweroff",
1183
+ "init",
1184
+ "systemctl",
1185
+ "docker",
1186
+ "kubectl",
1187
+ "iptables",
1188
+ "ip6tables",
1189
+ "nft",
1190
+ "crontab"
1191
+ ]);
1192
+
1193
+ // src/shared/utils/debug/debug-logger.ts
1194
+ import { appendFileSync, writeFileSync, statSync, readFileSync } from "fs";
1195
+ import { join } from "path";
1196
+
1197
+ // src/shared/utils/file-ops/file-utils.ts
1198
+ import { existsSync, mkdirSync } from "fs";
1199
+ function ensureDirExists(dirPath) {
1200
+ if (!existsSync(dirPath)) {
1201
+ mkdirSync(dirPath, { recursive: true });
1202
+ }
1203
+ }
1204
+ function fileTimestamp() {
1205
+ return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
1206
+ }
1207
+ function sanitizeFilename(name, maxLength = 30) {
1208
+ return name.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, maxLength);
1209
+ }
1210
+
1211
+ // src/shared/constants/files/extensions.ts
1212
+ var FILE_EXTENSIONS = {
1213
+ // Data formats
1214
+ JSON: ".json",
1215
+ MARKDOWN: ".md",
1216
+ TXT: ".txt",
1217
+ XML: ".xml",
1218
+ // Network capture
1219
+ PCAP: ".pcap",
1220
+ HOSTS: ".hosts",
1221
+ MITM: ".mitm",
1222
+ // Process I/O
1223
+ STDOUT: ".stdout",
1224
+ STDERR: ".stderr",
1225
+ STDIN: ".stdin",
1226
+ // Scripts
1227
+ SH: ".sh",
1228
+ PY: ".py",
1229
+ CJS: ".cjs",
1230
+ // Config
1231
+ ENV: ".env",
1232
+ BAK: ".bak"
1233
+ };
1234
+
1235
+ // src/shared/constants/files/special.ts
1236
+ var SPECIAL_FILES = {
1237
+ /** Latest state snapshot */
1238
+ LATEST_STATE: "latest.json",
1239
+ /** README */
1240
+ README: "README.md",
1241
+ /** Session summary (single source — LLM primary, deterministic fallback) */
1242
+ SUMMARY: "summary.md",
1243
+ /** Persistent knowledge store */
1244
+ PERSISTENT_KNOWLEDGE: "persistent-knowledge.json",
1245
+ /** Debug log file */
1246
+ DEBUG_LOG: "debug.log",
1247
+ /**
1248
+ * Session snapshot for Insane-level long session resumption (3차).
1249
+ * Stores current phase, achieved foothold, next steps, credentials.
1250
+ * Written by PlaybookSynthesizer on flag capture / manual update_mission.
1251
+ */
1252
+ SESSION_SNAPSHOT: "session-snapshot.json"
1253
+ };
1254
+
1255
+ // src/shared/constants/files/patterns.ts
1256
+ var FILE_PATTERNS = {
1257
+ /** Tool output filename: nmap.txt, gobuster.txt (sanitized) */
1258
+ toolOutput(toolName) {
1259
+ return `${sanitizeFilename(toolName)}.txt`;
1260
+ },
1261
+ /** Generate session snapshot filename: 2026-02-21T08-30-15.json */
1262
+ session() {
1263
+ return `${fileTimestamp()}.json`;
1264
+ }
1265
+ };
1266
+
1267
+ // src/shared/utils/debug/debug-logger.ts
1268
+ var ROTATE_BYTES = 5 * 1024 * 1024;
1269
+ var WIPE_BYTES = 20 * 1024 * 1024;
1270
+ var ROTATE_CHECK_INTERVAL = 500;
1271
+ var DebugLogger = class _DebugLogger {
1272
+ static instance;
1273
+ logPath;
1274
+ initialized = false;
1275
+ writeCount = 0;
1276
+ constructor(clearOnInit = false) {
1277
+ const debugDir = WORKSPACE.DEBUG;
1278
+ try {
1279
+ ensureDirExists(debugDir);
1280
+ this.logPath = join(debugDir, SPECIAL_FILES.DEBUG_LOG);
1281
+ if (clearOnInit) {
1282
+ this.clear();
1283
+ }
1284
+ this.initialized = true;
1285
+ this.log("general", "=== DEBUG LOGGER INITIALIZED ===");
1286
+ this.log("general", `Log file: ${this.logPath}`);
1287
+ } catch (e) {
1288
+ console.error("[DebugLogger] Failed to initialize:", e);
1289
+ this.logPath = "";
1290
+ }
1291
+ }
1292
+ static getInstance(clearOnInit = false) {
1293
+ if (!_DebugLogger.instance) {
1294
+ _DebugLogger.instance = new _DebugLogger(clearOnInit);
1295
+ }
1296
+ return _DebugLogger.instance;
1297
+ }
1298
+ /** Reset the singleton instance (used by initDebugLogger) */
1299
+ static resetInstance(clearOnInit = false) {
1300
+ _DebugLogger.instance = new _DebugLogger(clearOnInit);
1301
+ return _DebugLogger.instance;
1302
+ }
1303
+ /**
1304
+ * Rotate or wipe debug.log if it exceeds size thresholds.
1305
+ * Called every ROTATE_CHECK_INTERVAL writes to amortize statSync cost.
1306
+ *
1307
+ * Policy:
1308
+ * > 20 MB → wipe entirely (too much data, disk pressure)
1309
+ * > 5 MB → keep latest half (recent logs more useful than old ones)
1310
+ */
1311
+ rotateIfNeeded() {
1312
+ try {
1313
+ const size = statSync(this.logPath).size;
1314
+ if (size > WIPE_BYTES) {
1315
+ writeFileSync(this.logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [GENERAL] === LOG WIPED (exceeded ${Math.round(WIPE_BYTES / 1024 / 1024)}MB) ===
1316
+ `);
1317
+ } else if (size > ROTATE_BYTES) {
1318
+ const content = readFileSync(this.logPath, "utf-8");
1319
+ const half = content.slice(Math.floor(content.length / 2));
1320
+ const firstNewline = half.indexOf("\n");
1321
+ const trimmed = firstNewline >= 0 ? half.slice(firstNewline + 1) : half;
1322
+ writeFileSync(this.logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [GENERAL] === LOG ROTATED (exceeded ${Math.round(ROTATE_BYTES / 1024 / 1024)}MB, kept latest half) ===
1323
+ ` + trimmed);
1324
+ }
1325
+ } catch {
1326
+ }
1327
+ }
1328
+ log(category, message, data) {
1329
+ if (!this.initialized || !this.logPath) return;
1330
+ try {
1331
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1332
+ let logLine = `[${timestamp}] [${category.toUpperCase()}] ${message}`;
1333
+ if (data !== void 0) {
1334
+ logLine += ` | ${JSON.stringify(data)}`;
1335
+ }
1336
+ logLine += "\n";
1337
+ appendFileSync(this.logPath, logLine);
1338
+ if (++this.writeCount % ROTATE_CHECK_INTERVAL === 0) {
1339
+ this.rotateIfNeeded();
1340
+ }
1341
+ } catch (e) {
1342
+ console.error("[DebugLogger] Write error:", e);
1343
+ }
1344
+ }
1345
+ logRaw(category, label, raw) {
1346
+ if (!this.initialized || !this.logPath) return;
1347
+ try {
1348
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1349
+ const logLine = `[${timestamp}] [${category.toUpperCase()}] ${label}:
1350
+ ${raw}
1351
+ ---
1352
+ `;
1353
+ appendFileSync(this.logPath, logLine);
1354
+ if (++this.writeCount % ROTATE_CHECK_INTERVAL === 0) {
1355
+ this.rotateIfNeeded();
1356
+ }
1357
+ } catch (e) {
1358
+ console.error("[DebugLogger] Write error:", e);
1359
+ }
1360
+ }
1361
+ clear() {
1362
+ if (!this.logPath) return;
1363
+ try {
1364
+ writeFileSync(this.logPath, "");
1365
+ } catch (e) {
1366
+ console.error("[DebugLogger] Clear error:", e);
1367
+ }
1368
+ }
1369
+ };
1370
+ var logger = DebugLogger.getInstance(false);
1371
+ function initDebugLogger() {
1372
+ DebugLogger.resetInstance(true);
1373
+ }
1374
+ function debugLog(category, message, data) {
1375
+ logger.log(category, message, data);
1376
+ }
1377
+ function flowLog(actor, direction, store, detail) {
1378
+ logger.log("flow", `${actor} ${direction} ${store}: ${detail}`);
1379
+ }
1380
+
1381
+ // src/shared/utils/command-validator/types.ts
1382
+ var SHELL_OPERATORS = {
1383
+ AND: "&&",
1384
+ OR: "||",
1385
+ SEQUENCE: ";",
1386
+ PIPE: "|",
1387
+ BACKGROUND: "&"
1388
+ };
1389
+ var SHELL_CHARS = {
1390
+ SINGLE_QUOTE: "'",
1391
+ DOUBLE_QUOTE: '"',
1392
+ BACKTICK: "`"
1393
+ };
1394
+
1395
+ // src/shared/utils/command-validator/splitter.ts
1396
+ function splitChainedCommands(command) {
1397
+ const parts = [];
1398
+ let current = "";
1399
+ let inSingle = false;
1400
+ let inDouble = false;
1401
+ let i = 0;
1402
+ while (i < command.length) {
1403
+ const ch = command[i];
1404
+ if (ch === SHELL_CHARS.SINGLE_QUOTE && !inDouble) {
1405
+ inSingle = !inSingle;
1406
+ current += ch;
1407
+ i++;
1408
+ continue;
1409
+ }
1410
+ if (ch === SHELL_CHARS.DOUBLE_QUOTE && !inSingle) {
1411
+ inDouble = !inDouble;
1412
+ current += ch;
1413
+ i++;
1414
+ continue;
1415
+ }
1416
+ if (!inSingle && !inDouble) {
1417
+ if (ch === "&" && command[i + 1] === "&" || ch === "|" && command[i + 1] === "|") {
1418
+ if (current.trim()) parts.push(current.trim());
1419
+ current = "";
1420
+ i += 2;
1421
+ continue;
1422
+ }
1423
+ if (ch === SHELL_OPERATORS.SEQUENCE) {
1424
+ if (current.trim()) parts.push(current.trim());
1425
+ current = "";
1426
+ i++;
1427
+ continue;
1428
+ }
1429
+ }
1430
+ current += ch;
1431
+ i++;
1432
+ }
1433
+ if (current.trim()) parts.push(current.trim());
1434
+ return parts.length > 0 ? parts : [command];
1435
+ }
1436
+ function splitByPipe(command) {
1437
+ const parts = [];
1438
+ let current = "";
1439
+ let inSingle = false;
1440
+ let inDouble = false;
1441
+ let i = 0;
1442
+ while (i < command.length) {
1443
+ const ch = command[i];
1444
+ if (ch === "'" && !inDouble) {
1445
+ inSingle = !inSingle;
1446
+ current += ch;
1447
+ i++;
1448
+ continue;
1449
+ }
1450
+ if (ch === '"' && !inSingle) {
1451
+ inDouble = !inDouble;
1452
+ current += ch;
1453
+ i++;
1454
+ continue;
1455
+ }
1456
+ if (!inSingle && !inDouble && ch === "|") {
1457
+ if (command[i + 1] === "|") {
1458
+ current += "||";
1459
+ i += 2;
1460
+ continue;
1461
+ }
1462
+ if (current.trim()) parts.push(current.trim());
1463
+ current = "";
1464
+ i++;
1465
+ continue;
1466
+ }
1467
+ current += ch;
1468
+ i++;
1469
+ }
1470
+ if (current.trim()) parts.push(current.trim());
1471
+ return parts;
1472
+ }
1473
+
1474
+ // src/shared/utils/command-validator/redirect.ts
1475
+ function stripRedirects(segment) {
1476
+ return segment.replace(/\d*>\s*&\d+/g, "").replace(/\d*>>\s*\S+/g, "").replace(/\d*>\s*\S+/g, "").replace(/<\s*\S+/g, "").trim();
1477
+ }
1478
+ function validateRedirects(command) {
1479
+ const redirects = extractRedirectTargets(command);
1480
+ for (const { type, target } of redirects) {
1481
+ if (type === ">" || type === ">>") {
1482
+ if (target.startsWith("&") || target === "/dev/null") continue;
1483
+ const isAllowed = SAFE_REDIRECT_PATHS.some((p) => target.startsWith(p));
1484
+ if (!isAllowed) {
1485
+ return {
1486
+ isSafe: false,
1487
+ error: `Redirect to '${target}' is not in allowed paths`,
1488
+ suggestion: `Redirect output to ${WORK_DIR}/ paths. Example: > ${WORK_DIR}/output.txt`
1489
+ };
1490
+ }
1491
+ } else if (type === "<") {
1492
+ const isAllowed = SAFE_REDIRECT_PATHS.some((p) => target.startsWith(p));
1493
+ if (!isAllowed) {
1494
+ return {
1495
+ isSafe: false,
1496
+ error: `Input redirect from '${target}' is not in allowed paths`,
1497
+ suggestion: `Input redirect only from ${WORK_DIR}/ paths.`
1498
+ };
1499
+ }
1500
+ }
1501
+ }
1502
+ return { isSafe: true };
1503
+ }
1504
+ function extractRedirectTargets(command) {
1505
+ const redirects = [];
1506
+ let inSingle = false;
1507
+ let inDouble = false;
1508
+ let i = 0;
1509
+ while (i < command.length) {
1510
+ const ch = command[i];
1511
+ if (ch === "'" && !inDouble) {
1512
+ inSingle = !inSingle;
1513
+ i++;
1514
+ continue;
1515
+ }
1516
+ if (ch === '"' && !inSingle) {
1517
+ inDouble = !inDouble;
1518
+ i++;
1519
+ continue;
1520
+ }
1521
+ if (!inSingle && !inDouble) {
1522
+ if (ch === ">" || ch === "<") {
1523
+ const isAppend = ch === ">" && command[i + 1] === ">";
1524
+ const type = isAppend ? ">>" : ch;
1525
+ const jump = isAppend ? 2 : 1;
1526
+ let fd = "";
1527
+ if (ch === ">") {
1528
+ let b = i - 1;
1529
+ while (b >= 0 && /\d/.test(command[b])) {
1530
+ fd = command[b] + fd;
1531
+ b--;
1532
+ }
1533
+ }
1534
+ i += jump;
1535
+ while (i < command.length && /\s/.test(command[i])) i++;
1536
+ let target = "";
1537
+ let targetInSingle = false;
1538
+ let targetInDouble = false;
1539
+ while (i < command.length) {
1540
+ const tc = command[i];
1541
+ if (tc === "'" && !targetInDouble) {
1542
+ targetInSingle = !targetInSingle;
1543
+ i++;
1544
+ continue;
1545
+ }
1546
+ if (tc === '"' && !targetInSingle) {
1547
+ targetInDouble = !targetInDouble;
1548
+ i++;
1549
+ continue;
1550
+ }
1551
+ if (!targetInSingle && !targetInDouble && /[\s|;&<>]/.test(tc)) break;
1552
+ target += tc;
1553
+ i++;
1554
+ }
1555
+ if (target) {
1556
+ redirects.push({ type, fd, target });
1557
+ }
1558
+ continue;
1559
+ }
1560
+ }
1561
+ i++;
1562
+ }
1563
+ return redirects;
1564
+ }
1565
+
1566
+ // src/shared/utils/command-validator/binary.ts
1567
+ function extractBinary(command) {
1568
+ const parts = command.trim().split(/\s+/);
1569
+ let binary = parts[0];
1570
+ if (!binary) return null;
1571
+ let idx = 0;
1572
+ while (idx < parts.length && ["sudo", "env", "timeout"].includes(parts[idx])) {
1573
+ idx++;
1574
+ if (parts[idx - 1] === "env") {
1575
+ while (idx < parts.length && parts[idx].includes("=")) idx++;
1576
+ }
1577
+ if (parts[idx - 1] === "timeout") {
1578
+ if (idx < parts.length && /^\d/.test(parts[idx])) idx++;
1579
+ }
1580
+ }
1581
+ binary = parts[idx] || parts[0];
1582
+ if (binary.includes("/")) {
1583
+ binary = binary.split("/").pop() || binary;
1584
+ }
1585
+ return binary.toLowerCase();
1586
+ }
1587
+
1588
+ // src/shared/utils/command-validator/validator.ts
1589
+ function validateCommand(command) {
1590
+ if (!command || typeof command !== "string") {
1591
+ return { isSafe: false, error: "Empty or invalid command" };
1592
+ }
1593
+ const normalizedCommand = command.trim();
1594
+ if (normalizedCommand.length === 0) {
1595
+ return { isSafe: false, error: "Empty command" };
1596
+ }
1597
+ for (const pattern of INJECTION_PATTERNS) {
1598
+ if (pattern.test(normalizedCommand)) {
1599
+ return {
1600
+ isSafe: false,
1601
+ error: `Injection pattern detected: ${pattern.source}`,
1602
+ suggestion: "Avoid command substitution ($(), ``), variable expansion (${}) and control characters."
1603
+ };
1604
+ }
1605
+ }
1606
+ const subCommands = splitChainedCommands(normalizedCommand);
1607
+ for (const subCmd of subCommands) {
1608
+ const result = validateSingleCommand(subCmd.trim());
1609
+ if (!result.isSafe) {
1610
+ return result;
1611
+ }
1612
+ }
1613
+ return { isSafe: true };
1614
+ }
1615
+ function validateSingleCommand(command) {
1616
+ const pipeSegments = splitByPipe(command);
1617
+ for (let idx = 0; idx < pipeSegments.length; idx++) {
1618
+ const segment = pipeSegments[idx].trim();
1619
+ if (!segment) continue;
1620
+ const cleanSegment = stripRedirects(segment);
1621
+ const binary = extractBinary(cleanSegment);
1622
+ if (!binary) continue;
1623
+ if (BLOCKED_BINARIES.has(binary)) {
1624
+ return {
1625
+ isSafe: false,
1626
+ error: `Binary '${binary}' is blocked for security reasons`,
1627
+ suggestion: `'${binary}' is not allowed. Use a different approach.`
1628
+ };
1629
+ }
1630
+ if (idx > 0 && !SAFE_PIPE_TARGETS.has(binary) && !ALLOWED_BINARIES.has(binary)) {
1631
+ return {
1632
+ isSafe: false,
1633
+ error: `Pipe target '${binary}' is not in the allowed list`,
1634
+ suggestion: `Piping to '${binary}' is not allowed. Safe pipe targets: head, tail, grep, awk, sed, cut, sort, uniq, wc, jq, tee.`
1635
+ };
1636
+ }
1637
+ if (!ALLOWED_BINARIES.has(binary)) {
1638
+ debugLog("security", `Unknown binary '${binary}' - use with caution`);
1639
+ }
1640
+ }
1641
+ const redirectResult = validateRedirects(command);
1642
+ if (!redirectResult.isSafe) {
1643
+ return redirectResult;
1644
+ }
1645
+ return { isSafe: true };
1646
+ }
1647
+
1648
+ // src/engine/tool-installer/detector.ts
1649
+ import { spawn } from "child_process";
1650
+ async function detectPackageManager() {
1651
+ const managers = [
1652
+ { name: "apt", check: "which apt" },
1653
+ { name: "brew", check: "which brew" }
1654
+ ];
1655
+ for (const mgr of managers) {
1656
+ try {
1657
+ const result = await new Promise((resolve) => {
1658
+ const child = spawn("sh", ["-c", mgr.check]);
1659
+ child.on("close", (code) => resolve(code === 0));
1660
+ child.on("error", () => resolve(false));
1661
+ });
1662
+ if (result) return mgr.name;
1663
+ } catch {
1664
+ continue;
1665
+ }
1666
+ }
1667
+ return null;
1668
+ }
1669
+ function isCommandNotFound(stderr, stdout) {
1670
+ const combined = (stderr + stdout).toLowerCase();
1671
+ return [
1672
+ /command not found/,
1673
+ /not found/,
1674
+ /no such file or directory/,
1675
+ /is not recognized/,
1676
+ /cannot find/,
1677
+ /not installed/,
1678
+ /unable to locate package/,
1679
+ /enoent/
1680
+ ].some((p) => p.test(combined));
1681
+ }
1682
+
1683
+ // src/engine/tool-installer/engine.ts
1684
+ import { spawn as spawn2 } from "child_process";
1685
+
1686
+ // src/engine/tool-installer/package-map.ts
1687
+ var TOOL_PACKAGE_MAP = {
1688
+ "nmap": { apt: "nmap", brew: "nmap" },
1689
+ "rustscan": { apt: "rustscan", brew: "rustscan" },
1690
+ "ffuf": { apt: "ffuf", brew: "ffuf" },
1691
+ "gobuster": { apt: "gobuster", brew: "gobuster" },
1692
+ "nuclei": { apt: "nuclei", brew: "nuclei" },
1693
+ "nikto": { apt: "nikto", brew: "nikto" },
1694
+ "sqlmap": { apt: "sqlmap", brew: "sqlmap" },
1695
+ "hydra": { apt: "hydra", brew: "hydra" },
1696
+ "john": { apt: "john", brew: "john" },
1697
+ "hashcat": { apt: "hashcat", brew: "hashcat" },
1698
+ "enum4linux": { apt: "enum4linux", brew: "enum4linux" },
1699
+ "smbclient": { apt: "smbclient", brew: "samba" },
1700
+ "crackmapexec": { apt: "crackmapexec", brew: "crackmapexec" },
1701
+ "bloodhound": { apt: "bloodhound", brew: "bloodhound" },
1702
+ "kerberoast": { apt: "kerberoast", brew: "kerberoast" },
1703
+ "impacket": { apt: "python3-impacket", brew: "impacket", pip: "impacket" },
1704
+ "curl": { apt: "curl", brew: "curl" },
1705
+ "wget": { apt: "wget", brew: "wget" },
1706
+ "jq": { apt: "jq", brew: "jq" },
1707
+ "whois": { apt: "whois", brew: "whois" },
1708
+ "dig": { apt: "dnsutils", brew: "bind" },
1709
+ "netcat": { apt: "netcat", brew: "netcat" },
1710
+ "nc": { apt: "netcat", brew: "netcat" },
1711
+ "ssh": { apt: "openssh-client", brew: "openssh" },
1712
+ "scp": { apt: "openssh-client", brew: "openssh" },
1713
+ "ftp": { apt: "ftp", brew: "inetutils" },
1714
+ "telnet": { apt: "telnet", brew: "telnet" },
1715
+ "snmpwalk": { apt: "snmp", brew: "net-snmp" },
1716
+ "onesixtyone": { apt: "onesixtyone", brew: "onesixtyone" },
1717
+ "seclists": { apt: "seclists", brew: "seclists" },
1718
+ "wfuzz": { apt: "wfuzz", brew: "wfuzz", pip: "wfuzz" },
1719
+ "dirsearch": { apt: "dirsearch", brew: "dirsearch", pip: "dirsearch" },
1720
+ "wafw00f": { apt: "wafw00f", brew: "wafw00f", pip: "wafw00f" },
1721
+ "feroxbuster": { apt: "feroxbuster", brew: "feroxbuster" },
1722
+ "subfinder": { apt: "subfinder", brew: "subfinder" },
1723
+ "amass": { apt: "amass", brew: "amass" },
1724
+ "httpx": { apt: "httpx-toolkit", brew: "httpx" },
1725
+ "naabu": { apt: "naabu", brew: "naabu" },
1726
+ "dnsx": { apt: "dnsx", brew: "dnsx" },
1727
+ // Network attack tools
1728
+ "arpspoof": { apt: "dsniff", brew: "dsniff" },
1729
+ "ettercap": { apt: "ettercap-text-only", brew: "ettercap" },
1730
+ "mitmproxy": { apt: "mitmproxy", brew: "mitmproxy", pip: "mitmproxy" },
1731
+ "tcpdump": { apt: "tcpdump", brew: "tcpdump" },
1732
+ "tshark": { apt: "tshark", brew: "wireshark" },
1733
+ "bettercap": { apt: "bettercap", brew: "bettercap" },
1734
+ "responder": { apt: "responder", brew: "responder", pip: "Responder" },
1735
+ "searchsploit": { apt: "exploitdb", brew: "exploitdb" },
1736
+ "kerbrute": { apt: "kerbrute", brew: "kerbrute" },
1737
+ "evil-winrm": { apt: "evil-winrm", brew: "evil-winrm" },
1738
+ "netexec": { apt: "netexec", brew: "netexec" },
1739
+ // Forensic / analysis tools
1740
+ "file": { apt: "file", brew: "file-formula" },
1741
+ "xxd": { apt: "xxd", brew: "vim" },
1742
+ "binwalk": { apt: "binwalk", brew: "binwalk", pip: "binwalk" },
1743
+ "strings": { apt: "binutils", brew: "binutils" },
1744
+ // IMP-7: PWN domain tools
1745
+ "checksec": { apt: "checksec", brew: "checksec", pip: "checksec" },
1746
+ "gdb": { apt: "gdb", brew: "gdb" },
1747
+ "pwndbg": { apt: "pwndbg", brew: "pwndbg" },
1748
+ "ROPgadget": { pip: "ROPgadget" },
1749
+ "one_gadget": { gem: "one_gadget" },
1750
+ "pwntools": { pip: "pwntools" },
1751
+ "ltrace": { apt: "ltrace", brew: "ltrace" },
1752
+ "strace": { apt: "strace", brew: "strace" },
1753
+ // IMP-7: Crypto domain tools
1754
+ "hashid": { pip: "hashid" },
1755
+ "padbuster": { apt: "padbuster", brew: "padbuster" },
1756
+ "rsactftool": { pip: "RsaCtfTool" },
1757
+ // IMP-7: Forensics domain tools
1758
+ "steghide": { apt: "steghide", brew: "steghide" },
1759
+ "zsteg": { gem: "zsteg" },
1760
+ "stegseek": { apt: "stegseek" },
1761
+ "exiftool": { apt: "libimage-exiftool-perl", brew: "exiftool" },
1762
+ "foremost": { apt: "foremost", brew: "foremost" },
1763
+ "volatility": { apt: "volatility", pip: "volatility3" },
1764
+ "vol3": { pip: "volatility3" },
1765
+ // IMP-12: OSINT domain tools (tools not already listed above)
1766
+ "theHarvester": { apt: "theharvester", pip: "theHarvester" },
1767
+ "theharvester": { apt: "theharvester", pip: "theHarvester" },
1768
+ "shodan": { pip: "shodan" },
1769
+ "metabagoofil": { apt: "metabagoofil" },
1770
+ "spiderfoot": { apt: "spiderfoot" },
1771
+ "recon-ng": { apt: "recon-ng" }
1772
+ };
1773
+
1774
+ // src/engine/tool-installer/engine.ts
1775
+ var installedTools = /* @__PURE__ */ new Set();
1776
+ function installViaSimpleCmd(toolName, cmd, label, emitter) {
1777
+ emitter?.({ type: COMMAND_EVENT_TYPES.TOOL_INSTALL, message: `Installing via ${label}`, detail: cmd });
1778
+ return new Promise((resolve) => {
1779
+ const child = spawn2("sh", ["-c", cmd]);
1780
+ let out = "";
1781
+ child.stdout.on("data", (d) => {
1782
+ out += d.toString();
1783
+ });
1784
+ child.stderr.on("data", (d) => {
1785
+ out += d.toString();
1786
+ });
1787
+ child.on("close", (code) => {
1788
+ resolve(code === 0 ? { success: true, output: `Installed ${toolName} via ${label}` } : { success: false, output: `${label} install failed: ${out}` });
1789
+ });
1790
+ child.on("error", (err) => resolve({ success: false, output: err.message }));
1791
+ });
1792
+ }
1793
+ async function tryAlternateManagers(toolName, pkgInfo, emitter) {
1794
+ if (pkgInfo.pip) {
1795
+ return installViaSimpleCmd(toolName, `pip3 install ${pkgInfo.pip} 2>&1 || pip install ${pkgInfo.pip} 2>&1`, "pip", emitter);
1796
+ }
1797
+ if (pkgInfo.gem) {
1798
+ return installViaSimpleCmd(toolName, `gem install ${pkgInfo.gem} 2>&1`, "gem", emitter);
1799
+ }
1800
+ return null;
1801
+ }
1802
+ async function installTool(toolName, eventEmitter, inputHandler) {
1803
+ const pkgInfo = TOOL_PACKAGE_MAP[toolName];
1804
+ if (!pkgInfo) {
1805
+ return { success: false, output: `Unknown tool: ${toolName}. No package mapping available.` };
1806
+ }
1807
+ if (installedTools.has(toolName)) {
1808
+ return { success: false, output: `Already attempted to install ${toolName} this session.` };
1809
+ }
1810
+ installedTools.add(toolName);
1811
+ const pkgManager = await detectPackageManager();
1812
+ if (!pkgManager || !pkgInfo[pkgManager]) {
1813
+ const altResult = await tryAlternateManagers(toolName, pkgInfo, eventEmitter);
1814
+ if (altResult) return altResult;
1815
+ if (!pkgManager) {
1816
+ return { success: false, output: "No supported package manager (apt, brew, pip, gem). Install manually." };
1817
+ }
1818
+ }
1819
+ const packageName = pkgInfo[pkgManager] ?? pkgInfo.apt;
1820
+ if (!packageName) {
1821
+ return { success: false, output: `No ${String(pkgManager)} package for ${toolName}. Try: pip install ${pkgInfo.pip ?? toolName}` };
1822
+ }
1823
+ eventEmitter?.({
1824
+ type: COMMAND_EVENT_TYPES.TOOL_INSTALL,
1825
+ message: `Installing missing tool: ${toolName}`,
1826
+ detail: `Using ${String(pkgManager)} to install ${packageName}`
1827
+ });
1828
+ let installCmd;
1829
+ if (pkgManager === "apt") {
1830
+ installCmd = `apt update -qq 2>/dev/null; apt install -y ${packageName}`;
1831
+ } else if (pkgManager === "brew") {
1832
+ installCmd = `brew install ${packageName}`;
1833
+ } else {
1834
+ return { success: false, output: `Unsupported package manager: ${String(pkgManager)}` };
1835
+ }
1836
+ return new Promise((resolve) => {
1837
+ const child = spawn2("sh", ["-c", installCmd], {
1838
+ env: { ...process.env, DEBIAN_FRONTEND: "noninteractive" }
1839
+ });
1840
+ let stdout = "";
1841
+ let stderr = "";
1842
+ const checkForSudoPrompt = async (data) => {
1843
+ if (!inputHandler) return;
1844
+ for (const pattern of INPUT_PROMPT_PATTERNS) {
1845
+ if (pattern.test(data)) {
1846
+ const userInput = await inputHandler(data.trim());
1847
+ if (userInput !== null && child.stdin.writable) {
1848
+ child.stdin.write(userInput + "\n");
1849
+ }
1850
+ return;
1851
+ }
1852
+ }
1853
+ };
1854
+ child.stdout.on("data", (data) => {
1855
+ const text = data.toString();
1856
+ stdout += text;
1857
+ checkForSudoPrompt(text);
1858
+ });
1859
+ child.stderr.on("data", (data) => {
1860
+ const text = data.toString();
1861
+ stderr += text;
1862
+ checkForSudoPrompt(text);
1863
+ });
1864
+ child.on("close", (code) => {
1865
+ if (code === 0) {
1866
+ eventEmitter?.({
1867
+ type: COMMAND_EVENT_TYPES.TOOL_INSTALLED,
1868
+ message: `Successfully installed: ${toolName}`,
1869
+ detail: `Package: ${packageName}`
1870
+ });
1871
+ resolve({ success: true, output: `Successfully installed ${toolName}` });
1872
+ } else {
1873
+ eventEmitter?.({
1874
+ type: COMMAND_EVENT_TYPES.TOOL_INSTALL_FAILED,
1875
+ message: `Failed to install: ${toolName}`,
1876
+ detail: stderr.slice(0, SYSTEM_LIMITS.MAX_ERROR_DETAIL_SLICE)
1877
+ });
1878
+ resolve({
1879
+ success: false,
1880
+ output: `Failed to install ${toolName}: ${stderr.slice(0, SYSTEM_LIMITS.MAX_ERROR_DETAIL_SLICE)}`
1881
+ });
1882
+ }
1883
+ });
1884
+ child.on("error", (err) => {
1885
+ resolve({ success: false, output: `Install error: ${err.message}` });
1886
+ });
1887
+ });
1888
+ }
1889
+
1890
+ // src/shared/utils/config/env.ts
1891
+ var ENV_KEYS = {
1892
+ API_KEY: "PENTEST_API_KEY",
1893
+ BASE_URL: "PENTEST_BASE_URL",
1894
+ MODEL: "PENTEST_MODEL",
1895
+ SEARCH_API_KEY: "SEARCH_API_KEY",
1896
+ SEARCH_API_URL: "SEARCH_API_URL",
1897
+ THINKING: "PENTEST_THINKING",
1898
+ THINKING_BUDGET: "PENTEST_THINKING_BUDGET",
1899
+ APP_VERSION: "PENTESTING_VERSION"
1900
+ };
1901
+ var DEFAULT_SEARCH_API_URL = "https://api.search.brave.com/res/v1/web/search";
1902
+ var DEFAULT_MODEL = "glm-4.7";
1903
+ function getApiKey() {
1904
+ return process.env[ENV_KEYS.API_KEY] || "";
1905
+ }
1906
+ function getBaseUrl() {
1907
+ return process.env[ENV_KEYS.BASE_URL] || void 0;
1908
+ }
1909
+ function getModel() {
1910
+ return process.env[ENV_KEYS.MODEL] || "";
1911
+ }
1912
+ function getSearchApiKey() {
1913
+ if (process.env[ENV_KEYS.SEARCH_API_KEY]) {
1914
+ return process.env[ENV_KEYS.SEARCH_API_KEY];
1915
+ }
1916
+ return process.env[ENV_KEYS.API_KEY];
1917
+ }
1918
+ function getSearchApiUrl() {
1919
+ return process.env[ENV_KEYS.SEARCH_API_URL] || DEFAULT_SEARCH_API_URL;
1920
+ }
1921
+ function isZaiProvider() {
1922
+ const baseUrl = getBaseUrl() || "";
1923
+ return baseUrl.includes("z.ai");
1924
+ }
1925
+ function isThinkingEnabled() {
1926
+ return process.env[ENV_KEYS.THINKING] !== "false";
1927
+ }
1928
+ function getThinkingBudget() {
1929
+ const val = parseInt(process.env[ENV_KEYS.THINKING_BUDGET] || "", 10);
1930
+ return isNaN(val) ? 8e3 : Math.max(1024, val);
1931
+ }
1932
+
1933
+ // src/shared/utils/config/tor/core.ts
1934
+ var TOR_PROXY = {
1935
+ /** SOCKS5 proxy host (Tor daemon listens here) */
1936
+ SOCKS_HOST: "127.0.0.1",
1937
+ /** SOCKS5 proxy port */
1938
+ SOCKS_PORT: 9050,
1939
+ /** Shell wrapper command for proxying CLI tools */
1940
+ WRAPPER_CMD: "proxychains4",
1941
+ /** Flags for the wrapper command (-q = quiet, no banners) */
1942
+ WRAPPER_FLAGS: "-q"
1943
+ };
1944
+ var torEnabled = false;
1945
+ function isTorEnabled() {
1946
+ return torEnabled;
1947
+ }
1948
+ function setTorEnabled(enabled) {
1949
+ torEnabled = enabled;
1950
+ }
1951
+ function getTorBrowserArgs() {
1952
+ if (!isTorEnabled()) return [];
1953
+ return [`--proxy-server=socks5://${TOR_PROXY.SOCKS_HOST}:${TOR_PROXY.SOCKS_PORT}`];
1954
+ }
1955
+
1956
+ // src/shared/utils/config/tor/wrapper.ts
1957
+ function wrapCommandForTor(command) {
1958
+ if (!isTorEnabled()) return command;
1959
+ const SOCKS = `${TOR_PROXY.SOCKS_HOST}:${TOR_PROXY.SOCKS_PORT}`;
1960
+ if (/\bcurl\b/.test(command)) {
1961
+ if (/--socks5|--proxy\b|-x\s/.test(command)) return command;
1962
+ return command.replace(/\bcurl\b/, `curl --socks5-hostname ${SOCKS}`);
1963
+ }
1964
+ if (/\bwget\b/.test(command)) {
1965
+ if (/--execute.*proxy|http_proxy|https_proxy/i.test(command)) return command;
1966
+ return command.replace(
1967
+ /\bwget\b/,
1968
+ `wget -e use_proxy=yes -e http_proxy=socks5h://${SOCKS} -e https_proxy=socks5h://${SOCKS}`
1969
+ );
1970
+ }
1971
+ if (/\bsqlmap\b/.test(command)) {
1972
+ if (/--tor\b|--proxy\b/.test(command)) return command;
1973
+ return command.replace(
1974
+ /\bsqlmap\b/,
1975
+ `sqlmap --tor --tor-type=SOCKS5 --tor-port=${TOR_PROXY.SOCKS_PORT}`
1976
+ );
1977
+ }
1978
+ if (/\bgobuster\b/.test(command)) {
1979
+ if (/--proxy\b/.test(command)) return command;
1980
+ return command.replace(/\bgobuster\b/, `gobuster --proxy socks5://${SOCKS}`);
1981
+ }
1982
+ if (/\bffuf\b/.test(command)) {
1983
+ if (/-x\s/.test(command)) return command;
1984
+ return command.replace(/\bffuf\b/, `ffuf -x socks5://${SOCKS}`);
1985
+ }
1986
+ if (/\bnmap\b/.test(command)) {
1987
+ let nmapCmd = command;
1988
+ nmapCmd = nmapCmd.replace(/\s-s[SAXFN]\b/g, " -sT");
1989
+ if (!/\s-Pn\b/.test(nmapCmd)) {
1990
+ nmapCmd = nmapCmd.replace(/\bnmap\b/, "nmap -Pn");
1991
+ }
1992
+ return `${TOR_PROXY.WRAPPER_CMD} ${TOR_PROXY.WRAPPER_FLAGS} ${nmapCmd}`;
1993
+ }
1994
+ return `${TOR_PROXY.WRAPPER_CMD} ${TOR_PROXY.WRAPPER_FLAGS} ${command}`;
1995
+ }
1996
+
1997
+ // src/shared/utils/config/tor/leak-detector.ts
1998
+ function checkTorLeakRisk(command) {
1999
+ if (!isTorEnabled()) return { safe: true };
2000
+ if (/\bping\b/.test(command)) {
2001
+ return {
2002
+ safe: false,
2003
+ reason: "ping uses ICMP \u2014 bypasses Tor, real IP exposed to target",
2004
+ suggestion: "Use TCP probe instead: curl --socks5-hostname 127.0.0.1:9050 -s --connect-timeout 5 http://<target>:<port>"
2005
+ };
2006
+ }
2007
+ if (/\btraceroute\b|\btracepath\b|\bmtr\b/.test(command)) {
2008
+ return {
2009
+ safe: false,
2010
+ reason: "traceroute/tracepath/mtr uses ICMP/UDP \u2014 bypasses Tor, real IP exposed",
2011
+ suggestion: "Skip traceroute when Tor is enabled (reveals all hops including your IP)"
2012
+ };
2013
+ }
2014
+ if (/\bdig\b/.test(command) || /\bnslookup\b/.test(command) || /(?:^|[;&|\s])host(?:\s|$)/.test(command)) {
2015
+ return {
2016
+ safe: false,
2017
+ reason: "dig/host/nslookup use UDP \u2014 DNS query bypasses Tor, real IP visible to DNS server",
2018
+ suggestion: `Use DNS-over-HTTPS: curl --socks5-hostname ${TOR_PROXY.SOCKS_HOST}:${TOR_PROXY.SOCKS_PORT} "https://dns.google/resolve?name=<host>&type=A"`
2019
+ };
2020
+ }
2021
+ if (/\bnmap\b/.test(command) && /\s-sU\b/.test(command)) {
2022
+ return {
2023
+ safe: false,
2024
+ reason: "nmap -sU (UDP scan) bypasses Tor \u2014 UDP is not routed through SOCKS5",
2025
+ suggestion: `Skip UDP scan with Tor ON. Use TCP scan: ${TOR_PROXY.WRAPPER_CMD} ${TOR_PROXY.WRAPPER_FLAGS} nmap -sT -Pn`
2026
+ };
2027
+ }
2028
+ return { safe: true };
2029
+ }
2030
+
2031
+ // src/shared/utils/config/validation.ts
2032
+ function validateRequiredConfig() {
2033
+ const errors = [];
2034
+ if (!getApiKey()) {
2035
+ errors.push(
2036
+ `[config] PENTEST_API_KEY is required.
2037
+ Export it before running:
2038
+ export PENTEST_API_KEY=your_api_key
2039
+ For z.ai: get your key at https://api.z.ai`
2040
+ );
2041
+ }
2042
+ const budgetRaw = process.env[ENV_KEYS.THINKING_BUDGET];
2043
+ if (budgetRaw !== void 0 && budgetRaw !== "") {
2044
+ const parsed = parseInt(budgetRaw, 10);
2045
+ if (isNaN(parsed) || parsed < 1024) {
2046
+ errors.push(
2047
+ `[config] PENTEST_THINKING_BUDGET must be an integer \u2265 1024 (got: "${budgetRaw}")`
2048
+ );
2049
+ }
2050
+ }
2051
+ return errors;
2052
+ }
2053
+
2054
+ // src/engine/tools-base/command-executor/internal.ts
2055
+ import { spawn as spawn3 } from "child_process";
2056
+ function injectCurlMaxTime(command, timeoutSec) {
2057
+ if (!/\bcurl\b/.test(command)) return command;
2058
+ if (/--max-time\b|-m\s+\d/.test(command)) return command;
2059
+ return command.replace(/\bcurl\b/, `curl --max-time ${timeoutSec}`);
2060
+ }
2061
+ function prepareCommand(command) {
2062
+ const safeCommand = injectCurlMaxTime(command, CURL_MAX_TIME_SEC);
2063
+ const torLeak = checkTorLeakRisk(safeCommand);
2064
+ if (!torLeak.safe) {
2065
+ return {
2066
+ safeCommand,
2067
+ execCommand: "",
2068
+ error: `\u{1F6D1} TOR IP LEAK BLOCKED
2069
+ Reason: ${torLeak.reason}
2070
+ Suggestion: ${torLeak.suggestion}`
2071
+ };
2072
+ }
2073
+ return { safeCommand, execCommand: wrapCommandForTor(safeCommand) };
2074
+ }
2075
+ function setupTimeout(child, timeoutMs, onTimeout) {
2076
+ return setTimeout(() => {
2077
+ onTimeout();
2078
+ try {
2079
+ process.kill(-child.pid, "SIGKILL");
2080
+ } catch {
2081
+ try {
2082
+ child.kill("SIGKILL");
2083
+ } catch {
2084
+ }
2085
+ }
2086
+ }, timeoutMs);
2087
+ }
2088
+ var ProcessIOHandler = class {
2089
+ constructor(child, processState, eventEmitter, inputHandler) {
2090
+ this.child = child;
2091
+ this.processState = processState;
2092
+ this.eventEmitter = eventEmitter;
2093
+ this.inputHandler = inputHandler;
2094
+ }
2095
+ stdout = "";
2096
+ stderr = "";
2097
+ inputHandled = false;
2098
+ async checkForInputPrompt(data) {
2099
+ if (this.inputHandled || !this.inputHandler || this.processState.terminated) return;
2100
+ for (const pattern of INPUT_PROMPT_PATTERNS) {
2101
+ if (pattern.test(data)) {
2102
+ this.inputHandled = true;
2103
+ this.eventEmitter?.({
2104
+ type: COMMAND_EVENT_TYPES.INPUT_REQUIRED,
2105
+ message: `Input required: ${data.trim().slice(0, SYSTEM_LIMITS.MAX_PROMPT_PREVIEW)}`,
2106
+ detail: "Waiting for user input..."
2107
+ });
2108
+ try {
2109
+ const userInput = await this.inputHandler(data.trim());
2110
+ if (userInput !== null && !this.processState.terminated && this.child.stdin?.writable) {
2111
+ this.child.stdin.write(userInput + "\n");
2112
+ }
2113
+ } catch {
2114
+ }
2115
+ return;
2116
+ }
2117
+ }
2118
+ }
2119
+ handleStdout(data) {
2120
+ const text = data.toString();
2121
+ this.stdout += text;
2122
+ if (this.stdout.length > SYSTEM_LIMITS.MAX_STDOUT_SLICE) {
2123
+ this.stdout = this.stdout.slice(-SYSTEM_LIMITS.MAX_STDOUT_SLICE);
2124
+ }
2125
+ const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
2126
+ if (lines.length > 0) {
2127
+ this.eventEmitter?.({
2128
+ type: COMMAND_EVENT_TYPES.COMMAND_STDOUT,
2129
+ message: lines[lines.length - 1]
2130
+ });
2131
+ }
2132
+ this.checkForInputPrompt(text);
2133
+ }
2134
+ handleStderr(data) {
2135
+ const text = data.toString();
2136
+ this.stderr += text;
2137
+ if (this.stderr.length > SYSTEM_LIMITS.MAX_STDERR_SLICE) {
2138
+ this.stderr = this.stderr.slice(-SYSTEM_LIMITS.MAX_STDERR_SLICE);
2139
+ }
2140
+ this.checkForInputPrompt(text);
2141
+ }
2142
+ };
2143
+ async function executeCommandOnce(command, options = {}) {
2144
+ return new Promise((resolve) => {
2145
+ const eventEmitter = getEventEmitter();
2146
+ const { safeCommand, execCommand, error } = prepareCommand(command);
2147
+ if (error) {
2148
+ return resolve({ success: false, output: "", error });
2149
+ }
2150
+ eventEmitter?.({
2151
+ type: COMMAND_EVENT_TYPES.COMMAND_START,
2152
+ message: `Executing: ${safeCommand.slice(0, DISPLAY_LIMITS.COMMAND_PREVIEW)}${safeCommand.length > DISPLAY_LIMITS.COMMAND_PREVIEW ? "..." : ""}`
2153
+ });
2154
+ const child = spawn3("sh", ["-c", execCommand], {
2155
+ detached: true,
2156
+ env: { ...process.env, ...options.env },
2157
+ cwd: options.cwd
2158
+ });
2159
+ const processState = { terminated: false, timedOut: false };
2160
+ const timeoutMs = options.timeout ?? TOOL_TIMEOUTS.DEFAULT_COMMAND;
2161
+ const killTimer = setupTimeout(child, timeoutMs, () => {
2162
+ processState.timedOut = true;
2163
+ processState.terminated = true;
2164
+ });
2165
+ const ioHandler = new ProcessIOHandler(child, processState, eventEmitter, getInputHandler());
2166
+ child.stdout.on("data", (d) => ioHandler.handleStdout(d));
2167
+ child.stderr.on("data", (d) => ioHandler.handleStderr(d));
2168
+ child.on("close", (code) => {
2169
+ clearTimeout(killTimer);
2170
+ processState.terminated = true;
2171
+ const outTrimmed = ioHandler.stdout.trim();
2172
+ const errTrimmed = ioHandler.stderr.trim();
2173
+ if (processState.timedOut) {
2174
+ eventEmitter?.({ type: COMMAND_EVENT_TYPES.COMMAND_FAILED, message: `Command timed out after ${Math.round(timeoutMs / 1e3)}s` });
2175
+ return resolve({
2176
+ success: false,
2177
+ output: outTrimmed,
2178
+ error: `Command timed out after ${Math.round(timeoutMs / 1e3)}s. Output so far:
2179
+ ${(ioHandler.stdout + ioHandler.stderr).trim().slice(-500)}`
2180
+ });
2181
+ }
2182
+ if (code === 0) {
2183
+ eventEmitter?.({ type: COMMAND_EVENT_TYPES.COMMAND_SUCCESS, message: `Command completed` });
2184
+ return resolve({ success: true, output: outTrimmed || errTrimmed });
2185
+ }
2186
+ eventEmitter?.({ type: COMMAND_EVENT_TYPES.COMMAND_FAILED, message: `Command failed (exit ${code})`, detail: errTrimmed.slice(0, SYSTEM_LIMITS.MAX_INPUT_SLICE) });
2187
+ return resolve({ success: false, output: outTrimmed, error: errTrimmed || `Process exited with code ${code}` });
2188
+ });
2189
+ child.on("error", (err) => {
2190
+ clearTimeout(killTimer);
2191
+ processState.terminated = true;
2192
+ eventEmitter?.({ type: COMMAND_EVENT_TYPES.COMMAND_ERROR, message: `Command error: ${err.message}` });
2193
+ resolve({ success: false, output: "", error: err.message || String(err) });
2194
+ });
2195
+ });
2196
+ }
2197
+
2198
+ // src/engine/tools-base/command-executor/runner.ts
2199
+ async function runCommand(command, args = [], options = {}) {
2200
+ const eventEmitter = getEventEmitter();
2201
+ const inputHandler = getInputHandler();
2202
+ const fullCommand = args.length > 0 ? `${command} ${args.join(" ")}` : command;
2203
+ const validation = validateCommand(fullCommand);
2204
+ if (!validation.isSafe) {
2205
+ return {
2206
+ success: false,
2207
+ output: "",
2208
+ error: `Command blocked: ${validation.error}`
2209
+ };
2210
+ }
2211
+ const torLeak = checkTorLeakRisk(fullCommand);
2212
+ if (!torLeak.safe) {
2213
+ return {
2214
+ success: false,
2215
+ output: "",
2216
+ error: `\u{1F6D1} TOR IP LEAK BLOCKED
2217
+ Reason: ${torLeak.reason}
2218
+ Suggestion: ${torLeak.suggestion}`
2219
+ };
2220
+ }
2221
+ const timeout = options.timeout ?? TOOL_TIMEOUTS.DEFAULT_COMMAND;
2222
+ const maxRetries = options.maxRetries ?? AGENT_LIMITS.MAX_INSTALL_RETRIES;
2223
+ const tokens = command.trim().split(/\s+/);
2224
+ const binaryPath = tokens[0] || "";
2225
+ const toolName = binaryPath.split("/").pop() || binaryPath;
2226
+ let lastResult = { success: false, output: "", error: "Unknown error" };
2227
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
2228
+ if (attempt === 0 && toolName && !toolName.includes("=") && !["cd", "ls", "pwd", "echo", "cat"].includes(toolName)) {
2229
+ const checkResult = await executeCommandOnce(`which ${toolName}`, { timeout: 2e3 });
2230
+ if (!checkResult.success) {
2231
+ eventEmitter?.({
2232
+ type: COMMAND_EVENT_TYPES.TOOL_MISSING,
2233
+ message: `${STATUS_MARKERS.INFO} Tool not found: ${toolName}`,
2234
+ detail: `Pre-emptively attempting to install...`
2235
+ });
2236
+ const installResult2 = await installTool(toolName, eventEmitter, inputHandler);
2237
+ if (!installResult2.success) {
2238
+ eventEmitter?.({
2239
+ type: COMMAND_EVENT_TYPES.TOOL_INSTALL_FAILED,
2240
+ message: `Eager install failed for ${toolName}`,
2241
+ detail: installResult2.output
2242
+ });
2243
+ }
2244
+ }
2245
+ }
2246
+ const result = await executeCommandOnce(fullCommand, { ...options, timeout });
2247
+ if (result.success) {
2248
+ return result;
2249
+ }
2250
+ const missing = isCommandNotFound(result.error || "", result.output);
2251
+ if (!missing || attempt >= maxRetries) {
2252
+ return result;
2253
+ }
2254
+ lastResult = result;
2255
+ eventEmitter?.({
2256
+ type: COMMAND_EVENT_TYPES.TOOL_MISSING,
2257
+ message: `${STATUS_MARKERS.WARNING} Tool not found: ${toolName}`,
2258
+ detail: `Attempting to install...`
2259
+ });
2260
+ const installResult = await installTool(toolName, eventEmitter, inputHandler);
2261
+ if (!installResult.success) {
2262
+ return {
2263
+ success: false,
2264
+ output: result.output,
2265
+ error: `${result.error}
2266
+
2267
+ [Auto-install failed: ${installResult.output}]`
2268
+ };
2269
+ }
2270
+ eventEmitter?.({
2271
+ type: COMMAND_EVENT_TYPES.TOOL_RETRY,
2272
+ message: `Retrying command after installing ${toolName}...`,
2273
+ detail: command.slice(0, DISPLAY_LIMITS.COMMAND_PREVIEW)
2274
+ });
2275
+ }
2276
+ return lastResult;
2277
+ }
2278
+
2279
+ // src/engine/tools-base/file-operations.ts
2280
+ import { readFileSync as readFileSync2, existsSync as existsSync2, writeFileSync as writeFileSync2 } from "fs";
2281
+ import { dirname } from "path";
2282
+ import { join as join2 } from "path";
2283
+ import { tmpdir } from "os";
2284
+
2285
+ // src/shared/utils/id-gen/id-generator.ts
2286
+ var ID_DEFAULT_RADIX = 36;
2287
+ var ID_DEFAULT_LENGTH = 6;
2288
+ var generateId = (radix = 36, length = 8) => Math.random().toString(radix).substring(2, 2 + length);
2289
+ var generatePrefixedId = (prefix, radix = ID_DEFAULT_RADIX, randomLength = ID_DEFAULT_LENGTH) => `${prefix}_${Date.now()}_${Math.random().toString(radix).slice(2, 2 + randomLength)}`;
2290
+ var generateTempFilename = (suffix = "", prefix = "pentest") => `${prefix}-${Date.now()}-${Math.random().toString(ID_DEFAULT_RADIX).slice(2, 2 + ID_DEFAULT_LENGTH)}${suffix}`;
2291
+
2292
+ // src/shared/utils/error-utils/error-utils.ts
2293
+ function getErrorMessage(error) {
2294
+ return error instanceof Error ? error.message : String(error);
2295
+ }
2296
+
2297
+ // src/engine/tools-base/file-operations.ts
2298
+ async function readFileContent(filePath) {
2299
+ try {
2300
+ if (!existsSync2(filePath)) {
2301
+ return {
2302
+ success: false,
2303
+ output: "",
2304
+ error: `File not found: ${filePath}`
2305
+ };
2306
+ }
2307
+ const content = readFileSync2(filePath, "utf-8");
2308
+ return {
2309
+ success: true,
2310
+ output: content
2311
+ };
2312
+ } catch (error) {
2313
+ const message = getErrorMessage(error);
2314
+ return {
2315
+ success: false,
2316
+ output: "",
2317
+ error: message
2318
+ };
2319
+ }
2320
+ }
2321
+ async function writeFileContent(filePath, content) {
2322
+ try {
2323
+ const dir2 = dirname(filePath);
2324
+ ensureDirExists(dir2);
2325
+ writeFileSync2(filePath, content, "utf-8");
2326
+ return {
2327
+ success: true,
2328
+ output: `Written to ${filePath}`
2329
+ };
2330
+ } catch (error) {
2331
+ const message = getErrorMessage(error);
2332
+ return {
2333
+ success: false,
2334
+ output: "",
2335
+ error: message
2336
+ };
2337
+ }
2338
+ }
2339
+ function createTempFile(suffix = "") {
2340
+ return join2(tmpdir(), generateTempFilename(suffix));
2341
+ }
2342
+
2343
+ // src/engine/process/process-lifecycle.ts
2344
+ function startBackgroundProcess(command, options = {}) {
2345
+ const processId = generatePrefixedId("bg");
2346
+ const stdoutFile = createTempFile(FILE_EXTENSIONS.STDOUT);
2347
+ const stderrFile = createTempFile(FILE_EXTENSIONS.STDERR);
2348
+ const stdinFile = createTempFile(FILE_EXTENSIONS.STDIN);
2349
+ const { tags, port, role, isInteractive } = detectProcessRole(command);
2350
+ const torLeak = checkTorLeakRisk(command);
2351
+ if (!torLeak.safe) {
2352
+ for (const f of [stdoutFile, stderrFile, stdinFile]) {
2353
+ try {
2354
+ unlinkSync(f);
2355
+ } catch {
2356
+ }
2357
+ }
2358
+ throw new Error(`\u{1F6D1} TOR IP LEAK BLOCKED \u2014 ${torLeak.reason}. ${torLeak.suggestion}`);
2359
+ }
2360
+ const torCommand = wrapCommandForTor(command);
2361
+ let wrappedCmd;
2362
+ if (isInteractive) {
2363
+ writeFileSync3(stdinFile, "", "utf-8");
2364
+ wrappedCmd = `tail -f ${stdinFile} | ${torCommand} > ${stdoutFile} 2> ${stderrFile}`;
2365
+ } else {
2366
+ wrappedCmd = `${torCommand} > ${stdoutFile} 2> ${stderrFile}`;
2367
+ }
2368
+ const child = spawn4("sh", ["-c", wrappedCmd], {
2369
+ detached: true,
2370
+ stdio: "ignore",
2371
+ cwd: options.cwd,
2372
+ env: { ...process.env, ...options.env }
2373
+ });
2374
+ child.unref();
2375
+ const purpose = options.purpose || (role === "listener" ? `Listening on port ${port || "?"} for incoming connection` : role === "server" ? `Serving files on port ${port || "?"}` : role === "sniffer" ? "Capturing network traffic" : command.slice(0, SYSTEM_LIMITS.MAX_DESCRIPTION_LENGTH));
2376
+ const bgProcess = {
2377
+ id: processId,
2378
+ pid: child.pid,
2379
+ command: command.slice(0, SYSTEM_LIMITS.MAX_COMMAND_LENGTH),
2380
+ description: options.description || command.slice(0, SYSTEM_LIMITS.MAX_DESCRIPTION_LENGTH),
2381
+ startedAt: Date.now(),
2382
+ stdoutFile,
2383
+ stderrFile,
2384
+ stdinFile,
2385
+ childProcess: child,
2386
+ hasExited: false,
2387
+ exitCode: null,
2388
+ listeningPort: port,
2389
+ childPids: [],
2390
+ tags,
2391
+ role,
2392
+ purpose,
2393
+ isInteractive
2394
+ };
2395
+ child.on("exit", (code) => {
2396
+ bgProcess.hasExited = true;
2397
+ bgProcess.exitCode = code;
2398
+ logEvent(processId, PROCESS_EVENTS.DIED, `Process exited with code ${code}`);
2399
+ const emitter = getEventEmitter();
2400
+ if (emitter) {
2401
+ emitter({
2402
+ type: COMMAND_EVENT_TYPES.PROCESS_NOTIFICATION,
2403
+ message: `Process finished: ${bgProcess.description} (exit ${code}). Ready for analysis.`
2404
+ });
2405
+ }
2406
+ });
2407
+ setProcess(processId, bgProcess);
2408
+ logEvent(processId, PROCESS_EVENTS.STARTED, `${role} started: ${command.slice(0, SYSTEM_LIMITS.MAX_DESCRIPTION_LENGTH)} (PID:${child.pid})`);
2409
+ setTimeout(() => {
2410
+ if (!bgProcess.hasExited) {
2411
+ bgProcess.childPids = discoverAllDescendants(bgProcess.pid);
2412
+ }
2413
+ }, SYSTEM_LIMITS.CHILD_PID_DISCOVERY_MS);
2414
+ return bgProcess;
2415
+ }
2416
+
2417
+ // src/engine/process/process-interaction.ts
2418
+ import { existsSync as existsSync3, readFileSync as readFileSync3, appendFileSync as appendFileSync2 } from "fs";
2419
+ async function sendToProcess(processId, input, waitMs = SYSTEM_LIMITS.DEFAULT_WAIT_MS_INTERACT) {
2420
+ const proc = getProcess(processId);
2421
+ if (!proc) return { success: false, output: `Process ${processId} not found`, newOutput: "" };
2422
+ if (!proc.isInteractive) return { success: false, output: `Process ${processId} is not interactive`, newOutput: "" };
2423
+ if (proc.hasExited) return { success: false, output: `Process ${processId} has exited`, newOutput: "" };
2424
+ let currentLen = 0;
2425
+ try {
2426
+ if (existsSync3(proc.stdoutFile)) {
2427
+ currentLen = readFileSync3(proc.stdoutFile, "utf-8").length;
2428
+ }
2429
+ } catch {
2430
+ }
2431
+ try {
2432
+ appendFileSync2(proc.stdinFile, input + "\n", "utf-8");
2433
+ } catch (error) {
2434
+ return { success: false, output: `Failed to send input: ${error}`, newOutput: "" };
2435
+ }
2436
+ logEvent(processId, PROCESS_EVENTS.COMMAND_SENT, `Sent: ${input.slice(0, SYSTEM_LIMITS.MAX_INPUT_SLICE)}`);
2437
+ await new Promise((r) => setTimeout(r, waitMs));
2438
+ let fullStdout = "";
2439
+ try {
2440
+ if (existsSync3(proc.stdoutFile)) {
2441
+ fullStdout = readFileSync3(proc.stdoutFile, "utf-8");
2442
+ }
2443
+ } catch {
2444
+ }
2445
+ const newOutput = fullStdout.slice(currentLen);
2446
+ return {
2447
+ success: true,
2448
+ output: fullStdout.slice(-SYSTEM_LIMITS.MAX_STDOUT_SLICE),
2449
+ newOutput: newOutput || "(no new output \u2014 command may need more time)"
2450
+ };
2451
+ }
2452
+ function promoteToShell(processId, description) {
2453
+ const proc = getProcess(processId);
2454
+ if (!proc) return false;
2455
+ proc.role = PROCESS_ROLES.ACTIVE_SHELL;
2456
+ proc.purpose = description || "Reverse shell connected \u2014 target access available";
2457
+ proc.isInteractive = true;
2458
+ logEvent(processId, PROCESS_EVENTS.ROLE_CHANGED, `Promoted to active_shell: ${proc.purpose}`);
2459
+ return true;
2460
+ }
2461
+
2462
+ // src/engine/process/process-monitor.ts
2463
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
2464
+ function isProcessRunning(processId) {
2465
+ const proc = getProcess(processId);
2466
+ if (!proc) return false;
2467
+ if (proc.hasExited) return false;
2468
+ if (!isPidAlive(proc.pid)) {
2469
+ proc.hasExited = true;
2470
+ logEvent(processId, PROCESS_EVENTS.DIED, "Process no longer alive (PID check failed)");
2471
+ return false;
2472
+ }
2473
+ proc.childPids = discoverAllDescendants(proc.pid);
2474
+ if (proc.role === PROCESS_ROLES.LISTENER) {
2475
+ try {
2476
+ if (existsSync4(proc.stdoutFile)) {
2477
+ const stdout = readFileSync4(proc.stdoutFile, "utf-8");
2478
+ if (detectConnection(stdout)) {
2479
+ promoteToShell(processId, "Reverse shell connected (auto-detected)");
2480
+ logEvent(processId, PROCESS_EVENTS.CONNECTION_DETECTED, `Connection detected on port ${proc.listeningPort}`);
2481
+ }
2482
+ }
2483
+ } catch {
2484
+ }
2485
+ }
2486
+ return true;
2487
+ }
2488
+ function getProcessOutput(processId) {
2489
+ const proc = getProcess(processId);
2490
+ if (!proc) return null;
2491
+ const running = isProcessRunning(processId);
2492
+ let stdout = "";
2493
+ let stderr = "";
2494
+ try {
2495
+ if (existsSync4(proc.stdoutFile)) {
2496
+ const content = readFileSync4(proc.stdoutFile, "utf-8");
2497
+ stdout = content.length > SYSTEM_LIMITS.MAX_STDOUT_SLICE ? `... [truncated ${content.length - SYSTEM_LIMITS.MAX_STDOUT_SLICE} chars] ...
2498
+ ` + content.slice(-SYSTEM_LIMITS.MAX_STDOUT_SLICE) : content;
2499
+ }
2500
+ } catch {
2501
+ }
2502
+ try {
2503
+ if (existsSync4(proc.stderrFile)) {
2504
+ const content = readFileSync4(proc.stderrFile, "utf-8");
2505
+ stderr = content.length > SYSTEM_LIMITS.MAX_STDERR_SLICE ? `... [truncated ${content.length - SYSTEM_LIMITS.MAX_STDERR_SLICE} chars] ...
2506
+ ` + content.slice(-SYSTEM_LIMITS.MAX_STDERR_SLICE) : content;
2507
+ }
2508
+ } catch {
2509
+ }
2510
+ return {
2511
+ stdout,
2512
+ stderr,
2513
+ isRunning: running,
2514
+ exitCode: proc.exitCode,
2515
+ durationMs: Date.now() - proc.startedAt,
2516
+ listeningPort: proc.listeningPort,
2517
+ childPids: proc.childPids,
2518
+ tags: proc.tags,
2519
+ role: proc.role,
2520
+ purpose: proc.purpose,
2521
+ isInteractive: proc.isInteractive
2522
+ };
2523
+ }
2524
+ function listBackgroundProcesses() {
2525
+ const result = [];
2526
+ for (const [id, proc] of getAllProcessEntries()) {
2527
+ const isRunning = isProcessRunning(id);
2528
+ result.push({
2529
+ id,
2530
+ pid: proc.pid,
2531
+ command: proc.command,
2532
+ description: proc.description,
2533
+ isRunning,
2534
+ durationMs: Date.now() - proc.startedAt,
2535
+ exitCode: proc.exitCode,
2536
+ listeningPort: proc.listeningPort,
2537
+ childPids: proc.childPids,
2538
+ tags: proc.tags,
2539
+ role: proc.role,
2540
+ purpose: proc.purpose,
2541
+ isInteractive: proc.isInteractive,
2542
+ stdoutFile: proc.stdoutFile
2543
+ });
2544
+ }
2545
+ return result;
2546
+ }
2547
+ function getUsedPorts() {
2548
+ const ports = [];
2549
+ for (const proc of getAllProcesses()) {
2550
+ if (proc.listeningPort && isProcessRunning(proc.id)) ports.push(proc.listeningPort);
2551
+ }
2552
+ return ports;
2553
+ }
2554
+ function getAllProcessEntries() {
2555
+ const entries = [];
2556
+ for (const proc of getAllProcesses()) {
2557
+ entries.push([proc.id, proc]);
2558
+ }
2559
+ return entries;
2560
+ }
2561
+
2562
+ // src/engine/process/process-stopper.ts
2563
+ import { execSync as execSync2 } from "child_process";
2564
+ import { unlinkSync as unlinkSync2 } from "fs";
2565
+ var cleanupDone = false;
2566
+ async function stopBackgroundProcess(processId) {
2567
+ const proc = getProcess(processId);
2568
+ if (!proc) return { success: false, output: `Process ${processId} not found` };
2569
+ const wasActiveShell = proc.role === PROCESS_ROLES.ACTIVE_SHELL;
2570
+ const finalOutput = getProcessOutput(processId);
2571
+ if (!proc.hasExited) {
2572
+ proc.childPids = discoverAllDescendants(proc.pid);
2573
+ await killProcessTree(proc.pid, proc.childPids);
2574
+ }
2575
+ try {
2576
+ unlinkSync2(proc.stdoutFile);
2577
+ } catch {
2578
+ }
2579
+ try {
2580
+ unlinkSync2(proc.stderrFile);
2581
+ } catch {
2582
+ }
2583
+ try {
2584
+ unlinkSync2(proc.stdinFile);
2585
+ } catch {
2586
+ }
2587
+ logEvent(processId, PROCESS_EVENTS.STOPPED, `Stopped ${proc.role} (PID:${proc.pid}, children:${proc.childPids.length})`);
2588
+ deleteProcess(processId);
2589
+ const childInfo = proc.childPids.length > 0 ? `
2590
+ Child PIDs also killed: ${proc.childPids.join(", ")}` : "";
2591
+ const shellWarning = wasActiveShell ? `
2592
+ ${STATUS_MARKERS.WARNING} WARNING: Active shell was terminated. Target access lost.` : "";
2593
+ return {
2594
+ success: true,
2595
+ output: `Process stopped (PID: ${proc.pid}, role: ${proc.role}).${childInfo}${shellWarning}
2596
+ stdout:
2597
+ ${finalOutput?.stdout?.slice(-SYSTEM_LIMITS.MAX_STDOUT_SLICE) || "(empty)"}
2598
+ stderr:
2599
+ ${finalOutput?.stderr?.slice(-SYSTEM_LIMITS.MAX_STDERR_SLICE) || "(empty)"}`
2600
+ };
2601
+ }
2602
+ async function cleanupAllProcesses() {
2603
+ if (cleanupDone) return;
2604
+ cleanupDone = true;
2605
+ const ids = getAllProcessIds();
2606
+ if (ids.length === 0) {
2607
+ cleanupDone = false;
2608
+ return;
2609
+ }
2610
+ const { getBackgroundProcessesMap: getBackgroundProcessesMap2 } = await import("./process-registry-BDTYM4MC.js");
2611
+ const backgroundProcesses = getBackgroundProcessesMap2();
2612
+ terminateAllNatively(backgroundProcesses, "SIGTERM");
2613
+ await new Promise((r) => setTimeout(r, SYSTEM_LIMITS.CLEANUP_BATCH_WAIT_MS));
2614
+ terminateAllNatively(backgroundProcesses, "SIGKILL");
2615
+ cleanupTemporaryFiles(backgroundProcesses);
2616
+ clearAllProcesses();
2617
+ cleanupOrphanProcesses();
2618
+ cleanupDone = false;
2619
+ }
2620
+ function terminateAllNatively(processes, signal) {
2621
+ for (const [_id, proc] of processes) {
2622
+ if (!proc.hasExited && (signal === "SIGTERM" || isPidAlive(proc.pid))) {
2623
+ proc.childPids = discoverAllDescendants(proc.pid);
2624
+ try {
2625
+ process.kill(-proc.pid, signal);
2626
+ } catch {
2627
+ try {
2628
+ process.kill(proc.pid, signal);
2629
+ } catch {
2630
+ }
2631
+ for (const childPid of proc.childPids) {
2632
+ try {
2633
+ process.kill(childPid, signal);
2634
+ } catch {
2635
+ }
2636
+ }
2637
+ }
2638
+ }
2639
+ }
2640
+ }
2641
+ function cleanupTemporaryFiles(processes) {
2642
+ for (const [, proc] of processes) {
2643
+ try {
2644
+ unlinkSync2(proc.stdoutFile);
2645
+ } catch {
2646
+ }
2647
+ try {
2648
+ unlinkSync2(proc.stderrFile);
2649
+ } catch {
2650
+ }
2651
+ try {
2652
+ unlinkSync2(proc.stdinFile);
2653
+ } catch {
2654
+ }
2655
+ }
2656
+ }
2657
+ function cleanupOrphanProcesses() {
2658
+ let hasPkill = false;
2659
+ try {
2660
+ execSync2("command -v pkill", { stdio: "ignore" });
2661
+ hasPkill = true;
2662
+ } catch {
2663
+ }
2664
+ if (!hasPkill) return;
2665
+ for (const name of ORPHAN_PROCESS_NAMES) {
2666
+ try {
2667
+ execSync2(`pkill -x "${name}" 2>/dev/null || true`, {
2668
+ stdio: "ignore",
2669
+ timeout: SYSTEM_LIMITS.PROCESS_OP_TIMEOUT_MS
2670
+ });
2671
+ } catch {
2672
+ }
2673
+ }
2674
+ }
2675
+
2676
+ // src/engine/process/resource-summary.ts
2677
+ import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
2678
+ function getResourceSummary() {
2679
+ const procs = listBackgroundProcesses();
2680
+ const running = procs.filter((p) => p.isRunning);
2681
+ const exited = procs.filter((p) => !p.isRunning);
2682
+ const ports = getUsedPorts();
2683
+ const recentEvents = getProcessEventLog().slice(-SYSTEM_LIMITS.RECENT_EVENTS_IN_SUMMARY);
2684
+ if (running.length === 0 && exited.length === 0 && recentEvents.length === 0) return "";
2685
+ const lines = [];
2686
+ if (running.length > 0) {
2687
+ lines.push(`Active Assets (${running.length}):`);
2688
+ for (const p of running) {
2689
+ const dur = Math.round(p.durationMs / 1e3);
2690
+ const portInfo = p.listeningPort ? ` port:${p.listeningPort}` : "";
2691
+ const childInfo = p.childPids.length > 0 ? ` children:${p.childPids.join(",")}` : "";
2692
+ const interactiveFlag = p.isInteractive ? ` ${STATUS_MARKERS.INTERACTIVE}` : "";
2693
+ const roleIcon = PROCESS_ICONS[p.role] || PROCESS_ICONS[PROCESS_ROLES.BACKGROUND];
2694
+ let lastOutput = "";
2695
+ try {
2696
+ if (p.stdoutFile && existsSync5(p.stdoutFile)) {
2697
+ const content = readFileSync5(p.stdoutFile, "utf-8");
2698
+ const outputLines = content.trim().split("\n");
2699
+ lastOutput = outputLines.slice(-SYSTEM_LIMITS.RECENT_OUTPUT_LINES).join(" | ").replace(/\n/g, " ");
2700
+ }
2701
+ } catch {
2702
+ }
2703
+ const outputSnippet = lastOutput ? `
2704
+ > Log: ${lastOutput}` : "";
2705
+ lines.push(` ${roleIcon} [${p.id}] PID:${p.pid}${portInfo}${childInfo}${interactiveFlag} [${p.role}] ${p.purpose} (${dur}s)${outputSnippet}`);
2706
+ }
2707
+ }
2708
+ const orphans = [];
2709
+ for (const p of exited) {
2710
+ if (p.childPids.length > 0) {
2711
+ const aliveChildren = p.childPids.filter((pid) => isPidAlive(pid));
2712
+ if (aliveChildren.length > 0) {
2713
+ orphans.push({ id: p.id, childPids: aliveChildren });
2714
+ }
2715
+ }
2716
+ }
2717
+ if (orphans.length > 0) {
2718
+ lines.push(`
2719
+ ${STATUS_MARKERS.WARNING} SUSPECTED ZOMBIES (Orphaned Children):`);
2720
+ for (const o of orphans) {
2721
+ lines.push(` [${o.id}] Exited parent has alive children: ${o.childPids.join(", ")}`);
2722
+ }
2723
+ lines.push(` \u2192 Recommendation: Run bg_process({ action: "stop", process_id: "ID" }) to clean up.`);
2724
+ }
2725
+ if (ports.length > 0) {
2726
+ lines.push(`
2727
+ Ports In Use: ${ports.join(", ")}`);
2728
+ }
2729
+ const activeShells = running.filter((p) => p.role === PROCESS_ROLES.ACTIVE_SHELL);
2730
+ if (activeShells.length > 0) {
2731
+ lines.push(`
2732
+ [SHELL] ACTIVE REVERSE SHELLS \u2014 These are your CLI into the target:`);
2733
+ for (const s of activeShells) {
2734
+ lines.push(` \u2192 [${s.id}] Use interact to execute commands. Upgrade if unstable.`);
2735
+ }
2736
+ }
2737
+ if (exited.length > 0) {
2738
+ lines.push(`Exited Processes (${exited.length}):`);
2739
+ for (const p of exited) {
2740
+ lines.push(` ${STATUS_MARKERS.EXITED} [${p.id}] exit:${p.exitCode} [${p.role}] ${p.description}`);
2741
+ }
2742
+ }
2743
+ if (recentEvents.length > 0) {
2744
+ lines.push(`Recent Process Events:`);
2745
+ for (const e of recentEvents.slice(-SYSTEM_LIMITS.RECENT_EVENTS_DISPLAY)) {
2746
+ const ago = Math.round((Date.now() - e.timestamp) / 1e3);
2747
+ lines.push(` ${ago}s ago: [${e.event}] ${e.detail}`);
2748
+ }
2749
+ }
2750
+ return lines.join("\n");
2751
+ }
2752
+
2753
+ // src/engine/process/process-cleanup.ts
2754
+ import { unlinkSync as unlinkSync3 } from "fs";
2755
+ function syncCleanupAllProcesses(processMap) {
2756
+ for (const [, proc] of processMap) {
2757
+ if (!proc.hasExited) {
2758
+ killProcessTreeSync(proc.pid, proc.childPids);
2759
+ }
2760
+ try {
2761
+ unlinkSync3(proc.stdoutFile);
2762
+ } catch {
2763
+ }
2764
+ try {
2765
+ unlinkSync3(proc.stderrFile);
2766
+ } catch {
2767
+ }
2768
+ try {
2769
+ unlinkSync3(proc.stdinFile);
2770
+ } catch {
2771
+ }
2772
+ }
2773
+ }
2774
+ function registerExitHandlers(processMap) {
2775
+ process.on("exit", () => {
2776
+ syncCleanupAllProcesses(processMap);
2777
+ });
2778
+ process.on("SIGINT", () => {
2779
+ syncCleanupAllProcesses(processMap);
2780
+ processMap.clear();
2781
+ process.exit(EXIT_CODES.SIGINT);
2782
+ });
2783
+ process.on("SIGTERM", () => {
2784
+ syncCleanupAllProcesses(processMap);
2785
+ processMap.clear();
2786
+ process.exit(EXIT_CODES.SIGTERM);
2787
+ });
2788
+ }
2789
+
2790
+ // src/engine/process/process-manager.ts
2791
+ registerExitHandlers(getBackgroundProcessesMap());
2792
+
2793
+ // src/engine/state/persistence/serializer.ts
2794
+ var UNVERIFIED_CHAIN_DEPTH_THRESHOLD = 2;
2795
+ var ACTIONABLE_LOOT_TYPES = /* @__PURE__ */ new Set([
2796
+ LOOT_TYPES.CREDENTIAL,
2797
+ LOOT_TYPES.TOKEN,
2798
+ LOOT_TYPES.HASH,
2799
+ LOOT_TYPES.API_KEY,
2800
+ LOOT_TYPES.SSH_KEY,
2801
+ LOOT_TYPES.TICKET,
2802
+ LOOT_TYPES.SESSION
2803
+ ]);
2804
+ var StateSerializer = class {
2805
+ /**
2806
+ * Convert full state to a compact prompt summary
2807
+ */
2808
+ static toPrompt(state3) {
2809
+ const lines = [];
2810
+ this.formatContextAndMission(state3, lines);
2811
+ this.formatArtifactsAndObjectives(state3, lines);
2812
+ this.formatTargets(state3, lines);
2813
+ this.formatFindings(state3, lines);
2814
+ this.formatLoot(state3, lines);
2815
+ this.formatTodos(state3, lines);
2816
+ this.formatEnvironment(state3, lines);
2817
+ const snapshot = state3.persistentMemory.snapshotToPrompt();
2818
+ if (snapshot) lines.push(snapshot);
2819
+ lines.push(`Phase: ${state3.getPhase()}`);
2820
+ return lines.join("\n");
2821
+ }
2822
+ static formatArtifactsAndObjectives(state3, lines) {
2823
+ if (state3.currentObjective) {
2824
+ lines.push(`Current Objective: ${state3.currentObjective}`);
2825
+ }
2826
+ if (state3.artifacts && state3.artifacts.length > 0) {
2827
+ lines.push(`Artifacts (${state3.artifacts.length}):`);
2828
+ for (const a of state3.artifacts) {
2829
+ lines.push(` [${a.type}] ${a.id}: ${a.description}`);
2830
+ }
2831
+ }
2832
+ }
2833
+ static formatContextAndMission(state3, lines) {
2834
+ const engagement = state3.getEngagement();
2835
+ const scope = state3.getScope();
2836
+ if (engagement) {
2837
+ lines.push(`Engagement: ${engagement.name} (${engagement.client})`);
2838
+ }
2839
+ if (scope) {
2840
+ lines.push(`Scope: CIDR=[${scope.allowedCidrs.join(", ")}] Domains=[${scope.allowedDomains.join(", ")}]`);
2841
+ }
2842
+ const mission = state3.getMissionSummary();
2843
+ if (mission) {
2844
+ lines.push(`MISSION: ${mission}`);
2845
+ }
2846
+ const checklist = state3.getMissionChecklist();
2847
+ if (checklist.length > 0) {
2848
+ lines.push(`STRATEGIC CHECKLIST:`);
2849
+ for (const item of checklist) {
2850
+ const status = item.isCompleted ? "[x]" : "[ ]";
2851
+ lines.push(` ${status} ${item.text} [${item.id}]`);
2852
+ }
2853
+ }
2854
+ }
2855
+ static formatTargets(state3, lines) {
2856
+ const targets = state3.getAllTargets();
2857
+ if (targets.length === 0) return;
2858
+ const byCategory = /* @__PURE__ */ new Map();
2859
+ for (const t of targets) {
2860
+ const cat = t.primaryCategory || SERVICE_CATEGORIES.NETWORK;
2861
+ if (!byCategory.has(cat)) byCategory.set(cat, []);
2862
+ byCategory.get(cat).push(t);
2863
+ }
2864
+ lines.push(`Targets (${targets.length}):`);
2865
+ for (const [cat, catTargets] of byCategory) {
2866
+ lines.push(` [${cat}] ${catTargets.length} hosts`);
2867
+ for (const t of catTargets.slice(0, DISPLAY_LIMITS.TARGET_PREVIEW)) {
2868
+ const ports = (t.ports || []).map((p) => `${p.port}/${p.service}`).join(", ");
2869
+ lines.push(` ${t.ip}${t.hostname ? ` (${t.hostname})` : ""}: ${ports || "unknown"}`);
2870
+ }
2871
+ }
2872
+ }
2873
+ static formatFindings(state3, lines) {
2874
+ const findings = state3.getFindings();
2875
+ if (findings.length === 0) return;
2876
+ const confirmed = findings.filter((f) => f.confidence >= CONFIDENCE_THRESHOLDS.CONFIRMED).length;
2877
+ const probable = findings.filter((f) => f.confidence >= CONFIDENCE_THRESHOLDS.PROBABLE && f.confidence < CONFIDENCE_THRESHOLDS.CONFIRMED).length;
2878
+ const possible = findings.filter((f) => f.confidence < CONFIDENCE_THRESHOLDS.PROBABLE).length;
2879
+ const unverifiedChain = findings.filter((f) => (f.inferenceDepth ?? 0) >= UNVERIFIED_CHAIN_DEPTH_THRESHOLD).length;
2880
+ lines.push(`Findings: ${findings.length} total (confirmed:${confirmed} probable:${probable} possible:${possible}${unverifiedChain > 0 ? ` \u26A0\uFE0Funverified-chain:${unverifiedChain}` : ""})`);
2881
+ const highPriority = findings.filter((f) => f.confidence >= CONFIDENCE_THRESHOLDS.CONFIRMED).sort((a, b) => b.confidence - a.confidence);
2882
+ if (highPriority.length > 0) {
2883
+ lines.push(` Confirmed Findings (\u2265${CONFIDENCE_THRESHOLDS.CONFIRMED}):`);
2884
+ for (const f of highPriority.slice(0, DISPLAY_LIMITS.FINDING_PREVIEW)) {
2885
+ const tactic = f.attackPattern ? ` [ATT&CK:${f.attackPattern}]` : "";
2886
+ lines.push(` [conf:${f.confidence}|${f.severity.toUpperCase()}] ${f.title} (${f.category || "general"})${tactic}`);
2887
+ }
2888
+ }
2889
+ if (unverifiedChain > 0) {
2890
+ const unverified = findings.filter((f) => (f.inferenceDepth ?? 0) >= UNVERIFIED_CHAIN_DEPTH_THRESHOLD).sort((a, b) => (b.inferenceDepth ?? 0) - (a.inferenceDepth ?? 0));
2891
+ lines.push(` \u26A0\uFE0F Unverified Inference Chain Findings (direct verification recommended):`);
2892
+ for (const f of unverified.slice(0, DISPLAY_LIMITS.FINDING_PREVIEW)) {
2893
+ lines.push(` [depth:${f.inferenceDepth}|conf:${f.confidence}] ${f.title}`);
2894
+ }
2895
+ }
2896
+ }
2897
+ static formatLoot(state3, lines) {
2898
+ const loot = state3.getLoot();
2899
+ if (loot.length === 0) return;
2900
+ const actionable = loot.filter((l) => ACTIONABLE_LOOT_TYPES.has(l.type));
2901
+ const other = loot.filter((l) => !ACTIONABLE_LOOT_TYPES.has(l.type));
2902
+ lines.push(`Loot (${loot.length} items):`);
2903
+ for (const l of actionable.slice(0, DISPLAY_LIMITS.COMPACT_LIST_ITEMS)) {
2904
+ lines.push(` \u26A1 USABLE [${l.type}] ${l.host}: ${l.detail}`);
2905
+ }
2906
+ if (actionable.length > DISPLAY_LIMITS.COMPACT_LIST_ITEMS) {
2907
+ lines.push(` ... +${actionable.length - DISPLAY_LIMITS.COMPACT_LIST_ITEMS} more usable items`);
2908
+ }
2909
+ if (other.length > 0) {
2910
+ lines.push(` \u{1F4C1} Other: ${other.length} items (files, certificates, etc.)`);
2911
+ }
2912
+ if (actionable.length > 0) {
2913
+ lines.push(` \u26A0\uFE0F Cross-service attack: Try ALL \u26A1 USABLE credentials/tokens on ALL discovered services.`);
2914
+ }
2915
+ }
2916
+ static formatTodos(state3, lines) {
2917
+ const todo = state3.getTodo();
2918
+ if (todo.length === 0) return;
2919
+ lines.push(`TODO (${todo.length}):`);
2920
+ for (const t of todo.slice(0, DISPLAY_LIMITS.COMPACT_LIST_ITEMS)) {
2921
+ const status = t.status === TODO_STATUSES.DONE ? "[x]" : t.status === TODO_STATUSES.IN_PROGRESS ? "[->]" : "[ ]";
2922
+ lines.push(` ${status} ${t.content} (${t.priority})`);
2923
+ }
2924
+ }
2925
+ static formatEnvironment(state3, lines) {
2926
+ const resourceInfo = getResourceSummary();
2927
+ if (resourceInfo) {
2928
+ lines.push(resourceInfo);
2929
+ }
2930
+ const flags = state3.getFlags();
2931
+ if (flags.length > 0) {
2932
+ lines.push(`Flags Found (${flags.length}): \u{1F3F4}`);
2933
+ for (const f of flags) {
2934
+ lines.push(` \u{1F3F4} ${f}`);
2935
+ }
2936
+ }
2937
+ if (isTorEnabled()) {
2938
+ lines.push(
2939
+ `Tor Proxy: ON \u{1F9C5}
2940
+ Standard tools auto-proxied (curl, wget, nmap, sqlmap, gobuster, ffuf, hydra, etc.)
2941
+ Custom scripts: route ALL target connections through SOCKS5 127.0.0.1:9050.
2942
+ BLOCKED (leak real IP): ping, traceroute, dig, nslookup, nmap -sU`
2943
+ );
2944
+ } else {
2945
+ lines.push(`Tor Proxy: OFF \u2014 direct connections.`);
2946
+ }
2947
+ }
2948
+ };
2949
+
2950
+ // src/engine/state/persistence/saver.ts
2951
+ import { writeFileSync as writeFileSync4, readdirSync, statSync as statSync2, unlinkSync as unlinkSync4 } from "fs";
2952
+ import { join as join3 } from "path";
2953
+ function saveState(state3) {
2954
+ const sessionsDir = WORKSPACE.SESSIONS;
2955
+ ensureDirExists(sessionsDir);
2956
+ const snapshot = {
2957
+ version: 1,
2958
+ savedAt: Date.now(),
2959
+ engagement: state3.getEngagement(),
2960
+ targets: Array.from(state3.getTargets().entries()).map(([key, value]) => ({ key, value })),
2961
+ findings: state3.getFindings(),
2962
+ loot: state3.getLoot(),
2963
+ todo: state3.getTodo(),
2964
+ actionLog: state3.getRecentActions(AGENT_LIMITS.MAX_ACTION_LOG),
2965
+ currentPhase: state3.getPhase(),
2966
+ missionSummary: state3.getMissionSummary(),
2967
+ missionChecklist: state3.getMissionChecklist()
2968
+ };
2969
+ const sessionFile = join3(sessionsDir, FILE_PATTERNS.session());
2970
+ const json = JSON.stringify(snapshot, null, 2);
2971
+ writeFileSync4(sessionFile, json, "utf-8");
2972
+ const latestFile = join3(sessionsDir, "latest.json");
2973
+ writeFileSync4(latestFile, json, "utf-8");
2974
+ pruneOldSessions(sessionsDir);
2975
+ return sessionFile;
2976
+ }
2977
+ function pruneOldSessions(sessionsDir) {
2978
+ try {
2979
+ const sessionFiles = readdirSync(sessionsDir).filter((f) => f.endsWith(FILE_EXTENSIONS.JSON) && f !== SPECIAL_FILES.LATEST_STATE).map((f) => {
2980
+ const filePath = join3(sessionsDir, f);
2981
+ return {
2982
+ name: f,
2983
+ path: filePath,
2984
+ mtime: statSync2(filePath).mtimeMs
2985
+ };
2986
+ }).sort((a, b) => b.mtime - a.mtime);
2987
+ const toDelete = sessionFiles.slice(AGENT_LIMITS.MAX_SESSION_FILES);
2988
+ for (const file of toDelete) {
2989
+ unlinkSync4(file.path);
2990
+ }
2991
+ } catch {
2992
+ }
2993
+ }
2994
+
2995
+ // src/engine/state/persistence/loader.ts
2996
+ import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
2997
+ import { join as join4 } from "path";
2998
+ function loadState(state3) {
2999
+ const latestFile = join4(WORKSPACE.SESSIONS, "latest.json");
3000
+ if (!existsSync6(latestFile)) {
3001
+ return false;
3002
+ }
3003
+ try {
3004
+ const raw = readFileSync6(latestFile, "utf-8");
3005
+ const snapshot = JSON.parse(raw);
3006
+ if (snapshot.version !== 1) {
3007
+ debugLog("general", `Unknown snapshot version: ${snapshot.version}`);
3008
+ return false;
3009
+ }
3010
+ if (snapshot.engagement) {
3011
+ state3.setEngagement(snapshot.engagement);
3012
+ }
3013
+ for (const { value } of snapshot.targets) {
3014
+ state3.addTarget(value);
3015
+ }
3016
+ for (const finding of snapshot.findings) {
3017
+ const legacyFinding = finding;
3018
+ if (typeof legacyFinding.confidence !== "number") {
3019
+ legacyFinding.confidence = legacyFinding.isVerified === true ? 80 : 25;
3020
+ }
3021
+ state3.addFinding(legacyFinding);
3022
+ }
3023
+ for (const loot of snapshot.loot) {
3024
+ state3.addLoot(loot);
3025
+ }
3026
+ for (const item of snapshot.todo) {
3027
+ state3.restoreTodoItem(item);
3028
+ }
3029
+ const validPhases = new Set(Object.values(PHASES));
3030
+ const restoredPhase = validPhases.has(snapshot.currentPhase) ? snapshot.currentPhase : PHASES.RECON;
3031
+ state3.setPhase(restoredPhase);
3032
+ if (snapshot.missionSummary) {
3033
+ state3.setMissionSummary(snapshot.missionSummary);
3034
+ }
3035
+ if (snapshot.missionChecklist?.length > 0) {
3036
+ state3.restoreMissionChecklist(snapshot.missionChecklist);
3037
+ }
3038
+ return true;
3039
+ } catch (err) {
3040
+ debugLog("general", `Failed to load state: ${err}`);
3041
+ return false;
3042
+ }
3043
+ }
3044
+
3045
+ // src/engine/state/persistence/janitor.ts
3046
+ import { existsSync as existsSync7, rmSync } from "fs";
3047
+ function clearWorkspace() {
3048
+ const cleared = [];
3049
+ const errors = [];
3050
+ const dirsToClean = [
3051
+ { path: WORKSPACE.TURNS, label: "turn insights" },
3052
+ { path: WORKSPACE.ARCHIVE, label: "archive (legacy)" },
3053
+ { path: WORKSPACE.DEBUG, label: "debug logs" },
3054
+ { path: WORKSPACE.TMP, label: "workspace/temp" },
3055
+ { path: WORKSPACE.OUTPUTS, label: "outputs (legacy)" },
3056
+ { path: WORKSPACE.JOURNAL, label: "journal (legacy)" },
3057
+ { path: WORKSPACE.LOOT, label: "loot" },
3058
+ { path: WORKSPACE.REPORTS, label: "reports" }
3059
+ ];
3060
+ for (const dir2 of dirsToClean) {
3061
+ try {
3062
+ if (existsSync7(dir2.path)) {
3063
+ rmSync(dir2.path, { recursive: true, force: true });
3064
+ ensureDirExists(dir2.path);
3065
+ cleared.push(dir2.label);
3066
+ }
3067
+ } catch (err) {
3068
+ errors.push(`${dir2.label}: ${getErrorMessage(err)}`);
3069
+ }
3070
+ }
3071
+ return { cleared, errors };
3072
+ }
3073
+
3074
+ export {
3075
+ ensureDirExists,
3076
+ WORK_DIR,
3077
+ WORKSPACE,
3078
+ FILE_EXTENSIONS,
3079
+ SPECIAL_FILES,
3080
+ FILE_PATTERNS,
3081
+ initDebugLogger,
3082
+ debugLog,
3083
+ flowLog,
3084
+ DEFAULT_MODEL,
3085
+ getApiKey,
3086
+ getBaseUrl,
3087
+ getModel,
3088
+ getSearchApiKey,
3089
+ getSearchApiUrl,
3090
+ isZaiProvider,
3091
+ isThinkingEnabled,
3092
+ getThinkingBudget,
3093
+ isTorEnabled,
3094
+ setTorEnabled,
3095
+ getTorBrowserArgs,
3096
+ validateRequiredConfig,
3097
+ MS_PER_MINUTE,
3098
+ DISPLAY_LIMITS,
3099
+ RETRY_CONFIG,
3100
+ LLM_LIMITS,
3101
+ LLM_ERROR_TYPES,
3102
+ AGENT_LIMITS,
3103
+ MEMORY_LIMITS,
3104
+ APP_NAME,
3105
+ APP_VERSION,
3106
+ APP_DESCRIPTION,
3107
+ LLM_ROLES,
3108
+ INPUT_TYPES,
3109
+ SENSITIVE_INPUT_TYPES,
3110
+ AUXILIARY_WORK_TYPES,
3111
+ PHASES,
3112
+ PHASE_TRANSITIONS,
3113
+ AGENT_ROLES,
3114
+ SERVICES,
3115
+ SERVICE_CATEGORIES,
3116
+ APPROVAL_LEVELS,
3117
+ DANGER_LEVELS,
3118
+ CATEGORY_APPROVAL,
3119
+ DANGER_LEVEL_MAP,
3120
+ TODO_STATUSES,
3121
+ LOOT_TYPES,
3122
+ APPROVAL_STATUSES,
3123
+ SEVERITIES,
3124
+ PRIORITIES,
3125
+ NOISE_LEVELS,
3126
+ DEFAULTS,
3127
+ WORDLISTS,
3128
+ HASHCAT_MODES,
3129
+ NOISE_CLASSIFICATION,
3130
+ TOOL_NAMES,
3131
+ ATTACK_TACTICS,
3132
+ ATTACK_VALUE_RANK,
3133
+ CONFIDENCE_THRESHOLDS,
3134
+ EVENT_TYPES,
3135
+ COMMAND_EVENT_TYPES,
3136
+ UI_COMMANDS,
3137
+ generateId,
3138
+ generatePrefixedId,
3139
+ setCommandEventEmitter,
3140
+ setInputHandler,
3141
+ clearInputHandler,
3142
+ clearCommandEventEmitter,
3143
+ runCommand,
3144
+ getErrorMessage,
3145
+ readFileContent,
3146
+ writeFileContent,
3147
+ createTempFile,
3148
+ startBackgroundProcess,
3149
+ sendToProcess,
3150
+ promoteToShell,
3151
+ getProcessOutput,
3152
+ listBackgroundProcesses,
3153
+ getUsedPorts,
3154
+ stopBackgroundProcess,
3155
+ cleanupAllProcesses,
3156
+ StateSerializer,
3157
+ saveState,
3158
+ loadState,
3159
+ clearWorkspace
3160
+ };