From 9cf1eef96dce376c6fe8e46121f5667c0568f21b Mon Sep 17 00:00:00 2001 From: James <49045138+ghidracadabra@users.noreply.github.com> Date: Wed, 15 Apr 2026 16:21:37 -0400 Subject: [PATCH] GP-6706 h2 database name checks --- .../features/bsim/query/BSimServerInfo.java | 19 ++- .../file/BSimH2FileDBConnectionManager.java | 4 +- .../bsim/query/client/BSimServerInfoTest.java | 124 ++++++++++++++++++ 3 files changed, 138 insertions(+), 9 deletions(-) create mode 100644 Ghidra/Features/BSim/src/test/java/ghidra/features/bsim/query/client/BSimServerInfoTest.java diff --git a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/BSimServerInfo.java b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/BSimServerInfo.java index 3cb1182f16..6ac7969f29 100644 --- a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/BSimServerInfo.java +++ b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/BSimServerInfo.java @@ -19,6 +19,8 @@ import java.io.Closeable; import java.net.*; import java.nio.charset.StandardCharsets; import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.apache.commons.dbcp2.BasicDataSource; import org.apache.commons.lang3.StringUtils; @@ -58,6 +60,9 @@ public class BSimServerInfo implements Comparable { private String shortDbName; // lazy: short DB Name + private static String BAD_H2_CHARS = "';\""; + private static Pattern BAD_H2_CHARS_PATTERN = Pattern.compile(".*[" + BAD_H2_CHARS + "].*"); + /** * Construct a new {@link BSimServerInfo} object * @@ -196,13 +201,7 @@ public class BSimServerInfo implements Comparable { } path = urlDecode(checkURLField(path, "path")); if (dbType == DBType.file) { - if (path.endsWith("/")) { - throw new IllegalArgumentException("Missing DB filepath in URL: " + url); - } - if (!path.endsWith(H2_FILE_EXTENSION)) { - path += H2_FILE_EXTENSION; - } - // TODO: handle Windows path with drive letter - need to remove leading '/' + path = cleanupFilename(path); } else if (path.contains("/")) { throw new IllegalArgumentException("Invalid dbName in URL: " + path); @@ -246,6 +245,12 @@ public class BSimServerInfo implements Comparable { private static String cleanupFilename(String name) { // transform dbName into acceptable H2 DB file path + + Matcher m = BAD_H2_CHARS_PATTERN.matcher(name); + if (m.matches()) { + throw new IllegalArgumentException("Bad character in H2 database path. " + + "Disallowed characters: " + BAD_H2_CHARS); + } String dbName = name.trim(); dbName = dbName.replace("\\", "/"); if ((!dbName.startsWith("/") && !isWindowsFilePath(dbName)) || dbName.endsWith("/")) { diff --git a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/file/BSimH2FileDBConnectionManager.java b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/file/BSimH2FileDBConnectionManager.java index b04734f187..c7cc63708b 100644 --- a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/file/BSimH2FileDBConnectionManager.java +++ b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/file/BSimH2FileDBConnectionManager.java @@ -246,7 +246,6 @@ public class BSimH2FileDBConnectionManager { // Remove leading '/' before drive letter dbName = dbName.substring(1); } - return "jdbc:h2:" + dbName; } @@ -257,6 +256,7 @@ public class BSimH2FileDBConnectionManager { // Set database URL // NOTE: keywords 'key' and 'value' are used by KeyValueTable as column names + bds.setUrl(getH2FileUrl() + ";MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;DEFAULT_NULL_ORDERING=HIGH;NON_KEYWORDS=key,value"); @@ -276,7 +276,7 @@ public class BSimH2FileDBConnectionManager { /** * Get a connection to the H2 file database. - * It is important to note that if the database does not exist and empty one will + * It is important to note that if the database does not exist an empty one will * be created. The {@link #exists()} method should be used to check for the database * existence prior to connecting the first time. * @return database connection diff --git a/Ghidra/Features/BSim/src/test/java/ghidra/features/bsim/query/client/BSimServerInfoTest.java b/Ghidra/Features/BSim/src/test/java/ghidra/features/bsim/query/client/BSimServerInfoTest.java new file mode 100644 index 0000000000..f6a4825b22 --- /dev/null +++ b/Ghidra/Features/BSim/src/test/java/ghidra/features/bsim/query/client/BSimServerInfoTest.java @@ -0,0 +1,124 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.features.bsim.query.client; + +import static org.junit.Assert.*; + +import java.net.URI; +import java.net.URL; + +import org.junit.Test; + +import ghidra.features.bsim.query.BSimServerInfo; +import ghidra.features.bsim.query.BSimServerInfo.DBType; +import ghidra.test.AbstractGhidraHeadlessIntegrationTest; + +public class BSimServerInfoTest extends AbstractGhidraHeadlessIntegrationTest { + + @Test + public void testBadFilename1() { + Exception e = assertThrows(IllegalArgumentException.class, () -> { + BSimServerInfo h2Info = new BSimServerInfo("C:\\dir\\test\"test"); + assertNull(h2Info); + }); + assertTrue(e.getMessage().contains("characters")); + } + + @Test + public void testBadFilename2() { + Exception e = assertThrows(IllegalArgumentException.class, () -> { + BSimServerInfo h2Info = new BSimServerInfo(DBType.file, "user", 1, "/test'test"); + assertNull(h2Info); + }); + assertTrue(e.getMessage().contains("characters")); + } + + @Test + public void testBadFilename3() { + Exception e = assertThrows(IllegalArgumentException.class, () -> { + URL url = new URI("file:/test;test").toURL(); + BSimServerInfo h2Info = new BSimServerInfo(url); + assertNull(h2Info); + }); + assertTrue(e.getMessage().contains("characters")); + } + + @Test + public void testBadDirectory1() { + Exception e = assertThrows(IllegalArgumentException.class, () -> { + BSimServerInfo h2Info = new BSimServerInfo("C:\\dir;1\\test"); + assertNull(h2Info); + }); + assertTrue(e.getMessage().contains("characters")); + } + + @Test + public void testBadDirectory2() { + Exception e = assertThrows(IllegalArgumentException.class, () -> { + BSimServerInfo h2Info = new BSimServerInfo("C:\\dir'1\\test"); + assertNull(h2Info); + }); + assertTrue(e.getMessage().contains("characters")); + } + + @Test + public void testBadDirectory3() { + Exception e = assertThrows(IllegalArgumentException.class, () -> { + BSimServerInfo h2Info = new BSimServerInfo("/dir1/dir\"/2/testdb"); + assertNull(h2Info); + }); + assertTrue(e.getMessage().contains("characters")); + } + + @Test + public void testBadPathWindows() { + Exception e = assertThrows(IllegalArgumentException.class, () -> { + BSimServerInfo h2Info = new BSimServerInfo("C:\\directory\\"); + assertNull(h2Info); + }); + assertTrue(e.getMessage().contains("Invalid absolute file path")); + } + + @Test + public void testBadPathLinux() { + Exception e = assertThrows(IllegalArgumentException.class, () -> { + BSimServerInfo h2Info = new BSimServerInfo("/dir1/dir2/"); + assertNull(h2Info); + }); + assertTrue(e.getMessage().contains("Invalid absolute file path")); + } + + @Test + public void testBadPathLinux2() { + Exception e = assertThrows(IllegalArgumentException.class, () -> { + BSimServerInfo h2Info = new BSimServerInfo("dir1/dir2/dir3/file"); + assertNull(h2Info); + }); + assertTrue(e.getMessage().contains("Invalid absolute file path")); + } + + @Test + public void testPaths() { + BSimServerInfo h2Info = new BSimServerInfo("/dir1/dir2/dir3/file"); + assertEquals("/dir1/dir2/dir3/file.mv.db", h2Info.getDBName()); + h2Info = new BSimServerInfo("/dir1/dir2/dir3/file.mv.db"); + assertEquals("/dir1/dir2/dir3/file.mv.db", h2Info.getDBName()); + h2Info = new BSimServerInfo("C:\\dir1 2\\file3 4"); + assertEquals("C:/dir1 2/file3 4.mv.db", h2Info.getDBName()); + + } + +}