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.
- package/bin/cipher.js +566 -0
- package/lib/api/billing.js +321 -0
- package/lib/api/compliance.js +693 -0
- package/lib/api/controls.js +1401 -0
- package/lib/api/index.js +49 -0
- package/lib/api/marketplace.js +467 -0
- package/lib/api/openai-proxy.js +383 -0
- package/lib/api/server.js +685 -0
- package/lib/autonomous/feedback-loop.js +554 -0
- package/lib/autonomous/framework.js +512 -0
- package/lib/autonomous/index.js +97 -0
- package/lib/autonomous/leaderboard.js +594 -0
- package/lib/autonomous/modes/architect.js +412 -0
- package/lib/autonomous/modes/blue.js +386 -0
- package/lib/autonomous/modes/incident.js +684 -0
- package/lib/autonomous/modes/privacy.js +369 -0
- package/lib/autonomous/modes/purple.js +294 -0
- package/lib/autonomous/modes/recon.js +250 -0
- package/lib/autonomous/parallel.js +587 -0
- package/lib/autonomous/researcher.js +583 -0
- package/lib/autonomous/runner.js +955 -0
- package/lib/autonomous/scheduler.js +615 -0
- package/lib/autonomous/task-parser.js +127 -0
- package/lib/autonomous/validators/forensic.js +266 -0
- package/lib/autonomous/validators/osint.js +216 -0
- package/lib/autonomous/validators/privacy.js +296 -0
- package/lib/autonomous/validators/purple.js +298 -0
- package/lib/autonomous/validators/sigma.js +248 -0
- package/lib/autonomous/validators/threat-model.js +363 -0
- package/lib/benchmark/agent.js +119 -0
- package/lib/benchmark/baselines.js +43 -0
- package/lib/benchmark/builder.js +143 -0
- package/lib/benchmark/config.js +35 -0
- package/lib/benchmark/coordinator.js +91 -0
- package/lib/benchmark/index.js +20 -0
- package/lib/benchmark/llm.js +58 -0
- package/lib/benchmark/models.js +137 -0
- package/lib/benchmark/reporter.js +103 -0
- package/lib/benchmark/runner.js +103 -0
- package/lib/benchmark/sandbox.js +96 -0
- package/lib/benchmark/scorer.js +32 -0
- package/lib/benchmark/solver.js +166 -0
- package/lib/benchmark/tools.js +62 -0
- package/lib/bot/bot.js +238 -0
- package/lib/brand.js +105 -0
- package/lib/commands.js +100 -0
- package/lib/complexity.js +377 -0
- package/lib/config.js +213 -0
- package/lib/gateway/client.js +309 -0
- package/lib/gateway/commands.js +991 -0
- package/lib/gateway/config-validate.js +109 -0
- package/lib/gateway/gateway.js +367 -0
- package/lib/gateway/index.js +62 -0
- package/lib/gateway/mode.js +309 -0
- package/lib/gateway/plugins.js +222 -0
- package/lib/gateway/prompt.js +214 -0
- package/lib/mcp/server.js +262 -0
- package/lib/memory/compressor.js +425 -0
- package/lib/memory/engine.js +763 -0
- package/lib/memory/evolution.js +668 -0
- package/lib/memory/index.js +58 -0
- package/lib/memory/orchestrator.js +506 -0
- package/lib/memory/retriever.js +515 -0
- package/lib/memory/synthesizer.js +333 -0
- package/lib/pipeline/async-scanner.js +510 -0
- package/lib/pipeline/binary-analysis.js +1043 -0
- package/lib/pipeline/dom-xss-scanner.js +435 -0
- package/lib/pipeline/github-actions.js +792 -0
- package/lib/pipeline/index.js +124 -0
- package/lib/pipeline/osint.js +498 -0
- package/lib/pipeline/sarif.js +373 -0
- package/lib/pipeline/scanner.js +880 -0
- package/lib/pipeline/template-manager.js +525 -0
- package/lib/pipeline/xss-scanner.js +353 -0
- package/lib/setup-wizard.js +288 -0
- 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
|
+
};
|