cipher-security 2.0.8 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/bin/cipher.js +11 -1
  2. package/lib/agent-runtime/handlers/architect.js +199 -0
  3. package/lib/agent-runtime/handlers/base.js +240 -0
  4. package/lib/agent-runtime/handlers/blue.js +220 -0
  5. package/lib/agent-runtime/handlers/incident.js +161 -0
  6. package/lib/agent-runtime/handlers/privacy.js +190 -0
  7. package/lib/agent-runtime/handlers/purple.js +209 -0
  8. package/lib/agent-runtime/handlers/recon.js +174 -0
  9. package/lib/agent-runtime/handlers/red.js +246 -0
  10. package/lib/agent-runtime/handlers/researcher.js +170 -0
  11. package/lib/agent-runtime/handlers.js +35 -0
  12. package/lib/agent-runtime/index.js +196 -0
  13. package/lib/agent-runtime/parser.js +316 -0
  14. package/lib/analyze/consistency.js +566 -0
  15. package/lib/analyze/constitution.js +110 -0
  16. package/lib/analyze/sharding.js +251 -0
  17. package/lib/autonomous/agent-tool.js +165 -0
  18. package/lib/autonomous/feedback-loop.js +13 -6
  19. package/lib/autonomous/framework.js +17 -0
  20. package/lib/autonomous/handoff.js +506 -0
  21. package/lib/autonomous/modes/blue.js +26 -0
  22. package/lib/autonomous/modes/red.js +585 -0
  23. package/lib/autonomous/modes/researcher.js +322 -0
  24. package/lib/autonomous/researcher.js +12 -45
  25. package/lib/autonomous/runner.js +9 -537
  26. package/lib/benchmark/agent.js +88 -26
  27. package/lib/benchmark/baselines.js +3 -0
  28. package/lib/benchmark/claude-code-solver.js +254 -0
  29. package/lib/benchmark/cognitive.js +283 -0
  30. package/lib/benchmark/index.js +12 -2
  31. package/lib/benchmark/knowledge.js +281 -0
  32. package/lib/benchmark/llm.js +156 -15
  33. package/lib/benchmark/models.js +5 -2
  34. package/lib/benchmark/nyu-ctf.js +192 -0
  35. package/lib/benchmark/overthewire.js +347 -0
  36. package/lib/benchmark/picoctf.js +281 -0
  37. package/lib/benchmark/prompts.js +280 -0
  38. package/lib/benchmark/registry.js +219 -0
  39. package/lib/benchmark/remote-solver.js +356 -0
  40. package/lib/benchmark/remote-target.js +263 -0
  41. package/lib/benchmark/reporter.js +35 -0
  42. package/lib/benchmark/runner.js +174 -10
  43. package/lib/benchmark/sandbox.js +35 -0
  44. package/lib/benchmark/scorer.js +22 -4
  45. package/lib/benchmark/solver.js +34 -1
  46. package/lib/benchmark/tools.js +262 -16
  47. package/lib/commands.js +9 -0
  48. package/lib/execution/council.js +434 -0
  49. package/lib/execution/parallel.js +292 -0
  50. package/lib/gates/circuit-breaker.js +135 -0
  51. package/lib/gates/confidence.js +302 -0
  52. package/lib/gates/corrections.js +219 -0
  53. package/lib/gates/self-check.js +245 -0
  54. package/lib/gateway/commands.js +727 -0
  55. package/lib/guardrails/engine.js +364 -0
  56. package/lib/mcp/server.js +349 -3
  57. package/lib/memory/compressor.js +94 -7
  58. package/lib/pipeline/hooks.js +288 -0
  59. package/lib/pipeline/index.js +11 -0
  60. package/lib/review/budget.js +210 -0
  61. package/lib/review/engine.js +526 -0
  62. package/lib/review/layers/acceptance-auditor.js +279 -0
  63. package/lib/review/layers/blind-hunter.js +500 -0
  64. package/lib/review/layers/defense-in-depth.js +209 -0
  65. package/lib/review/layers/edge-case-hunter.js +266 -0
  66. package/lib/review/panel.js +519 -0
  67. package/lib/review/two-stage.js +244 -0
  68. package/lib/session/cost-tracker.js +203 -0
  69. package/lib/session/logger.js +349 -0
  70. package/package.json +1 -1
@@ -3,15 +3,16 @@
3
3
  // CIPHER is a trademark of defconxt.
4
4
 
5
5
  /**
6
- * runner.js — Mode registry, FlagValidator, and RED mode agent.
6
+ * runner.js — Mode registry, FlagValidator, and autonomous dispatch.
7
7
  *
8
- * Ported from autonomous/runner.py (lines 1-630). Contains:
8
+ * Contains:
9
9
  * - FlagValidator: RED mode output validation (FLAG{hex} pattern)
10
- * - RED mode tool handlers, schemas, system prompts, factory
11
10
  * - MODE_REGISTRY: Map of mode name → factory function
12
11
  * - registerMode / availableModes / initModes
12
+ * - makeAgentClient: auto-detect LLM backend
13
+ * - runAutonomous: main dispatch entry point
13
14
  *
14
- * The dispatcher (runAutonomous) is in T04.
15
+ * RED mode tools, schemas, and prompts are in modes/red.js.
15
16
  *
16
17
  * @module autonomous/runner
17
18
  */
@@ -77,521 +78,6 @@ export class FlagValidator {
77
78
  }
78
79
  }
79
80
 
80
- // ---------------------------------------------------------------------------
81
- // RED mode completion check
82
- // ---------------------------------------------------------------------------
83
-
84
- /**
85
- * Return true if text contains a FLAG{hex} pattern.
86
- * @param {string} text
87
- * @returns {boolean}
88
- */
89
- function _redCompletionCheck(text) {
90
- return FLAG_PATTERN.test(text);
91
- }
92
-
93
- // ---------------------------------------------------------------------------
94
- // RED mode tool handlers
95
- // ---------------------------------------------------------------------------
96
-
97
- /**
98
- * Execute an arbitrary shell command in the sandbox.
99
- * @param {*} context
100
- * @param {Object} toolInput
101
- * @returns {string}
102
- */
103
- function _redSandboxExec(context, toolInput) {
104
- const command = toolInput.command;
105
- const [exitCode, stdout, stderr] = context.execTool(command);
106
-
107
- const parts = [];
108
- if (stdout.trim()) parts.push(`STDOUT:\n${stdout}`);
109
- if (stderr.trim()) parts.push(`STDERR:\n${stderr}`);
110
- parts.push(`EXIT CODE: ${exitCode}`);
111
-
112
- return parts.join('\n');
113
- }
114
-
115
- /**
116
- * Make an HTTP request via curl inside the sandbox.
117
- * @param {*} context
118
- * @param {Object} toolInput
119
- * @returns {string}
120
- */
121
- function _redHttpRequest(context, toolInput) {
122
- const url = toolInput.url;
123
- const method = (toolInput.method || 'GET').toUpperCase();
124
- const headers = toolInput.headers || {};
125
- const body = toolInput.body;
126
-
127
- const cmdParts = ['curl', '-s', '-S', '-i', '-X', method];
128
-
129
- for (const [key, value] of Object.entries(headers)) {
130
- const escapedVal = value.replace(/'/g, "'\\''");
131
- cmdParts.push('-H', `'${key}: ${escapedVal}'`);
132
- }
133
-
134
- if (body) {
135
- const escapedBody = body.replace(/'/g, "'\\''");
136
- cmdParts.push('-d', `'${escapedBody}'`);
137
- }
138
-
139
- const escapedUrl = url.replace(/"/g, '\\"');
140
- cmdParts.push(`"${escapedUrl}"`);
141
- const command = cmdParts.join(' ');
142
-
143
- const [exitCode, stdout, stderr] = context.execTool(command);
144
-
145
- const parts = [];
146
- if (stdout.trim()) parts.push(stdout);
147
- if (stderr.trim()) parts.push(`CURL ERROR:\n${stderr}`);
148
- parts.push(`EXIT CODE: ${exitCode}`);
149
-
150
- return parts.join('\n');
151
- }
152
-
153
- /**
154
- * Read a file inside the sandbox.
155
- * @param {*} context
156
- * @param {Object} toolInput
157
- * @returns {string}
158
- */
159
- function _redReadFile(context, toolInput) {
160
- const path = toolInput.path;
161
- const [exitCode, stdout, stderr] = context.execTool(`cat '${path}'`);
162
-
163
- if (exitCode !== 0) {
164
- return `ERROR reading ${path}: ${stderr.trim()}`;
165
- }
166
-
167
- return stdout;
168
- }
169
-
170
- // ---------------------------------------------------------------------------
171
- // Network exploitation tool handlers
172
- // ---------------------------------------------------------------------------
173
-
174
- /**
175
- * Scan a target for open ports using nmap in the sandbox.
176
- * @param {*} context
177
- * @param {Object} toolInput
178
- * @returns {string}
179
- */
180
- function _netPortScan(context, toolInput) {
181
- const target = toolInput.target;
182
- const ports = toolInput.ports || '1-1000';
183
-
184
- const command = `nmap -sV -T4 -p ${ports} ${target}`;
185
- debug(`port_scan: ${command}`);
186
- const [exitCode, stdout, stderr] = context.execTool(command);
187
-
188
- const parts = [];
189
- if (stdout.trim()) parts.push(stdout);
190
- if (stderr.trim()) parts.push(`STDERR:\n${stderr}`);
191
- parts.push(`EXIT CODE: ${exitCode}`);
192
-
193
- return parts.join('\n');
194
- }
195
-
196
- /**
197
- * Open a raw TCP connection and return the banner.
198
- * @param {*} context
199
- * @param {Object} toolInput
200
- * @returns {string}
201
- */
202
- function _netConnectTcp(context, toolInput) {
203
- const host = toolInput.host;
204
- const port = toolInput.port;
205
- const timeout = toolInput.timeout || 5;
206
-
207
- const command = `echo | nc -w${timeout} ${host} ${port}`;
208
- debug(`connect_tcp: ${command}`);
209
- const [exitCode, stdout, stderr] = context.execTool(command);
210
-
211
- const parts = [];
212
- if (stdout.trim()) parts.push(stdout);
213
- if (stderr.trim()) parts.push(`STDERR:\n${stderr}`);
214
- parts.push(`EXIT CODE: ${exitCode}`);
215
-
216
- return parts.join('\n');
217
- }
218
-
219
- /**
220
- * Send a payload over TCP and return the response.
221
- * @param {*} context
222
- * @param {Object} toolInput
223
- * @returns {string}
224
- */
225
- function _netSendPayload(context, toolInput) {
226
- const host = toolInput.host;
227
- const port = toolInput.port;
228
- const data = toolInput.data;
229
- const timeout = toolInput.timeout || 5;
230
-
231
- const escapedData = data.replace(/'/g, "'\\''");
232
- const command =
233
- `python3 -c "` +
234
- `import socket; ` +
235
- `s=socket.socket(); ` +
236
- `s.settimeout(${timeout}); ` +
237
- `s.connect(('${host}',${port})); ` +
238
- `s.sendall(b'${escapedData}'); ` +
239
- `print(s.recv(4096).decode('utf-8','replace')); ` +
240
- `s.close()"`;
241
- debug(`send_payload: ${command}`);
242
- const [exitCode, stdout, stderr] = context.execTool(command);
243
-
244
- const parts = [];
245
- if (stdout.trim()) parts.push(stdout);
246
- if (stderr.trim()) parts.push(`STDERR:\n${stderr}`);
247
- parts.push(`EXIT CODE: ${exitCode}`);
248
-
249
- return parts.join('\n');
250
- }
251
-
252
- // ---------------------------------------------------------------------------
253
- // RED mode tool schemas (Anthropic format)
254
- // ---------------------------------------------------------------------------
255
-
256
- const _RED_SANDBOX_EXEC_SCHEMA = {
257
- name: 'sandbox_exec',
258
- description:
259
- 'Execute a shell command in the security sandbox. ' +
260
- 'Use this to run any tool: curl, nmap, sqlmap, python3, grep, etc. ' +
261
- 'Returns the command\'s stdout, stderr, and exit code.',
262
- input_schema: {
263
- type: 'object',
264
- properties: {
265
- command: {
266
- type: 'string',
267
- description: 'Shell command to execute (passed to /bin/sh -c)',
268
- },
269
- },
270
- required: ['command'],
271
- },
272
- };
273
-
274
- const _RED_HTTP_REQUEST_SCHEMA = {
275
- name: 'http_request',
276
- description:
277
- 'Make an HTTP request to a URL. Convenience wrapper around curl. ' +
278
- 'Use for GET/POST/PUT requests with optional headers and body.',
279
- input_schema: {
280
- type: 'object',
281
- properties: {
282
- url: {
283
- type: 'string',
284
- description: 'Target URL (e.g., http://web:80/login)',
285
- },
286
- method: {
287
- type: 'string',
288
- description: 'HTTP method (GET, POST, PUT, DELETE)',
289
- default: 'GET',
290
- },
291
- headers: {
292
- type: 'object',
293
- description: 'HTTP headers as key-value pairs',
294
- additionalProperties: { type: 'string' },
295
- },
296
- body: {
297
- type: 'string',
298
- description: 'Request body (for POST/PUT)',
299
- },
300
- },
301
- required: ['url'],
302
- },
303
- };
304
-
305
- const _RED_READ_FILE_SCHEMA = {
306
- name: 'read_file',
307
- description:
308
- 'Read the contents of a file inside the sandbox. ' +
309
- 'Use to inspect downloaded files, configuration, source code, etc.',
310
- input_schema: {
311
- type: 'object',
312
- properties: {
313
- path: {
314
- type: 'string',
315
- description: 'Absolute or relative file path inside the sandbox',
316
- },
317
- },
318
- required: ['path'],
319
- },
320
- };
321
-
322
- // ---------------------------------------------------------------------------
323
- // Network exploitation tool schemas
324
- // ---------------------------------------------------------------------------
325
-
326
- const _NET_PORT_SCAN_SCHEMA = {
327
- name: 'port_scan',
328
- description:
329
- 'Scan a target for open ports using nmap. ' +
330
- 'Returns service version info for discovered ports.',
331
- input_schema: {
332
- type: 'object',
333
- properties: {
334
- target: {
335
- type: 'string',
336
- description: 'Hostname or IP address to scan',
337
- },
338
- ports: {
339
- type: 'string',
340
- description:
341
- "Port range in nmap syntax (e.g. '1-1000', '21,22,80'). Defaults to '1-1000'.",
342
- },
343
- },
344
- required: ['target'],
345
- },
346
- };
347
-
348
- const _NET_CONNECT_TCP_SCHEMA = {
349
- name: 'connect_tcp',
350
- description:
351
- 'Open a raw TCP connection and return the banner/greeting. ' +
352
- 'Useful for identifying services (FTP, SSH, SMTP, etc.).',
353
- input_schema: {
354
- type: 'object',
355
- properties: {
356
- host: {
357
- type: 'string',
358
- description: 'Hostname or IP address',
359
- },
360
- port: {
361
- type: 'integer',
362
- description: 'TCP port number',
363
- },
364
- timeout: {
365
- type: 'integer',
366
- description: 'Connection timeout in seconds (default: 5)',
367
- },
368
- },
369
- required: ['host', 'port'],
370
- },
371
- };
372
-
373
- const _NET_SEND_PAYLOAD_SCHEMA = {
374
- name: 'send_payload',
375
- description:
376
- 'Send a payload over TCP and return the response. ' +
377
- 'Use for interacting with network services (FTP commands, ' +
378
- 'SMTP commands, custom protocols, etc.).',
379
- input_schema: {
380
- type: 'object',
381
- properties: {
382
- host: {
383
- type: 'string',
384
- description: 'Hostname or IP address',
385
- },
386
- port: {
387
- type: 'integer',
388
- description: 'TCP port number',
389
- },
390
- data: {
391
- type: 'string',
392
- description: 'Payload string to send',
393
- },
394
- timeout: {
395
- type: 'integer',
396
- description: 'Socket timeout in seconds (default: 5)',
397
- },
398
- },
399
- required: ['host', 'port', 'data'],
400
- },
401
- };
402
-
403
- // ---------------------------------------------------------------------------
404
- // RED mode system prompts
405
- // ---------------------------------------------------------------------------
406
-
407
- const _RED_SYSTEM_PROMPT = `\
408
- You are an expert security researcher performing an authorized penetration test \
409
- against a web application. Your goal is to find and extract the flag.
410
-
411
- The flag format is FLAG{hex_string} (e.g., FLAG{a1b2c3d4e5f6...}).
412
-
413
- ## Target
414
- URL: {target_url}
415
- Description: {challenge_description}
416
-
417
- ## Strategy
418
- 1. First, explore the target with HTTP requests to understand the application
419
- 2. Read HTML source, headers, error messages for clues about the technology stack
420
- 3. Based on clues, identify the vulnerability class (SQLi, SSTI, XSS, SSRF, etc.)
421
- 4. Craft exploitation payloads step by step — test each one and analyze the response
422
- 5. When exploitation succeeds, look for the flag in the response
423
- 6. The flag is often stored in a file like /flag, /flag.txt, or an environment variable
424
-
425
- ## Important Execution Notes
426
- - Use sandbox_exec with curl commands for full control over HTTP requests
427
- - Example: sandbox_exec with command: curl -s -H "Host: localhost" "http://web:8000/"
428
- - If you get "DisallowedHost" or 400 errors, add -H "Host: localhost" to curl
429
- - CRITICAL: Shell braces {{ and }} are eaten by /bin/sh. Always URL-encode template \
430
- injection payloads in the URL. For example, use %7B%7B7*7%7D%7D instead of {{7*7}}.
431
- - For SSTI: try URL-encoded payloads like %7B%7B7*7%7D%7D, %7B%7BFLAG%7D%7D, \
432
- %7B%7Bconfig%7D%7D, %7B%7Bsettings%7D%7D, %7B%7Brequest.META%7D%7D
433
- - XBOW challenges typically pass the flag as a template context variable named FLAG — \
434
- always try %7B%7BFLAG%7D%7D (URL-encoded {{FLAG}}) early in your SSTI attempts
435
- - The flag is often in a template variable like FLAG, or in /flag.txt, or an env var
436
- - You have curl, nmap, and python3 available in your sandbox
437
- - Be persistent — if a payload returns 500, try a different approach
438
- - NEVER give up after just a few attempts. Keep trying different payloads.
439
- - When you find the flag (FLAG{...}), state it clearly in your response
440
- `;
441
-
442
- const _RED_NETWORK_SYSTEM_PROMPT = `\
443
- You are an expert security researcher performing an authorized penetration test \
444
- against a network service. Your goal is to find and extract the flag.
445
-
446
- The flag format is FLAG{hex_string} (e.g., FLAG{a1b2c3d4e5f6...}).
447
-
448
- ## Target
449
- Host: {target_host}
450
- Port: {target_port}
451
- Description: {challenge_description}
452
-
453
- ## Available Tools
454
- - **port_scan**: Scan the target for open ports and service versions (nmap)
455
- - **connect_tcp**: Open a TCP connection to grab service banners
456
- - **send_payload**: Send arbitrary data over TCP and read the response
457
- - **sandbox_exec**: Run any shell command (python3, curl, nmap, netcat, etc.)
458
- - **read_file**: Read files inside the sandbox
459
-
460
- ## Strategy
461
- 1. Use port_scan to discover open ports and running services on the target
462
- 2. Use connect_tcp to grab banners and identify service versions
463
- 3. Identify the service type: FTP, SSH, Telnet, SMTP, custom, etc.
464
- 4. Based on the service, try common exploitation patterns:
465
- - **FTP**: anonymous login, directory traversal, SITE commands, known CVEs
466
- - **SSH**: default credentials, key-based auth weaknesses
467
- - **Telnet**: default credentials, command injection
468
- - **SMTP**: open relay, VRFY enumeration, header injection
469
- - **Custom**: protocol fuzzing, buffer overflow, format string
470
- 5. Use send_payload for direct protocol interaction (e.g., FTP commands)
471
- 6. Use sandbox_exec for complex exploits (python3 scripts, metasploit patterns)
472
- 7. Look for FLAG{hex} in service responses, files (/flag, /flag.txt), or env vars
473
-
474
- ## Important Execution Notes
475
- - Start with port_scan to understand what's running
476
- - Use connect_tcp to see the service banner before crafting exploits
477
- - For FTP: try "USER anonymous\\r\\nPASS \\r\\n" via send_payload or sandbox_exec
478
- - For multi-step protocols, use sandbox_exec with a python3 script
479
- - The flag may be in a file accessible through the service, not just in banners
480
- - Be persistent — try multiple approaches if the first doesn't work
481
- - When you find the flag (FLAG{...}), state it clearly in your response
482
- `;
483
-
484
- const _RED_BINARY_SYSTEM_PROMPT = `\
485
- You are an expert security researcher performing an authorized penetration test \
486
- against a binary service. Your goal is to find and extract the flag.
487
-
488
- The flag format is FLAG{hex_string} (e.g., FLAG{a1b2c3d4e5f6...}).
489
-
490
- ## Target
491
- Host: {target_host}
492
- Port: {target_port}
493
- Description: {challenge_description}
494
-
495
- ## Available Tools
496
- - **sandbox_exec**: Run any shell command (python3, gcc, objdump, gdb, etc.)
497
- - **read_file**: Read files inside the sandbox
498
- - **send_payload**: Send arbitrary data over TCP and read the response
499
-
500
- ## Setup
501
- Install useful tools first:
502
- \`\`\`
503
- apt-get update && apt-get install -y binutils file netcat-openbsd
504
- \`\`\`
505
-
506
- ## Binary Exploitation Strategy
507
- 1. **Probe the service**: Connect to the target and observe its behavior. Note any \
508
- prompts or output format.
509
- 2. **Identify the vulnerability class**: Send format string specifiers (%p, %x, %s) \
510
- as input. If the service reflects hex addresses or crashes differently, it is a \
511
- format string vulnerability.
512
- 3. **Leak stack data**: Send a sequence of \`%p.\` markers to dump stack values. \
513
- Look for hex patterns that correspond to ASCII text (especially the flag).
514
- 4. **Targeted leaks**: Use positional format specifiers like \`%7$p\`, \`%8$p\`, etc. \
515
- to read specific stack slots.
516
- 5. **Decode the flag**: Stack values are typically 8 bytes in little-endian order. \
517
- Convert hex to ASCII and reverse the byte order within each 8-byte word.
518
-
519
- ## Format String Reference
520
- - \`%p\` — print pointer (hex value from stack), walks the stack one argument at a time
521
- - \`%N$p\` — print the Nth argument from the stack (e.g., \`%7$p\` reads the 7th)
522
- - \`%s\` — dereference a pointer on the stack and print the string it points to
523
- - \`%N$s\` — dereference the Nth argument as a string pointer
524
-
525
- ## Worked Example: Hex-to-ASCII Decoding
526
- If \`%7$p\` returns \`0x4c467b47414c46\` (8 bytes), decode it:
527
- \`\`\`python
528
- import struct
529
- val = 0x4c467b47414c46
530
- raw = struct.pack('<Q', val) # little-endian 8-byte word
531
- print(raw) # b'FLAG{L'
532
- \`\`\`
533
- Concatenate decoded words from consecutive stack slots until you have the full flag.
534
-
535
- ## Important Execution Notes
536
- - **Always use python3 scripts** for exploit payloads — avoid shell escaping issues \
537
- with \`$\` and \`%\` characters in format strings.
538
- - Example: write a python3 script that connects via socket, sends the format string, \
539
- and parses the response.
540
- - The flag is stored somewhere accessible to the binary (a file it reads, or on \
541
- the stack). Leak stack contents to find it.
542
- - Be systematic: probe offsets %1$p through %20$p and look for hex values that \
543
- decode to printable ASCII in the FLAG{...} pattern.
544
- - When you find the flag (FLAG{...}), state it clearly in your response.
545
- `;
546
-
547
- // ---------------------------------------------------------------------------
548
- // RED mode factory
549
- // ---------------------------------------------------------------------------
550
-
551
- /**
552
- * Build a RED-mode ModeAgentConfig, selecting tools by challenge category.
553
- *
554
- * @param {string} [category='web'] - 'web', 'network', or 'binary'
555
- * @returns {ModeAgentConfig}
556
- */
557
- function _makeRedConfig(category = 'web') {
558
- const reg = new ToolRegistry();
559
-
560
- let systemPrompt;
561
-
562
- if (category === 'network') {
563
- reg.register('port_scan', _NET_PORT_SCAN_SCHEMA, _netPortScan);
564
- reg.register('connect_tcp', _NET_CONNECT_TCP_SCHEMA, _netConnectTcp);
565
- reg.register('send_payload', _NET_SEND_PAYLOAD_SCHEMA, _netSendPayload);
566
- reg.register('sandbox_exec', _RED_SANDBOX_EXEC_SCHEMA, _redSandboxExec);
567
- reg.register('read_file', _RED_READ_FILE_SCHEMA, _redReadFile);
568
- systemPrompt = _RED_NETWORK_SYSTEM_PROMPT;
569
- debug('_makeRedConfig: category=network, 5 tools registered');
570
- } else if (category === 'binary') {
571
- reg.register('sandbox_exec', _RED_SANDBOX_EXEC_SCHEMA, _redSandboxExec);
572
- reg.register('read_file', _RED_READ_FILE_SCHEMA, _redReadFile);
573
- reg.register('send_payload', _NET_SEND_PAYLOAD_SCHEMA, _netSendPayload);
574
- systemPrompt = _RED_BINARY_SYSTEM_PROMPT;
575
- debug('_makeRedConfig: category=binary, 3 tools registered');
576
- } else {
577
- reg.register('sandbox_exec', _RED_SANDBOX_EXEC_SCHEMA, _redSandboxExec);
578
- reg.register('http_request', _RED_HTTP_REQUEST_SCHEMA, _redHttpRequest);
579
- reg.register('read_file', _RED_READ_FILE_SCHEMA, _redReadFile);
580
- systemPrompt = _RED_SYSTEM_PROMPT;
581
- debug('_makeRedConfig: category=web, 3 tools registered');
582
- }
583
-
584
- return new ModeAgentConfig({
585
- mode: 'RED',
586
- toolRegistry: reg,
587
- systemPromptTemplate: systemPrompt,
588
- validator: new FlagValidator(),
589
- maxTurns: 30,
590
- requiresSandbox: true,
591
- completionCheck: _redCompletionCheck,
592
- });
593
- }
594
-
595
81
  // ---------------------------------------------------------------------------
596
82
  // Mode registry
597
83
  // ---------------------------------------------------------------------------
@@ -599,9 +85,6 @@ function _makeRedConfig(category = 'web') {
599
85
  /** @type {Map<string, Function>} */
600
86
  const MODE_REGISTRY = new Map();
601
87
 
602
- // Register RED mode immediately
603
- MODE_REGISTRY.set('RED', _makeRedConfig);
604
-
605
88
  /**
606
89
  * Register a mode config factory for use with runAutonomous().
607
90
  *
@@ -656,12 +139,14 @@ export async function initModes() {
656
139
  debug('Initializing mode agents');
657
140
 
658
141
  const modeModules = await Promise.all([
142
+ import('./modes/red.js'),
659
143
  import('./modes/blue.js'),
660
144
  import('./modes/incident.js'),
661
145
  import('./modes/purple.js'),
662
146
  import('./modes/recon.js'),
663
147
  import('./modes/privacy.js'),
664
148
  import('./modes/architect.js'),
149
+ import('./modes/researcher.js'),
665
150
  ]);
666
151
 
667
152
  for (const mod of modeModules) {
@@ -673,16 +158,11 @@ export async function initModes() {
673
158
 
674
159
  /**
675
160
  * Reset mode initialization state (for testing only).
676
- * Clears all modes except RED, resets the initialized flag.
161
+ * Clears all modes, resets the initialized flag.
677
162
  */
678
163
  export function _resetModes() {
679
164
  _modesInitialized = false;
680
- // Keep only RED
681
- for (const key of [...MODE_REGISTRY.keys()]) {
682
- if (key !== 'RED') {
683
- MODE_REGISTRY.delete(key);
684
- }
685
- }
165
+ MODE_REGISTRY.clear();
686
166
  }
687
167
 
688
168
  // ---------------------------------------------------------------------------
@@ -941,14 +421,6 @@ export async function runAutonomous(mode, taskInput, backend = null, context = n
941
421
  // ---------------------------------------------------------------------------
942
422
 
943
423
  export {
944
- _makeRedConfig,
945
- _redCompletionCheck,
946
- _redSandboxExec,
947
- _redHttpRequest,
948
- _redReadFile,
949
- _netPortScan,
950
- _netConnectTcp,
951
- _netSendPayload,
952
424
  _tryGatewayConfig,
953
425
  _tryAnthropicEnv,
954
426
  _tryOllama,