CVE-2023-41886 (Java, published 2023-09-15T21:15Z)
Description: value: OpenRefine is a powerful free, open source tool for working with messy data. Prior to version 3.7.5, an arbitrary file read vulnerability allows any unauthenticated user to read a file on a server. Version 3.7.5 fixes this issue.
Severity: nan (CVSS v3 base score: 7.5)
Weakness: CWE-89 \u2014 Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.
Repository: https://github.com/OpenRefine/OpenRefine
Files changed:
extensions/database/src/com/google/refine/extension/database/DatabaseConfiguration.java
extensions/database/src/com/google/refine/extension/database/mariadb/MariaDBConnectionManager.java
extensions/database/src/com/google/refine/extension/database/mysql/MySQLConnectionManager.java
extensions/database/src/com/google/refine/extension/database/pgsql/PgSQLConnectionManager.java
extensions/database/src/com/google/refine/extension/database/sqlite/SQLiteConnectionManager.java
extensions/database/tests/src/com/google/refine/extension/database/DatabaseConfigurationTest.java
Commit message: Merge pull request from GHSA-qqh2-wvmv-h72m



Vulnerable code:
            String dbURL = getDatabaseUrl(databaseConfiguration);

    private static String getDatabaseUrl(DatabaseConfiguration dbConfig) {

        int port = dbConfig.getDatabasePort();
        return "jdbc:" + dbConfig.getDatabaseType().toLowerCase() + "://" + dbConfig.getDatabaseHost()
                + ((port == 0) ? "" : (":" + port)) + "/" + dbConfig.getDatabaseName();

    }
            String dbURL = getDatabaseUrl(databaseConfiguration);
    private String getDatabaseUrl(DatabaseConfiguration dbConfig) {

        int port = dbConfig.getDatabasePort();
        return "jdbc:" + dbConfig.getDatabaseType() + "://" + dbConfig.getDatabaseHost()
                + ((port == 0) ? "" : (":" + port)) + "/" + dbConfig.getDatabaseName() + "?useSSL=" + dbConfig.isUseSSL();

    }
            String dbURL = getDatabaseUrl(databaseConfiguration);
    private static String getDatabaseUrl(DatabaseConfiguration dbConfig) {

        int port = dbConfig.getDatabasePort();
        return "jdbc:" + dbConfig.getDatabaseType().toLowerCase() + "://" + dbConfig.getDatabaseHost()
                + ((port == 0) ? "" : (":" + port)) + "/" + dbConfig.getDatabaseName();

    }
        return "jdbc:" + dbConfig.getDatabaseType().toLowerCase() + ":" + dbConfig.getDatabaseName();

Fixed code:
import java.net.URI;
import java.net.URISyntaxException;

    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);
        }
    }
            String dbURL = databaseConfiguration.toURI().toString();
            String dbURL = databaseConfiguration.toURI().toString();
            String dbURL = databaseConfiguration.toURI().toString();
import java.net.URI;
import java.net.URISyntaxException;
        try {
            URI uri = new URI("jdbc:" + dbConfig.getDatabaseType().toLowerCase(), dbConfig.getDatabaseName(), null);
            return uri.toASCIIString();
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException(e);
        }
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");
    }
}