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
package/lib/bot/bot.js ADDED
@@ -0,0 +1,238 @@
1
+ // Copyright (c) 2026 defconxt. All rights reserved.
2
+ // Licensed under AGPL-3.0 — see LICENSE file for details.
3
+
4
+ /**
5
+ * CIPHER Signal Bot — receives messages via signal-cli-rest-api WebSocket,
6
+ * dispatches through the gateway, and replies.
7
+ */
8
+
9
+ import { WebSocket } from 'ws';
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Prefix mapping
13
+ // ---------------------------------------------------------------------------
14
+
15
+ const PREFIX_RE = /^(RED|BLUE|PURPLE|PRIVACY|RECON|INCIDENT|ARCHITECT):\s*/i;
16
+
17
+ export function mapPrefix(text) {
18
+ const m = text.match(PREFIX_RE);
19
+ if (m) {
20
+ const mode = m[1].toUpperCase();
21
+ const rest = text.slice(m[0].length);
22
+ return `[MODE: ${mode}] ${rest}`;
23
+ }
24
+ return text;
25
+ }
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Markdown stripping
29
+ // ---------------------------------------------------------------------------
30
+
31
+ const MD_PATTERNS = [
32
+ [/```[^\n]*\n([\s\S]*?)```/g, '$1'],
33
+ [/^#{1,6}\s+/gm, ''],
34
+ [/\*{2}([^*\n]+)\*{2}/g, '$1'],
35
+ [/\*([^*\n]+)\*/g, '$1'],
36
+ [/`([^`\n]+)`/g, '$1'],
37
+ [/\[([^\]]+)\]\([^)]+\)/g, '$1'],
38
+ [/^(\s*)[*-]\s+/gm, '$1• '],
39
+ ];
40
+
41
+ export function stripMarkdown(text) {
42
+ for (const [pattern, replacement] of MD_PATTERNS) {
43
+ text = text.replace(pattern, replacement);
44
+ }
45
+ return text.trim();
46
+ }
47
+
48
+ // ---------------------------------------------------------------------------
49
+ // Session manager
50
+ // ---------------------------------------------------------------------------
51
+
52
+ export class SessionManager {
53
+ constructor({ timeoutSeconds = 3600, maxPairs = 20 } = {}) {
54
+ this._sessions = new Map();
55
+ this._timeout = timeoutSeconds * 1000;
56
+ this._maxPairs = maxPairs;
57
+ }
58
+
59
+ get(sender) {
60
+ this.cleanup();
61
+ const session = this._sessions.get(sender);
62
+ return session ? [...session.history] : [];
63
+ }
64
+
65
+ update(sender, userMessage, assistantMessage) {
66
+ if (!this._sessions.has(sender)) {
67
+ this._sessions.set(sender, { history: [], lastActive: Date.now() });
68
+ }
69
+ const session = this._sessions.get(sender);
70
+ session.history.push({ role: 'user', content: userMessage });
71
+ session.history.push({ role: 'assistant', content: assistantMessage });
72
+ session.lastActive = Date.now();
73
+
74
+ const maxItems = this._maxPairs * 2;
75
+ if (session.history.length > maxItems) {
76
+ session.history = session.history.slice(-maxItems);
77
+ }
78
+ }
79
+
80
+ reset(sender) { this._sessions.delete(sender); }
81
+
82
+ cleanup() {
83
+ const now = Date.now();
84
+ for (const [sender, session] of this._sessions) {
85
+ if (now - session.lastActive > this._timeout) {
86
+ this._sessions.delete(sender);
87
+ }
88
+ }
89
+ }
90
+
91
+ get size() { return this._sessions.size; }
92
+ }
93
+
94
+ // ---------------------------------------------------------------------------
95
+ // Signal REST API client
96
+ // ---------------------------------------------------------------------------
97
+
98
+ async function sendMessage(signalService, phoneNumber, recipient, message) {
99
+ const resp = await fetch(`${signalService}/v2/send`, {
100
+ method: 'POST',
101
+ headers: { 'Content-Type': 'application/json' },
102
+ body: JSON.stringify({
103
+ message,
104
+ number: phoneNumber,
105
+ recipients: [recipient],
106
+ }),
107
+ });
108
+ if (!resp.ok) {
109
+ throw new Error(`Send failed: ${resp.status} ${await resp.text()}`);
110
+ }
111
+ }
112
+
113
+ async function sendReaction(signalService, phoneNumber, recipient, emoji, targetTimestamp) {
114
+ try {
115
+ await fetch(`${signalService}/v1/reactions/${phoneNumber}`, {
116
+ method: 'POST',
117
+ headers: { 'Content-Type': 'application/json' },
118
+ body: JSON.stringify({
119
+ reaction: emoji,
120
+ recipient,
121
+ timestamp: targetTimestamp,
122
+ }),
123
+ });
124
+ } catch { /* best-effort */ }
125
+ }
126
+
127
+ // ---------------------------------------------------------------------------
128
+ // Bot runner
129
+ // ---------------------------------------------------------------------------
130
+
131
+ /**
132
+ * Start the CIPHER Signal bot.
133
+ *
134
+ * Connects to signal-cli-rest-api WebSocket, listens for messages,
135
+ * dispatches through the gateway, and replies.
136
+ *
137
+ * @param {object} config
138
+ * @param {string} config.signalService - signal-cli-rest-api URL
139
+ * @param {string} config.phoneNumber - bot's phone number
140
+ * @param {string[]} [config.whitelist] - allowed sender numbers
141
+ * @param {number} [config.sessionTimeout] - session timeout in seconds
142
+ */
143
+ export async function runBot(config) {
144
+ const { signalService, phoneNumber, whitelist = [], sessionTimeout = 3600 } = config;
145
+ const sessions = new SessionManager({ timeoutSeconds: sessionTimeout });
146
+ const whitelistSet = new Set(whitelist);
147
+
148
+ const wsUrl = `${signalService.replace('http', 'ws')}/v1/receive/${phoneNumber}`;
149
+
150
+ let reconnectDelay = 1000;
151
+ const maxDelay = 60000;
152
+
153
+ function connect() {
154
+ const ws = new WebSocket(wsUrl);
155
+
156
+ ws.on('open', () => {
157
+ process.stderr.write(` ● Connected to ${wsUrl}\n`);
158
+ reconnectDelay = 1000;
159
+ });
160
+
161
+ ws.on('message', async (data) => {
162
+ try {
163
+ const envelope = JSON.parse(data.toString());
164
+ await handleEnvelope(envelope, config, sessions, whitelistSet);
165
+ } catch (err) {
166
+ process.stderr.write(` ✖ Message error: ${err.message}\n`);
167
+ }
168
+ });
169
+
170
+ ws.on('close', () => {
171
+ process.stderr.write(` ⚠ WebSocket closed. Reconnecting in ${reconnectDelay / 1000}s...\n`);
172
+ setTimeout(connect, reconnectDelay);
173
+ reconnectDelay = Math.min(reconnectDelay * 2, maxDelay);
174
+ });
175
+
176
+ ws.on('error', (err) => {
177
+ process.stderr.write(` ✖ WebSocket error: ${err.message}\n`);
178
+ });
179
+ }
180
+
181
+ connect();
182
+ }
183
+
184
+ async function handleEnvelope(envelope, config, sessions, whitelistSet) {
185
+ const msg = envelope.envelope?.dataMessage;
186
+ if (!msg || !msg.message) return;
187
+
188
+ const sender = envelope.envelope?.source;
189
+ if (!sender) return;
190
+
191
+ const text = msg.message.trim();
192
+ if (!text) return;
193
+
194
+ // Whitelist check
195
+ if (whitelistSet.size > 0 && !whitelistSet.has(sender)) {
196
+ return;
197
+ }
198
+
199
+ const timestamp = msg.timestamp;
200
+
201
+ // /reset command
202
+ if (text === '/reset') {
203
+ sessions.reset(sender);
204
+ await sendMessage(config.signalService, config.phoneNumber, sender, 'Session cleared.');
205
+ return;
206
+ }
207
+
208
+ // React with brain emoji to acknowledge
209
+ await sendReaction(config.signalService, config.phoneNumber, sender, '🧠', timestamp);
210
+
211
+ // Map prefix and get history
212
+ const mapped = mapPrefix(text);
213
+ const history = sessions.get(sender);
214
+
215
+ // Dispatch through gateway
216
+ let response;
217
+ try {
218
+ const { Gateway } = await import('../gateway/gateway.js');
219
+ const gw = new Gateway({ rag: true });
220
+ response = await gw.send(mapped, { history });
221
+ } catch (err) {
222
+ response = `Error: ${err.message}`;
223
+ }
224
+
225
+ // Update session and reply
226
+ sessions.update(sender, mapped, response);
227
+ const reply = stripMarkdown(response);
228
+
229
+ // Signal has a ~6000 char limit per message
230
+ if (reply.length > 5500) {
231
+ const chunks = reply.match(/.{1,5500}/gs) || [reply];
232
+ for (const chunk of chunks) {
233
+ await sendMessage(config.signalService, config.phoneNumber, sender, chunk);
234
+ }
235
+ } else {
236
+ await sendMessage(config.signalService, config.phoneNumber, sender, reply);
237
+ }
238
+ }
package/lib/brand.js ADDED
@@ -0,0 +1,105 @@
1
+ // Copyright (c) 2026 defconxt. All rights reserved.
2
+ // Licensed under AGPL-3.0 — see LICENSE file for details.
3
+
4
+ /**
5
+ * CIPHER branding — ASCII art, status indicators, formatted output.
6
+ */
7
+
8
+ // ANSI colors
9
+ const R = '\x1b[0m'; // reset
10
+ const B = '\x1b[1m'; // bold
11
+ const D = '\x1b[2m'; // dim
12
+ const RED = '\x1b[31m';
13
+ const GRN = '\x1b[32m';
14
+ const YLW = '\x1b[33m';
15
+ const BLU = '\x1b[34m';
16
+ const MAG = '\x1b[35m';
17
+ const CYN = '\x1b[36m';
18
+
19
+ const LOGO = `${CYN}${B}
20
+ ██████╗██╗██████╗ ██╗ ██╗███████╗██████╗
21
+ ██╔════╝██║██╔══██╗██║ ██║██╔════╝██╔══██╗
22
+ ██║ ██║██████╔╝███████║█████╗ ██████╔╝
23
+ ██║ ██║██╔═══╝ ██╔══██║██╔══╝ ██╔══██╗
24
+ ╚██████╗██║██║ ██║ ██║███████╗██║ ██║
25
+ ╚═════╝╚═╝╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝${R}`;
26
+
27
+ const LOGO_SMALL = `${CYN}${B}▸ CIPHER${R}`;
28
+
29
+ /** Status indicator dots */
30
+ export const dot = {
31
+ ok: `${GRN}●${R}`,
32
+ warn: `${YLW}●${R}`,
33
+ fail: `${RED}●${R}`,
34
+ info: `${BLU}●${R}`,
35
+ skip: `${D}○${R}`,
36
+ };
37
+
38
+ /** Print the full ASCII banner with version */
39
+ export function banner(version = '') {
40
+ const ver = version ? `${D}v${version}${R}` : '';
41
+ process.stderr.write(`${LOGO}\n ${D}Claude Integrated Privacy & Hardening Expert Resource${R} ${ver}\n\n`);
42
+ }
43
+
44
+ /** Print a compact one-line header */
45
+ export function header(text, version = '') {
46
+ const ver = version ? ` ${D}v${version}${R}` : '';
47
+ process.stderr.write(`${LOGO_SMALL}${ver} ${D}—${R} ${text}\n`);
48
+ }
49
+
50
+ /** Print a status line with colored dot */
51
+ export function status(name, state, detail = '') {
52
+ const d = dot[state] || dot.info;
53
+ const detailStr = detail ? ` ${D}${detail}${R}` : '';
54
+ process.stderr.write(` ${d} ${name}${detailStr}\n`);
55
+ }
56
+
57
+ /** Print a section divider */
58
+ export function divider() {
59
+ process.stderr.write(`${D}${'─'.repeat(50)}${R}\n`);
60
+ }
61
+
62
+ /** Print a success message */
63
+ export function success(msg) {
64
+ process.stderr.write(` ${GRN}✔${R} ${msg}\n`);
65
+ }
66
+
67
+ /** Print a warning message */
68
+ export function warn(msg) {
69
+ process.stderr.write(` ${YLW}⚠${R} ${msg}\n`);
70
+ }
71
+
72
+ /** Print an error message */
73
+ export function error(msg) {
74
+ process.stderr.write(` ${RED}✖${R} ${msg}\n`);
75
+ }
76
+
77
+ /** Print an info message */
78
+ export function info(msg) {
79
+ process.stderr.write(` ${BLU}ℹ${R} ${msg}\n`);
80
+ }
81
+
82
+ /**
83
+ * Format doctor checks with status dots.
84
+ * @param {object[]} checks - Array of {name, status, detail}
85
+ * @returns {string} Formatted output for terminal
86
+ */
87
+ export function formatDoctorChecks(checks) {
88
+ const lines = [];
89
+ for (const c of checks) {
90
+ const state = c.status === 'ok' ? 'ok' : c.status === 'optional' ? 'skip' : c.status === 'missing' ? 'fail' : 'warn';
91
+ const d = dot[state];
92
+ lines.push(` ${d} ${B}${c.name}${R} ${D}${c.detail}${R}`);
93
+ }
94
+ return lines.join('\n');
95
+ }
96
+
97
+ /**
98
+ * Format version output.
99
+ * @param {string} version
100
+ */
101
+ export function formatVersion(version) {
102
+ return `${LOGO_SMALL} ${B}${version}${R}`;
103
+ }
104
+
105
+ export { LOGO, LOGO_SMALL, R, B, D, RED, GRN, YLW, BLU, MAG, CYN };
@@ -0,0 +1,100 @@
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
+ * commands.js — Command routing table for the CIPHER CLI.
7
+ *
8
+ * Maps all 29 CLI commands to one of three dispatch modes:
9
+ * - native: Dispatched directly through Node.js handler functions (no Python)
10
+ * - passthrough: Spawned directly as `python -m gateway.app <cmd>` with stdio: 'inherit'
11
+ * (full terminal access for Rich panels, Textual TUIs, long-running services)
12
+ * - bridge: (Legacy) Dispatched via JSON-RPC through python-bridge.js — no commands
13
+ * use this mode as of M007/S03, retained for backward compatibility
14
+ *
15
+ * @module commands
16
+ */
17
+
18
+ /**
19
+ * Dispatch mode for each CLI command.
20
+ *
21
+ * @type {Record<string, { mode: 'native' | 'bridge' | 'passthrough', description: string }>}
22
+ */
23
+ export const COMMAND_MODES = {
24
+ // ── Native commands (Node.js handler dispatch, no Python) ──────────────
25
+ query: { mode: 'native', description: 'Run a security query (smart routing + streaming)' },
26
+ ingest: { mode: 'native', description: 'Ingest security data' },
27
+ status: { mode: 'native', description: 'Show system status' },
28
+ doctor: { mode: 'native', description: 'Diagnose installation health' },
29
+ version: { mode: 'native', description: 'Print version information' },
30
+ plugin: { mode: 'native', description: 'Manage plugins' },
31
+ search: { mode: 'native', description: 'Search security data' },
32
+ store: { mode: 'native', description: 'Manage data stores' },
33
+ diff: { mode: 'native', description: 'Compare security states' },
34
+ workflow: { mode: 'native', description: 'Manage workflows' },
35
+ stats: { mode: 'native', description: 'Show statistics' },
36
+ domains: { mode: 'native', description: 'Manage domains' },
37
+ skills: { mode: 'native', description: 'Manage skills' },
38
+ score: { mode: 'native', description: 'Show security score' },
39
+ marketplace: { mode: 'native', description: 'Browse marketplace' },
40
+ compliance: { mode: 'native', description: 'Run compliance checks' },
41
+ leaderboard: { mode: 'native', description: 'Show leaderboard' },
42
+ feedback: { mode: 'native', description: 'Submit feedback' },
43
+ 'memory-export': { mode: 'native', description: 'Export memory' },
44
+ 'memory-import': { mode: 'native', description: 'Import memory' },
45
+ sarif: { mode: 'native', description: 'SARIF report tools' },
46
+ osint: { mode: 'native', description: 'OSINT intelligence tools' },
47
+ update: { mode: 'native', description: 'Update CIPHER to latest version' },
48
+
49
+ // ── Passthrough commands (direct Python spawn, full terminal) ──────────
50
+ // These were Python-dependent — now ported to Node.js.
51
+ scan: { mode: 'native', description: 'Run a security scan' },
52
+ dashboard: { mode: 'native', description: 'System dashboard' },
53
+ web: { mode: 'native', description: 'Web interface (use `cipher api` instead)' },
54
+ bot: { mode: 'native', description: 'Manage bot integrations (long-running service)' },
55
+ mcp: { mode: 'native', description: 'MCP server tools (long-running service)' },
56
+ api: { mode: 'native', description: 'API management (long-running server)' },
57
+ 'setup-signal': { mode: 'native', description: 'Configure Signal integration' },
58
+ };
59
+
60
+ /**
61
+ * Set of command names dispatched via passthrough (direct Python spawn).
62
+ * @type {Set<string>}
63
+ */
64
+ export const PASSTHROUGH_COMMANDS = new Set(
65
+ Object.entries(COMMAND_MODES)
66
+ .filter(([, v]) => v.mode === 'passthrough')
67
+ .map(([k]) => k)
68
+ );
69
+
70
+ /**
71
+ * Set of command names dispatched via Node.js native handlers.
72
+ * @type {Set<string>}
73
+ */
74
+ export const NATIVE_COMMANDS = new Set(
75
+ Object.entries(COMMAND_MODES)
76
+ .filter(([, v]) => v.mode === 'native')
77
+ .map(([k]) => k)
78
+ );
79
+
80
+ /**
81
+ * Set of command names dispatched via the JSON-RPC bridge.
82
+ * As of M007/S03, no commands use bridge mode — retained for backward compatibility.
83
+ * @type {Set<string>}
84
+ */
85
+ export const BRIDGE_COMMANDS = new Set(
86
+ Object.entries(COMMAND_MODES)
87
+ .filter(([, v]) => v.mode === 'bridge')
88
+ .map(([k]) => k)
89
+ );
90
+
91
+ /**
92
+ * Look up the dispatch mode for a command.
93
+ *
94
+ * @param {string} name — command name (e.g., 'scan', 'status')
95
+ * @returns {'native' | 'bridge' | 'passthrough' | null} — mode string, or null if unknown
96
+ */
97
+ export function getCommandMode(name) {
98
+ const entry = COMMAND_MODES[name];
99
+ return entry ? entry.mode : null;
100
+ }