From 1024a1baa761e226275da373768c62e085f92770 Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Wed, 7 Apr 2021 16:01:16 -0400 Subject: [PATCH] GP-843 - PR 2846 - Updated after merge; added tests for certify header processing --- .../java/ghidra/app/script/ScriptInfo.java | 122 ++++++++++-------- .../core/script/JavaScriptInfoTest.java | 56 +++++++- .../ghidra/python/PythonScriptInfoTest.java | 62 +++++++-- 3 files changed, 166 insertions(+), 74 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/ScriptInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/ScriptInfo.java index a4686aa309..49bca25d09 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/ScriptInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/ScriptInfo.java @@ -189,75 +189,39 @@ public class ScriptInfo { // Note that skipping certification header presumes that the header // is intact with an appropriate start and end String certifyHeaderStart = provider.getCertifyHeaderStart(); - String certifyHeaderEnd = provider.getCertifyHeaderEnd(); - String certifyHeaderBodyPrefix = provider.getCertificationBodyPrefix(); boolean allowCertifyHeader = (certifyHeaderStart != null); - boolean skipCertifyHeader = false; - BufferedReader reader = null; - try { - StringBuffer buffer = new StringBuffer(); + try (BufferedReader reader = + new BufferedReader(new InputStreamReader(sourceFile.getInputStream()))) { + StringBuilder buffer = new StringBuilder(); boolean hitAtSign = false; - reader = new BufferedReader(new InputStreamReader(sourceFile.getInputStream())); while (true) { String line = reader.readLine(); if (line == null) { break; } - if (allowCertifyHeader) { - // Skip past certification header if found - if (skipCertifyHeader) { - String trimLine = line.trim(); - if (trimLine.startsWith(certifyHeaderEnd)) { - allowCertifyHeader = false; - skipCertifyHeader = false; - continue; - } - if (certifyHeaderBodyPrefix == null || - trimLine.startsWith(certifyHeaderBodyPrefix)) { - continue; // skip certification header body - } - // broken certification header - unexpected line - Msg.error(this, - "Script contains invalid certification header: " + getName()); - allowCertifyHeader = false; - skipCertifyHeader = false; - } - else if (line.startsWith(certifyHeaderStart)) { - skipCertifyHeader = true; - continue; - } + if (allowCertifyHeader && skipCertifyHeader(reader, line)) { + allowCertifyHeader = false; + continue; } - Pattern blockStart = provider.getBlockCommentStart(); - Pattern blockEnd = provider.getBlockCommentEnd(); - - if (blockStart != null && blockEnd != null) { - Matcher startMatcher = blockStart.matcher(line); - if (startMatcher.find()) { - int last_offset = startMatcher.end(); - while (line != null && !blockEnd.matcher(line).find(last_offset)) { - line = reader.readLine(); - last_offset = 0; - } - continue; - } + if (parseBlockComment(reader, line)) { + allowCertifyHeader = false; + continue; // read block comment; move to next line } if (line.startsWith(commentPrefix)) { allowCertifyHeader = false; line = line.substring(commentPrefix.length()).trim(); - if (line.startsWith("@")) { hitAtSign = true; parseMetaDataLine(line); } else if (!hitAtSign) { - buffer.append(line); - buffer.append(' '); - buffer.append('\n'); + // only consume line comments that come before metadata + buffer.append(line).append(' ').append('\n'); } } else if (line.trim().isEmpty()) { @@ -274,16 +238,62 @@ public class ScriptInfo { catch (IOException e) { Msg.debug(this, "Unexpected exception reading script: " + sourceFile, e); } - finally { - if (reader != null) { - try { - reader.close(); - } - catch (IOException e) { - // don't care; we tried - } - } + } + + private boolean skipCertifyHeader(BufferedReader reader, String line) throws IOException { + + // Note that skipping certification header presumes that the header + // is intact with an appropriate start and end + String certifyHeaderStart = provider.getCertifyHeaderStart(); + if (certifyHeaderStart == null) { + return false; } + + if (!line.startsWith(certifyHeaderStart)) { + return false; + } + + String certifyHeaderEnd = provider.getCertifyHeaderEnd(); + String certifyHeaderBodyPrefix = provider.getCertificationBodyPrefix(); + certifyHeaderBodyPrefix = certifyHeaderBodyPrefix == null ? "" : certifyHeaderBodyPrefix; + + while ((line = reader.readLine()) != null) { + + // Skip past certification header if found + String trimLine = line.trim(); + if (trimLine.startsWith(certifyHeaderEnd)) { + return true; + } + + if (trimLine.startsWith(certifyHeaderBodyPrefix)) { + continue; // skip certification header body + } + + // broken certification header - unexpected line + Msg.error(this, + "Script contains invalid certification header: " + getName()); + } + return false; + } + + private boolean parseBlockComment(BufferedReader reader, String line) throws IOException { + Pattern blockStart = provider.getBlockCommentStart(); + Pattern blockEnd = provider.getBlockCommentEnd(); + + if (blockStart == null || blockEnd == null) { + return false; + } + + Matcher startMatcher = blockStart.matcher(line); + if (startMatcher.find()) { + int lastOffset = startMatcher.end(); + while (line != null && !blockEnd.matcher(line).find(lastOffset)) { + line = reader.readLine(); + lastOffset = 0; + } + return true; + } + return false; } private void parseMetaDataLine(String line) { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/JavaScriptInfoTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/JavaScriptInfoTest.java index e62263d254..012dda1cf3 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/JavaScriptInfoTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/JavaScriptInfoTest.java @@ -15,8 +15,7 @@ */ package ghidra.app.plugin.core.script; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; import java.io.IOException; @@ -62,7 +61,8 @@ public class JavaScriptInfoTest extends AbstractGhidraScriptMgrPluginTest { " // for a blank class, it sure is well documented!", "}"); //@formatter:on - } catch (IOException e) { + } + catch (IOException e) { fail("couldn't create a test script: " + e.getMessage()); } @@ -101,7 +101,49 @@ public class JavaScriptInfoTest extends AbstractGhidraScriptMgrPluginTest { " // just a blank class, nothing to see here", "}"); //@formatter:on - } catch (IOException e) { + } + catch (IOException e) { + fail("couldn't create a test script: " + e.getMessage()); + } + + ScriptInfo info = GhidraScriptUtil.newScriptInfo(scriptFile); + assertEquals(description + " \n", info.getDescription()); + + String[] actualCategory = info.getCategory(); + assertEquals(1, actualCategory.length); + assertEquals(category, actualCategory[0]); + } + + @Test + public void testJavaScriptWithBlockCommentAndCertifyHeader() { + String description = "Script with a block comment at the top."; + String category = "Test"; + ResourceFile scriptFile = null; + + try { + //@formatter:off + scriptFile = createTempScriptFileWithLines( + "/* ###" + + " * IP: GHIDRA" + + " * " + + " * Some license text..." + + " * you may not use this file except in compliance with the License." + + " * " + + " * blah blah blah" + + " */" + + " " + + "/*", + " * This is a test block comment. It will be ignored.", + " * @category NotTheRealCategory", + " */", + "//" + description, + "//@category " + category, + "class BlockCommentScript {", + " // just a blank class, nothing to see here", + "}"); + //@formatter:on + } + catch (IOException e) { fail("couldn't create a test script: " + e.getMessage()); } @@ -128,7 +170,8 @@ public class JavaScriptInfoTest extends AbstractGhidraScriptMgrPluginTest { " // just a blank class, nothing to see here", "}"); //@formatter:on - } catch (IOException e) { + } + catch (IOException e) { fail("couldn't create a test script: " + e.getMessage()); } @@ -156,7 +199,8 @@ public class JavaScriptInfoTest extends AbstractGhidraScriptMgrPluginTest { " // just a blank class, nothing to see here", "}"); //@formatter:on - } catch (IOException e) { + } + catch (IOException e) { fail("couldn't create a test script: " + e.getMessage()); } diff --git a/Ghidra/Features/Python/src/test.slow/java/ghidra/python/PythonScriptInfoTest.java b/Ghidra/Features/Python/src/test.slow/java/ghidra/python/PythonScriptInfoTest.java index b3650454ea..f960a16b67 100644 --- a/Ghidra/Features/Python/src/test.slow/java/ghidra/python/PythonScriptInfoTest.java +++ b/Ghidra/Features/Python/src/test.slow/java/ghidra/python/PythonScriptInfoTest.java @@ -15,20 +15,15 @@ */ package ghidra.python; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; -import java.io.File; -import java.io.IOException; -import java.io.PrintWriter; +import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import javax.swing.KeyStroke; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.*; import generic.jar.ResourceFile; import ghidra.app.plugin.core.osgi.BundleHost; @@ -82,7 +77,8 @@ public class PythonScriptInfoTest extends AbstractGhidraHeadedIntegrationTest { "#@importpackage " + importPackage, "print('for a blank class, it sure is well documented!')"); //@formatter:on - } catch (IOException e) { + } + catch (IOException e) { fail("couldn't create a test script: " + e.getMessage()); } @@ -119,7 +115,47 @@ public class PythonScriptInfoTest extends AbstractGhidraHeadedIntegrationTest { "#@category " + category, "print 'hello!'"); //@formatter:on - } catch (IOException e) { + } + catch (IOException e) { + fail("couldn't create a test script: " + e.getMessage()); + } + + ScriptInfo info = GhidraScriptUtil.newScriptInfo(scriptFile); + assertEquals(description + " \n", info.getDescription()); + + String[] actualCategory = info.getCategory(); + assertEquals(1, actualCategory.length); + assertEquals(category, actualCategory[0]); + } + + @Test + public void testPythonScriptWithBlockCommentAndCertifyHeader() { + String description = "Script with a block comment at the top."; + String category = "Test"; + ResourceFile scriptFile = null; + + try { + //@formatter:off + scriptFile = createTempPyScriptFileWithLines( + "## ###" + + "# IP: GHIDRA" + + "# " + + "# Some license text..." + + "# you may not use this file except in compliance with the License." + + "# " + + "# blah blah blah" + + "##" + + "" + + "'''", + "This is a test block comment. It will be ignored.", + "@category NotTheRealCategory", + "'''", + "#" + description, + "#@category " + category, + "print 'hello!'"); + //@formatter:on + } + catch (IOException e) { fail("couldn't create a test script: " + e.getMessage()); } @@ -144,7 +180,8 @@ public class PythonScriptInfoTest extends AbstractGhidraHeadedIntegrationTest { "#@category " + category, "print 'hello!'"); //@formatter:on - } catch (IOException e) { + } + catch (IOException e) { fail("couldn't create a test script: " + e.getMessage()); } @@ -170,7 +207,8 @@ public class PythonScriptInfoTest extends AbstractGhidraHeadedIntegrationTest { "#@category " + category, "print 'hello!'"); //@formatter:on - } catch (IOException e) { + } + catch (IOException e) { fail("couldn't create a test script: " + e.getMessage()); }