cipher-security 2.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 (76) hide show
  1. package/bin/cipher.js +566 -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 +238 -0
  45. package/lib/brand.js +105 -0
  46. package/lib/commands.js +100 -0
  47. package/lib/complexity.js +377 -0
  48. package/lib/config.js +213 -0
  49. package/lib/gateway/client.js +309 -0
  50. package/lib/gateway/commands.js +991 -0
  51. package/lib/gateway/config-validate.js +109 -0
  52. package/lib/gateway/gateway.js +367 -0
  53. package/lib/gateway/index.js +62 -0
  54. package/lib/gateway/mode.js +309 -0
  55. package/lib/gateway/plugins.js +222 -0
  56. package/lib/gateway/prompt.js +214 -0
  57. package/lib/mcp/server.js +262 -0
  58. package/lib/memory/compressor.js +425 -0
  59. package/lib/memory/engine.js +763 -0
  60. package/lib/memory/evolution.js +668 -0
  61. package/lib/memory/index.js +58 -0
  62. package/lib/memory/orchestrator.js +506 -0
  63. package/lib/memory/retriever.js +515 -0
  64. package/lib/memory/synthesizer.js +333 -0
  65. package/lib/pipeline/async-scanner.js +510 -0
  66. package/lib/pipeline/binary-analysis.js +1043 -0
  67. package/lib/pipeline/dom-xss-scanner.js +435 -0
  68. package/lib/pipeline/github-actions.js +792 -0
  69. package/lib/pipeline/index.js +124 -0
  70. package/lib/pipeline/osint.js +498 -0
  71. package/lib/pipeline/sarif.js +373 -0
  72. package/lib/pipeline/scanner.js +880 -0
  73. package/lib/pipeline/template-manager.js +525 -0
  74. package/lib/pipeline/xss-scanner.js +353 -0
  75. package/lib/setup-wizard.js +288 -0
  76. package/package.json +31 -0
@@ -0,0 +1,333 @@
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
+ * CIPHER Memory — Stage 2: Online Semantic Synthesis
7
+ *
8
+ * Performs intra-session consolidation of memory entries:
9
+ * - Detects near-duplicate and semantically related entries
10
+ * - Merges related entries into unified abstract representations
11
+ * - Maintains compact, coherent memory topology
12
+ * - Eliminates redundancy while preserving information
13
+ *
14
+ * Ported from Python memory/core/synthesizer.py.
15
+ */
16
+
17
+ import { createHash } from 'node:crypto';
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // Helpers (standalone — separate from MemoryConsolidator in engine.js)
21
+ // ---------------------------------------------------------------------------
22
+
23
+ /**
24
+ * Generate word trigrams for similarity comparison.
25
+ * @param {string} text
26
+ * @returns {Set<string>}
27
+ */
28
+ function _trigramSet(text) {
29
+ const words = text.toLowerCase().split(/\s+/).filter(Boolean);
30
+ if (words.length < 3) return new Set(words);
31
+ const trigrams = new Set();
32
+ for (let i = 0; i <= words.length - 3; i++) {
33
+ trigrams.add(`${words[i]} ${words[i + 1]} ${words[i + 2]}`);
34
+ }
35
+ return trigrams;
36
+ }
37
+
38
+ /**
39
+ * Jaccard similarity on word trigrams.
40
+ * @param {string} a
41
+ * @param {string} b
42
+ * @returns {number}
43
+ */
44
+ function _jaccardSimilarity(a, b) {
45
+ const tgA = _trigramSet(a);
46
+ const tgB = _trigramSet(b);
47
+ if (tgA.size === 0 || tgB.size === 0) return 0.0;
48
+
49
+ let intersectionSize = 0;
50
+ for (const t of tgA) {
51
+ if (tgB.has(t)) intersectionSize++;
52
+ }
53
+
54
+ const unionSize = tgA.size + tgB.size - intersectionSize;
55
+ return unionSize > 0 ? intersectionSize / unionSize : 0.0;
56
+ }
57
+
58
+ /**
59
+ * Generate content fingerprint for near-duplicate detection.
60
+ * Normalize text, MD5 hash (first 16 chars).
61
+ * @param {string} text
62
+ * @returns {string}
63
+ */
64
+ function _contentFingerprint(text) {
65
+ // Normalize: lowercase, collapse whitespace, remove punctuation
66
+ let normalized = text.toLowerCase().replace(/[^\w\s]/g, '');
67
+ normalized = normalized.replace(/\s+/g, ' ').trim();
68
+ return createHash('md5').update(normalized).digest('hex').slice(0, 16);
69
+ }
70
+
71
+ // ---------------------------------------------------------------------------
72
+ // SynthesisResult
73
+ // ---------------------------------------------------------------------------
74
+
75
+ /**
76
+ * Result of a synthesis operation.
77
+ */
78
+ class SynthesisResult {
79
+ /**
80
+ * @param {{ mergedEntries?: any[], removedEntryIds?: string[], newEntries?: any[], stats?: Record<string, number> }} opts
81
+ */
82
+ constructor(opts = {}) {
83
+ this.mergedEntries = opts.mergedEntries ?? [];
84
+ this.removedEntryIds = opts.removedEntryIds ?? [];
85
+ this.newEntries = opts.newEntries ?? [];
86
+ this.stats = opts.stats ?? {};
87
+ }
88
+ }
89
+
90
+ // ---------------------------------------------------------------------------
91
+ // SemanticSynthesizer
92
+ // ---------------------------------------------------------------------------
93
+
94
+ // Severity ordering for merge comparison
95
+ const _SEVERITY_ORDER = {
96
+ critical: 5,
97
+ high: 4,
98
+ medium: 3,
99
+ low: 2,
100
+ info: 1,
101
+ '': 0,
102
+ };
103
+
104
+ // Confidence ordering for merge comparison
105
+ const _CONF_ORDER = {
106
+ confirmed: 3,
107
+ inferred: 2,
108
+ uncertain: 1,
109
+ };
110
+
111
+ /**
112
+ * Stage 2: Online Semantic Synthesis.
113
+ *
114
+ * Consolidates memory entries during the session:
115
+ * 1. Deduplication — Remove near-identical entries (fingerprint match)
116
+ * 2. Merging — Combine highly similar entries (Jaccard > threshold)
117
+ * 3. Linking — Connect related entries via shared entities/topics
118
+ */
119
+ class SemanticSynthesizer {
120
+ /**
121
+ * @param {{ mergeThreshold?: number, dedupThreshold?: number }} opts
122
+ */
123
+ constructor(opts = {}) {
124
+ this.mergeThreshold = opts.mergeThreshold ?? 0.7;
125
+ this.dedupThreshold = opts.dedupThreshold ?? 0.95;
126
+ /** @type {Map<string, string>} fingerprint → entry_id */
127
+ this._fingerprints = new Map();
128
+ }
129
+
130
+ /**
131
+ * Synthesize new entries with existing memory.
132
+ * @param {import('./compressor.js').CompressedEntry[]} newEntries
133
+ * @param {import('./compressor.js').CompressedEntry[]} existingEntries
134
+ * @returns {SynthesisResult}
135
+ */
136
+ synthesize(newEntries, existingEntries) {
137
+ // Build fingerprint index for existing entries
138
+ for (const entry of existingEntries) {
139
+ const fp = _contentFingerprint(entry.losslessRestatement);
140
+ this._fingerprints.set(fp, entry.entryId);
141
+ }
142
+
143
+ const merged = [];
144
+ const removedIds = [];
145
+ const trulyNew = [];
146
+ const stats = { deduplicated: 0, merged: 0, linked: 0, new: 0 };
147
+
148
+ for (const newEntry of newEntries) {
149
+ const fp = _contentFingerprint(newEntry.losslessRestatement);
150
+
151
+ // Step 1: Exact deduplication
152
+ if (this._fingerprints.has(fp)) {
153
+ stats.deduplicated++;
154
+ continue;
155
+ }
156
+
157
+ // Step 2: Find merge candidates
158
+ let bestMatch = null;
159
+ let bestSim = 0.0;
160
+
161
+ for (const existing of existingEntries) {
162
+ if (removedIds.includes(existing.entryId)) continue;
163
+
164
+ const sim = _jaccardSimilarity(
165
+ newEntry.losslessRestatement,
166
+ existing.losslessRestatement,
167
+ );
168
+ if (sim > bestSim) {
169
+ bestSim = sim;
170
+ bestMatch = existing;
171
+ }
172
+ }
173
+
174
+ if (bestMatch && bestSim >= this.mergeThreshold) {
175
+ const mergedEntry = this._mergeEntries(bestMatch, newEntry);
176
+ merged.push(mergedEntry);
177
+ stats.merged++;
178
+ } else {
179
+ // Step 3: Link to related entries via shared entities
180
+ const linked = this._linkEntry(newEntry, existingEntries);
181
+ if (linked) stats.linked++;
182
+
183
+ trulyNew.push(newEntry);
184
+ this._fingerprints.set(fp, newEntry.entryId);
185
+ stats.new++;
186
+ }
187
+ }
188
+
189
+ return new SynthesisResult({
190
+ mergedEntries: merged,
191
+ removedEntryIds: removedIds,
192
+ newEntries: trulyNew,
193
+ stats,
194
+ });
195
+ }
196
+
197
+ /**
198
+ * Merge two similar entries, keeping the richer content.
199
+ * @private
200
+ */
201
+ _mergeEntries(existing, newEntry) {
202
+ // Keep the longer/richer restatement
203
+ if (newEntry.losslessRestatement.length > existing.losslessRestatement.length) {
204
+ existing.losslessRestatement = newEntry.losslessRestatement;
205
+ }
206
+
207
+ // Union all list fields
208
+ existing.targets = [...new Set([...existing.targets, ...newEntry.targets])];
209
+ existing.cveIds = [...new Set([...existing.cveIds, ...newEntry.cveIds])];
210
+ existing.mitreAttack = [...new Set([...existing.mitreAttack, ...newEntry.mitreAttack])];
211
+ existing.keywords = [...new Set([...existing.keywords, ...newEntry.keywords])].slice(0, 15);
212
+ existing.toolsUsed = [...new Set([...existing.toolsUsed, ...newEntry.toolsUsed])];
213
+ existing.hashes = [...new Set([...existing.hashes, ...newEntry.hashes])];
214
+ existing.persons = [...new Set([...existing.persons, ...newEntry.persons])];
215
+ existing.entities = [...new Set([...existing.entities, ...newEntry.entities])];
216
+ existing.sourceTurns = [...new Set([...existing.sourceTurns, ...newEntry.sourceTurns])];
217
+
218
+ // Keep higher severity
219
+ if ((_SEVERITY_ORDER[newEntry.severity] ?? 0) > (_SEVERITY_ORDER[existing.severity] ?? 0)) {
220
+ existing.severity = newEntry.severity;
221
+ }
222
+
223
+ // Keep higher confidence
224
+ if ((_CONF_ORDER[newEntry.confidence] ?? 0) > (_CONF_ORDER[existing.confidence] ?? 0)) {
225
+ existing.confidence = newEntry.confidence;
226
+ }
227
+
228
+ return existing;
229
+ }
230
+
231
+ /**
232
+ * Link entry to related existing entries via shared entities.
233
+ * @private
234
+ */
235
+ _linkEntry(newEntry, existingEntries) {
236
+ let linked = false;
237
+ const newEntities = new Set([
238
+ ...newEntry.targets,
239
+ ...newEntry.cveIds,
240
+ ...newEntry.mitreAttack,
241
+ ]);
242
+
243
+ if (newEntities.size === 0) return false;
244
+
245
+ for (const existing of existingEntries) {
246
+ const existingEntities = new Set([
247
+ ...existing.targets,
248
+ ...existing.cveIds,
249
+ ...existing.mitreAttack,
250
+ ]);
251
+
252
+ // Check for shared entities
253
+ let hasShared = false;
254
+ for (const e of newEntities) {
255
+ if (existingEntities.has(e)) {
256
+ hasShared = true;
257
+ break;
258
+ }
259
+ }
260
+
261
+ if (hasShared) {
262
+ linked = true;
263
+ const linkKeywords = [`related:${existing.entryId.slice(0, 8)}`];
264
+ newEntry.keywords = [...new Set([...newEntry.keywords, ...linkKeywords])];
265
+ }
266
+ }
267
+
268
+ return linked;
269
+ }
270
+
271
+ /**
272
+ * Consolidate a batch of entries among themselves.
273
+ * Groups entries by shared entities and merges within groups.
274
+ * @param {import('./compressor.js').CompressedEntry[]} entries
275
+ * @returns {import('./compressor.js').CompressedEntry[]}
276
+ */
277
+ consolidateBatch(entries) {
278
+ if (entries.length <= 1) return entries;
279
+
280
+ // Group by shared entities
281
+ const groups = new Map();
282
+ const ungrouped = [];
283
+
284
+ for (const entry of entries) {
285
+ const keyEntities = [...entry.targets, ...entry.cveIds];
286
+ if (keyEntities.length > 0) {
287
+ const groupKey = [...keyEntities].sort()[0];
288
+ if (!groups.has(groupKey)) groups.set(groupKey, []);
289
+ groups.get(groupKey).push(entry);
290
+ } else {
291
+ ungrouped.push(entry);
292
+ }
293
+ }
294
+
295
+ // Merge within groups
296
+ const consolidated = [...ungrouped];
297
+ for (const [, groupEntries] of groups) {
298
+ if (groupEntries.length === 1) {
299
+ consolidated.push(groupEntries[0]);
300
+ continue;
301
+ }
302
+
303
+ // Sort by content length (richest first)
304
+ groupEntries.sort(
305
+ (a, b) => b.losslessRestatement.length - a.losslessRestatement.length,
306
+ );
307
+ let base = groupEntries[0];
308
+ for (let i = 1; i < groupEntries.length; i++) {
309
+ const other = groupEntries[i];
310
+ const sim = _jaccardSimilarity(
311
+ base.losslessRestatement,
312
+ other.losslessRestatement,
313
+ );
314
+ if (sim >= this.mergeThreshold) {
315
+ base = this._mergeEntries(base, other);
316
+ } else {
317
+ consolidated.push(other);
318
+ }
319
+ }
320
+ consolidated.push(base);
321
+ }
322
+
323
+ return consolidated;
324
+ }
325
+ }
326
+
327
+ export {
328
+ _trigramSet,
329
+ _jaccardSimilarity,
330
+ _contentFingerprint,
331
+ SynthesisResult,
332
+ SemanticSynthesizer,
333
+ };