cipher-security 2.0.8 → 2.2.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 +11 -1
- package/lib/agent-runtime/handlers/architect.js +199 -0
- package/lib/agent-runtime/handlers/base.js +240 -0
- package/lib/agent-runtime/handlers/blue.js +220 -0
- package/lib/agent-runtime/handlers/incident.js +161 -0
- package/lib/agent-runtime/handlers/privacy.js +190 -0
- package/lib/agent-runtime/handlers/purple.js +209 -0
- package/lib/agent-runtime/handlers/recon.js +174 -0
- package/lib/agent-runtime/handlers/red.js +246 -0
- package/lib/agent-runtime/handlers/researcher.js +170 -0
- package/lib/agent-runtime/handlers.js +35 -0
- package/lib/agent-runtime/index.js +196 -0
- package/lib/agent-runtime/parser.js +316 -0
- package/lib/analyze/consistency.js +566 -0
- package/lib/analyze/constitution.js +110 -0
- package/lib/analyze/sharding.js +251 -0
- package/lib/autonomous/agent-tool.js +165 -0
- package/lib/autonomous/feedback-loop.js +13 -6
- package/lib/autonomous/framework.js +17 -0
- package/lib/autonomous/handoff.js +506 -0
- package/lib/autonomous/modes/blue.js +26 -0
- package/lib/autonomous/modes/red.js +585 -0
- package/lib/autonomous/modes/researcher.js +322 -0
- package/lib/autonomous/researcher.js +12 -45
- package/lib/autonomous/runner.js +9 -537
- package/lib/benchmark/agent.js +88 -26
- package/lib/benchmark/baselines.js +3 -0
- package/lib/benchmark/claude-code-solver.js +254 -0
- package/lib/benchmark/cognitive.js +283 -0
- package/lib/benchmark/index.js +12 -2
- package/lib/benchmark/knowledge.js +281 -0
- package/lib/benchmark/llm.js +156 -15
- package/lib/benchmark/models.js +5 -2
- package/lib/benchmark/nyu-ctf.js +192 -0
- package/lib/benchmark/overthewire.js +347 -0
- package/lib/benchmark/picoctf.js +281 -0
- package/lib/benchmark/prompts.js +280 -0
- package/lib/benchmark/registry.js +219 -0
- package/lib/benchmark/remote-solver.js +356 -0
- package/lib/benchmark/remote-target.js +263 -0
- package/lib/benchmark/reporter.js +35 -0
- package/lib/benchmark/runner.js +174 -10
- package/lib/benchmark/sandbox.js +35 -0
- package/lib/benchmark/scorer.js +22 -4
- package/lib/benchmark/solver.js +34 -1
- package/lib/benchmark/tools.js +262 -16
- package/lib/commands.js +9 -0
- package/lib/execution/council.js +434 -0
- package/lib/execution/parallel.js +292 -0
- package/lib/gates/circuit-breaker.js +135 -0
- package/lib/gates/confidence.js +302 -0
- package/lib/gates/corrections.js +219 -0
- package/lib/gates/self-check.js +245 -0
- package/lib/gateway/commands.js +727 -0
- package/lib/guardrails/engine.js +364 -0
- package/lib/mcp/server.js +349 -3
- package/lib/memory/compressor.js +94 -7
- package/lib/pipeline/hooks.js +288 -0
- package/lib/pipeline/index.js +11 -0
- package/lib/review/budget.js +210 -0
- package/lib/review/engine.js +526 -0
- package/lib/review/layers/acceptance-auditor.js +279 -0
- package/lib/review/layers/blind-hunter.js +500 -0
- package/lib/review/layers/defense-in-depth.js +209 -0
- package/lib/review/layers/edge-case-hunter.js +266 -0
- package/lib/review/panel.js +519 -0
- package/lib/review/two-stage.js +244 -0
- package/lib/session/cost-tracker.js +203 -0
- package/lib/session/logger.js +349 -0
- package/package.json +1 -1
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// Copyright (c) 2026 defconxt. All rights reserved.
|
|
2
|
+
// Licensed under AGPL-3.0 — see LICENSE file for details.
|
|
3
|
+
|
|
4
|
+
import { BaseHandler } from './base.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* RECON mode handler — OSINT & threat intelligence.
|
|
8
|
+
* Adds: sources, indicators, profile
|
|
9
|
+
*/
|
|
10
|
+
export class ReconHandler extends BaseHandler {
|
|
11
|
+
constructor() { super('recon'); }
|
|
12
|
+
|
|
13
|
+
domainActions() {
|
|
14
|
+
return ['sources', 'indicators', 'profile'];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describeDomain(action) {
|
|
18
|
+
return {
|
|
19
|
+
'sources': 'List OSINT sources and collection methods from technique',
|
|
20
|
+
'indicators': 'Extract indicators of compromise and intelligence artifacts',
|
|
21
|
+
'profile': 'Build target profile structure from technique methodology',
|
|
22
|
+
}[action];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async executeDomain(command, ctx) {
|
|
26
|
+
switch (command) {
|
|
27
|
+
case 'sources': return this.sources(ctx);
|
|
28
|
+
case 'indicators': return this.indicators(ctx);
|
|
29
|
+
case 'profile': return this.profile(ctx);
|
|
30
|
+
default: throw new Error(`Unknown RECON command: ${command}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** List OSINT sources and methods. */
|
|
35
|
+
sources(ctx) {
|
|
36
|
+
const { skill, technique } = ctx;
|
|
37
|
+
if (!skill) throw new Error('No SKILL.md found');
|
|
38
|
+
|
|
39
|
+
const sources = [];
|
|
40
|
+
|
|
41
|
+
// Extract from tables
|
|
42
|
+
for (const table of skill.tables) {
|
|
43
|
+
if (table.headers.some(h => /source|tool|platform|service|api|url/i.test(h))) {
|
|
44
|
+
for (const row of table.rows) {
|
|
45
|
+
sources.push(row);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Extract tool/API references from code blocks
|
|
51
|
+
const apis = skill.codeBlocks
|
|
52
|
+
.filter(b => /api|curl|request|fetch|http/i.test(b.code))
|
|
53
|
+
.map(b => ({
|
|
54
|
+
context: b.context,
|
|
55
|
+
lang: b.lang,
|
|
56
|
+
snippet: b.code.split('\n').slice(0, 5).join('\n'),
|
|
57
|
+
}));
|
|
58
|
+
|
|
59
|
+
// Classify sources as passive/active
|
|
60
|
+
const passiveKeywords = /passive|public|osint|open.source|search|query|lookup/i;
|
|
61
|
+
const activeKeywords = /active|scan|probe|enumerate|brute|fuzz/i;
|
|
62
|
+
|
|
63
|
+
const collectionType = passiveKeywords.test(skill.raw) ? 'passive' :
|
|
64
|
+
activeKeywords.test(skill.raw) ? 'active' : 'mixed';
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
action: 'sources',
|
|
68
|
+
technique,
|
|
69
|
+
mode: 'RECON',
|
|
70
|
+
tools: skill.tools,
|
|
71
|
+
sources: sources.slice(0, 20),
|
|
72
|
+
apis: apis.slice(0, 10),
|
|
73
|
+
collectionType,
|
|
74
|
+
collectionClassification: this.classifyCollectionType(collectionType, skill.raw),
|
|
75
|
+
status: 'sources_complete',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Classify collection type with confidence score and OPSEC risk. */
|
|
80
|
+
classifyCollectionType(type, raw) {
|
|
81
|
+
const passiveKw = /passive|public|osint|open.source|search|query|lookup|whois|dns|certificate/gi;
|
|
82
|
+
const activeKw = /active|scan|probe|enumerate|brute|fuzz|exploit|inject|crawl|spider/gi;
|
|
83
|
+
const passiveMatches = (String(raw).match(passiveKw) || []).length;
|
|
84
|
+
const activeMatches = (String(raw).match(activeKw) || []).length;
|
|
85
|
+
const totalMatches = passiveMatches + activeMatches;
|
|
86
|
+
const confidence = totalMatches >= 3 ? 'high' : totalMatches >= 1 ? 'medium' : 'low';
|
|
87
|
+
const opsecRiskMap = { active: 'high', mixed: 'medium', passive: 'low' };
|
|
88
|
+
return {
|
|
89
|
+
type: type || 'mixed',
|
|
90
|
+
confidence,
|
|
91
|
+
opsecRisk: opsecRiskMap[type] || 'medium',
|
|
92
|
+
passiveIndicators: passiveMatches,
|
|
93
|
+
activeIndicators: activeMatches,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Extract indicators and intelligence artifacts. */
|
|
98
|
+
indicators(ctx) {
|
|
99
|
+
const { skill, technique } = ctx;
|
|
100
|
+
if (!skill) throw new Error('No SKILL.md found');
|
|
101
|
+
|
|
102
|
+
const indicators = [];
|
|
103
|
+
|
|
104
|
+
// Extract from tables with indicator columns
|
|
105
|
+
for (const table of skill.tables) {
|
|
106
|
+
if (table.headers.some(h => /indicator|ioc|artifact|hash|ip|domain|url|signature/i.test(h))) {
|
|
107
|
+
for (const row of table.rows) {
|
|
108
|
+
indicators.push(row);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Extract patterns that look like IOCs from content
|
|
114
|
+
const iocPatterns = {
|
|
115
|
+
ipv4: /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g,
|
|
116
|
+
domain: /\b[\w-]+\.(?:com|net|org|io|xyz|tk|top|cc|ru)\b/gi,
|
|
117
|
+
hash_md5: /\b[a-f0-9]{32}\b/gi,
|
|
118
|
+
hash_sha256: /\b[a-f0-9]{64}\b/gi,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const extractedIocs = {};
|
|
122
|
+
for (const [type, re] of Object.entries(iocPatterns)) {
|
|
123
|
+
const matches = [...new Set(skill.raw.match(re) || [])];
|
|
124
|
+
if (matches.length) extractedIocs[type] = matches.slice(0, 10);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
action: 'indicators',
|
|
129
|
+
technique,
|
|
130
|
+
mode: 'RECON',
|
|
131
|
+
attackIds: skill.attackIds,
|
|
132
|
+
indicators: indicators.slice(0, 20),
|
|
133
|
+
extractedIocs,
|
|
134
|
+
status: 'indicators_complete',
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Build target profile structure. */
|
|
139
|
+
profile(ctx) {
|
|
140
|
+
const { skill, technique } = ctx;
|
|
141
|
+
if (!skill) throw new Error('No SKILL.md found');
|
|
142
|
+
|
|
143
|
+
const target = ctx.flags.target || 'unspecified';
|
|
144
|
+
|
|
145
|
+
// Extract methodology steps
|
|
146
|
+
const methodology = skill.sections
|
|
147
|
+
.filter(s => /step|workflow|method|approach|procedure|phase/i.test(s.title))
|
|
148
|
+
.map(s => ({
|
|
149
|
+
phase: s.title,
|
|
150
|
+
content: s.content.slice(0, 300),
|
|
151
|
+
}));
|
|
152
|
+
|
|
153
|
+
// Extract data points to collect
|
|
154
|
+
const dataPoints = [];
|
|
155
|
+
for (const table of skill.tables) {
|
|
156
|
+
if (table.headers.some(h => /field|data|attribute|property|element/i.test(h))) {
|
|
157
|
+
for (const row of table.rows) {
|
|
158
|
+
dataPoints.push(row);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
action: 'profile',
|
|
165
|
+
technique,
|
|
166
|
+
mode: 'RECON',
|
|
167
|
+
target,
|
|
168
|
+
tools: skill.tools,
|
|
169
|
+
methodology: methodology.slice(0, 10),
|
|
170
|
+
dataPoints: dataPoints.slice(0, 20),
|
|
171
|
+
status: 'profile_complete',
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
// Copyright (c) 2026 defconxt. All rights reserved.
|
|
2
|
+
// Licensed under AGPL-3.0 — see LICENSE file for details.
|
|
3
|
+
|
|
4
|
+
import { BaseHandler } from './base.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* RED mode handler — offensive security.
|
|
8
|
+
* Adds: generate (attack commands, payloads), attack-path, ttps
|
|
9
|
+
*/
|
|
10
|
+
// Static ATT&CK technique → tactic mapping (70+ common techniques)
|
|
11
|
+
const TECHNIQUE_TACTIC_MAP = {
|
|
12
|
+
T1001: 'command-and-control', T1003: 'credential-access', T1005: 'collection',
|
|
13
|
+
T1007: 'discovery', T1010: 'discovery', T1011: 'exfiltration',
|
|
14
|
+
T1012: 'discovery', T1016: 'discovery', T1018: 'discovery',
|
|
15
|
+
T1020: 'exfiltration', T1021: 'lateral-movement', T1027: 'defense-evasion',
|
|
16
|
+
T1033: 'discovery', T1036: 'defense-evasion', T1037: 'persistence',
|
|
17
|
+
T1039: 'collection', T1040: 'credential-access', T1041: 'exfiltration',
|
|
18
|
+
T1046: 'discovery', T1047: 'execution', T1048: 'exfiltration',
|
|
19
|
+
T1049: 'discovery', T1053: 'execution', T1055: 'defense-evasion',
|
|
20
|
+
T1056: 'collection', T1057: 'discovery', T1059: 'execution',
|
|
21
|
+
T1068: 'privilege-escalation', T1069: 'discovery', T1070: 'defense-evasion',
|
|
22
|
+
T1071: 'command-and-control', T1072: 'lateral-movement', T1074: 'collection',
|
|
23
|
+
T1078: 'defense-evasion', T1080: 'lateral-movement', T1082: 'discovery',
|
|
24
|
+
T1083: 'discovery', T1087: 'discovery', T1090: 'command-and-control',
|
|
25
|
+
T1091: 'lateral-movement', T1095: 'command-and-control', T1098: 'persistence',
|
|
26
|
+
T1102: 'command-and-control', T1104: 'command-and-control', T1105: 'command-and-control',
|
|
27
|
+
T1106: 'execution', T1110: 'credential-access', T1111: 'credential-access',
|
|
28
|
+
T1112: 'defense-evasion', T1113: 'collection', T1114: 'collection',
|
|
29
|
+
T1115: 'collection', T1119: 'collection', T1120: 'discovery',
|
|
30
|
+
T1123: 'collection', T1124: 'discovery', T1125: 'collection',
|
|
31
|
+
T1127: 'defense-evasion', T1129: 'execution', T1132: 'command-and-control',
|
|
32
|
+
T1133: 'persistence', T1134: 'defense-evasion', T1135: 'discovery',
|
|
33
|
+
T1136: 'persistence', T1137: 'persistence', T1140: 'defense-evasion',
|
|
34
|
+
T1176: 'persistence', T1185: 'collection', T1187: 'credential-access',
|
|
35
|
+
T1189: 'initial-access', T1190: 'initial-access', T1195: 'initial-access',
|
|
36
|
+
T1197: 'defense-evasion', T1199: 'initial-access', T1200: 'initial-access',
|
|
37
|
+
T1201: 'discovery', T1202: 'defense-evasion', T1203: 'execution',
|
|
38
|
+
T1204: 'execution', T1205: 'defense-evasion', T1210: 'lateral-movement',
|
|
39
|
+
T1211: 'defense-evasion', T1213: 'collection', T1218: 'defense-evasion',
|
|
40
|
+
T1219: 'command-and-control', T1220: 'defense-evasion',
|
|
41
|
+
T1221: 'defense-evasion', T1222: 'defense-evasion',
|
|
42
|
+
T1486: 'impact', T1489: 'impact', T1490: 'impact',
|
|
43
|
+
T1491: 'impact', T1496: 'impact', T1498: 'impact',
|
|
44
|
+
T1499: 'impact', T1529: 'impact',
|
|
45
|
+
T1543: 'persistence', T1546: 'persistence', T1547: 'persistence',
|
|
46
|
+
T1548: 'privilege-escalation', T1550: 'defense-evasion',
|
|
47
|
+
T1552: 'credential-access', T1553: 'defense-evasion',
|
|
48
|
+
T1555: 'credential-access', T1556: 'credential-access',
|
|
49
|
+
T1557: 'credential-access', T1558: 'credential-access',
|
|
50
|
+
T1559: 'execution', T1560: 'collection',
|
|
51
|
+
T1562: 'defense-evasion', T1563: 'lateral-movement',
|
|
52
|
+
T1564: 'defense-evasion', T1565: 'impact',
|
|
53
|
+
T1566: 'initial-access', T1567: 'exfiltration',
|
|
54
|
+
T1568: 'command-and-control', T1569: 'execution',
|
|
55
|
+
T1570: 'lateral-movement', T1571: 'command-and-control',
|
|
56
|
+
T1572: 'command-and-control', T1573: 'command-and-control',
|
|
57
|
+
T1574: 'persistence', T1578: 'defense-evasion',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Tactic → kill chain phase mapping
|
|
61
|
+
const TACTIC_PHASE_MAP = {
|
|
62
|
+
'reconnaissance': 'reconnaissance',
|
|
63
|
+
'resource-development': 'weaponization',
|
|
64
|
+
'initial-access': 'delivery',
|
|
65
|
+
'execution': 'exploitation',
|
|
66
|
+
'persistence': 'installation',
|
|
67
|
+
'privilege-escalation': 'exploitation',
|
|
68
|
+
'defense-evasion': 'exploitation',
|
|
69
|
+
'credential-access': 'exploitation',
|
|
70
|
+
'discovery': 'exploitation',
|
|
71
|
+
'lateral-movement': 'exploitation',
|
|
72
|
+
'collection': 'actions-on-objectives',
|
|
73
|
+
'command-and-control': 'command-and-control',
|
|
74
|
+
'exfiltration': 'actions-on-objectives',
|
|
75
|
+
'impact': 'actions-on-objectives',
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export class RedHandler extends BaseHandler {
|
|
79
|
+
constructor() { super('red'); }
|
|
80
|
+
|
|
81
|
+
domainActions() {
|
|
82
|
+
return ['generate', 'attack-path', 'ttps'];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
describeDomain(action) {
|
|
86
|
+
return {
|
|
87
|
+
'generate': 'Generate attack commands and payloads from SKILL.md patterns',
|
|
88
|
+
'attack-path': 'Map attack paths with ATT&CK technique chain',
|
|
89
|
+
'ttps': 'Extract TTPs with MITRE ATT&CK mappings',
|
|
90
|
+
}[action];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async executeDomain(command, ctx) {
|
|
94
|
+
switch (command) {
|
|
95
|
+
case 'generate': return this.generate(ctx);
|
|
96
|
+
case 'attack-path': return this.attackPath(ctx);
|
|
97
|
+
case 'ttps': return this.ttps(ctx);
|
|
98
|
+
default: throw new Error(`Unknown RED command: ${command}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Generate attack commands from code blocks. */
|
|
103
|
+
generate(ctx) {
|
|
104
|
+
const source = ctx.apiRef || ctx.skill;
|
|
105
|
+
if (!source) throw new Error('No source material found');
|
|
106
|
+
|
|
107
|
+
const attackLangs = ['bash', 'powershell', 'python', 'sh', 'cmd', 'text'];
|
|
108
|
+
const commands = source.codeBlocks
|
|
109
|
+
.filter(b => attackLangs.includes(b.lang))
|
|
110
|
+
.map(b => ({
|
|
111
|
+
lang: b.lang,
|
|
112
|
+
context: b.context,
|
|
113
|
+
commands: b.code.split('\n')
|
|
114
|
+
.filter(l => l.trim() && !l.trim().startsWith('#') && !l.trim().startsWith('//'))
|
|
115
|
+
.map(l => l.trim()),
|
|
116
|
+
}))
|
|
117
|
+
.filter(b => b.commands.length > 0);
|
|
118
|
+
|
|
119
|
+
const target = ctx.flags.target || 'unspecified';
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
action: 'generate',
|
|
123
|
+
technique: ctx.technique,
|
|
124
|
+
mode: 'RED',
|
|
125
|
+
target,
|
|
126
|
+
attackIds: source.attackIds,
|
|
127
|
+
tools: source.tools,
|
|
128
|
+
commandGroups: commands.slice(0, 20),
|
|
129
|
+
totalCommands: commands.reduce((n, g) => n + g.commands.length, 0),
|
|
130
|
+
warning: 'Authorized testing only. Verify scope before execution.',
|
|
131
|
+
tactics: this.classifyAttackTactic(source.attackIds),
|
|
132
|
+
status: 'generate_complete',
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Map attack paths from technique content. */
|
|
137
|
+
attackPath(ctx) {
|
|
138
|
+
const { skill, technique } = ctx;
|
|
139
|
+
if (!skill) throw new Error('No SKILL.md found');
|
|
140
|
+
|
|
141
|
+
// Extract attack path patterns from content
|
|
142
|
+
const paths = [];
|
|
143
|
+
const pathRe = /(?:→|->|==>|leads to|then|next)/gi;
|
|
144
|
+
for (const section of skill.sections) {
|
|
145
|
+
if (/path|chain|flow|step|workflow|kill.chain/i.test(section.title)) {
|
|
146
|
+
paths.push({
|
|
147
|
+
phase: section.title,
|
|
148
|
+
content: section.content.slice(0, 300),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Extract from code blocks with arrow patterns
|
|
154
|
+
for (const block of skill.codeBlocks) {
|
|
155
|
+
if (pathRe.test(block.code)) {
|
|
156
|
+
paths.push({
|
|
157
|
+
phase: block.context || 'attack-flow',
|
|
158
|
+
content: block.code.slice(0, 300),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
action: 'attack-path',
|
|
165
|
+
technique,
|
|
166
|
+
mode: 'RED',
|
|
167
|
+
attackIds: skill.attackIds,
|
|
168
|
+
paths: paths.slice(0, 10),
|
|
169
|
+
detectionOpportunities: this.extractDetectionNotes(skill),
|
|
170
|
+
status: 'attack_path_complete',
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Extract TTPs. */
|
|
175
|
+
ttps(ctx) {
|
|
176
|
+
const { skill, technique } = ctx;
|
|
177
|
+
if (!skill) throw new Error('No SKILL.md found');
|
|
178
|
+
|
|
179
|
+
const ttps = skill.attackIds.map(id => ({
|
|
180
|
+
id,
|
|
181
|
+
technique: ctx.technique,
|
|
182
|
+
tools: skill.tools,
|
|
183
|
+
}));
|
|
184
|
+
|
|
185
|
+
// Cross-reference with tables that have ATT&CK column
|
|
186
|
+
for (const table of skill.tables) {
|
|
187
|
+
const attackCol = table.headers.find(h => /att&ck|mitre|technique/i.test(h));
|
|
188
|
+
if (attackCol) {
|
|
189
|
+
for (const row of table.rows) {
|
|
190
|
+
const desc = row[table.headers.find(h => /description|name|action/i.test(h))] || '';
|
|
191
|
+
if (row[attackCol]) {
|
|
192
|
+
ttps.push({
|
|
193
|
+
id: row[attackCol],
|
|
194
|
+
description: desc,
|
|
195
|
+
technique: ctx.technique,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
action: 'ttps',
|
|
204
|
+
technique,
|
|
205
|
+
mode: 'RED',
|
|
206
|
+
ttps: this.dedup(ttps, 'id'),
|
|
207
|
+
tactics: this.classifyAttackTactic(skill.attackIds),
|
|
208
|
+
detectionOpportunities: this.extractDetectionNotes(skill),
|
|
209
|
+
status: 'ttps_complete',
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** PURPLE layer — every RED output includes detection opportunities. */
|
|
214
|
+
extractDetectionNotes(skill) {
|
|
215
|
+
const notes = [];
|
|
216
|
+
for (const section of skill.sections) {
|
|
217
|
+
if (/detect|monitor|alert|log|hunt|blue|defend/i.test(section.title)) {
|
|
218
|
+
notes.push({
|
|
219
|
+
area: section.title,
|
|
220
|
+
summary: section.content.slice(0, 200),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return notes.slice(0, 5);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** Classify ATT&CK technique IDs to tactic names and kill chain phases. */
|
|
228
|
+
classifyAttackTactic(attackIds) {
|
|
229
|
+
if (!Array.isArray(attackIds)) return [];
|
|
230
|
+
return attackIds.map(id => {
|
|
231
|
+
const baseId = String(id).replace(/\.\d+$/, '');
|
|
232
|
+
const tactic = TECHNIQUE_TACTIC_MAP[baseId] || 'unknown';
|
|
233
|
+
const phase = TACTIC_PHASE_MAP[tactic] || 'unknown';
|
|
234
|
+
return { id, tactic, phase };
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
dedup(arr, key) {
|
|
239
|
+
const seen = new Set();
|
|
240
|
+
return arr.filter(item => {
|
|
241
|
+
if (seen.has(item[key])) return false;
|
|
242
|
+
seen.add(item[key]);
|
|
243
|
+
return true;
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// Copyright (c) 2026 defconxt. All rights reserved.
|
|
2
|
+
// Licensed under AGPL-3.0 — see LICENSE file for details.
|
|
3
|
+
|
|
4
|
+
import { BaseHandler } from './base.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* RESEARCHER mode handler — CVE analysis, reverse engineering, AI security.
|
|
8
|
+
* Adds: vulnerabilities, techniques, research
|
|
9
|
+
*/
|
|
10
|
+
export class ResearcherHandler extends BaseHandler {
|
|
11
|
+
constructor() { super('researcher'); }
|
|
12
|
+
|
|
13
|
+
domainActions() {
|
|
14
|
+
return ['vulnerabilities', 'techniques', 'research'];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describeDomain(action) {
|
|
18
|
+
return {
|
|
19
|
+
'vulnerabilities': 'Extract CVE references, vulnerability patterns, and exploitation vectors',
|
|
20
|
+
'techniques': 'Map reverse engineering and analysis techniques with tool commands',
|
|
21
|
+
'research': 'Compile research notes, references, and threat landscape context',
|
|
22
|
+
}[action];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async executeDomain(command, ctx) {
|
|
26
|
+
switch (command) {
|
|
27
|
+
case 'vulnerabilities': return this.vulnerabilities(ctx);
|
|
28
|
+
case 'techniques': return this.techniques(ctx);
|
|
29
|
+
case 'research': return this.research(ctx);
|
|
30
|
+
default: throw new Error(`Unknown RESEARCHER command: ${command}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Extract vulnerability information. */
|
|
35
|
+
vulnerabilities(ctx) {
|
|
36
|
+
const { skill, technique } = ctx;
|
|
37
|
+
if (!skill) throw new Error('No SKILL.md found');
|
|
38
|
+
|
|
39
|
+
// Extract CVE references
|
|
40
|
+
const cves = new Set();
|
|
41
|
+
const cveRe = /CVE-\d{4}-\d{4,}/g;
|
|
42
|
+
let m;
|
|
43
|
+
while ((m = cveRe.exec(skill.raw))) {
|
|
44
|
+
cves.add(m[0]);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Extract CWE references
|
|
48
|
+
const cwes = new Set();
|
|
49
|
+
const cweRe = /CWE-\d+/g;
|
|
50
|
+
while ((m = cweRe.exec(skill.raw))) {
|
|
51
|
+
cwes.add(m[0]);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Extract vulnerability tables
|
|
55
|
+
const vulnTables = [];
|
|
56
|
+
for (const table of skill.tables) {
|
|
57
|
+
if (table.headers.some(h => /vuln|cve|cwe|weakness|flaw|bug|exploit/i.test(h))) {
|
|
58
|
+
vulnTables.push({
|
|
59
|
+
headers: table.headers,
|
|
60
|
+
rows: table.rows.slice(0, 15),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const cvesList = [...cves];
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
action: 'vulnerabilities',
|
|
69
|
+
technique,
|
|
70
|
+
mode: 'RESEARCHER',
|
|
71
|
+
attackIds: skill.attackIds,
|
|
72
|
+
cves: cvesList,
|
|
73
|
+
cveValidation: this.validateCveIds(cvesList),
|
|
74
|
+
cwes: [...cwes],
|
|
75
|
+
vulnTables: vulnTables.slice(0, 5),
|
|
76
|
+
status: 'vulnerabilities_complete',
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Validate CVE IDs for correct format (CVE-YYYY-NNNNN, year≥1999, 4-7 digit suffix). */
|
|
81
|
+
validateCveIds(cves) {
|
|
82
|
+
if (!Array.isArray(cves)) return { valid: [], invalid: [], total: 0 };
|
|
83
|
+
const valid = [];
|
|
84
|
+
const invalid = [];
|
|
85
|
+
const cveRe = /^CVE-(\d{4})-(\d{4,7})$/;
|
|
86
|
+
for (const id of cves) {
|
|
87
|
+
const m = String(id).match(cveRe);
|
|
88
|
+
if (m && parseInt(m[1], 10) >= 1999) { valid.push(id); } else { invalid.push(id); }
|
|
89
|
+
}
|
|
90
|
+
return { valid, invalid, total: cves.length };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Map analysis techniques. */
|
|
94
|
+
techniques(ctx) {
|
|
95
|
+
const { skill, technique } = ctx;
|
|
96
|
+
if (!skill) throw new Error('No SKILL.md found');
|
|
97
|
+
|
|
98
|
+
// Extract analysis methods from sections
|
|
99
|
+
const methods = skill.sections
|
|
100
|
+
.filter(s => /method|technique|approach|analys|revers|debug|disassembl/i.test(s.title))
|
|
101
|
+
.map(s => ({
|
|
102
|
+
method: s.title,
|
|
103
|
+
content: s.content.slice(0, 400),
|
|
104
|
+
}));
|
|
105
|
+
|
|
106
|
+
// Extract tool-specific commands
|
|
107
|
+
const toolCommands = skill.codeBlocks
|
|
108
|
+
.filter(b => ['bash', 'python', 'sh', 'powershell', 'gdb', 'r2'].includes(b.lang))
|
|
109
|
+
.map(b => ({
|
|
110
|
+
lang: b.lang,
|
|
111
|
+
context: b.context,
|
|
112
|
+
commands: b.code.split('\n')
|
|
113
|
+
.filter(l => l.trim() && !l.trim().startsWith('#'))
|
|
114
|
+
.slice(0, 10),
|
|
115
|
+
}));
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
action: 'techniques',
|
|
119
|
+
technique,
|
|
120
|
+
mode: 'RESEARCHER',
|
|
121
|
+
attackIds: skill.attackIds,
|
|
122
|
+
tools: skill.tools,
|
|
123
|
+
methods: methods.slice(0, 10),
|
|
124
|
+
toolCommands: toolCommands.slice(0, 15),
|
|
125
|
+
status: 'techniques_complete',
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Compile research notes and context. */
|
|
130
|
+
research(ctx) {
|
|
131
|
+
const { skill, technique } = ctx;
|
|
132
|
+
if (!skill) throw new Error('No SKILL.md found');
|
|
133
|
+
|
|
134
|
+
// Extract references
|
|
135
|
+
const refs = [];
|
|
136
|
+
const urlRe = /https?:\/\/[^\s)]+/g;
|
|
137
|
+
let m;
|
|
138
|
+
while ((m = urlRe.exec(skill.raw))) {
|
|
139
|
+
refs.push(m[0]);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Extract key concepts
|
|
143
|
+
const concepts = skill.sections
|
|
144
|
+
.filter(s => /concept|background|overview|theory|fundamentals/i.test(s.title))
|
|
145
|
+
.map(s => ({
|
|
146
|
+
topic: s.title,
|
|
147
|
+
content: s.content.slice(0, 400),
|
|
148
|
+
}));
|
|
149
|
+
|
|
150
|
+
// Extract related techniques
|
|
151
|
+
const related = skill.sections
|
|
152
|
+
.filter(s => /related|see.also|further|reference/i.test(s.title))
|
|
153
|
+
.map(s => ({
|
|
154
|
+
topic: s.title,
|
|
155
|
+
content: s.content.slice(0, 300),
|
|
156
|
+
}));
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
action: 'research',
|
|
160
|
+
technique,
|
|
161
|
+
mode: 'RESEARCHER',
|
|
162
|
+
attackIds: skill.attackIds,
|
|
163
|
+
tools: skill.tools,
|
|
164
|
+
concepts: concepts.slice(0, 5),
|
|
165
|
+
references: [...new Set(refs)].slice(0, 20),
|
|
166
|
+
related: related.slice(0, 5),
|
|
167
|
+
status: 'research_complete',
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Copyright (c) 2026 defconxt. All rights reserved.
|
|
2
|
+
// Licensed under AGPL-3.0 — see LICENSE file for details.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Domain handler registry.
|
|
6
|
+
* Maps mode names to handler modules.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { RedHandler } from './handlers/red.js';
|
|
10
|
+
import { BlueHandler } from './handlers/blue.js';
|
|
11
|
+
import { PurpleHandler } from './handlers/purple.js';
|
|
12
|
+
import { IncidentHandler } from './handlers/incident.js';
|
|
13
|
+
import { PrivacyHandler } from './handlers/privacy.js';
|
|
14
|
+
import { ReconHandler } from './handlers/recon.js';
|
|
15
|
+
import { ArchitectHandler } from './handlers/architect.js';
|
|
16
|
+
import { ResearcherHandler } from './handlers/researcher.js';
|
|
17
|
+
|
|
18
|
+
const HANDLERS = {
|
|
19
|
+
red: new RedHandler(),
|
|
20
|
+
blue: new BlueHandler(),
|
|
21
|
+
purple: new PurpleHandler(),
|
|
22
|
+
incident: new IncidentHandler(),
|
|
23
|
+
privacy: new PrivacyHandler(),
|
|
24
|
+
recon: new ReconHandler(),
|
|
25
|
+
architect: new ArchitectHandler(),
|
|
26
|
+
researcher: new ResearcherHandler(),
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get the handler for a given mode.
|
|
31
|
+
* Falls back to architect if mode is unknown.
|
|
32
|
+
*/
|
|
33
|
+
export function getHandler(mode) {
|
|
34
|
+
return HANDLERS[mode] || HANDLERS.architect;
|
|
35
|
+
}
|