diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptProvider.java index a9f18480ab..d94e14cab1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptProvider.java @@ -133,4 +133,19 @@ public abstract class GhidraScriptProvider protected void writeBody(PrintWriter writer) { writer.println(getCommentCharacter() + "TODO Add User Code Here"); } + + /** + * Fixup a script name for searching in script directories. + * + *

This method is part of a poorly specified behavior that is due for future amendment, + * see {@link GhidraScriptUtil#fixupName(String)}. + * + * @param scriptName the name of the script, must end with this provider's extension + * @return a (relative) file path to the corresponding script + */ + @Deprecated + protected String fixupName(String scriptName) { + return scriptName; + } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptUtil.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptUtil.java index 5f2ac752b6..e386c800d9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptUtil.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptUtil.java @@ -28,6 +28,7 @@ import generic.jar.ResourceFile; import ghidra.app.plugin.core.osgi.BundleHost; import ghidra.app.plugin.core.osgi.OSGiException; import ghidra.app.plugin.core.script.GhidraScriptMgrPlugin; +import ghidra.app.util.headless.HeadlessAnalyzer; import ghidra.framework.Application; import ghidra.util.Msg; import ghidra.util.classfinder.ClassSearcher; @@ -139,8 +140,7 @@ public class GhidraScriptUtil { } catch (IOException e) { Msg.error(GhidraScriptUtil.class, - "Failed to find script in any script directory: " + sourceFile.toString(), - e); + "Failed to find script in any script directory: " + sourceFile.toString(), e); } return null; } @@ -293,14 +293,7 @@ public class GhidraScriptUtil { * @return the Ghidra script provider */ public static GhidraScriptProvider getProvider(ResourceFile scriptFile) { - String scriptFileName = scriptFile.getName().toLowerCase(); - - for (GhidraScriptProvider provider : getProviders()) { - if (scriptFileName.endsWith(provider.getExtension().toLowerCase())) { - return provider; - } - } - return null; + return findProvider(scriptFile.getName()); } /** @@ -310,13 +303,23 @@ public class GhidraScriptUtil { * @return true if a provider exists that can process the specified file */ public static boolean hasScriptProvider(ResourceFile scriptFile) { - String scriptFileName = scriptFile.getName().toLowerCase(); + return findProvider(scriptFile.getName()) != null; + } + + /** + * Find the provider whose extension matches the given filename extension. + * + * @param fileName name of script file + * @return the first matching provider or null if no provider matches + */ + private static GhidraScriptProvider findProvider(String fileName) { + fileName = fileName.toLowerCase(); for (GhidraScriptProvider provider : getProviders()) { - if (scriptFileName.endsWith(provider.getExtension().toLowerCase())) { - return true; + if (fileName.endsWith(provider.getExtension().toLowerCase())) { + return provider; } } - return false; + return null; } /** @@ -362,31 +365,35 @@ public class GhidraScriptUtil { } /** - * Fixup name issues, such as package parts in the name and inner class names. + * Fix script name issues for searching in script directories. + * If no provider can be identified, Java is assumed. * - *

This method can handle names with or without '.java' at the end; names with - * '$' (inner classes) and names with '.' characters for package separators + *

This method is part of a poorly specified behavior that is due for future amendment. * + *

It is used by {@link GhidraScript#runScript(String)} methods, + * {@link #createNewScript(String, String, ResourceFile, List)}, and by {@link HeadlessAnalyzer} for + * {@code preScript} and {@code postScript}. The intent was to allow some freedom in how a user specifies + * a script in two ways: 1) if the extension is omitted ".java" is assumed and 2) if a Java class name is + * given it's converted to a relative path. + * * @param name the name of the script - * @return the name as a '.java' file path (with '/'s and not '.'s) + * @return the name as a file path */ + @Deprecated static String fixupName(String name) { - if (name.endsWith(".java")) { - name = name.substring(0, name.length() - 5); + GhidraScriptProvider provider = findProvider(name); + // assume Java if no provider matched + if (provider == null) { + name = name + ".java"; + provider = findProvider(".java"); } - - String path = name.replace('.', '/'); - int innerClassIndex = path.indexOf('$'); - if (innerClassIndex != -1) { - path = path.substring(0, innerClassIndex); - } - return path + ".java"; + return provider.fixupName(name); } static ResourceFile findScriptFileInPaths(Collection scriptDirectories, - String filename) { + String name) { - String validatedName = fixupName(filename); + String validatedName = fixupName(name); for (ResourceFile resourceFile : scriptDirectories) { if (resourceFile.isDirectory()) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/JavaScriptProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/JavaScriptProvider.java index d58578db7b..39f99ffd91 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/JavaScriptProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/JavaScriptProvider.java @@ -164,4 +164,29 @@ public class JavaScriptProvider extends GhidraScriptProvider { return "//"; } + /** + * + * Fix script name for search in script directories, such as Java package parts in the name and inner class names. + * + *

This method can handle names with '$' (inner classes) and names with '.' + * characters for package separators + * + *

It is part of a poorly specified behavior that is due for future amendment, + * see {@link GhidraScriptUtil#fixupName(String)}. + * + * @param scriptName the name of the script + * @return the name as a '.java' file path (with '/'s and not '.'s) + */ + @Override + protected String fixupName(String scriptName) { + scriptName = scriptName.substring(0, scriptName.length() - 5); + + String path = scriptName.replace('.', '/'); + int innerClassIndex = path.indexOf('$'); + if (innerClassIndex != -1) { + path = path.substring(0, innerClassIndex); + } + return path + ".java"; + } + } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/script/GhidraScriptUtilTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/script/GhidraScriptUtilTest.java similarity index 78% rename from Ghidra/Features/Base/src/test.slow/java/ghidra/app/script/GhidraScriptUtilTest.java rename to Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/script/GhidraScriptUtilTest.java index b56bba04a0..c05490e0c3 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/script/GhidraScriptUtilTest.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/script/GhidraScriptUtilTest.java @@ -15,14 +15,23 @@ */ package ghidra.app.script; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; +import org.junit.Before; import org.junit.Test; import generic.test.AbstractGenericTest; +import ghidra.util.classfinder.ClassSearcher; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.ConsoleTaskMonitor; public class GhidraScriptUtilTest extends AbstractGenericTest { + @Before + public void setup() throws CancelledException { + ClassSearcher.search(false, new ConsoleTaskMonitor()); + } + @Test public void fixupName_WithExtension() { String input = "Bob.java"; @@ -58,4 +67,11 @@ public class GhidraScriptUtilTest extends AbstractGenericTest { String input = "a.b.c.Bob$InnerClass"; assertEquals(GhidraScriptUtil.fixupName(input), "a/b/c/Bob.java"); } + + @Test + public void fixupName_Python() { + String input = "Bob.py"; + assertEquals(GhidraScriptUtil.fixupName(input), "Bob.py"); + } + }