cipher-security 2.0.8 → 2.1.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.
@@ -0,0 +1,322 @@
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
+ * RESEARCHER mode agent — Security Research & Analysis.
7
+ *
8
+ * Provides CVE analysis, vulnerability research, reverse engineering support,
9
+ * and threat landscape mapping. Tools focus on structured analysis and
10
+ * knowledge extraction rather than active exploitation.
11
+ *
12
+ * @module autonomous/modes/researcher
13
+ */
14
+
15
+ import { ModeAgentConfig, ToolRegistry } from '../framework.js';
16
+ import { readFileSync, existsSync, readdirSync } from 'node:fs';
17
+ import { join, dirname } from 'node:path';
18
+ import { fileURLToPath } from 'node:url';
19
+
20
+ const __dirname = dirname(fileURLToPath(import.meta.url));
21
+
22
+ const debug = process.env.CIPHER_DEBUG === '1'
23
+ ? (/** @type {string} */ msg) => process.stderr.write(`[researcher-mode] ${msg}\n`)
24
+ : () => {};
25
+
26
+ // ---------------------------------------------------------------------------
27
+ // Tool handlers
28
+ // ---------------------------------------------------------------------------
29
+
30
+ /**
31
+ * Analyze a CVE by ID — extract structured vulnerability information.
32
+ * @param {*} context
33
+ * @param {Object} toolInput
34
+ * @returns {string}
35
+ */
36
+ function _researcherAnalyzeCve(context, toolInput) {
37
+ const cveId = (toolInput.cve_id || '').toUpperCase();
38
+ if (!/^CVE-\d{4}-\d{4,}$/.test(cveId)) {
39
+ return `Invalid CVE ID format: "${cveId}". Expected format: CVE-YYYY-NNNNN`;
40
+ }
41
+
42
+ return JSON.stringify({
43
+ cve_id: cveId,
44
+ analysis_template: {
45
+ summary: `Analyze ${cveId} — provide description, affected software, CVSS score, exploitability, patch status`,
46
+ sections: [
47
+ 'Vulnerability Description',
48
+ 'Affected Versions',
49
+ 'CVSS Score & Vector',
50
+ 'Exploitability Assessment',
51
+ 'Proof of Concept Status',
52
+ 'Patch / Mitigation Status',
53
+ 'Detection Opportunities',
54
+ 'ATT&CK Technique Mapping',
55
+ 'Recommendations',
56
+ ],
57
+ data_sources: [
58
+ `https://nvd.nist.gov/vuln/detail/${cveId}`,
59
+ `https://cve.mitre.org/cgi-bin/cvename.cgi?name=${cveId}`,
60
+ 'Vendor advisories',
61
+ 'ExploitDB / PacketStorm',
62
+ ],
63
+ },
64
+ instruction: `Research ${cveId} using available knowledge and web search. Fill in each section with findings. Rate confidence per claim.`,
65
+ }, null, 2);
66
+ }
67
+
68
+ /**
69
+ * Search CIPHER knowledge base for relevant security research material.
70
+ * @param {*} context
71
+ * @param {Object} toolInput
72
+ * @returns {string}
73
+ */
74
+ function _researcherSearchKnowledge(context, toolInput) {
75
+ const query = (toolInput.query || '').toLowerCase();
76
+ if (!query) {
77
+ return 'Error: query is required';
78
+ }
79
+
80
+ const knowledgeDir = join(__dirname, '..', '..', '..', 'knowledge');
81
+ const results = [];
82
+
83
+ if (!existsSync(knowledgeDir)) {
84
+ return JSON.stringify({ query, results: [], note: 'Knowledge directory not found' });
85
+ }
86
+
87
+ try {
88
+ const files = readdirSync(knowledgeDir);
89
+ const queryTerms = query.split(/\s+/).filter(t => t.length > 2);
90
+
91
+ for (const file of files) {
92
+ if (!file.endsWith('.md')) continue;
93
+ const filePath = join(knowledgeDir, file);
94
+ try {
95
+ const content = readFileSync(filePath, 'utf8');
96
+ const lower = content.toLowerCase();
97
+
98
+ // Score by term matches
99
+ let score = 0;
100
+ for (const term of queryTerms) {
101
+ const matches = (lower.match(new RegExp(term, 'g')) || []).length;
102
+ score += Math.min(matches, 10);
103
+ }
104
+
105
+ if (score > 0) {
106
+ const headingMatch = content.match(/^# (.+)$/m);
107
+ const title = headingMatch ? headingMatch[1] : file;
108
+
109
+ results.push({
110
+ file,
111
+ title,
112
+ score,
113
+ preview: content.slice(0, 200).replace(/\n/g, ' ').trim(),
114
+ });
115
+ }
116
+ } catch {
117
+ // skip unreadable files
118
+ }
119
+ }
120
+ } catch {
121
+ // knowledge dir scan failed
122
+ }
123
+
124
+ results.sort((a, b) => b.score - a.score);
125
+
126
+ return JSON.stringify({
127
+ query,
128
+ results: results.slice(0, 10),
129
+ totalMatches: results.length,
130
+ }, null, 2);
131
+ }
132
+
133
+ /**
134
+ * Generate a structured research report template.
135
+ * @param {*} context
136
+ * @param {Object} toolInput
137
+ * @returns {string}
138
+ */
139
+ function _researcherWriteReport(context, toolInput) {
140
+ const topic = toolInput.topic || 'Security Research';
141
+ const findings = toolInput.findings || [];
142
+ const recommendations = toolInput.recommendations || [];
143
+ const confidence = toolInput.confidence || 'MEDIUM';
144
+
145
+ const report = {
146
+ title: topic,
147
+ generated: new Date().toISOString(),
148
+ confidence,
149
+ sections: {
150
+ executive_summary: `Research analysis of: ${topic}`,
151
+ findings: findings.length > 0 ? findings : ['No findings provided — populate from research'],
152
+ recommendations: recommendations.length > 0 ? recommendations : ['No recommendations provided'],
153
+ attack_surface: 'Map relevant ATT&CK techniques',
154
+ detection_opportunities: 'Identify defensive detection points',
155
+ references: 'Cite sources with confidence ratings',
156
+ },
157
+ metadata: {
158
+ researcher: 'CIPHER Autonomous Researcher',
159
+ mode: 'RESEARCHER',
160
+ format: 'structured-json',
161
+ },
162
+ };
163
+
164
+ return JSON.stringify(report, null, 2);
165
+ }
166
+
167
+ // ---------------------------------------------------------------------------
168
+ // Tool schemas
169
+ // ---------------------------------------------------------------------------
170
+
171
+ const _RESEARCHER_ANALYZE_CVE_SCHEMA = {
172
+ name: 'analyze_cve',
173
+ description:
174
+ 'Analyze a CVE by ID. Returns a structured analysis template with sections to investigate. ' +
175
+ 'Use this to begin CVE research — it provides the framework, then fill sections with findings.',
176
+ input_schema: {
177
+ type: 'object',
178
+ properties: {
179
+ cve_id: {
180
+ type: 'string',
181
+ description: 'CVE identifier (e.g., CVE-2024-1234)',
182
+ },
183
+ },
184
+ required: ['cve_id'],
185
+ },
186
+ };
187
+
188
+ const _RESEARCHER_SEARCH_KNOWLEDGE_SCHEMA = {
189
+ name: 'search_knowledge',
190
+ description:
191
+ 'Search the CIPHER knowledge base for security research material. ' +
192
+ 'Returns matching documents ranked by relevance with previews.',
193
+ input_schema: {
194
+ type: 'object',
195
+ properties: {
196
+ query: {
197
+ type: 'string',
198
+ description: 'Search query (e.g., "buffer overflow exploitation", "ransomware detection")',
199
+ },
200
+ },
201
+ required: ['query'],
202
+ },
203
+ };
204
+
205
+ const _RESEARCHER_WRITE_REPORT_SCHEMA = {
206
+ name: 'write_research_report',
207
+ description:
208
+ 'Generate a structured security research report. ' +
209
+ 'Provide topic, findings, and recommendations to produce a formatted report.',
210
+ input_schema: {
211
+ type: 'object',
212
+ properties: {
213
+ topic: {
214
+ type: 'string',
215
+ description: 'Research topic or title',
216
+ },
217
+ findings: {
218
+ type: 'array',
219
+ items: { type: 'string' },
220
+ description: 'List of research findings',
221
+ },
222
+ recommendations: {
223
+ type: 'array',
224
+ items: { type: 'string' },
225
+ description: 'List of actionable recommendations',
226
+ },
227
+ confidence: {
228
+ type: 'string',
229
+ enum: ['HIGH', 'MEDIUM', 'LOW'],
230
+ description: 'Overall confidence level in the research',
231
+ },
232
+ },
233
+ required: ['topic'],
234
+ },
235
+ };
236
+
237
+ // ---------------------------------------------------------------------------
238
+ // System prompt
239
+ // ---------------------------------------------------------------------------
240
+
241
+ const _RESEARCHER_SYSTEM_PROMPT = `\
242
+ You are an expert security researcher performing in-depth analysis on a security topic. \
243
+ Your goal is to produce thorough, actionable research with clear confidence ratings.
244
+
245
+ ## Research Protocol
246
+
247
+ 1. **Scope the topic** — understand what's being asked and what deliverables are needed
248
+ 2. **Search knowledge** — use search_knowledge to find relevant CIPHER docs
249
+ 3. **Analyze** — for CVEs use analyze_cve, for other topics use structured analysis
250
+ 4. **Synthesize** — combine findings into a coherent narrative with ATT&CK mappings
251
+ 5. **Report** — use write_research_report to produce the final structured output
252
+
253
+ ## Confidence Ratings
254
+ Tag every substantive claim:
255
+ - **[CONFIRMED]** — directly supported by evidence or established literature
256
+ - **[INFERRED]** — logically derived; reasonable but verify
257
+ - **[EXTERNAL]** — requires knowledge beyond current material; source cited
258
+ - **[UNCERTAIN]** — insufficient data; states what additional information is needed
259
+
260
+ ## Output Format
261
+ Your research must include:
262
+ - Executive summary (2-3 sentences)
263
+ - Detailed findings with evidence
264
+ - ATT&CK technique mappings where applicable
265
+ - Actionable recommendations ordered by priority
266
+ - Detection opportunities for defensive teams
267
+ - Confidence assessment per finding
268
+
269
+ ## Important Notes
270
+ - Be thorough but concise — depth over breadth
271
+ - Cite specific sources, files, CVEs, or standards
272
+ - Flag gaps in knowledge explicitly rather than guessing
273
+ - If the topic requires current information, note what needs web search verification
274
+ `;
275
+
276
+ // ---------------------------------------------------------------------------
277
+ // Factory
278
+ // ---------------------------------------------------------------------------
279
+
280
+ /**
281
+ * Build a RESEARCHER-mode ModeAgentConfig.
282
+ * @returns {ModeAgentConfig}
283
+ */
284
+ function _makeResearcherConfig() {
285
+ const reg = new ToolRegistry();
286
+ reg.register('analyze_cve', _RESEARCHER_ANALYZE_CVE_SCHEMA, _researcherAnalyzeCve);
287
+ reg.register('search_knowledge', _RESEARCHER_SEARCH_KNOWLEDGE_SCHEMA, _researcherSearchKnowledge);
288
+ reg.register('write_research_report', _RESEARCHER_WRITE_REPORT_SCHEMA, _researcherWriteReport);
289
+
290
+ return new ModeAgentConfig({
291
+ mode: 'RESEARCHER',
292
+ toolRegistry: reg,
293
+ systemPromptTemplate: _RESEARCHER_SYSTEM_PROMPT,
294
+ validator: null, // No structured validation — output is freeform research
295
+ maxTurns: 20,
296
+ requiresSandbox: false,
297
+ completionCheck: null,
298
+ });
299
+ }
300
+
301
+ // ---------------------------------------------------------------------------
302
+ // Registration
303
+ // ---------------------------------------------------------------------------
304
+
305
+ /**
306
+ * Register RESEARCHER mode with the given registerMode function.
307
+ * @param {Function} registerMode
308
+ */
309
+ export function register(registerMode) {
310
+ registerMode('RESEARCHER', _makeResearcherConfig);
311
+ }
312
+
313
+ // ---------------------------------------------------------------------------
314
+ // Exports for testing
315
+ // ---------------------------------------------------------------------------
316
+
317
+ export {
318
+ _makeResearcherConfig,
319
+ _researcherAnalyzeCve,
320
+ _researcherSearchKnowledge,
321
+ _researcherWriteReport,
322
+ };
@@ -318,47 +318,9 @@ node scripts/agent.js report --format json
318
318
  }
319
319
 
320
320
  function _generateAgentPy(domain, techniqueName) {
321
- const funcPrefix = techniqueName.replace(/-/g, '_').slice(0, 20);
322
- return `#!/usr/bin/env python3
323
- """Agent tooling for ${_titleCase(techniqueName).toLowerCase()}."""
324
-
325
- import argparse
326
- import json
327
- import sys
328
-
329
-
330
- def ${funcPrefix}_analyze(target, verbose=False):
331
- """Analyze a target for ${techniqueName} indicators."""
332
- results = {
333
- "action": "analyze",
334
- "technique": "${techniqueName}",
335
- "domain": "${domain}",
336
- "target": target,
337
- "findings": [],
338
- "risk_level": "medium",
339
- }
340
- if verbose:
341
- results["debug"] = {"verbose": True}
342
- return results
343
-
344
-
345
- def main():
346
- parser = argparse.ArgumentParser(description="${techniqueName} agent")
347
- parser.add_argument("--target", required=True)
348
- parser.add_argument("--verbose", action="store_true")
349
- parser.add_argument("--format", choices=["json", "text"], default="json")
350
- args = parser.parse_args()
351
-
352
- result = ${funcPrefix}_analyze(args.target, args.verbose)
353
- if args.format == "json":
354
- print(json.dumps(result, indent=2))
355
- else:
356
- for k, v in result.items():
357
- print(f"{k}: {v}")
358
-
359
-
360
- if __name__ == "__main__":
361
- main()
321
+ return `#!/usr/bin/env node
322
+ import { run } from '../../../../../cli/lib/agent-runtime/index.js';
323
+ run(import.meta.url);
362
324
  `;
363
325
  }
364
326
 
@@ -473,9 +435,12 @@ export class AutonomousResearcher {
473
435
  }
474
436
 
475
437
  static _checkCompiles(script) {
476
- // For JS port: just verify it's a non-empty string with valid Python syntax indicators
477
- // We can't actually parse Python in Node.js, but we can check for structure
478
- return typeof script === 'string' && script.length > 100 && script.includes('def ') && script.includes('import ');
438
+ // Verify agent.js is either a runtime wrapper or a standalone Node.js script
439
+ if (typeof script !== 'string' || script.length < 10) return false;
440
+ // New runtime wrapper pattern
441
+ if (script.includes('agent-runtime') && script.includes('import')) return true;
442
+ // Legacy standalone pattern
443
+ return script.includes('process.argv') || script.includes('import ');
479
444
  }
480
445
 
481
446
  static _checkReferences(refContent) {
@@ -489,7 +454,9 @@ export class AutonomousResearcher {
489
454
  }
490
455
 
491
456
  static _checkContentLength(skill, script, reference) {
492
- return skill.length >= 200 && script.length >= 200 && reference.length >= 100;
457
+ // Runtime wrapper scripts are short by design (3 lines) check for runtime import
458
+ const scriptOk = script.includes('agent-runtime') || script.length >= 200;
459
+ return skill.length >= 200 && scriptOk && reference.length >= 100;
493
460
  }
494
461
 
495
462
  /**