don't assume that scripts are Java, fixes #2562

This commit is contained in:
Jason P. Leasure 2021-01-04 08:50:47 -05:00
parent b9129348fc
commit e4e15cdb9d
4 changed files with 93 additions and 30 deletions

View file

@ -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.
*
* <p>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;
}
}

View file

@ -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.
*
* <p>This method can handle names with or without '.java' at the end; names with
* '$' (inner classes) and names with '.' characters for package separators
* <p>This method is part of a poorly specified behavior that is due for future amendment.
*
* <p>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<ResourceFile> scriptDirectories,
String filename) {
String name) {
String validatedName = fixupName(filename);
String validatedName = fixupName(name);
for (ResourceFile resourceFile : scriptDirectories) {
if (resourceFile.isDirectory()) {

View file

@ -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.
*
* <p>This method can handle names with '$' (inner classes) and names with '.'
* characters for package separators
*
* <p>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";
}
}

View file

@ -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");
}
}