Package: release.debian.org
Severity: normal
Tags: bookworm
User: release.debian....@packages.debian.org
Usertags: pu
X-Debbugs-Cc: Debian Java Maintainers 
<pkg-java-maintain...@lists.alioth.debian.org>, secur...@debian.org

  * New upstream release.
    - CVE-2024-1597: SQL Injection via line comment generation

The only changes in this next upstream version are the CVE fixes,
and upstream update of the version number in a few places.
diffstat for libpgjava-42.5.4 libpgjava-42.5.5

 debian/changelog                                               |    8 
 pom.xml                                                        |    2 
 src/main/java/org/postgresql/core/v3/SimpleParameterList.java  |  149 
++++++----
 src/main/java/org/postgresql/util/DriverInfo.java              |    4 
 src/main/resources/META-INF/MANIFEST.MF                        |   10 
 src/test/java/org/postgresql/core/v3/V3ParameterListTests.java |    6 
 src/test/java/org/postgresql/jdbc/ParameterInjectionTest.java  |  144 +++++++++
 7 files changed, 265 insertions(+), 58 deletions(-)

diff -Nru libpgjava-42.5.4/debian/changelog libpgjava-42.5.5/debian/changelog
--- libpgjava-42.5.4/debian/changelog   2023-02-17 19:19:35.000000000 +0200
+++ libpgjava-42.5.5/debian/changelog   2024-12-18 21:11:06.000000000 +0200
@@ -1,3 +1,11 @@
+libpgjava (42.5.5-0+deb12u1) bookworm; urgency=medium
+
+  * Non-maintainer upload.
+  * New upstream release.
+    - CVE-2024-1597: SQL Injection via line comment generation
+
+ -- Adrian Bunk <b...@debian.org>  Wed, 18 Dec 2024 21:11:06 +0200
+
 libpgjava (42.5.4-1) unstable; urgency=medium
 
   * New upstream version 42.5.4.
diff -Nru libpgjava-42.5.4/pom.xml libpgjava-42.5.5/pom.xml
--- libpgjava-42.5.4/pom.xml    1970-01-02 02:00:00.000000000 +0200
+++ libpgjava-42.5.5/pom.xml    1970-01-02 02:00:00.000000000 +0200
@@ -10,7 +10,7 @@
     <artifactId>postgresql</artifactId>
     <packaging>jar</packaging>
     <name>PostgreSQL JDBC Driver - JDBC 4.2</name>
-    <version>42.5.4</version>
+    <version>42.5.5</version>
     <description>Java JDBC 4.2 (JRE 8+) driver for PostgreSQL 
database</description>
     <url>https://github.com/pgjdbc/pgjdbc</url>
 
diff -Nru 
libpgjava-42.5.4/src/main/java/org/postgresql/core/v3/SimpleParameterList.java 
libpgjava-42.5.5/src/main/java/org/postgresql/core/v3/SimpleParameterList.java
--- 
libpgjava-42.5.4/src/main/java/org/postgresql/core/v3/SimpleParameterList.java  
    1970-01-02 02:00:00.000000000 +0200
+++ 
libpgjava-42.5.5/src/main/java/org/postgresql/core/v3/SimpleParameterList.java  
    1970-01-02 02:00:00.000000000 +0200
@@ -173,6 +173,59 @@
     bind(index, NULL_OBJECT, oid, binaryTransfer);
   }
 
+  /**
+   * <p>Escapes a given text value as a literal, wraps it in single quotes, 
casts it to the
+   * to the given data type, and finally wraps the whole thing in 
parentheses.</p>
+   *
+   * <p>For example, "123" and "int4" becomes "('123'::int)"</p>
+   *
+   * <p>The additional parentheses is added to ensure that the surrounding 
text of where the
+   * parameter value is entered does modify the interpretation of the 
value.</p>
+   *
+   * <p>For example if our input SQL is: <code>SELECT ?b</code></p>
+   *
+   * <p>Using a parameter value of '{}' and type of json we'd get:</p>
+   *
+   * <pre>
+   * test=# SELECT ('{}'::json)b;
+   *  b
+   * ----
+   *  {}
+   * </pre>
+   *
+   * <p>But without the parentheses the result changes:</p>
+   *
+   * <pre>
+   * test=# SELECT '{}'::jsonb;
+   * jsonb
+   * -------
+   * {}
+   * </pre>
+   **/
+  private static String quoteAndCast(String text, /* @Nullable */ String type, 
boolean standardConformingStrings) {
+
+    StringBuilder sb = new StringBuilder((text.length() + 10) / 10 * 11); // 
Add 10% for escaping.
+    sb.append("('");
+    try {
+      Utils.escapeLiteral(sb, text, standardConformingStrings);
+    } catch (SQLException e) {
+      // This should only happen if we have an embedded null
+      // and there's not much we can do if we do hit one.
+      //
+      // To force a server side failure, we deliberately include
+      // a zero byte character in the literal to force the server
+      // to reject the command.
+      sb.append('\u0000');
+    }
+    sb.append("'");
+    if (type != null) {
+      sb.append("::");
+      sb.append(type);
+    }
+    sb.append(")");
+    return sb.toString();
+  }
+
   @Override
   public String toString(/* @Positive */ int index, boolean 
standardConformingStrings) {
     --index;
@@ -180,101 +233,103 @@
     if (paramValue == null) {
       return "?";
     } else if (paramValue == NULL_OBJECT) {
-      return "NULL";
-    } else if ((flags[index] & BINARY) == BINARY) {
+      return "(NULL)";
+    }
+    String textValue;
+    String type;
+    if ((flags[index] & BINARY) == BINARY) {
       // handle some of the numeric types
-
       switch (paramTypes[index]) {
         case Oid.INT2:
           short s = ByteConverter.int2((byte[]) paramValue, 0);
-          return Short.toString(s);
+          textValue = Short.toString(s);
+          type = "int2";
+          break;
 
         case Oid.INT4:
           int i = ByteConverter.int4((byte[]) paramValue, 0);
-          return Integer.toString(i);
+          textValue = Integer.toString(i);
+          type = "int4";
+          break;
 
         case Oid.INT8:
           long l = ByteConverter.int8((byte[]) paramValue, 0);
-          return Long.toString(l);
+          textValue = Long.toString(l);
+          type = "int8";
+          break;
 
         case Oid.FLOAT4:
           float f = ByteConverter.float4((byte[]) paramValue, 0);
           if (Float.isNaN(f)) {
-            return "'NaN'::real";
+            return "('NaN'::real)";
           }
-          return Float.toString(f);
+          textValue = Float.toString(f);
+          type = "real";
+          break;
 
         case Oid.FLOAT8:
           double d = ByteConverter.float8((byte[]) paramValue, 0);
           if (Double.isNaN(d)) {
-            return "'NaN'::double precision";
+            return "('NaN'::double precision)";
           }
-          return Double.toString(d);
+          textValue = Double.toString(d);
+          type = "double precision";
+          break;
 
         case Oid.NUMERIC:
           Number n = ByteConverter.numeric((byte[]) paramValue);
           if (n instanceof Double) {
             assert ((Double) n).isNaN();
-            return "'NaN'::numeric";
+            return "('NaN'::numeric)";
           }
-          return n.toString();
+          textValue = n.toString();
+          type = "numeric";
+          break;
 
         case Oid.UUID:
-          String uuid =
+          textValue =
               new UUIDArrayAssistant().buildElement((byte[]) paramValue, 0, 
16).toString();
-          return "'" + uuid + "'::uuid";
-
+          type = "uuid";
+          break;
         case Oid.POINT:
           PGpoint pgPoint = new PGpoint();
           pgPoint.setByteValue((byte[]) paramValue, 0);
-          return "'" + pgPoint.toString() + "'::point";
+          textValue = pgPoint.toString();
+          type = "point";
+          break;
 
         case Oid.BOX:
           PGbox pgBox = new PGbox();
           pgBox.setByteValue((byte[]) paramValue, 0);
-          return "'" + pgBox.toString() + "'::box";
-      }
-      return "?";
-    } else {
-      String param = paramValue.toString();
-
-      // add room for quotes + potential escaping.
-      StringBuilder p = new StringBuilder(3 + (param.length() + 10) / 10 * 11);
+          textValue = pgBox.toString();
+          type = "box";
+          break;
 
-      // No E'..' here since escapeLiteral escapes all things and it does not 
use \123 kind of
-      // escape codes
-      p.append('\'');
-      try {
-        p = Utils.escapeLiteral(p, param, standardConformingStrings);
-      } catch (SQLException sqle) {
-        // This should only happen if we have an embedded null
-        // and there's not much we can do if we do hit one.
-        //
-        // The goal of toString isn't to be sent to the server,
-        // so we aren't 100% accurate (see StreamWrapper), put
-        // the unescaped version of the data.
-        //
-        p.append(param);
+        default:
+          return "?";
       }
-      p.append('\'');
+    } else {
+      textValue = paramValue.toString();
       int paramType = paramTypes[index];
       if (paramType == Oid.TIMESTAMP) {
-        p.append("::timestamp");
+        type = "timestamp";
       } else if (paramType == Oid.TIMESTAMPTZ) {
-        p.append("::timestamp with time zone");
+        type = "timestamp with time zone";
       } else if (paramType == Oid.TIME) {
-        p.append("::time");
+        type = "time";
       } else if (paramType == Oid.TIMETZ) {
-        p.append("::time with time zone");
+        type = "time with time zone";
       } else if (paramType == Oid.DATE) {
-        p.append("::date");
+        type = "date";
       } else if (paramType == Oid.INTERVAL) {
-        p.append("::interval");
+        type = "interval";
       } else if (paramType == Oid.NUMERIC) {
-        p.append("::numeric");
+        type = "numeric";
+      } else {
+        type = null;
       }
-      return p.toString();
     }
+    return quoteAndCast(textValue, type, standardConformingStrings);
   }
 
   @Override
diff -Nru libpgjava-42.5.4/src/main/java/org/postgresql/util/DriverInfo.java 
libpgjava-42.5.5/src/main/java/org/postgresql/util/DriverInfo.java
--- libpgjava-42.5.4/src/main/java/org/postgresql/util/DriverInfo.java  
1970-01-02 02:00:00.000000000 +0200
+++ libpgjava-42.5.5/src/main/java/org/postgresql/util/DriverInfo.java  
1970-01-02 02:00:00.000000000 +0200
@@ -16,13 +16,13 @@
   // Driver name
   public static final String DRIVER_NAME = "PostgreSQL JDBC Driver";
   public static final String DRIVER_SHORT_NAME = "PgJDBC";
-  public static final String DRIVER_VERSION = "42.5.4";
+  public static final String DRIVER_VERSION = "42.5.5";
   public static final String DRIVER_FULL_NAME = DRIVER_NAME + " " + 
DRIVER_VERSION;
 
   // Driver version
   public static final int MAJOR_VERSION = 42;
   public static final int MINOR_VERSION = 5;
-  public static final int PATCH_VERSION = 4;
+  public static final int PATCH_VERSION = 5;
 
   // JDBC specification
   public static final String JDBC_VERSION = "4.2";
diff -Nru libpgjava-42.5.4/src/main/resources/META-INF/MANIFEST.MF 
libpgjava-42.5.5/src/main/resources/META-INF/MANIFEST.MF
--- libpgjava-42.5.4/src/main/resources/META-INF/MANIFEST.MF    1970-01-02 
02:00:00.000000000 +0200
+++ libpgjava-42.5.5/src/main/resources/META-INF/MANIFEST.MF    1970-01-02 
02:00:00.000000000 +0200
@@ -1,12 +1,12 @@
 Manifest-Version: 1.0
-Implementation-Title: PostgreSQL JDBC Driver
 Bundle-License: BSD-2-Clause
-Automatic-Module-Name: org.postgresql.jdbc
-Implementation-Version: 42.5.4
+Implementation-Title: PostgreSQL JDBC Driver
+Implementation-Version: 42.5.5
 Specification-Vendor: Oracle Corporation
-Specification-Title: JDBC
-Implementation-Vendor-Id: org.postgresql
 Specification-Version: 4.2
+Specification-Title: JDBC
 Implementation-Vendor: PostgreSQL Global Development Group
+Implementation-Vendor-Id: org.postgresql
 Main-Class: org.postgresql.util.PGJDBCMain
+Automatic-Module-Name: org.postgresql.jdbc
 
diff -Nru 
libpgjava-42.5.4/src/test/java/org/postgresql/core/v3/V3ParameterListTests.java 
libpgjava-42.5.5/src/test/java/org/postgresql/core/v3/V3ParameterListTests.java
--- 
libpgjava-42.5.4/src/test/java/org/postgresql/core/v3/V3ParameterListTests.java 
    1970-01-02 02:00:00.000000000 +0200
+++ 
libpgjava-42.5.5/src/test/java/org/postgresql/core/v3/V3ParameterListTests.java 
    1970-01-02 02:00:00.000000000 +0200
@@ -58,8 +58,8 @@
     s2SPL.setIntParameter(4, 8);
 
     s1SPL.appendAll(s2SPL);
-    assertEquals(
-        "Expected string representation of values does not match outcome.",
-        "<[1 ,2 ,3 ,4 ,5 ,6 ,7 ,8]>", s1SPL.toString());
+    assertEquals("Expected string representation of values does not match 
outcome.",
+        "<[('1'::int4) ,('2'::int4) ,('3'::int4) ,('4'::int4) ,('5'::int4) 
,('6'::int4) ,('7'::int4) ,('8'::int4)]>", s1SPL.toString());
+
   }
 }
diff -Nru 
libpgjava-42.5.4/src/test/java/org/postgresql/jdbc/ParameterInjectionTest.java 
libpgjava-42.5.5/src/test/java/org/postgresql/jdbc/ParameterInjectionTest.java
--- 
libpgjava-42.5.4/src/test/java/org/postgresql/jdbc/ParameterInjectionTest.java  
    1970-01-01 02:00:00.000000000 +0200
+++ 
libpgjava-42.5.5/src/test/java/org/postgresql/jdbc/ParameterInjectionTest.java  
    1970-01-02 02:00:00.000000000 +0200
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2024, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.postgresql.test.TestUtil;
+
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class ParameterInjectionTest {
+  private interface ParameterBinder {
+    void bind(PreparedStatement stmt) throws SQLException;
+  }
+
+  private void testParamInjection(ParameterBinder bindPositiveOne, 
ParameterBinder bindNegativeOne)
+      throws SQLException {
+    try (Connection conn = TestUtil.openDB()) {
+      {
+        PreparedStatement stmt = conn.prepareStatement("SELECT -?");
+        bindPositiveOne.bind(stmt);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next());
+          assertEquals(1, rs.getMetaData().getColumnCount(),
+              "number of result columns must match");
+          int value = rs.getInt(1);
+          assertEquals(-1, value);
+        }
+        bindNegativeOne.bind(stmt);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next());
+          assertEquals(1, rs.getMetaData().getColumnCount(),
+              "number of result columns must match");
+          int value = rs.getInt(1);
+          assertEquals(1, value);
+        }
+      }
+      {
+        PreparedStatement stmt = conn.prepareStatement("SELECT -?, ?");
+        bindPositiveOne.bind(stmt);
+        stmt.setString(2, "\nWHERE false --");
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next(), "ResultSet should contain a row");
+          assertEquals(2, rs.getMetaData().getColumnCount(),
+              "rs.getMetaData().getColumnCount(");
+          int value = rs.getInt(1);
+          assertEquals(-1, value);
+        }
+
+        bindNegativeOne.bind(stmt);
+        stmt.setString(2, "\nWHERE false --");
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next(), "ResultSet should contain a row");
+          assertEquals(2, rs.getMetaData().getColumnCount(), 
"rs.getMetaData().getColumnCount(");
+          int value = rs.getInt(1);
+          assertEquals(1, value);
+        }
+
+      }
+    }
+  }
+
+  @Test
+  public void handleInt2() throws SQLException {
+    testParamInjection(
+        stmt -> {
+          stmt.setShort(1, (short) 1);
+        },
+        stmt -> {
+          stmt.setShort(1, (short) -1);
+        }
+    );
+  }
+
+  @Test
+  public void handleInt4() throws SQLException {
+    testParamInjection(
+        stmt -> {
+          stmt.setInt(1, 1);
+        },
+        stmt -> {
+          stmt.setInt(1, -1);
+        }
+    );
+  }
+
+  @Test
+  public void handleBigInt() throws SQLException {
+    testParamInjection(
+        stmt -> {
+          stmt.setLong(1, (long) 1);
+        },
+        stmt -> {
+          stmt.setLong(1, (long) -1);
+        }
+    );
+  }
+
+  @Test
+  public void handleNumeric() throws SQLException {
+    testParamInjection(
+        stmt -> {
+          stmt.setBigDecimal(1, new BigDecimal("1"));
+        },
+        stmt -> {
+          stmt.setBigDecimal(1, new BigDecimal("-1"));
+        }
+    );
+  }
+
+  @Test
+  public void handleFloat() throws SQLException {
+    testParamInjection(
+        stmt -> {
+          stmt.setFloat(1, 1);
+        },
+        stmt -> {
+          stmt.setFloat(1, -1);
+        }
+    );
+  }
+
+  @Test
+  public void handleDouble() throws SQLException {
+    testParamInjection(
+        stmt -> {
+          stmt.setDouble(1, 1);
+        },
+        stmt -> {
+          stmt.setDouble(1, -1);
+        }
+    );
+  }
+}

Reply via email to