cipher-security 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/bin/cipher.js +465 -0
  2. package/lib/api/billing.js +321 -0
  3. package/lib/api/compliance.js +693 -0
  4. package/lib/api/controls.js +1401 -0
  5. package/lib/api/index.js +49 -0
  6. package/lib/api/marketplace.js +467 -0
  7. package/lib/api/openai-proxy.js +383 -0
  8. package/lib/api/server.js +685 -0
  9. package/lib/autonomous/feedback-loop.js +554 -0
  10. package/lib/autonomous/framework.js +512 -0
  11. package/lib/autonomous/index.js +97 -0
  12. package/lib/autonomous/leaderboard.js +594 -0
  13. package/lib/autonomous/modes/architect.js +412 -0
  14. package/lib/autonomous/modes/blue.js +386 -0
  15. package/lib/autonomous/modes/incident.js +684 -0
  16. package/lib/autonomous/modes/privacy.js +369 -0
  17. package/lib/autonomous/modes/purple.js +294 -0
  18. package/lib/autonomous/modes/recon.js +250 -0
  19. package/lib/autonomous/parallel.js +587 -0
  20. package/lib/autonomous/researcher.js +583 -0
  21. package/lib/autonomous/runner.js +955 -0
  22. package/lib/autonomous/scheduler.js +615 -0
  23. package/lib/autonomous/task-parser.js +127 -0
  24. package/lib/autonomous/validators/forensic.js +266 -0
  25. package/lib/autonomous/validators/osint.js +216 -0
  26. package/lib/autonomous/validators/privacy.js +296 -0
  27. package/lib/autonomous/validators/purple.js +298 -0
  28. package/lib/autonomous/validators/sigma.js +248 -0
  29. package/lib/autonomous/validators/threat-model.js +363 -0
  30. package/lib/benchmark/agent.js +119 -0
  31. package/lib/benchmark/baselines.js +43 -0
  32. package/lib/benchmark/builder.js +143 -0
  33. package/lib/benchmark/config.js +35 -0
  34. package/lib/benchmark/coordinator.js +91 -0
  35. package/lib/benchmark/index.js +20 -0
  36. package/lib/benchmark/llm.js +58 -0
  37. package/lib/benchmark/models.js +137 -0
  38. package/lib/benchmark/reporter.js +103 -0
  39. package/lib/benchmark/runner.js +103 -0
  40. package/lib/benchmark/sandbox.js +96 -0
  41. package/lib/benchmark/scorer.js +32 -0
  42. package/lib/benchmark/solver.js +166 -0
  43. package/lib/benchmark/tools.js +62 -0
  44. package/lib/bot/bot.js +130 -0
  45. package/lib/commands.js +99 -0
  46. package/lib/complexity.js +377 -0
  47. package/lib/config.js +213 -0
  48. package/lib/gateway/client.js +309 -0
  49. package/lib/gateway/commands.js +830 -0
  50. package/lib/gateway/config-validate.js +109 -0
  51. package/lib/gateway/gateway.js +367 -0
  52. package/lib/gateway/index.js +62 -0
  53. package/lib/gateway/mode.js +309 -0
  54. package/lib/gateway/plugins.js +222 -0
  55. package/lib/gateway/prompt.js +214 -0
  56. package/lib/mcp/server.js +262 -0
  57. package/lib/memory/compressor.js +425 -0
  58. package/lib/memory/engine.js +763 -0
  59. package/lib/memory/evolution.js +668 -0
  60. package/lib/memory/index.js +58 -0
  61. package/lib/memory/orchestrator.js +506 -0
  62. package/lib/memory/retriever.js +515 -0
  63. package/lib/memory/synthesizer.js +333 -0
  64. package/lib/pipeline/async-scanner.js +510 -0
  65. package/lib/pipeline/binary-analysis.js +1043 -0
  66. package/lib/pipeline/dom-xss-scanner.js +435 -0
  67. package/lib/pipeline/github-actions.js +792 -0
  68. package/lib/pipeline/index.js +124 -0
  69. package/lib/pipeline/osint.js +498 -0
  70. package/lib/pipeline/sarif.js +373 -0
  71. package/lib/pipeline/scanner.js +880 -0
  72. package/lib/pipeline/template-manager.js +525 -0
  73. package/lib/pipeline/xss-scanner.js +353 -0
  74. package/lib/setup-wizard.js +229 -0
  75. package/package.json +30 -0
@@ -0,0 +1,587 @@
1
+ // Copyright (c) 2026 defconxt. All rights reserved.
2
+ // Licensed under AGPL-3.0 — see LICENSE file for details.
3
+ // CIPHER is a trademark of defconxt.
4
+
5
+ /**
6
+ * Parallel agent execution engine for CIPHER autonomous operations.
7
+ *
8
+ * Provides Promise-based parallel execution of agent tasks with
9
+ * fan-out/fan-in, map-reduce, pipeline, and callback patterns.
10
+ *
11
+ * Python threading.Thread → Promise.all + concurrency limiter.
12
+ *
13
+ * @module autonomous/parallel
14
+ */
15
+
16
+ import { randomUUID } from 'node:crypto';
17
+ import { existsSync } from 'node:fs';
18
+ import { basename, dirname } from 'node:path';
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Data classes
22
+ // ---------------------------------------------------------------------------
23
+
24
+ export class AgentTask {
25
+ constructor({
26
+ taskId = randomUUID(),
27
+ name = '',
28
+ skillPath = '',
29
+ inputData = {},
30
+ status = 'pending',
31
+ result = null,
32
+ error = null,
33
+ startTime = 0,
34
+ endTime = 0,
35
+ workerId = 0,
36
+ } = {}) {
37
+ this.taskId = taskId;
38
+ this.name = name;
39
+ this.skillPath = skillPath;
40
+ this.inputData = inputData;
41
+ this.status = status;
42
+ this.result = result;
43
+ this.error = error;
44
+ this.startTime = startTime;
45
+ this.endTime = endTime;
46
+ this.workerId = workerId;
47
+ }
48
+ }
49
+
50
+ export class AgentResult {
51
+ constructor({
52
+ taskId = '',
53
+ success = false,
54
+ output = {},
55
+ duration = 0,
56
+ workerId = 0,
57
+ error = null,
58
+ } = {}) {
59
+ this.taskId = taskId;
60
+ this.success = success;
61
+ this.output = output;
62
+ this.duration = duration;
63
+ this.workerId = workerId;
64
+ this.error = error;
65
+ }
66
+ }
67
+
68
+ // ---------------------------------------------------------------------------
69
+ // WorkerPool — Promise-based concurrency limiter
70
+ // ---------------------------------------------------------------------------
71
+
72
+ export class WorkerPool {
73
+ /**
74
+ * @param {number} [maxWorkers=4]
75
+ */
76
+ constructor(maxWorkers = 4) {
77
+ this.maxWorkers = maxWorkers;
78
+ /** @type {Map<string, AgentTask>} */
79
+ this._tasks = new Map();
80
+ /** @type {Map<string, AgentResult>} */
81
+ this._results = new Map();
82
+ this._pendingCount = 0;
83
+ this._activeCount = 0;
84
+ this._completedCount = 0;
85
+ this._shutdown = false;
86
+ /** @type {AgentTask[]} */
87
+ this._queue = [];
88
+ /** @type {Array<{resolve: Function}>} */
89
+ this._waiters = [];
90
+ }
91
+
92
+ /**
93
+ * Submit a single task. Returns its taskId.
94
+ * @param {AgentTask} task
95
+ * @returns {string}
96
+ */
97
+ submit(task) {
98
+ this._tasks.set(task.taskId, task);
99
+ this._pendingCount += 1;
100
+ this._queue.push(task);
101
+ return task.taskId;
102
+ }
103
+
104
+ /**
105
+ * Submit several tasks at once.
106
+ * @param {AgentTask[]} tasks
107
+ * @returns {string[]}
108
+ */
109
+ submitBatch(tasks) {
110
+ return tasks.map(t => this.submit(t));
111
+ }
112
+
113
+ /**
114
+ * Return result for taskId, or null if not yet done.
115
+ * @param {string} taskId
116
+ * @returns {AgentResult|null}
117
+ */
118
+ getResult(taskId) {
119
+ return this._results.get(taskId) || null;
120
+ }
121
+
122
+ /** Return all results collected so far. */
123
+ getAllResults() {
124
+ return [...this._results.values()];
125
+ }
126
+
127
+ /**
128
+ * Execute all queued tasks with concurrency limit, then return results.
129
+ * @param {number} [timeout=300000] - ms
130
+ * @returns {Promise<AgentResult[]>}
131
+ */
132
+ async waitAll(timeout = 300000) {
133
+ const tasks = [...this._queue];
134
+ this._queue = [];
135
+
136
+ // Process tasks with concurrency limit
137
+ const inFlight = new Set();
138
+ let workerCounter = 0;
139
+
140
+ for (const task of tasks) {
141
+ if (this._shutdown) break;
142
+ if (task.status === 'cancelled') continue;
143
+
144
+ const workerId = workerCounter++ % this.maxWorkers;
145
+
146
+ const promise = this._executeTask(task, workerId).then(result => {
147
+ inFlight.delete(promise);
148
+ this._results.set(task.taskId, result);
149
+ task.status = result.success ? 'completed' : 'failed';
150
+ task.result = result.output;
151
+ task.error = result.error;
152
+ this._activeCount -= 1;
153
+ this._completedCount += 1;
154
+ this._pendingCount -= 1;
155
+ });
156
+
157
+ this._activeCount += 1;
158
+ inFlight.add(promise);
159
+
160
+ if (inFlight.size >= this.maxWorkers) {
161
+ await Promise.race(inFlight);
162
+ }
163
+ }
164
+
165
+ // Wait for remaining in-flight tasks
166
+ if (inFlight.size > 0) {
167
+ await Promise.race([
168
+ Promise.all(inFlight),
169
+ new Promise((_, reject) => setTimeout(() => reject(new Error('waitAll timeout')), timeout)),
170
+ ]).catch(() => { /* timeout — return what we have */ });
171
+ }
172
+
173
+ // Notify any waiters
174
+ for (const w of this._waiters) w.resolve();
175
+ this._waiters = [];
176
+
177
+ return this.getAllResults();
178
+ }
179
+
180
+ /**
181
+ * Cancel a pending task.
182
+ * @param {string} taskId
183
+ * @returns {boolean}
184
+ */
185
+ cancel(taskId) {
186
+ const task = this._tasks.get(taskId);
187
+ if (task && task.status === 'pending') {
188
+ task.status = 'cancelled';
189
+ this._pendingCount -= 1;
190
+ this._results.set(taskId, new AgentResult({
191
+ taskId,
192
+ success: false,
193
+ output: {},
194
+ duration: 0,
195
+ workerId: 0,
196
+ error: 'cancelled',
197
+ }));
198
+ return true;
199
+ }
200
+ return false;
201
+ }
202
+
203
+ /** Cancel every pending task in the queue. */
204
+ cancelAll() {
205
+ for (const task of this._tasks.values()) {
206
+ if (task.status === 'pending') {
207
+ task.status = 'cancelled';
208
+ this._pendingCount -= 1;
209
+ this._results.set(task.taskId, new AgentResult({
210
+ taskId: task.taskId,
211
+ success: false,
212
+ output: {},
213
+ duration: 0,
214
+ workerId: 0,
215
+ error: 'cancelled',
216
+ }));
217
+ }
218
+ }
219
+ }
220
+
221
+ /** Return a snapshot of pool state. */
222
+ getStatus() {
223
+ return {
224
+ active: this._activeCount,
225
+ pending: this._pendingCount,
226
+ completed: this._completedCount,
227
+ totalTasks: this._tasks.size,
228
+ maxWorkers: this.maxWorkers,
229
+ shutdown: this._shutdown,
230
+ };
231
+ }
232
+
233
+ /** Signal workers to stop. */
234
+ shutdown() {
235
+ this._shutdown = true;
236
+ }
237
+
238
+ // -- internal -----------------------------------------------------------
239
+
240
+ /**
241
+ * @param {AgentTask} task
242
+ * @param {number} workerId
243
+ * @returns {Promise<AgentResult>}
244
+ */
245
+ async _executeTask(task, workerId = 0) {
246
+ const start = performance.now() / 1000;
247
+ try {
248
+ const output = {};
249
+
250
+ // Check skill path
251
+ if (task.skillPath && existsSync(task.skillPath)) {
252
+ output.skillLoaded = task.skillPath;
253
+ output.skillName = basename(dirname(task.skillPath));
254
+ } else {
255
+ output.skillLoaded = null;
256
+ }
257
+
258
+ // Run callable if present
259
+ const fn = task.inputData.fn;
260
+ if (typeof fn === 'function') {
261
+ const args = task.inputData.args || [];
262
+ const kwargs = task.inputData.kwargs || {};
263
+ const fnResult = await fn(...args, kwargs);
264
+ output.fnResult = fnResult;
265
+ } else {
266
+ output.inputEcho = task.inputData;
267
+ }
268
+
269
+ const duration = performance.now() / 1000 - start;
270
+ task.startTime = start;
271
+ task.endTime = start + duration;
272
+ task.workerId = workerId;
273
+
274
+ return new AgentResult({
275
+ taskId: task.taskId,
276
+ success: true,
277
+ output,
278
+ duration,
279
+ workerId,
280
+ });
281
+ } catch (exc) {
282
+ const duration = performance.now() / 1000 - start;
283
+ task.startTime = start;
284
+ task.endTime = start + duration;
285
+ task.workerId = workerId;
286
+
287
+ return new AgentResult({
288
+ taskId: task.taskId,
289
+ success: false,
290
+ output: {},
291
+ duration,
292
+ workerId,
293
+ error: `${exc.constructor.name}: ${exc.message}`,
294
+ });
295
+ }
296
+ }
297
+ }
298
+
299
+ // ---------------------------------------------------------------------------
300
+ // ParallelScanner
301
+ // ---------------------------------------------------------------------------
302
+
303
+ export class ParallelScanner {
304
+ /**
305
+ * @param {WorkerPool} [pool]
306
+ */
307
+ constructor(pool) {
308
+ this.pool = pool || new WorkerPool();
309
+ }
310
+
311
+ /**
312
+ * Create one task per target, fan-out, collect results.
313
+ * @param {string[]} targets
314
+ * @param {string} [profile='pentest']
315
+ */
316
+ async scanTargets(targets, profile = 'pentest') {
317
+ const tasks = targets.map(target => new AgentTask({
318
+ name: `scan-${target}`,
319
+ inputData: {
320
+ fn: ParallelScanner._runScan,
321
+ args: [target, profile],
322
+ },
323
+ }));
324
+ const ids = this.pool.submitBatch(tasks);
325
+ const idSet = new Set(ids);
326
+ await this.pool.waitAll();
327
+ const allResults = this.pool.getAllResults();
328
+ const results = allResults.filter(r => idSet.has(r.taskId));
329
+ const idToTarget = Object.fromEntries(ids.map((id, i) => [id, targets[i]]));
330
+ return results.map(r => ({
331
+ taskId: r.taskId,
332
+ target: idToTarget[r.taskId] || 'unknown',
333
+ success: r.success,
334
+ output: r.output,
335
+ duration: r.duration,
336
+ error: r.error,
337
+ }));
338
+ }
339
+
340
+ /**
341
+ * Run domain-specific recon scans in parallel.
342
+ * @param {string[]} domains
343
+ */
344
+ async scanDomains(domains) {
345
+ const tasks = domains.map(domain => new AgentTask({
346
+ name: `domain-scan-${domain}`,
347
+ inputData: {
348
+ fn: ParallelScanner._runDomainScan,
349
+ args: [domain],
350
+ },
351
+ }));
352
+ const ids = this.pool.submitBatch(tasks);
353
+ const idSet = new Set(ids);
354
+ await this.pool.waitAll();
355
+ const allResults = this.pool.getAllResults();
356
+ const results = allResults.filter(r => idSet.has(r.taskId));
357
+ return {
358
+ domainsScanned: domains.length,
359
+ successful: results.filter(r => r.success).length,
360
+ failed: results.filter(r => !r.success).length,
361
+ results: results.map(r => ({
362
+ taskId: r.taskId,
363
+ success: r.success,
364
+ output: r.output,
365
+ error: r.error,
366
+ })),
367
+ };
368
+ }
369
+
370
+ static _runScan(target, profile) {
371
+ return {
372
+ target,
373
+ profile,
374
+ findings: [],
375
+ status: 'complete',
376
+ timestamp: Date.now() / 1000,
377
+ };
378
+ }
379
+
380
+ static _runDomainScan(domain) {
381
+ return {
382
+ domain,
383
+ dnsRecords: [],
384
+ subdomains: [],
385
+ ports: [],
386
+ status: 'complete',
387
+ timestamp: Date.now() / 1000,
388
+ };
389
+ }
390
+ }
391
+
392
+ // ---------------------------------------------------------------------------
393
+ // ParallelAnalyzer
394
+ // ---------------------------------------------------------------------------
395
+
396
+ export class ParallelAnalyzer {
397
+ /**
398
+ * @param {WorkerPool} [pool]
399
+ */
400
+ constructor(pool) {
401
+ this.pool = pool || new WorkerPool();
402
+ }
403
+
404
+ /**
405
+ * Analyze multiple diffs concurrently for security issues.
406
+ * @param {string[]} diffs
407
+ */
408
+ async analyzeDiffs(diffs) {
409
+ const tasks = diffs.map((diff, idx) => new AgentTask({
410
+ name: `diff-analysis-${idx}`,
411
+ inputData: {
412
+ fn: ParallelAnalyzer._analyzeSingleDiff,
413
+ args: [diff],
414
+ },
415
+ }));
416
+ const ids = this.pool.submitBatch(tasks);
417
+ const idSet = new Set(ids);
418
+ await this.pool.waitAll();
419
+ const allResults = this.pool.getAllResults();
420
+ const results = allResults.filter(r => idSet.has(r.taskId));
421
+ return results.map(r => ({
422
+ taskId: r.taskId,
423
+ success: r.success,
424
+ analysis: r.output?.fnResult || {},
425
+ error: r.error,
426
+ }));
427
+ }
428
+
429
+ /**
430
+ * Score multiple responses in parallel.
431
+ * @param {object[]} responses
432
+ */
433
+ async bulkScore(responses) {
434
+ const tasks = responses.map((resp, idx) => new AgentTask({
435
+ name: `score-${idx}`,
436
+ inputData: {
437
+ fn: ParallelAnalyzer._scoreResponse,
438
+ args: [resp],
439
+ },
440
+ }));
441
+ const ids = this.pool.submitBatch(tasks);
442
+ const idSet = new Set(ids);
443
+ await this.pool.waitAll();
444
+ const allResults = this.pool.getAllResults();
445
+ const results = allResults.filter(r => idSet.has(r.taskId));
446
+ return results.map(r => ({
447
+ taskId: r.taskId,
448
+ success: r.success,
449
+ score: r.output?.fnResult || {},
450
+ error: r.error,
451
+ }));
452
+ }
453
+
454
+ static _analyzeSingleDiff(diff) {
455
+ const lines = diff ? diff.trim().split('\n') : [];
456
+ const additions = lines.filter(l => l.startsWith('+')).length;
457
+ const deletions = lines.filter(l => l.startsWith('-')).length;
458
+ return {
459
+ totalLines: lines.length,
460
+ additions,
461
+ deletions,
462
+ riskIndicators: [],
463
+ severity: 'info',
464
+ };
465
+ }
466
+
467
+ static _scoreResponse(response) {
468
+ let score = 0.5;
469
+ const flags = [];
470
+ if ('error' in response) {
471
+ score -= 0.3;
472
+ flags.push('contains_error');
473
+ }
474
+ if (response.status === 'complete') {
475
+ score += 0.2;
476
+ flags.push('complete');
477
+ }
478
+ return {
479
+ score: Math.max(0.0, Math.min(1.0, score)),
480
+ flags,
481
+ };
482
+ }
483
+ }
484
+
485
+ // ---------------------------------------------------------------------------
486
+ // TaskOrchestrator
487
+ // ---------------------------------------------------------------------------
488
+
489
+ export class TaskOrchestrator {
490
+ /**
491
+ * @param {number} [maxWorkers=4]
492
+ */
493
+ constructor(maxWorkers = 4) {
494
+ this.maxWorkers = maxWorkers;
495
+ this._totalTasks = 0;
496
+ this._totalDuration = 0;
497
+ this._totalFailures = 0;
498
+ }
499
+
500
+ /**
501
+ * Submit all tasks, wait for completion, return results.
502
+ * @param {AgentTask[]} tasks
503
+ * @returns {Promise<AgentResult[]>}
504
+ */
505
+ async fanOut(tasks) {
506
+ const pool = new WorkerPool(this.maxWorkers);
507
+ pool.submitBatch(tasks);
508
+ const results = await pool.waitAll();
509
+ pool.shutdown();
510
+ this._recordMetrics(results);
511
+ return results;
512
+ }
513
+
514
+ /**
515
+ * Fan-out tasks; invoke callback as each completes.
516
+ * @param {AgentTask[]} tasks
517
+ * @param {(result: AgentResult) => void} callback
518
+ */
519
+ async fanOutWithCallback(tasks, callback) {
520
+ const pool = new WorkerPool(this.maxWorkers);
521
+ pool.submitBatch(tasks);
522
+ const results = await pool.waitAll();
523
+ for (const r of results) {
524
+ callback(r);
525
+ }
526
+ pool.shutdown();
527
+ this._recordMetrics(results);
528
+ }
529
+
530
+ /**
531
+ * Fan-out tasks then apply reduceFn to collected results.
532
+ * @param {AgentTask[]} tasks
533
+ * @param {(results: AgentResult[]) => *} reduceFn
534
+ */
535
+ async mapReduce(tasks, reduceFn) {
536
+ const results = await this.fanOut(tasks);
537
+ return reduceFn(results);
538
+ }
539
+
540
+ /**
541
+ * Execute stages sequentially; within each stage tasks run in parallel.
542
+ * @param {AgentTask[][]} stages
543
+ * @returns {Promise<AgentResult[]>}
544
+ */
545
+ async pipeline(stages) {
546
+ const allResults = [];
547
+ let priorResults = [];
548
+
549
+ for (const stageTasks of stages) {
550
+ for (const task of stageTasks) {
551
+ task.inputData.priorResults = priorResults.map(r => ({
552
+ taskId: r.taskId,
553
+ success: r.success,
554
+ output: r.output,
555
+ error: r.error,
556
+ }));
557
+ }
558
+
559
+ priorResults = await this.fanOut(stageTasks);
560
+ allResults.push(...priorResults);
561
+ }
562
+
563
+ return allResults;
564
+ }
565
+
566
+ /** Return throughput, average duration, and failure rate. */
567
+ getMetrics() {
568
+ const avg = this._totalTasks > 0 ? this._totalDuration / this._totalTasks : 0;
569
+ const failureRate = this._totalTasks > 0 ? this._totalFailures / this._totalTasks : 0;
570
+ return {
571
+ totalTasks: this._totalTasks,
572
+ totalDuration: Math.round(this._totalDuration * 10000) / 10000,
573
+ avgDuration: Math.round(avg * 10000) / 10000,
574
+ failureRate: Math.round(failureRate * 10000) / 10000,
575
+ maxWorkers: this.maxWorkers,
576
+ };
577
+ }
578
+
579
+ /** @param {AgentResult[]} results */
580
+ _recordMetrics(results) {
581
+ for (const r of results) {
582
+ this._totalTasks += 1;
583
+ this._totalDuration += r.duration;
584
+ if (!r.success) this._totalFailures += 1;
585
+ }
586
+ }
587
+ }