fix: bump version to 0.1.7 and update command filters in Ghidra CLI

This commit is contained in:
Alexander Kiselev
2026-02-22 18:37:41 -08:00
parent 1614c24f76
commit 23c2b697dc
5 changed files with 53 additions and 21 deletions
Generated
+1 -1
View File
@@ -739,7 +739,7 @@ dependencies = [
[[package]]
name = "ghidra-cli"
version = "0.1.6"
version = "0.1.7"
dependencies = [
"anyhow",
"assert_cmd",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "ghidra-cli"
version = "0.1.6"
version = "0.1.7"
edition = "2021"
authors = ["Alexander Kiselev"]
description = "Rust CLI to run Ghidra headless for reverse engineering with Claude Code and other agents"
+34 -2
View File
@@ -217,7 +217,7 @@ public class GhidraCliBridge extends GhidraScript {
case "type_create": return handleTypeCreate(args);
case "type_apply": return handleTypeApply(args);
// Comment commands
case "comment_list": return handleCommentList();
case "comment_list": return handleCommentList(args);
case "comment_get": return handleCommentGet(args);
case "comment_set": return handleCommentSet(args);
case "comment_delete": return handleCommentDelete(args);
@@ -519,6 +519,7 @@ public class GhidraCliBridge extends GhidraScript {
}
int limit = getArgInt(args, "limit", 0);
String nameFilter = getArgString(args, "filter");
JsonArray strings = new JsonArray();
Listing listing = currentProgram.getListing();
@@ -532,6 +533,11 @@ public class GhidraCliBridge extends GhidraScript {
if (data.hasStringValue()) {
try {
String val = data.getValue().toString();
if (nameFilter != null && !val.toLowerCase().contains(nameFilter.toLowerCase())) {
continue;
}
JsonObject strData = new JsonObject();
strData.addProperty("address", data.getAddress().toString());
strData.addProperty("value", val);
@@ -1435,13 +1441,17 @@ public class GhidraCliBridge extends GhidraScript {
private JsonObject handleSymbolList(JsonObject args) {
if (currentProgram == null) return errorResult("No program loaded");
int limit = getArgInt(args, "limit", 0);
String nameFilter = getArgString(args, "filter");
SymbolTable symbolTable = currentProgram.getSymbolTable();
JsonArray symbols = new JsonArray();
int count = 0;
SymbolIterator symIter = symbolTable.getAllSymbols(true);
while (symIter.hasNext()) {
if (limit > 0 && count >= limit) break;
Symbol symbol = symIter.next();
String name = symbol.getName();
@@ -1456,6 +1466,7 @@ public class GhidraCliBridge extends GhidraScript {
symData.addProperty("source", symbol.getSource().toString());
symData.addProperty("is_primary", symbol.isPrimary());
symbols.add(symData);
count++;
}
JsonObject result = new JsonObject();
@@ -1645,6 +1656,7 @@ public class GhidraCliBridge extends GhidraScript {
if (currentProgram == null) return errorResult("No program loaded");
int limit = getArgInt(args, "limit", 0);
String nameFilter = getArgString(args, "filter");
DataTypeManager dtm = currentProgram.getDataTypeManager();
JsonArray types = new JsonArray();
@@ -1653,6 +1665,11 @@ public class GhidraCliBridge extends GhidraScript {
while (dtIter.hasNext()) {
DataType dt = dtIter.next();
if (limit > 0 && count >= limit) break;
if (nameFilter != null && !dt.getName().toLowerCase().contains(nameFilter.toLowerCase())) {
continue;
}
JsonObject typeData = new JsonObject();
typeData.addProperty("name", dt.getName());
typeData.addProperty("path", dt.getPathName());
@@ -1818,12 +1835,16 @@ public class GhidraCliBridge extends GhidraScript {
}
}
private JsonObject handleCommentList() {
private JsonObject handleCommentList(JsonObject args) {
if (currentProgram == null) return errorResult("No program loaded");
int limit = getArgInt(args, "limit", 0);
String nameFilter = getArgString(args, "filter");
Listing listing = currentProgram.getListing();
Memory memory = currentProgram.getMemory();
JsonArray comments = new JsonArray();
int count = 0;
int[][] commentTypes = {
{CodeUnit.EOL_COMMENT},
@@ -1834,6 +1855,8 @@ public class GhidraCliBridge extends GhidraScript {
String[] commentNames = {"EOL", "PRE", "POST", "PLATE"};
for (MemoryBlock block : memory.getBlocks()) {
if (limit > 0 && count >= limit) break;
ghidra.program.model.address.AddressSet addrSet =
new ghidra.program.model.address.AddressSet(block.getStart(), block.getEnd());
@@ -1841,18 +1864,27 @@ public class GhidraCliBridge extends GhidraScript {
listing.getCommentAddressIterator(addrSet, true);
while (addrIter.hasNext()) {
if (limit > 0 && count >= limit) break;
Address addr = addrIter.next();
CodeUnit cu = listing.getCodeUnitAt(addr);
if (cu == null) continue;
for (int i = 0; i < commentNames.length; i++) {
if (limit > 0 && count >= limit) break;
String text = cu.getComment(commentTypes[i][0]);
if (text != null) {
if (nameFilter != null && !text.toLowerCase().contains(nameFilter.toLowerCase())) {
continue;
}
JsonObject commentObj = new JsonObject();
commentObj.addProperty("address", addr.toString());
commentObj.addProperty("type", commentNames[i]);
commentObj.addProperty("text", text);
comments.add(commentObj);
count++;
}
}
}
+8 -8
View File
@@ -121,8 +121,8 @@ impl BridgeClient {
}
/// List strings.
pub fn list_strings(&self, limit: Option<usize>) -> Result<serde_json::Value> {
self.send_command("list_strings", Some(json!({"limit": limit})))
pub fn list_strings(&self, limit: Option<usize>, filter: Option<String>) -> Result<serde_json::Value> {
self.send_command("list_strings", Some(json!({"limit": limit, "filter": filter})))
}
/// List imports.
@@ -184,8 +184,8 @@ impl BridgeClient {
// === Extended commands (symbols, types, comments, etc.) ===
pub fn symbol_list(&self, filter: Option<&str>) -> Result<serde_json::Value> {
self.send_command("symbol_list", Some(json!({"filter": filter})))
pub fn symbol_list(&self, limit: Option<usize>, filter: Option<&str>) -> Result<serde_json::Value> {
self.send_command("symbol_list", Some(json!({"limit": limit, "filter": filter})))
}
pub fn symbol_get(&self, name: &str) -> Result<serde_json::Value> {
@@ -210,8 +210,8 @@ impl BridgeClient {
)
}
pub fn type_list(&self, limit: Option<usize>) -> Result<serde_json::Value> {
self.send_command("type_list", Some(json!({"limit": limit})))
pub fn type_list(&self, limit: Option<usize>, filter: Option<&str>) -> Result<serde_json::Value> {
self.send_command("type_list", Some(json!({"limit": limit, "filter": filter})))
}
pub fn type_get(&self, name: &str) -> Result<serde_json::Value> {
@@ -229,8 +229,8 @@ impl BridgeClient {
)
}
pub fn comment_list(&self) -> Result<serde_json::Value> {
self.send_command("comment_list", None)
pub fn comment_list(&self, limit: Option<usize>, filter: Option<&str>) -> Result<serde_json::Value> {
self.send_command("comment_list", Some(json!({"limit": limit, "filter": filter})))
}
pub fn comment_get(&self, address: &str) -> Result<serde_json::Value> {
+9 -9
View File
@@ -621,8 +621,8 @@ fn execute_via_bridge(
}))
}
Commands::Query(args) => match args.data_type.as_str() {
"functions" => client.list_functions(args.limit.or(default_limit), None),
"strings" => client.list_strings(args.limit.or(default_limit)),
"functions" => client.list_functions(args.limit.or(default_limit), args.filter.clone()),
"strings" => client.list_strings(args.limit.or(default_limit), args.filter.clone()),
"imports" => client.list_imports(),
"exports" => client.list_exports(),
"memory" => client.memory_map(),
@@ -633,7 +633,7 @@ fn execute_via_bridge(
use cli::FunctionCommands;
match cmd {
FunctionCommands::List(opts) => {
client.list_functions(opts.limit.or(default_limit), None)
client.list_functions(opts.limit.or(default_limit), opts.filter.clone())
}
FunctionCommands::Decompile(args) => client.decompile(args.target.clone()),
FunctionCommands::Get(args) => {
@@ -667,7 +667,7 @@ fn execute_via_bridge(
Commands::Strings(cmd) => {
use cli::StringsCommands;
match cmd {
StringsCommands::List(opts) => client.list_strings(opts.limit.or(default_limit)),
StringsCommands::List(opts) => client.list_strings(opts.limit.or(default_limit), opts.filter.clone()),
StringsCommands::Refs(args) => client.xrefs_to(args.string.clone()),
}
}
@@ -703,9 +703,9 @@ fn execute_via_bridge(
DumpCommands::Imports(_) => client.list_imports(),
DumpCommands::Exports(_) => client.list_exports(),
DumpCommands::Functions(opts) => {
client.list_functions(opts.limit.or(default_limit), None)
client.list_functions(opts.limit.or(default_limit), opts.filter.clone())
}
DumpCommands::Strings(opts) => client.list_strings(opts.limit.or(default_limit)),
DumpCommands::Strings(opts) => client.list_strings(opts.limit.or(default_limit), opts.filter.clone()),
}
}
Commands::Summary(_) => client.program_info(),
@@ -744,7 +744,7 @@ fn execute_via_bridge(
Commands::Symbol(cmd) => {
use cli::SymbolCommands;
match cmd {
SymbolCommands::List(_) => client.symbol_list(None),
SymbolCommands::List(opts) => client.symbol_list(opts.limit.or(default_limit), opts.filter.as_deref()),
SymbolCommands::Get(args) => client.symbol_get(&args.name),
SymbolCommands::Create(args) => client.symbol_create(&args.address, &args.name),
SymbolCommands::Delete(args) => client.symbol_delete(&args.name),
@@ -756,7 +756,7 @@ fn execute_via_bridge(
Commands::Type(cmd) => {
use cli::TypeCommands;
match cmd {
TypeCommands::List(opts) => client.type_list(opts.limit.or(default_limit)),
TypeCommands::List(opts) => client.type_list(opts.limit.or(default_limit), opts.filter.as_deref()),
TypeCommands::Get(args) => client.type_get(&args.name),
TypeCommands::Create(args) => client.type_create(&args.definition),
TypeCommands::Apply(args) => client.type_apply(&args.address, &args.type_name),
@@ -765,7 +765,7 @@ fn execute_via_bridge(
Commands::Comment(cmd) => {
use cli::CommentCommands;
match cmd {
CommentCommands::List(_) => client.comment_list(),
CommentCommands::List(opts) => client.comment_list(opts.limit.or(default_limit), opts.filter.as_deref()),
CommentCommands::Get(args) => client.comment_get(&args.address),
CommentCommands::Set(args) => {
client.comment_set(&args.address, &args.text, args.comment_type.as_deref())