Remote Code exec in project import with mysql jdbc url attack
Description
OpenRefine is a powerful free, open source tool for working with messy data. Prior to version 3.7.5, a remote code execution vulnerability allows any unauthenticated user to execute code on the server. Version 3.7.5 has a patch for this issue.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
OpenRefine prior to 3.7.5 allows unauthenticated remote code execution via a crafted JDBC URL in the database import feature.
Vulnerability
The database extension in OpenRefine prior to version 3.7.5 constructs JDBC URLs by directly concatenating user-supplied host, port, and database name without proper escaping. This allows an attacker to inject arbitrary parameters into the connection string, enabling a JDBC URL attack. Specifically, when using a MySQL connector, an attacker can set parameters such as autoDeserialize and queryInterceptors to trigger deserialization of malicious objects [1][4].
Exploitation
An unauthenticated attacker can exploit this by setting up a rogue MySQL server and sending a crafted database connection request to OpenRefine. The request includes a database name containing the malicious parameters (e.g., test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor#). OpenRefine then connects to the attacker's MySQL server, which returns a serialized object. The server deserializes this object, leading to arbitrary code execution [4].
Impact
Successful exploitation grants the attacker remote code execution on the OpenRefine server with the privileges of the OpenRefine process. This can result in full compromise of the server, including data exfiltration, modification, or further lateral movement within the network [1][4].
Mitigation
The vulnerability is patched in OpenRefine version 3.7.5. The fix properly escapes JDBC URL components using the java.net.URI class, preventing parameter injection [2]. Users are strongly advised to upgrade to the latest version immediately.
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.openrefine:databaseMaven | < 3.7.5 | 3.7.5 |
Affected products
2- Range: <= 3.7.4
Patches
1693fde606d4bProperly escape JDBC URL components in database extension
6 files changed · +48 −26
extensions/database/src/com/google/refine/extension/database/DatabaseConfiguration.java+16 −0 modified@@ -29,6 +29,9 @@ package com.google.refine.extension.database; +import java.net.URI; +import java.net.URISyntaxException; + public class DatabaseConfiguration { private String connectionName; @@ -123,4 +126,17 @@ public String toString() { + databaseSchema + ", useSSL=" + useSSL + "]"; } + public URI toURI() { + try { + return new URI( + "jdbc:" + databaseType.toLowerCase(), + databaseHost + ((databasePort == 0) ? "" : (":" + databasePort)), + "/" + databaseName, + useSSL ? "useSSL=true" : null, + null + ); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } + } }
extensions/database/src/com/google/refine/extension/database/mariadb/MariaDBConnectionManager.java+1 −9 modified@@ -134,7 +134,7 @@ public Connection getConnection(DatabaseConfiguration databaseConfiguration, boo Class.forName(type.getClassPath()); DriverManager.setLoginTimeout(10); - String dbURL = getDatabaseUrl(databaseConfiguration); + String dbURL = databaseConfiguration.toURI().toString(); connection = DriverManager.getConnection(dbURL, databaseConfiguration.getDatabaseUser(), databaseConfiguration.getDatabasePassword()); @@ -162,14 +162,6 @@ public void shutdown() { logger.warn("Non-Managed connection could not be closed. Whoops!", e); } } - } - private static String getDatabaseUrl(DatabaseConfiguration dbConfig) { - - int port = dbConfig.getDatabasePort(); - return "jdbc:" + dbConfig.getDatabaseType().toLowerCase() + "://" + dbConfig.getDatabaseHost() - + ((port == 0) ? "" : (":" + port)) + "/" + dbConfig.getDatabaseName(); - - } }
extensions/database/src/com/google/refine/extension/database/mysql/MySQLConnectionManager.java+1 −8 modified@@ -129,7 +129,7 @@ public Connection getConnection(DatabaseConfiguration databaseConfiguration, boo return connection; } } - String dbURL = getDatabaseUrl(databaseConfiguration); + String dbURL = databaseConfiguration.toURI().toString(); Class.forName(type.getClassPath()); // logger.info("*** type.getClassPath() ::{}, {}**** ", type.getClassPath()); @@ -166,11 +166,4 @@ public void shutdown() { } - private String getDatabaseUrl(DatabaseConfiguration dbConfig) { - - int port = dbConfig.getDatabasePort(); - return "jdbc:" + dbConfig.getDatabaseType() + "://" + dbConfig.getDatabaseHost() - + ((port == 0) ? "" : (":" + port)) + "/" + dbConfig.getDatabaseName() + "?useSSL=" + dbConfig.isUseSSL(); - - } }
extensions/database/src/com/google/refine/extension/database/pgsql/PgSQLConnectionManager.java+1 −8 modified@@ -136,7 +136,7 @@ public Connection getConnection(DatabaseConfiguration databaseConfiguration, boo Class.forName(type.getClassPath()); DriverManager.setLoginTimeout(10); - String dbURL = getDatabaseUrl(databaseConfiguration); + String dbURL = databaseConfiguration.toURI().toString(); connection = DriverManager.getConnection(dbURL, databaseConfiguration.getDatabaseUser(), databaseConfiguration.getDatabasePassword()); @@ -165,11 +165,4 @@ public void shutdown() { } - private static String getDatabaseUrl(DatabaseConfiguration dbConfig) { - - int port = dbConfig.getDatabasePort(); - return "jdbc:" + dbConfig.getDatabaseType().toLowerCase() + "://" + dbConfig.getDatabaseHost() - + ((port == 0) ? "" : (":" + port)) + "/" + dbConfig.getDatabaseName(); - - } }
extensions/database/src/com/google/refine/extension/database/sqlite/SQLiteConnectionManager.java+8 −1 modified@@ -35,6 +35,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.net.URI; +import java.net.URISyntaxException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; @@ -66,7 +68,12 @@ public static SQLiteConnectionManager getInstance() { } public static String getDatabaseUrl(DatabaseConfiguration dbConfig) { - return "jdbc:" + dbConfig.getDatabaseType().toLowerCase() + ":" + dbConfig.getDatabaseName(); + try { + URI uri = new URI("jdbc:" + dbConfig.getDatabaseType().toLowerCase(), dbConfig.getDatabaseName(), null); + return uri.toASCIIString(); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } } /**
extensions/database/tests/src/com/google/refine/extension/database/DatabaseConfigurationTest.java+21 −0 added@@ -0,0 +1,21 @@ +package com.google.refine.extension.database; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +public class DatabaseConfigurationTest { + + @Test + public void testToURI() { + DatabaseConfiguration config = new DatabaseConfiguration(); + config.setDatabaseType("mysql"); + config.setDatabaseHost("my.host"); + // maliciously crafted database name which attempts to enable local file reads for an exploit + config.setDatabaseName("test?allowLoadLocalInfile=true#"); + + String url = config.toURI().toString(); + // the database name is escaped, preventing the exploit + assertEquals(url, "jdbc:mysql://my.host/test%3FallowLoadLocalInfile=true%23"); + } +}
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-p3r5-x3hr-gpg5ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-41887ghsaADVISORY
- github.com/OpenRefine/OpenRefine/commit/693fde606d4b5b78b16391c29d110389eb605511ghsax_refsource_MISCWEB
- github.com/OpenRefine/OpenRefine/security/advisories/GHSA-p3r5-x3hr-gpg5ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.