Bug#1116986: bookworm-pu: package openrefine/3.6.2-2+deb12u3
Package: release.debian.org
Severity: normal
Tags: bookworm
User: release.debian.org@packages.debian.org
Usertags: pu
X-Debbugs-Cc: openrefine@packages.debian.org, apo@debian.org
Control: affects -1 + src:openrefine
[ Reason ]
Fixing no-dsa CVE in bookworm. I have backported patches to fix six
CVE in bookworm. The patch for CVE-2024-47879 introduced a regression
which is the reason why it is currently not included. I may follow up
later but the issue is not that severe IMO.
[ Impact ]
OpenRefine in bookworm would continue to be vulnerable.
[ Tests ]
I have tested the update manually and run the maven test suite. I
didn't spot any regressions.
[ Risks ]
Nothing I would be aware of. OpenRefine is a standalone application
and CVE fixes should not affect other software on the user's system.
[ Checklist ]
[x] *all* changes are documented in the d/changelog
[x] I reviewed all changes and I approve them
[x] attach debdiff against the package in (old)stable
[x] the issue is verified as fixed in unstable
[ Changes ]
Applied six patches to fix open CVE in bookworm. See also the
openrefine.debdiff.
Thank you
diff -Nru openrefine-3.6.2/debian/changelog openrefine-3.6.2/debian/changelog
--- openrefine-3.6.2/debian/changelog 2023-10-04 15:02:45.000000000 +0200
+++ openrefine-3.6.2/debian/changelog 2025-09-28 00:52:50.000000000 +0200
@@ -1,3 +1,13 @@
+openrefine (3.6.2-2+deb12u3) bookworm; urgency=medium
+
+ * Fix CVE-2024-23833, CVE-2024-47878, CVE-2024-47880, CVE-2024-47881,
+ CVE-2024-47882 and CVE-2024-49760.
+ OpenRefine is a free, open source tool for data processing. Users could be
+ tricked into opening malicious websites which then enabled attackers to run
+ arbitrary code on the server due to improper escaping or code restrictions.
+
+ -- Markus Koschany <apo@debian.org> Sun, 28 Sep 2025 00:52:50 +0200
+
openrefine (3.6.2-2+deb12u2) bookworm; urgency=medium
* Fix CVE-2023-41887 and CVE-2023-41886:
diff -Nru openrefine-3.6.2/debian/patches/build.patch openrefine-3.6.2/debian/patches/build.patch
--- openrefine-3.6.2/debian/patches/build.patch 2023-10-04 15:02:45.000000000 +0200
+++ openrefine-3.6.2/debian/patches/build.patch 2025-09-28 00:52:50.000000000 +0200
@@ -7,7 +7,7 @@
1 file changed, 4 insertions(+)
diff --git a/main/pom.xml b/main/pom.xml
-index c0b409c..27f789d 100644
+index 17b8ddc..4b61428 100644
--- a/main/pom.xml
+++ b/main/pom.xml
@@ -337,6 +337,10 @@
diff -Nru openrefine-3.6.2/debian/patches/CVE-2024-23833.patch openrefine-3.6.2/debian/patches/CVE-2024-23833.patch
--- openrefine-3.6.2/debian/patches/CVE-2024-23833.patch 1970-01-01 01:00:00.000000000 +0100
+++ openrefine-3.6.2/debian/patches/CVE-2024-23833.patch 2025-09-28 00:52:50.000000000 +0200
@@ -0,0 +1,56 @@
+From: Markus Koschany <apo@debian.org>
+Date: Sat, 27 Sep 2025 15:13:00 +0200
+Subject: CVE-2024-23833
+
+Origin: https://github.com/OpenRefine/OpenRefine/commit/41ccf574847d856e22488a7c0987ad8efa12a84a
+Debian-Bug: https://bugs.debian.org/1064192
+---
+ .../refine/extension/database/DatabaseConfiguration.java | 7 +++++++
+ .../refine/extension/database/DatabaseConfigurationTest.java | 12 ++++++++++++
+ 2 files changed, 19 insertions(+)
+
+diff --git a/extensions/database/src/com/google/refine/extension/database/DatabaseConfiguration.java b/extensions/database/src/com/google/refine/extension/database/DatabaseConfiguration.java
+index 3f0dd57..579ee00 100644
+--- a/extensions/database/src/com/google/refine/extension/database/DatabaseConfiguration.java
++++ b/extensions/database/src/com/google/refine/extension/database/DatabaseConfiguration.java
+@@ -68,6 +68,13 @@ public class DatabaseConfiguration {
+ }
+
+ public void setDatabaseHost(String databaseServer) {
++ // forbid setting settings inside the host parameter:
++ // https://dev.mysql.com/doc/connector-j/en/connector-j-reference-jdbc-url-format.html
++ if (databaseServer == null ||
++ databaseServer.contains("(") ||
++ databaseServer.contains("=")) {
++ throw new IllegalArgumentException("Invalid host supplied");
++ }
+ this.databaseHost = databaseServer;
+ }
+
+diff --git a/extensions/database/tests/src/com/google/refine/extension/database/DatabaseConfigurationTest.java b/extensions/database/tests/src/com/google/refine/extension/database/DatabaseConfigurationTest.java
+index 5a571e8..928aeac 100644
+--- a/extensions/database/tests/src/com/google/refine/extension/database/DatabaseConfigurationTest.java
++++ b/extensions/database/tests/src/com/google/refine/extension/database/DatabaseConfigurationTest.java
+@@ -1,5 +1,8 @@
+ package com.google.refine.extension.database;
+
++import static org.testng.Assert.assertEquals;
++import static org.testng.Assert.assertThrows;
++
+ import org.testng.annotations.Test;
+
+ import static org.testng.Assert.assertEquals;
+@@ -18,4 +21,13 @@ public class DatabaseConfigurationTest {
+ // the database name is escaped, preventing the exploit
+ assertEquals(url, "jdbc:mysql://my.host/test%3FallowLoadLocalInfile=true%23");
+ }
++
++ @Test
++ public void testSetMaliciousHost() {
++ DatabaseConfiguration config = new DatabaseConfiguration();
++ config.setDatabaseType("mysql");
++
++ assertThrows(IllegalArgumentException.class,
++ () -> config.setDatabaseHost("127.0.0.1:3306,(allowLoadLocalInfile=true,allowUrlInLocalInfile=true),127.0.0.1"));
++ }
+ }
diff -Nru openrefine-3.6.2/debian/patches/CVE-2024-47878.patch openrefine-3.6.2/debian/patches/CVE-2024-47878.patch
--- openrefine-3.6.2/debian/patches/CVE-2024-47878.patch 1970-01-01 01:00:00.000000000 +0100
+++ openrefine-3.6.2/debian/patches/CVE-2024-47878.patch 2025-09-28 00:52:50.000000000 +0200
@@ -0,0 +1,156 @@
+From: Markus Koschany <apo@debian.org>
+Date: Sat, 27 Sep 2025 15:15:58 +0200
+Subject: CVE-2024-47878
+
+Origin: https://github.com/OpenRefine/OpenRefine/commit/37b375478eca41b8948b104bf6790ebf659a88cb
+Bug-Debian: https://bugs.debian.org/1086041
+---
+ extensions/gdata/module/MOD-INF/controller.js | 21 +-----
+ extensions/gdata/module/authorized.vt | 6 +-
+ .../refine/extension/gdata/AuthorizedCommand.java | 88 ++++++++++++++++++++++
+ 3 files changed, 94 insertions(+), 21 deletions(-)
+ create mode 100644 extensions/gdata/src/com/google/refine/extension/gdata/AuthorizedCommand.java
+
+diff --git a/extensions/gdata/module/MOD-INF/controller.js b/extensions/gdata/module/MOD-INF/controller.js
+index fc6ff96..549c6be 100644
+--- a/extensions/gdata/module/MOD-INF/controller.js
++++ b/extensions/gdata/module/MOD-INF/controller.js
+@@ -101,23 +101,10 @@ function process(path, request, response) {
+
+ send(request, response, "authorize.vt", context);
+ } else if (path == "authorized") {
+- var context = {};
+- context.state = request.getParameter("state");
+-
+- (function() {
+- if (Packages.com.google.refine.extension.gdata.TokenCookie.getToken(request) !== null) {
+- return;
+- }
+- var tokenAndExpiresInSeconds = Packages.com.google.refine.extension.gdata.GoogleAPIExtension.getTokenFromCode(module,request);
+- if (tokenAndExpiresInSeconds) {
+- var tokenInfo = tokenAndExpiresInSeconds.split(",");
+- Packages.com.google.refine.extension.gdata.TokenCookie.setToken(request, response, tokenInfo[0], tokenInfo[1]);
+- return;
+- }
+- Packages.com.google.refine.extension.gdata.TokenCookie.deleteToken(request, response);
+- })();
+-
+- send(request, response, "authorized.vt", context);
++ // it's a command but we handle it manually here, so as to preserve the URL
++ var command = new Packages.com.google.refine.extension.gdata.AuthorizedCommand(module);
++ command.doGet(request, response);
++ butterfly.responded();
+ } else if (path == "/" || path == "") {
+ var context = {};
+ context.version = version;
+diff --git a/extensions/gdata/module/authorized.vt b/extensions/gdata/module/authorized.vt
+index 67ed3f5..3c16371 100644
+--- a/extensions/gdata/module/authorized.vt
++++ b/extensions/gdata/module/authorized.vt
+@@ -40,10 +40,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ <span id="gdata-authorized"></span>
+
+ <script>
+- var state = JSON.parse(window.atob("$state"));
+-
+- var windowName = state.winname;
+- var callbackName = state.cb;
++ var windowName = "$winname";
++ var callbackName = "$cb";
+
+ var w = window.open("", windowName);
+ var callback = w[callbackName];
+diff --git a/extensions/gdata/src/com/google/refine/extension/gdata/AuthorizedCommand.java b/extensions/gdata/src/com/google/refine/extension/gdata/AuthorizedCommand.java
+new file mode 100644
+index 0000000..b3dbb4b
+--- /dev/null
++++ b/extensions/gdata/src/com/google/refine/extension/gdata/AuthorizedCommand.java
+@@ -0,0 +1,88 @@
++
++package com.google.refine.extension.gdata;
++
++import java.io.IOException;
++import java.util.Base64;
++import java.util.regex.Pattern;
++
++import javax.servlet.ServletException;
++import javax.servlet.http.HttpServletRequest;
++import javax.servlet.http.HttpServletResponse;
++
++import com.fasterxml.jackson.databind.JsonNode;
++import com.fasterxml.jackson.databind.node.ObjectNode;
++import edu.mit.simile.butterfly.ButterflyModule;
++import org.apache.velocity.VelocityContext;
++
++import com.google.refine.commands.Command;
++import com.google.refine.util.ParsingUtilities;
++
++public class AuthorizedCommand extends Command {
++
++ Pattern callbackPattern = Pattern.compile("^cb[0-9]+$");
++ Pattern winnamePattern = Pattern.compile("^openrefine[0-9]+$");
++
++ ButterflyModule module;
++
++ public AuthorizedCommand(ButterflyModule module) {
++ this.module = module;
++ }
++
++ @Override
++ public void doGet(HttpServletRequest request, HttpServletResponse response)
++ throws ServletException, IOException {
++ VelocityContext velocityContext = new VelocityContext();
++ String state = request.getParameter("state");
++ if (state == null) {
++ respond(response, "error", "No 'state' parameter provided");
++ return;
++ }
++ try {
++ byte[] decoded = Base64.getDecoder().decode(state);
++ JsonNode parsed = ParsingUtilities.mapper.readTree(decoded);
++ if (parsed instanceof ObjectNode && parsed.has("winname") && parsed.has("cb")) {
++ ObjectNode object = (ObjectNode) parsed;
++ String cb = object.get("cb").asText();
++ String winname = object.get("winname").asText();
++ if (!winnamePattern.matcher(winname).find()) {
++ respond(response, "error", "Invalid winname provided");
++ return;
++ }
++ if (!callbackPattern.matcher(cb).find()) {
++ respond(response, "error", "Invalid callback provided");
++ return;
++ }
++ velocityContext.internalPut("winname", winname);
++ velocityContext.internalPut("cb", cb);
++
++ } else {
++ throw new IllegalArgumentException("expected a JSON object");
++ }
++ } catch (IllegalArgumentException | IOException e) {
++ respond(response, "error", "Invalid 'state' parameter provided");
++ return;
++ }
++
++ updateToken(request, response);
++
++ try {
++ module.sendTextFromTemplate(request, response, velocityContext, "authorized.vt", "UTF-8", "text/html", false);
++ } catch (Exception e) {
++ respondException(response, e);
++ }
++ }
++
++ private void updateToken(HttpServletRequest request, HttpServletResponse response) throws IOException {
++ if (TokenCookie.getToken(request) != null) {
++ return;
++ }
++ var tokenAndExpiresInSeconds = GoogleAPIExtension.getTokenFromCode(module, request);
++ if (tokenAndExpiresInSeconds != null) {
++ var tokenInfo = tokenAndExpiresInSeconds.split(",");
++ TokenCookie.setToken(request, response, tokenInfo[0], tokenInfo[1]);
++ return;
++ }
++ TokenCookie.deleteToken(request, response);
++ }
++
++}
diff -Nru openrefine-3.6.2/debian/patches/CVE-2024-47880.patch openrefine-3.6.2/debian/patches/CVE-2024-47880.patch
--- openrefine-3.6.2/debian/patches/CVE-2024-47880.patch 1970-01-01 01:00:00.000000000 +0100
+++ openrefine-3.6.2/debian/patches/CVE-2024-47880.patch 2025-09-28 00:52:50.000000000 +0200
@@ -0,0 +1,40 @@
+From: Markus Koschany <apo@debian.org>
+Date: Sat, 27 Sep 2025 15:27:12 +0200
+Subject: CVE-2024-47880
+
+Origin: https://github.com/OpenRefine/OpenRefine/commit/fbf94fe3f001d6e2aa02e890930cf1affb0847b0
+Bug-Debian: https://bugs.debian.org/1086041
+---
+ .../google/refine/commands/project/ExportRowsCommand.java | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/main/src/com/google/refine/commands/project/ExportRowsCommand.java b/main/src/com/google/refine/commands/project/ExportRowsCommand.java
+index 1912078..78d1f13 100644
+--- a/main/src/com/google/refine/commands/project/ExportRowsCommand.java
++++ b/main/src/com/google/refine/commands/project/ExportRowsCommand.java
+@@ -64,7 +64,8 @@ public class ExportRowsCommand extends Command {
+ private static final Logger logger = LoggerFactory.getLogger("ExportRowsCommand");
+
+ /**
+- * This command uses POST but is left CSRF-unprotected as it does not incur a state change.
++ * This command uses POST but is left CSRF-unprotected as it does not incur a state change. TODO: add CSRF
++ * protection anyway, as it does not cost much and could still have prevented an XSS vulnerability
+ */
+
+ @SuppressWarnings("unchecked")
+@@ -96,11 +97,10 @@ public class ExportRowsCommand extends Command {
+ exporter = new CsvExporter('\t');
+ }
+
+- String contentType = params.getProperty("contentType");
+- if (contentType == null) {
+- contentType = exporter.getContentType();
+- }
+- response.setHeader("Content-Type", contentType);
++
++ response.setHeader("Content-Type", exporter.getContentType());
++ // in case the content-type is text/html, to avoid XSS attacks
++ response.setHeader("Content-Security-Policy", "script-src 'none'; connect-src 'none'");
+
+ String preview = params.getProperty("preview");
+ if (!"true".equals(preview)) {
diff -Nru openrefine-3.6.2/debian/patches/CVE-2024-47881.patch openrefine-3.6.2/debian/patches/CVE-2024-47881.patch
--- openrefine-3.6.2/debian/patches/CVE-2024-47881.patch 1970-01-01 01:00:00.000000000 +0100
+++ openrefine-3.6.2/debian/patches/CVE-2024-47881.patch 2025-09-28 00:52:50.000000000 +0200
@@ -0,0 +1,181 @@
+From: Markus Koschany <apo@debian.org>
+Date: Sat, 27 Sep 2025 15:59:27 +0200
+Subject: CVE-2024-47881
+
+Origin: https://github.com/OpenRefine/OpenRefine/commit/8a5cced755f9d4544cfc9fd1b9dc9274807b5020
+Bug-Debian: https://bugs.debian.org/1086041
+---
+ .../database/sqlite/SQLiteConnectionManager.java | 16 ++++++++-
+ extensions/database/tests/conf/tests.xml | 2 +-
+ .../extension/database/DBExtensionTests.java | 2 +-
+ .../sqlite/SQLiteConnectionManagerTest.java | 10 ------
+ .../database/sqlite/SQLiteDatabaseServiceTest.java | 42 +++++++++++++++-------
+ 5 files changed, 47 insertions(+), 25 deletions(-)
+
+diff --git a/extensions/database/src/com/google/refine/extension/database/sqlite/SQLiteConnectionManager.java b/extensions/database/src/com/google/refine/extension/database/sqlite/SQLiteConnectionManager.java
+index 7d42e00..0def4d2 100644
+--- a/extensions/database/src/com/google/refine/extension/database/sqlite/SQLiteConnectionManager.java
++++ b/extensions/database/src/com/google/refine/extension/database/sqlite/SQLiteConnectionManager.java
+@@ -35,6 +35,7 @@ import com.google.refine.extension.database.SQLType;
+ import org.slf4j.Logger;
+ import org.slf4j.LoggerFactory;
+
++import java.io.File;
+ import java.net.URI;
+ import java.net.URISyntaxException;
+ import java.sql.Connection;
+@@ -68,8 +69,21 @@ public class SQLiteConnectionManager {
+ }
+
+ public static String getDatabaseUrl(DatabaseConfiguration dbConfig) {
++ String dbPath = dbConfig.getDatabaseName();
++ if (dbPath.contains("?")) {
++ throw new IllegalArgumentException("Paths to SQLite databases are not allowed to contain '?'");
++ }
++ if (dbPath.startsWith("//") || dbPath.startsWith("\\\\") || dbPath.startsWith("\\/") || dbPath.startsWith("/\\")) {
++ throw new IllegalArgumentException("File path starts with illegal prefix; only local files are accepted.");
++ }
++ if (!new File(dbPath).isFile()) {
++ throw new IllegalArgumentException("File could not be read: " + dbPath);
++ }
+ try {
+- URI uri = new URI("jdbc:" + dbConfig.getDatabaseType().toLowerCase(), dbConfig.getDatabaseName(), null);
++ URI uri = new URI(
++ "jdbc:" + dbConfig.getDatabaseType().toLowerCase(),
++ dbPath + "?open_mode=1&limit_attached=0", // open_mode=1 means read-only
++ null);
+ return uri.toASCIIString();
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException(e);
+diff --git a/extensions/database/tests/conf/tests.xml b/extensions/database/tests/conf/tests.xml
+index 7885174..a0910c9 100644
+--- a/extensions/database/tests/conf/tests.xml
++++ b/extensions/database/tests/conf/tests.xml
+@@ -29,7 +29,7 @@
+ <parameter name = "mariadbDbPassword" value=""/>
+ <parameter name = "mariadbTestTable" value="test_table"/>
+
+- <parameter name = "sqliteDbName" value="extension_test_db.sqlite"/>
++ <parameter name = "sqliteDbName" value="tests/resources/test_db.sqlite"/>
+ <parameter name = "sqliteDbHost" value=""/>
+ <parameter name = "sqliteDbPort" value=""/>
+ <parameter name = "sqliteDbUser" value=""/>
+diff --git a/extensions/database/tests/src/com/google/refine/extension/database/DBExtensionTests.java b/extensions/database/tests/src/com/google/refine/extension/database/DBExtensionTests.java
+index bd1d0bc..a98f55a 100644
+--- a/extensions/database/tests/src/com/google/refine/extension/database/DBExtensionTests.java
++++ b/extensions/database/tests/src/com/google/refine/extension/database/DBExtensionTests.java
+@@ -61,7 +61,7 @@ public class DBExtensionTests {
+ protected final String DEFAULT_MARIADB_NAME = "testdb";
+
+ protected final String SQLITE_DB_NAME = "sqlite";
+- protected final String DEFAULT_SQLITE_DB_NAME = "extension_test_db.sqlite";
++ protected final String DEFAULT_SQLITE_DB_NAME = "tests/resources/test_db.sqlite";
+
+ protected final String DEFAULT_TEST_TABLE = "test_data";
+
+diff --git a/extensions/database/tests/src/com/google/refine/extension/database/sqlite/SQLiteConnectionManagerTest.java b/extensions/database/tests/src/com/google/refine/extension/database/sqlite/SQLiteConnectionManagerTest.java
+index 2daf059..5afcdcb 100644
+--- a/extensions/database/tests/src/com/google/refine/extension/database/sqlite/SQLiteConnectionManagerTest.java
++++ b/extensions/database/tests/src/com/google/refine/extension/database/sqlite/SQLiteConnectionManagerTest.java
+@@ -37,7 +37,6 @@ import org.mockito.MockitoAnnotations;
+ import org.testng.Assert;
+ import org.testng.annotations.*;
+
+-import java.io.File;
+ import java.sql.Connection;
+ import java.sql.SQLException;
+
+@@ -61,15 +60,6 @@ public class SQLiteConnectionManagerTest extends DBExtensionTests {
+ DatabaseService.DBType.registerDatabase(SQLiteDatabaseService.DB_NAME, SQLiteDatabaseService.getInstance());
+ }
+
+- @AfterTest
+- @Parameters({ "sqliteDbName" })
+- public void afterTest(@Optional(DEFAULT_SQLITE_DB_NAME) String sqliteDbName) {
+- File f = new File(sqliteDbName);
+- if (f.exists()) {
+- f.delete();
+- }
+- }
+-
+ @Test
+ public void testTestConnection() throws DatabaseServiceException {
+ boolean isConnected = SQLiteConnectionManager.getInstance().testConnection(testDbConfig);
+diff --git a/extensions/database/tests/src/com/google/refine/extension/database/sqlite/SQLiteDatabaseServiceTest.java b/extensions/database/tests/src/com/google/refine/extension/database/sqlite/SQLiteDatabaseServiceTest.java
+index c29cc82..cd6f244 100644
+--- a/extensions/database/tests/src/com/google/refine/extension/database/sqlite/SQLiteDatabaseServiceTest.java
++++ b/extensions/database/tests/src/com/google/refine/extension/database/sqlite/SQLiteDatabaseServiceTest.java
+@@ -37,7 +37,6 @@ import org.mockito.MockitoAnnotations;
+ import org.testng.Assert;
+ import org.testng.annotations.*;
+
+-import java.io.File;
+ import java.sql.Connection;
+ import java.sql.SQLException;
+ import java.util.List;
+@@ -60,20 +59,10 @@ public class SQLiteDatabaseServiceTest extends DBExtensionTests {
+ testDbConfig.setDatabaseType(SQLiteDatabaseService.DB_NAME);
+
+ testTable = sqliteTestTable;
+- DBExtensionTestUtils.initTestData(testDbConfig, sqliteTestTable);
+
+ DatabaseService.DBType.registerDatabase(SQLiteDatabaseService.DB_NAME, SQLiteDatabaseService.getInstance());
+ }
+
+- @AfterTest
+- @Parameters({ "sqliteDbName" })
+- public void afterTest(@Optional(DEFAULT_SQLITE_DB_NAME) String sqliteDbName) {
+- File f = new File(sqliteDbName);
+- if (f.exists()) {
+- f.delete();
+- }
+- }
+-
+ @Test
+ public void testGetDatabaseUrl() {
+ SQLiteDatabaseService sqLiteDatabaseService = (SQLiteDatabaseService) DatabaseService
+@@ -81,7 +70,7 @@ public class SQLiteDatabaseServiceTest extends DBExtensionTests {
+ String dbUrl = sqLiteDatabaseService.getDatabaseUrl(testDbConfig);
+
+ Assert.assertNotNull(dbUrl);
+- Assert.assertEquals(dbUrl, "jdbc:sqlite:extension_test_db.sqlite");
++ Assert.assertEquals(dbUrl, "jdbc:sqlite:tests/resources/test_db.sqlite?open_mode=1&limit_attached=0");
+ }
+
+ @Test
+@@ -94,6 +83,35 @@ public class SQLiteDatabaseServiceTest extends DBExtensionTests {
+ Assert.assertNotNull(conn);
+ }
+
++ /*
++ * We don't allow loading extensions because that executes arbitrary code
++ */
++ @Test(expectedExceptions = IllegalArgumentException.class)
++ public void testGetConnectionWithExtensions() throws DatabaseServiceException {
++ DatabaseConfiguration testDbConfigWithExtensions = new DatabaseConfiguration();
++ testDbConfigWithExtensions.setDatabaseName("test_db.sqlite?enable_load_extension=true");
++ testDbConfigWithExtensions.setDatabaseType(SQLiteDatabaseService.DB_NAME);
++
++ SQLiteDatabaseService sqLiteDatabaseService = (SQLiteDatabaseService) DatabaseService
++ .get(SQLiteDatabaseService.DB_NAME);
++ sqLiteDatabaseService.getConnection(testDbConfigWithExtensions);
++ }
++
++ /*
++ * We don't allow loading a remote SQLite file to make remote code execution harder
++ */
++ @Test(expectedExceptions = IllegalArgumentException.class)
++ public void testGetConnectionWithRemoteFile() throws DatabaseServiceException {
++ DatabaseConfiguration testDbConfigWithExtensions = new DatabaseConfiguration();
++ testDbConfigWithExtensions
++ .setDatabaseName("https://github.com/xerial/sqlite-jdbc/raw/master/src/test/resources/org/sqlite/sample.db");
++ testDbConfigWithExtensions.setDatabaseType(SQLiteDatabaseService.DB_NAME);
++
++ SQLiteDatabaseService sqLiteDatabaseService = (SQLiteDatabaseService) DatabaseService
++ .get(SQLiteDatabaseService.DB_NAME);
++ sqLiteDatabaseService.getConnection(testDbConfigWithExtensions);
++ }
++
+ @Test
+ public void testTestConnection() throws DatabaseServiceException {
+ SQLiteDatabaseService sqLiteDatabaseService = (SQLiteDatabaseService) DatabaseService
diff -Nru openrefine-3.6.2/debian/patches/CVE-2024-47882.patch openrefine-3.6.2/debian/patches/CVE-2024-47882.patch
--- openrefine-3.6.2/debian/patches/CVE-2024-47882.patch 1970-01-01 01:00:00.000000000 +0100
+++ openrefine-3.6.2/debian/patches/CVE-2024-47882.patch 2025-09-28 00:52:50.000000000 +0200
@@ -0,0 +1,33 @@
+From: Markus Koschany <apo@debian.org>
+Date: Sat, 27 Sep 2025 16:40:21 +0200
+Subject: CVE-2024-47882
+
+Bug-Debian: https://bugs.debian.org/1086041
+Origin: https://github.com/OpenRefine/OpenRefine/commit/b0d5dd0a6a40369593f4a6b593e3e0ffa213339e
+---
+ main/src/com/google/refine/commands/HttpUtilities.java | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/main/src/com/google/refine/commands/HttpUtilities.java b/main/src/com/google/refine/commands/HttpUtilities.java
+index defea78..faa64cc 100644
+--- a/main/src/com/google/refine/commands/HttpUtilities.java
++++ b/main/src/com/google/refine/commands/HttpUtilities.java
+@@ -41,6 +41,8 @@ import org.slf4j.Logger;
+ import org.slf4j.LoggerFactory;
+
+ import com.fasterxml.jackson.core.JsonGenerator;
++import com.google.common.escape.Escaper;
++import com.google.common.html.HtmlEscapers;
+ import com.google.refine.RefineServlet;
+ import com.google.refine.util.ParsingUtilities;
+
+@@ -171,7 +173,8 @@ abstract public class HttpUtilities {
+
+ e.printStackTrace(new PrintWriter(writer));
+
+- context.put("stack", writer.toString());
++ Escaper escaper = HtmlEscapers.htmlEscaper();
++ context.put("stack", escaper.escape(writer.toString()));
+ } else {
+ context.put("stack", "");
+ }
diff -Nru openrefine-3.6.2/debian/patches/CVE-2024-49760.patch openrefine-3.6.2/debian/patches/CVE-2024-49760.patch
--- openrefine-3.6.2/debian/patches/CVE-2024-49760.patch 1970-01-01 01:00:00.000000000 +0100
+++ openrefine-3.6.2/debian/patches/CVE-2024-49760.patch 2025-09-28 00:52:50.000000000 +0200
@@ -0,0 +1,54 @@
+From: Markus Koschany <apo@debian.org>
+Date: Sat, 27 Sep 2025 17:51:56 +0200
+Subject: CVE-2024-49760
+
+Bug-Debian: https://bugs.debian.org/1086041
+Origin: https://github.com/OpenRefine/OpenRefine/commit/478285afffea59c893ac472faa74898ab9e5e95a
+---
+ .../com/google/refine/commands/lang/LoadLanguageCommand.java | 9 ++++++++-
+ .../refine/commands/lang/LoadLanguageCommandTests.java | 12 ++++++++++++
+ 2 files changed, 20 insertions(+), 1 deletion(-)
+
+diff --git a/main/src/com/google/refine/commands/lang/LoadLanguageCommand.java b/main/src/com/google/refine/commands/lang/LoadLanguageCommand.java
+index 002b908..85bf2e0 100644
+--- a/main/src/com/google/refine/commands/lang/LoadLanguageCommand.java
++++ b/main/src/com/google/refine/commands/lang/LoadLanguageCommand.java
+@@ -126,7 +126,14 @@ public class LoadLanguageCommand extends Command {
+ static ObjectNode loadLanguage(RefineServlet servlet, String modname, String lang) throws UnsupportedEncodingException {
+
+ ButterflyModule module = servlet.getModule(modname);
+- File langFile = new File(module.getPath(), "langs" + File.separator + "translation-" + lang + ".json");
++ String strLangFile = "translation-" + lang + ".json";
++ File langsDir = new File(module.getPath(), "langs");
++ File langFile = new File(langsDir, strLangFile);
++ if (!langFile.toPath().normalize().toAbsolutePath().startsWith(langsDir.toPath().normalize().toAbsolutePath())) {
++ logger.error("Security: Attempt to escape the langs directory to read another file");
++ return null;
++ }
++
+ try {
+ Reader reader = new BufferedReader(new InputStreamReader(new FileInputStream(langFile), "UTF-8"));
+ return ParsingUtilities.mapper.readValue(reader, ObjectNode.class);
+diff --git a/main/tests/server/src/com/google/refine/commands/lang/LoadLanguageCommandTests.java b/main/tests/server/src/com/google/refine/commands/lang/LoadLanguageCommandTests.java
+index ce569b5..0e073d3 100644
+--- a/main/tests/server/src/com/google/refine/commands/lang/LoadLanguageCommandTests.java
++++ b/main/tests/server/src/com/google/refine/commands/lang/LoadLanguageCommandTests.java
+@@ -77,6 +77,18 @@ public class LoadLanguageCommandTests extends CommandTestBase {
+ assertEquals(response.get("lang").asText(), "en");
+ }
+
++ @Test
++ public void testLoadLanguageWithDirectorySlip() throws JsonParseException, JsonMappingException, IOException, ServletException {
++ when(request.getParameter("module")).thenReturn("core");
++ when(request.getParameterValues("lang")).thenReturn(new String[] { "../../../secrets" });
++
++ command.doPost(request, response);
++
++ JsonNode response = ParsingUtilities.mapper.readValue(writer.toString(), JsonNode.class);
++ assertTrue(response.has("dictionary"));
++ assertEquals(response.get("lang").asText(), "en");
++ }
++
+ @Test
+ public void testLanguageFallback() throws JsonParseException, JsonMappingException, IOException {
+ String fallbackJson = "{"
diff -Nru openrefine-3.6.2/debian/patches/gdata-extension.patch openrefine-3.6.2/debian/patches/gdata-extension.patch
--- openrefine-3.6.2/debian/patches/gdata-extension.patch 2023-10-04 15:02:45.000000000 +0200
+++ openrefine-3.6.2/debian/patches/gdata-extension.patch 2025-09-28 00:52:50.000000000 +0200
@@ -7,7 +7,7 @@
1 file changed, 5 insertions(+)
diff --git a/extensions/gdata/pom.xml b/extensions/gdata/pom.xml
-index 4fc64e5..ce9b906 100644
+index ed8b740..e310e8c 100644
--- a/extensions/gdata/pom.xml
+++ b/extensions/gdata/pom.xml
@@ -97,6 +97,11 @@
diff -Nru openrefine-3.6.2/debian/patches/log4j-api.patch openrefine-3.6.2/debian/patches/log4j-api.patch
--- openrefine-3.6.2/debian/patches/log4j-api.patch 2023-10-04 15:02:45.000000000 +0200
+++ openrefine-3.6.2/debian/patches/log4j-api.patch 2025-09-28 00:52:50.000000000 +0200
@@ -7,7 +7,7 @@
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/server/pom.xml b/server/pom.xml
-index 8bb528a..89dd5ab 100644
+index 2408c76..e168085 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -180,7 +180,7 @@
diff -Nru openrefine-3.6.2/debian/patches/no-java-files.patch openrefine-3.6.2/debian/patches/no-java-files.patch
--- openrefine-3.6.2/debian/patches/no-java-files.patch 2023-10-04 15:02:45.000000000 +0200
+++ openrefine-3.6.2/debian/patches/no-java-files.patch 2025-09-28 00:52:50.000000000 +0200
@@ -8,7 +8,7 @@
2 files changed, 10 deletions(-)
diff --git a/main/pom.xml b/main/pom.xml
-index 27f789d..0ef3eab 100644
+index 4b61428..8ea4ef6 100644
--- a/main/pom.xml
+++ b/main/pom.xml
@@ -27,11 +27,6 @@
@@ -24,7 +24,7 @@
<testResources>
<testResource>
diff --git a/server/pom.xml b/server/pom.xml
-index 89dd5ab..94fda4c 100644
+index e168085..f2955e4 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -28,11 +28,6 @@
diff -Nru openrefine-3.6.2/debian/patches/series openrefine-3.6.2/debian/patches/series
--- openrefine-3.6.2/debian/patches/series 2023-10-04 15:02:45.000000000 +0200
+++ openrefine-3.6.2/debian/patches/series 2025-09-28 00:52:50.000000000 +0200
@@ -6,3 +6,9 @@
gdata-extension.patch
CVE-2023-37476.patch
CVE-2023-41887-and-CVE-2023-41886.patch
+CVE-2024-23833.patch
+CVE-2024-47878.patch
+CVE-2024-47880.patch
+CVE-2024-47881.patch
+CVE-2024-47882.patch
+CVE-2024-49760.patch
Reply to: