@trenchwork/erosolar 1.1.26 → 1.1.27

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,25 @@
1
+ /**
2
+ * Ghidra `analyzeHeadless` wrappers — direct binding to the
3
+ * support/analyzeHeadless driver Project Zero / NSA / Trail of Bits
4
+ * use at scale, no MCP bridge required. Each tool spawns
5
+ * analyzeHeadless with one of the embedded Jython post-scripts;
6
+ * scripts write JSON to a temp file the TS handler reads. Project
7
+ * databases live under ~/.erosolar/ghidra-projects/<project>/ so
8
+ * a follow-up tool call (decompile, xrefs, list-functions) reuses
9
+ * the analysis from the prior `ghidra_analyze` run.
10
+ *
11
+ * The post-script approach is preferred over running scripts via
12
+ * the Ghidra GUI / MCP bridge because:
13
+ * - No process to keep alive; analyzeHeadless exits per call.
14
+ * - Version-stable; the Jython script API is much more stable
15
+ * than the MCP server's surface.
16
+ * - First-class to Ghidra; this is the path the security
17
+ * research community already uses.
18
+ */
19
+ import type { CapabilityContext, CapabilityContribution, CapabilityModule } from '../runtime/agentHost.js';
20
+ export declare class GhidraHeadlessCapabilityModule implements CapabilityModule {
21
+ readonly id = "capability.ghidraHeadless";
22
+ readonly description = "Ghidra analyzeHeadless wrappers \u2014 ghidra_analyze, ghidra_list_functions, ghidra_decompile, ghidra_xrefs, ghidra_fid_matches, ghidra_function_diff, ghidra_delete_project. Each tool spawns analyzeHeadless once with an embedded Jython post-script; project databases persist under ~/.erosolar/ghidra-projects/.";
23
+ create(_context: CapabilityContext): Promise<CapabilityContribution>;
24
+ }
25
+ //# sourceMappingURL=ghidraHeadlessCapability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ghidraHeadlessCapability.d.ts","sourceRoot":"","sources":["../../src/capabilities/ghidraHeadlessCapability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAcH,OAAO,KAAK,EACV,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,EACjB,MAAM,yBAAyB,CAAC;AAqkBjC,qBAAa,8BAA+B,YAAW,gBAAgB;IACrE,QAAQ,CAAC,EAAE,+BAA+B;IAC1C,QAAQ,CAAC,WAAW,6TAAwT;IAEtU,MAAM,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,sBAAsB,CAAC;CAU3E"}
@@ -0,0 +1,593 @@
1
+ /**
2
+ * Ghidra `analyzeHeadless` wrappers — direct binding to the
3
+ * support/analyzeHeadless driver Project Zero / NSA / Trail of Bits
4
+ * use at scale, no MCP bridge required. Each tool spawns
5
+ * analyzeHeadless with one of the embedded Jython post-scripts;
6
+ * scripts write JSON to a temp file the TS handler reads. Project
7
+ * databases live under ~/.erosolar/ghidra-projects/<project>/ so
8
+ * a follow-up tool call (decompile, xrefs, list-functions) reuses
9
+ * the analysis from the prior `ghidra_analyze` run.
10
+ *
11
+ * The post-script approach is preferred over running scripts via
12
+ * the Ghidra GUI / MCP bridge because:
13
+ * - No process to keep alive; analyzeHeadless exits per call.
14
+ * - Version-stable; the Jython script API is much more stable
15
+ * than the MCP server's surface.
16
+ * - First-class to Ghidra; this is the path the security
17
+ * research community already uses.
18
+ */
19
+ import { spawn } from 'node:child_process';
20
+ import { existsSync, mkdirSync, readFileSync, rmSync, unlinkSync, writeFileSync, } from 'node:fs';
21
+ import { homedir, tmpdir } from 'node:os';
22
+ import { join } from 'node:path';
23
+ import { logDebug } from '../utils/debugLogger.js';
24
+ const GHIDRA_HEADLESS = process.env['GHIDRA_HEADLESS']
25
+ || (existsSync('/usr/share/ghidra/support/analyzeHeadless')
26
+ ? '/usr/share/ghidra/support/analyzeHeadless'
27
+ : '/opt/ghidra/support/analyzeHeadless');
28
+ const PROJECTS_ROOT = join(homedir(), '.erosolar', 'ghidra-projects');
29
+ const DEFAULT_TIMEOUT_MS = 30 * 60 * 1000; // analyzeHeadless can take a while on large binaries
30
+ const SCRIPT_DIR = join(tmpdir(), 'erosolar-ghidra-scripts');
31
+ /* ------------------------------------------------------------------
32
+ * Embedded Java GhidraScript post-scripts.
33
+ *
34
+ * Modern Ghidra (12.x in Kali 2026) replaced Jython with PyGhidra
35
+ * (CPython bridge), which requires `pip install pyghidra`. Java
36
+ * GhidraScripts compile at script-load time inside Ghidra's JVM —
37
+ * no extra deps for end users. Each script writes a JSON file the
38
+ * TS handler reads back.
39
+ *
40
+ * All scripts share the same shape:
41
+ * public class <Name> extends GhidraScript {
42
+ * public void run() throws Exception {
43
+ * String[] args = getScriptArgs();
44
+ * String out = args[0];
45
+ * // ... build JSON, write to out ...
46
+ * }
47
+ * }
48
+ *
49
+ * JSON is hand-built. The shape is small per-call and Ghidra's
50
+ * classpath doesn't reliably expose Jackson/Gson to scripts.
51
+ * ----------------------------------------------------------------*/
52
+ const JSON_HELPERS = `
53
+ private static String jsonStr(String s) {
54
+ if (s == null) return "null";
55
+ StringBuilder sb = new StringBuilder("\\"");
56
+ for (int i = 0; i < s.length(); i++) {
57
+ char c = s.charAt(i);
58
+ switch (c) {
59
+ case '\\\\': sb.append("\\\\\\\\"); break;
60
+ case '"': sb.append("\\\\\\""); break;
61
+ case '\\n': sb.append("\\\\n"); break;
62
+ case '\\r': sb.append("\\\\r"); break;
63
+ case '\\t': sb.append("\\\\t"); break;
64
+ case '\\b': sb.append("\\\\b"); break;
65
+ case '\\f': sb.append("\\\\f"); break;
66
+ default:
67
+ if (c < 0x20) {
68
+ sb.append(String.format("\\\\u%04x", (int) c));
69
+ } else {
70
+ sb.append(c);
71
+ }
72
+ }
73
+ }
74
+ sb.append("\\"");
75
+ return sb.toString();
76
+ }
77
+ private static String hex(long v) { return "0x" + Long.toHexString(v); }
78
+ `;
79
+ const SCRIPT_LIST_FUNCTIONS = `// EvrpListFunctions.java
80
+ import ghidra.app.script.GhidraScript;
81
+ import ghidra.program.model.listing.Function;
82
+ import java.io.FileWriter;
83
+ import java.util.ArrayList;
84
+ import java.util.List;
85
+
86
+ public class EvrpListFunctions extends GhidraScript {
87
+ ${JSON_HELPERS}
88
+ @Override
89
+ public void run() throws Exception {
90
+ String[] args = getScriptArgs();
91
+ String out = args[0];
92
+ List<String> entries = new ArrayList<>();
93
+ for (Function f : currentProgram.getFunctionManager().getFunctions(true)) {
94
+ String sig;
95
+ try { sig = f.getPrototypeString(true, true); } catch (Exception e) { sig = f.getName() + "()"; }
96
+ entries.add("{\\"name\\":" + jsonStr(f.getName())
97
+ + ",\\"entry\\":" + jsonStr(hex(f.getEntryPoint().getOffset()))
98
+ + ",\\"size\\":" + f.getBody().getNumAddresses()
99
+ + ",\\"signature\\":" + jsonStr(sig)
100
+ + ",\\"thunk\\":" + f.isThunk()
101
+ + ",\\"external\\":" + f.isExternal() + "}");
102
+ }
103
+ StringBuilder sb = new StringBuilder();
104
+ sb.append("{\\"program\\":").append(jsonStr(currentProgram.getName()));
105
+ sb.append(",\\"functionCount\\":").append(entries.size());
106
+ sb.append(",\\"functions\\":[");
107
+ for (int i = 0; i < entries.size(); i++) {
108
+ if (i > 0) sb.append(",");
109
+ sb.append(entries.get(i));
110
+ }
111
+ sb.append("]}");
112
+ try (FileWriter fw = new FileWriter(out)) { fw.write(sb.toString()); }
113
+ }
114
+ }
115
+ `;
116
+ const SCRIPT_DECOMPILE = `// EvrpDecompile.java
117
+ import ghidra.app.script.GhidraScript;
118
+ import ghidra.app.decompiler.DecompInterface;
119
+ import ghidra.app.decompiler.DecompileResults;
120
+ import ghidra.program.model.address.Address;
121
+ import ghidra.program.model.listing.Function;
122
+ import ghidra.program.model.listing.FunctionManager;
123
+ import ghidra.util.task.ConsoleTaskMonitor;
124
+ import java.io.FileWriter;
125
+
126
+ public class EvrpDecompile extends GhidraScript {
127
+ ${JSON_HELPERS}
128
+ @Override
129
+ public void run() throws Exception {
130
+ String[] args = getScriptArgs();
131
+ String out = args[0];
132
+ String target = args[1];
133
+
134
+ FunctionManager fm = currentProgram.getFunctionManager();
135
+ Function fn = null;
136
+ if (target.startsWith("0x")) {
137
+ Address addr = currentProgram.getAddressFactory().getAddress(target);
138
+ fn = fm.getFunctionAt(addr);
139
+ if (fn == null) fn = fm.getFunctionContaining(addr);
140
+ } else {
141
+ for (Function f : fm.getFunctions(true)) {
142
+ if (f.getName().equals(target)) { fn = f; break; }
143
+ }
144
+ }
145
+
146
+ try (FileWriter fw = new FileWriter(out)) {
147
+ if (fn == null) {
148
+ fw.write("{\\"error\\":\\"function not found\\",\\"target\\":" + jsonStr(target) + "}");
149
+ return;
150
+ }
151
+ DecompInterface di = new DecompInterface();
152
+ di.openProgram(currentProgram);
153
+ DecompileResults res = di.decompileFunction(fn, 60, new ConsoleTaskMonitor());
154
+ if (res.decompileCompleted()) {
155
+ String code = res.getDecompiledFunction().getC();
156
+ fw.write("{\\"name\\":" + jsonStr(fn.getName())
157
+ + ",\\"entry\\":" + jsonStr(hex(fn.getEntryPoint().getOffset()))
158
+ + ",\\"signature\\":" + jsonStr(fn.getPrototypeString(true, true))
159
+ + ",\\"decompilation\\":" + jsonStr(code) + "}");
160
+ } else {
161
+ fw.write("{\\"name\\":" + jsonStr(fn.getName())
162
+ + ",\\"error\\":" + jsonStr(res.getErrorMessage() != null ? res.getErrorMessage() : "decompile failed") + "}");
163
+ }
164
+ }
165
+ }
166
+ }
167
+ `;
168
+ const SCRIPT_XREFS = `// EvrpXrefs.java
169
+ import ghidra.app.script.GhidraScript;
170
+ import ghidra.program.model.address.Address;
171
+ import ghidra.program.model.listing.Function;
172
+ import ghidra.program.model.listing.FunctionManager;
173
+ import ghidra.program.model.symbol.Reference;
174
+ import ghidra.util.task.ConsoleTaskMonitor;
175
+ import java.io.FileWriter;
176
+ import java.util.Set;
177
+ import java.util.TreeSet;
178
+
179
+ public class EvrpXrefs extends GhidraScript {
180
+ ${JSON_HELPERS}
181
+ @Override
182
+ public void run() throws Exception {
183
+ String[] args = getScriptArgs();
184
+ String out = args[0];
185
+ String target = args[1];
186
+
187
+ FunctionManager fm = currentProgram.getFunctionManager();
188
+ Function fn = null;
189
+ if (target.startsWith("0x")) {
190
+ Address addr = currentProgram.getAddressFactory().getAddress(target);
191
+ fn = fm.getFunctionAt(addr);
192
+ if (fn == null) fn = fm.getFunctionContaining(addr);
193
+ } else {
194
+ for (Function f : fm.getFunctions(true)) {
195
+ if (f.getName().equals(target)) { fn = f; break; }
196
+ }
197
+ }
198
+
199
+ try (FileWriter fw = new FileWriter(out)) {
200
+ if (fn == null) {
201
+ fw.write("{\\"error\\":\\"function not found\\",\\"target\\":" + jsonStr(target) + "}");
202
+ return;
203
+ }
204
+ Set<String> callers = new TreeSet<>();
205
+ for (Reference ref : currentProgram.getReferenceManager().getReferencesTo(fn.getEntryPoint())) {
206
+ Function cf = fm.getFunctionContaining(ref.getFromAddress());
207
+ if (cf != null) callers.add(cf.getName());
208
+ }
209
+ Set<String> callees = new TreeSet<>();
210
+ for (Function callee : fn.getCalledFunctions(new ConsoleTaskMonitor())) {
211
+ callees.add(callee.getName());
212
+ }
213
+ StringBuilder sb = new StringBuilder();
214
+ sb.append("{\\"name\\":").append(jsonStr(fn.getName()));
215
+ sb.append(",\\"entry\\":").append(jsonStr(hex(fn.getEntryPoint().getOffset())));
216
+ sb.append(",\\"callers\\":[");
217
+ int i = 0;
218
+ for (String c : callers) { if (i > 0) sb.append(","); sb.append(jsonStr(c)); i++; }
219
+ sb.append("],\\"callees\\":[");
220
+ i = 0;
221
+ for (String c : callees) { if (i > 0) sb.append(","); sb.append(jsonStr(c)); i++; }
222
+ sb.append("]}");
223
+ fw.write(sb.toString());
224
+ }
225
+ }
226
+ }
227
+ `;
228
+ const SCRIPT_FID_MATCHES = `// EvrpFidMatches.java
229
+ import ghidra.app.script.GhidraScript;
230
+ import ghidra.program.model.listing.Function;
231
+ import java.io.FileWriter;
232
+ import java.util.ArrayList;
233
+ import java.util.List;
234
+
235
+ public class EvrpFidMatches extends GhidraScript {
236
+ ${JSON_HELPERS}
237
+ @Override
238
+ public void run() throws Exception {
239
+ String[] args = getScriptArgs();
240
+ String out = args[0];
241
+ List<String> recognized = new ArrayList<>();
242
+ int unrecognized = 0;
243
+ for (Function f : currentProgram.getFunctionManager().getFunctions(true)) {
244
+ String name = f.getName();
245
+ if (name.startsWith("FUN_") || name.startsWith("sub_")) { unrecognized++; continue; }
246
+ if (f.isThunk() || f.isExternal()) continue;
247
+ if (recognized.size() < 1000) {
248
+ recognized.add("{\\"name\\":" + jsonStr(name)
249
+ + ",\\"entry\\":" + jsonStr(hex(f.getEntryPoint().getOffset()))
250
+ + ",\\"size\\":" + f.getBody().getNumAddresses() + "}");
251
+ }
252
+ }
253
+ StringBuilder sb = new StringBuilder();
254
+ sb.append("{\\"program\\":").append(jsonStr(currentProgram.getName()));
255
+ sb.append(",\\"recognized_count\\":").append(recognized.size());
256
+ sb.append(",\\"unrecognized_count\\":").append(unrecognized);
257
+ sb.append(",\\"recognized\\":[");
258
+ for (int i = 0; i < recognized.size(); i++) {
259
+ if (i > 0) sb.append(",");
260
+ sb.append(recognized.get(i));
261
+ }
262
+ sb.append("]}");
263
+ try (FileWriter fw = new FileWriter(out)) { fw.write(sb.toString()); }
264
+ }
265
+ }
266
+ `;
267
+ function ensureScriptDir() {
268
+ if (!existsSync(SCRIPT_DIR))
269
+ mkdirSync(SCRIPT_DIR, { recursive: true });
270
+ return SCRIPT_DIR;
271
+ }
272
+ function ensureProjectDir(name) {
273
+ if (!/^[A-Za-z0-9._-]+$/.test(name)) {
274
+ throw new Error(`bad project name "${name}" — use [A-Za-z0-9._-]+`);
275
+ }
276
+ if (!existsSync(PROJECTS_ROOT))
277
+ mkdirSync(PROJECTS_ROOT, { recursive: true });
278
+ const dir = join(PROJECTS_ROOT, name);
279
+ if (!existsSync(dir))
280
+ mkdirSync(dir, { recursive: true });
281
+ return dir;
282
+ }
283
+ async function runGhidra(options) {
284
+ const scriptDir = ensureScriptDir();
285
+ const scriptPath = join(scriptDir, options.scriptName);
286
+ writeFileSync(scriptPath, options.scriptBody, 'utf8');
287
+ const projectDir = ensureProjectDir(options.project);
288
+ const outFile = join(tmpdir(), `evrp-ghidra-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`);
289
+ const argv = [projectDir, options.project];
290
+ if (options.importBinary) {
291
+ argv.push('-import', options.importBinary, '-overwrite');
292
+ }
293
+ else {
294
+ argv.push('-process', '-noanalysis');
295
+ }
296
+ if (options.readOnly)
297
+ argv.push('-readOnly');
298
+ argv.push('-scriptPath', scriptDir);
299
+ argv.push('-postScript', options.scriptName, outFile, ...options.scriptArgs);
300
+ argv.push('-analysisTimeoutPerFile', '600');
301
+ return await new Promise((resolve, reject) => {
302
+ const start = Date.now();
303
+ const child = spawn(GHIDRA_HEADLESS, argv, {
304
+ stdio: ['ignore', 'pipe', 'pipe'],
305
+ });
306
+ let stdout = '';
307
+ let stderr = '';
308
+ let stdoutBytes = 0;
309
+ let stderrBytes = 0;
310
+ const MAX = 1 * 1024 * 1024;
311
+ child.stdout?.on('data', (b) => {
312
+ if (stdoutBytes < MAX) {
313
+ const room = MAX - stdoutBytes;
314
+ const slice = b.length > room ? b.subarray(0, room) : b;
315
+ stdout += slice.toString();
316
+ stdoutBytes += slice.length;
317
+ }
318
+ });
319
+ child.stderr?.on('data', (b) => {
320
+ if (stderrBytes < MAX) {
321
+ const room = MAX - stderrBytes;
322
+ const slice = b.length > room ? b.subarray(0, room) : b;
323
+ stderr += slice.toString();
324
+ stderrBytes += slice.length;
325
+ }
326
+ });
327
+ const timer = setTimeout(() => {
328
+ child.kill('SIGTERM');
329
+ setTimeout(() => child.kill('SIGKILL'), 1500);
330
+ }, options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
331
+ child.on('error', (err) => {
332
+ clearTimeout(timer);
333
+ reject(err);
334
+ });
335
+ child.on('close', (code) => {
336
+ clearTimeout(timer);
337
+ let output = null;
338
+ if (existsSync(outFile)) {
339
+ try {
340
+ output = JSON.parse(readFileSync(outFile, 'utf8'));
341
+ }
342
+ catch (e) {
343
+ output = { error: `failed to parse script output: ${e.message}` };
344
+ }
345
+ try {
346
+ unlinkSync(outFile);
347
+ }
348
+ catch { /* ignore */ }
349
+ }
350
+ resolve({ exitCode: code, stdout, stderr, output, durationMs: Date.now() - start });
351
+ });
352
+ });
353
+ }
354
+ function summarize(stdout, stderr) {
355
+ // analyzeHeadless is loud — extract just the final ERROR/WARN lines so
356
+ // the LLM-visible string isn't dominated by JVM banner text.
357
+ const lines = (stdout + '\n' + stderr).split('\n');
358
+ const interesting = lines.filter((l) => /\b(ERROR|WARN|FATAL|Exception|Traceback)\b/.test(l) ||
359
+ /^Analyzing|^DECOMPILER|^FID/i.test(l));
360
+ return interesting.slice(-20).join('\n');
361
+ }
362
+ function buildGhidraTools() {
363
+ return [
364
+ {
365
+ name: 'ghidra_analyze',
366
+ description: 'Run Ghidra analyzeHeadless against a binary. Creates / overwrites a project under ~/.erosolar/ghidra-projects/<project>/. Subsequent ghidra_decompile / ghidra_xrefs / ghidra_list_functions / ghidra_fid_matches calls reuse this project (no re-analysis). Use after any binary change.',
367
+ parameters: {
368
+ type: 'object',
369
+ properties: {
370
+ binary: { type: 'string', description: 'Absolute path to the binary to analyze' },
371
+ project: { type: 'string', description: 'Project name (alphanum/_-./) — pick one per binary so subsequent reads target the right db' },
372
+ processor: { type: 'string', description: 'Optional Ghidra languageID (e.g. "x86:LE:64:default") to skip auto-detect' },
373
+ timeoutMs: { type: 'number', description: 'Wall-clock cap; default 30min' },
374
+ },
375
+ required: ['binary', 'project'],
376
+ },
377
+ handler: async (args) => {
378
+ const r = await runGhidra({
379
+ project: String(args.project),
380
+ scriptName: 'EvrpListFunctions.java',
381
+ scriptBody: SCRIPT_LIST_FUNCTIONS,
382
+ scriptArgs: [],
383
+ importBinary: String(args.binary),
384
+ timeoutMs: typeof args.timeoutMs === 'number' ? args.timeoutMs : undefined,
385
+ });
386
+ const out = r.output;
387
+ const lines = [
388
+ `analyzed binary=${args.binary} project=${args.project}`,
389
+ `exit=${r.exitCode} duration=${r.durationMs}ms`,
390
+ `program=${out?.program ?? '?'} functions=${out?.functionCount ?? 0}`,
391
+ `project_dir=${join(PROJECTS_ROOT, String(args.project))}`,
392
+ ];
393
+ const tail = summarize(r.stdout, r.stderr);
394
+ if (tail)
395
+ lines.push('--- analyzeHeadless tail ---', tail);
396
+ return lines.join('\n');
397
+ },
398
+ },
399
+ {
400
+ name: 'ghidra_list_functions',
401
+ description: 'List every defined function in an already-analyzed project (name, entry, size, signature, thunk/external flag). Run ghidra_analyze first.',
402
+ parameters: {
403
+ type: 'object',
404
+ properties: {
405
+ project: { type: 'string' },
406
+ },
407
+ required: ['project'],
408
+ },
409
+ handler: async (args) => {
410
+ const r = await runGhidra({
411
+ project: String(args.project),
412
+ scriptName: 'EvrpListFunctions.java',
413
+ scriptBody: SCRIPT_LIST_FUNCTIONS,
414
+ scriptArgs: [],
415
+ readOnly: true,
416
+ });
417
+ if (r.output === null) {
418
+ return `ghidra_list_functions failed (exit ${r.exitCode})\n${summarize(r.stdout, r.stderr)}`;
419
+ }
420
+ return JSON.stringify(r.output);
421
+ },
422
+ },
423
+ {
424
+ name: 'ghidra_decompile',
425
+ description: 'Decompile one function in an already-analyzed project. Target by name or 0x-address. Returns C pseudocode + signature.',
426
+ parameters: {
427
+ type: 'object',
428
+ properties: {
429
+ project: { type: 'string' },
430
+ target: { type: 'string', description: 'Function name (e.g. "main") or hex address (e.g. "0x401040")' },
431
+ },
432
+ required: ['project', 'target'],
433
+ },
434
+ handler: async (args) => {
435
+ const r = await runGhidra({
436
+ project: String(args.project),
437
+ scriptName: 'EvrpDecompile.java',
438
+ scriptBody: SCRIPT_DECOMPILE,
439
+ scriptArgs: [String(args.target)],
440
+ readOnly: true,
441
+ });
442
+ if (r.output === null) {
443
+ return `ghidra_decompile failed (exit ${r.exitCode})\n${summarize(r.stdout, r.stderr)}`;
444
+ }
445
+ return JSON.stringify(r.output);
446
+ },
447
+ },
448
+ {
449
+ name: 'ghidra_xrefs',
450
+ description: 'List callers and callees of one function in an already-analyzed project. Target by name or 0x-address.',
451
+ parameters: {
452
+ type: 'object',
453
+ properties: {
454
+ project: { type: 'string' },
455
+ target: { type: 'string' },
456
+ },
457
+ required: ['project', 'target'],
458
+ },
459
+ handler: async (args) => {
460
+ const r = await runGhidra({
461
+ project: String(args.project),
462
+ scriptName: 'EvrpXrefs.java',
463
+ scriptBody: SCRIPT_XREFS,
464
+ scriptArgs: [String(args.target)],
465
+ readOnly: true,
466
+ });
467
+ if (r.output === null) {
468
+ return `ghidra_xrefs failed (exit ${r.exitCode})\n${summarize(r.stdout, r.stderr)}`;
469
+ }
470
+ return JSON.stringify(r.output);
471
+ },
472
+ },
473
+ {
474
+ name: 'ghidra_fid_matches',
475
+ description: 'Dump library-function name recognition results from Ghidra FID. Useful for reasoning about library calls inside a stripped binary.',
476
+ parameters: {
477
+ type: 'object',
478
+ properties: {
479
+ project: { type: 'string' },
480
+ },
481
+ required: ['project'],
482
+ },
483
+ handler: async (args) => {
484
+ const r = await runGhidra({
485
+ project: String(args.project),
486
+ scriptName: 'EvrpFidMatches.java',
487
+ scriptBody: SCRIPT_FID_MATCHES,
488
+ scriptArgs: [],
489
+ readOnly: true,
490
+ });
491
+ if (r.output === null) {
492
+ return `ghidra_fid_matches failed (exit ${r.exitCode})\n${summarize(r.stdout, r.stderr)}`;
493
+ }
494
+ return JSON.stringify(r.output);
495
+ },
496
+ },
497
+ {
498
+ name: 'ghidra_function_diff',
499
+ description: 'Compare two already-analyzed projects at function-set granularity. Returns: only-in-A names, only-in-B names, and signature-changed pairs. For deep semantic diff, decompile each changed function in both projects and compare the C pseudocode.',
500
+ parameters: {
501
+ type: 'object',
502
+ properties: {
503
+ projectA: { type: 'string', description: 'Vulnerable / pre-patch project' },
504
+ projectB: { type: 'string', description: 'Patched project' },
505
+ },
506
+ required: ['projectA', 'projectB'],
507
+ },
508
+ handler: async (args) => {
509
+ const projA = String(args.projectA);
510
+ const projB = String(args.projectB);
511
+ const [rA, rB] = await Promise.all([
512
+ runGhidra({
513
+ project: projA,
514
+ scriptName: 'EvrpListFunctions.java',
515
+ scriptBody: SCRIPT_LIST_FUNCTIONS,
516
+ scriptArgs: [],
517
+ readOnly: true,
518
+ }),
519
+ runGhidra({
520
+ project: projB,
521
+ scriptName: 'EvrpListFunctions.java',
522
+ scriptBody: SCRIPT_LIST_FUNCTIONS,
523
+ scriptArgs: [],
524
+ readOnly: true,
525
+ }),
526
+ ]);
527
+ const a = rA.output?.functions ?? [];
528
+ const b = rB.output?.functions ?? [];
529
+ const aMap = new Map(a.map((f) => [f.name, f]));
530
+ const bMap = new Map(b.map((f) => [f.name, f]));
531
+ const onlyInA = [];
532
+ const onlyInB = [];
533
+ const sigChanged = [];
534
+ for (const [name, fa] of aMap) {
535
+ const fb = bMap.get(name);
536
+ if (!fb) {
537
+ onlyInA.push(name);
538
+ }
539
+ else if (fa.signature !== fb.signature) {
540
+ sigChanged.push({ name, a: fa.signature, b: fb.signature });
541
+ }
542
+ }
543
+ for (const name of bMap.keys()) {
544
+ if (!aMap.has(name))
545
+ onlyInB.push(name);
546
+ }
547
+ return JSON.stringify({
548
+ projectA: projA,
549
+ projectB: projB,
550
+ functionsA: a.length,
551
+ functionsB: b.length,
552
+ onlyInA: onlyInA.slice(0, 200),
553
+ onlyInB: onlyInB.slice(0, 200),
554
+ signatureChanged: sigChanged.slice(0, 200),
555
+ });
556
+ },
557
+ },
558
+ {
559
+ name: 'ghidra_delete_project',
560
+ description: 'Delete a Ghidra project directory under ~/.erosolar/ghidra-projects/. Use sparingly — projects represent expensive analysis runs.',
561
+ parameters: {
562
+ type: 'object',
563
+ properties: { project: { type: 'string' } },
564
+ required: ['project'],
565
+ },
566
+ handler: async (args) => {
567
+ const dir = join(PROJECTS_ROOT, String(args.project));
568
+ if (!/^[A-Za-z0-9._-]+$/.test(String(args.project))) {
569
+ return `bad project name "${args.project}"`;
570
+ }
571
+ if (!existsSync(dir))
572
+ return `no such project: ${args.project}`;
573
+ rmSync(dir, { recursive: true, force: true });
574
+ return `deleted ${dir}`;
575
+ },
576
+ },
577
+ ];
578
+ }
579
+ export class GhidraHeadlessCapabilityModule {
580
+ id = 'capability.ghidraHeadless';
581
+ description = 'Ghidra analyzeHeadless wrappers — ghidra_analyze, ghidra_list_functions, ghidra_decompile, ghidra_xrefs, ghidra_fid_matches, ghidra_function_diff, ghidra_delete_project. Each tool spawns analyzeHeadless once with an embedded Jython post-script; project databases persist under ~/.erosolar/ghidra-projects/.';
582
+ async create(_context) {
583
+ void _context;
584
+ logDebug('[GhidraHeadless] Capability initialized');
585
+ const toolSuite = {
586
+ id: 'ghidraHeadless.tools',
587
+ description: 'Ghidra analyzeHeadless wrappers',
588
+ tools: buildGhidraTools(),
589
+ };
590
+ return { id: this.id, description: this.description, toolSuite };
591
+ }
592
+ }
593
+ //# sourceMappingURL=ghidraHeadlessCapability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ghidraHeadlessCapability.js","sourceRoot":"","sources":["../../src/capabilities/ghidraHeadlessCapability.ts"],"names":[],"mappings":"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"}
@@ -13,5 +13,6 @@ export { AflppCapabilityModule } from './aflppCapability.js';
13
13
  export { GdbCapabilityModule } from './gdbCapability.js';
14
14
  export { BinaryAnalysisCapabilityModule } from './binaryAnalysisCapability.js';
15
15
  export { PwntoolsCapabilityModule } from './pwntoolsCapability.js';
16
+ export { GhidraHeadlessCapabilityModule } from './ghidraHeadlessCapability.js';
16
17
  export { BaseCapabilityModule, type BaseCapabilityOptions, ToolSuiteBuilder, SharedUtilities } from './baseCapability.js';
17
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/capabilities/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,6BAA6B,EAAE,6BAA6B,EAAE,KAAK,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAGvI,OAAO,EAAE,0BAA0B,EAAE,KAAK,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AACzG,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACvF,OAAO,EAAE,sBAAsB,EAAE,KAAK,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAAE,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACvF,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AAC/E,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAInE,OAAO,EAAE,oBAAoB,EAAE,KAAK,qBAAqB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/capabilities/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,6BAA6B,EAAE,6BAA6B,EAAE,KAAK,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAGvI,OAAO,EAAE,0BAA0B,EAAE,KAAK,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AACzG,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACvF,OAAO,EAAE,sBAAsB,EAAE,KAAK,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAAE,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACvF,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AAC/E,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AAI/E,OAAO,EAAE,oBAAoB,EAAE,KAAK,qBAAqB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC"}
@@ -15,6 +15,7 @@ export { AflppCapabilityModule } from './aflppCapability.js';
15
15
  export { GdbCapabilityModule } from './gdbCapability.js';
16
16
  export { BinaryAnalysisCapabilityModule } from './binaryAnalysisCapability.js';
17
17
  export { PwntoolsCapabilityModule } from './pwntoolsCapability.js';
18
+ export { GhidraHeadlessCapabilityModule } from './ghidraHeadlessCapability.js';
18
19
  // CoworkCapabilityModule moved to ~/GitHub/erosolar-cowork (2026-05) —
19
20
  // productivity-assistant surface kept separate from the coding CLI.
20
21
  // See CLAUDE.md "Capability separation".
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/capabilities/index.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,OAAO,EAAE,6BAA6B,EAAE,6BAA6B,EAA6B,MAAM,8BAA8B,CAAC;AAEvI,4BAA4B;AAC5B,OAAO,EAAE,0BAA0B,EAAoC,MAAM,2BAA2B,CAAC;AACzG,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAA8B,MAAM,qBAAqB,CAAC;AACvF,OAAO,EAAE,sBAAsB,EAAgC,MAAM,uBAAuB,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAA6B,MAAM,oBAAoB,CAAC;AACpF,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAA8B,MAAM,qBAAqB,CAAC;AACvF,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AAC/E,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,uEAAuE;AACvE,oEAAoE;AACpE,yCAAyC;AACzC,OAAO,EAAE,oBAAoB,EAA8B,gBAAgB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/capabilities/index.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,OAAO,EAAE,6BAA6B,EAAE,6BAA6B,EAA6B,MAAM,8BAA8B,CAAC;AAEvI,4BAA4B;AAC5B,OAAO,EAAE,0BAA0B,EAAoC,MAAM,2BAA2B,CAAC;AACzG,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAA8B,MAAM,qBAAqB,CAAC;AACvF,OAAO,EAAE,sBAAsB,EAAgC,MAAM,uBAAuB,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAA6B,MAAM,oBAAoB,CAAC;AACpF,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAA8B,MAAM,qBAAqB,CAAC;AACvF,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AAC/E,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AAC/E,uEAAuE;AACvE,oEAAoE;AACpE,yCAAyC;AACzC,OAAO,EAAE,oBAAoB,EAA8B,gBAAgB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trenchwork/erosolar",
3
- "version": "1.1.26",
3
+ "version": "1.1.27",
4
4
  "description": "DeepSeek AI-powered CLI agent for code assistance and automation",
5
5
  "deepseek": {
6
6
  "rulebookSchema": "src/contracts/schemas/agent-rules.schema.json"