From 1bc03de1b629f425bf0781f591f77c280dcb32fb Mon Sep 17 00:00:00 2001
From: Patrick Malouin <patrick.malouin@auth0.com>
Date: Tue, 15 Feb 2022 22:20:13 -0500
Subject: [PATCH 1/9] fix(pg-connection-string): get closer to libpq semantics
 for `sslmode`

---
 packages/pg-connection-string/README.md     |  6 ++++--
 packages/pg-connection-string/index.js      | 21 ++++++++++++++++-----
 packages/pg-connection-string/test/parse.js | 17 +++++++++++------
 3 files changed, 31 insertions(+), 13 deletions(-)

diff --git a/packages/pg-connection-string/README.md b/packages/pg-connection-string/README.md
index d3a16881a..90dbd8678 100644
--- a/packages/pg-connection-string/README.md
+++ b/packages/pg-connection-string/README.md
@@ -89,8 +89,10 @@ Query parameters follow a `?` character, including the following special query p
  * `ssl=1`, `ssl=true`, `ssl=0`, `ssl=false` - sets `ssl` to true or false, accordingly
  * `sslmode=<sslmode>`
    * `sslmode=disable` - sets `ssl` to false
-   * `sslmode=no-verify` - sets `ssl` to `{ rejectUnauthorized: false }`
-   * `sslmode=prefer`, `sslmode=require`, `sslmode=verify-ca`, `sslmode=verify-full` - sets `ssl` to true
+   * `sslmode=no-verify`, `sslmode=prefer`, - sets `ssl` to `{ rejectUnauthorized: false }`
+   * `sslmode=require`, - sets `ssl` to `{ rejectUnauthorized: false }` unless `sslrootcert` is specified, in which case it behaves like `verify-ca`
+   * `sslmode=verify-ca` - sets `ssl` to `{ checkServerIdentity: no-op}` (verify CA, but not server identity)
+   * `sslmode=verify-full` - sets `ssl` to `{}` (verify CA and server identity)
  * `sslcert=<filename>` - reads data from the given file and includes the result as `ssl.cert`
  * `sslkey=<filename>` - reads data from the given file and includes the result as `ssl.key`
  * `sslrootcert=<filename>` - reads data from the given file and includes the result as `ssl.ca`
diff --git a/packages/pg-connection-string/index.js b/packages/pg-connection-string/index.js
index 53c3859e5..5a4d59bbf 100644
--- a/packages/pg-connection-string/index.js
+++ b/packages/pg-connection-string/index.js
@@ -93,15 +93,26 @@ function parse(str) {
       break
     }
     case 'prefer':
-    case 'require':
-    case 'verify-ca':
-    case 'verify-full': {
-      break
-    }
     case 'no-verify': {
       config.ssl.rejectUnauthorized = false
       break
     }
+    case 'require': {
+      if (config.sslrootcert) {
+        // If a root CA is specified, behavior of `sslmode=require` will be the same as that of `verify-ca`
+        config.ssl.checkServerIdentity = function () {}
+      } else {
+        config.ssl.rejectUnauthorized = false
+      }
+      break
+    }
+    case 'verify-ca': {
+      config.ssl.checkServerIdentity = function () {}
+      break
+    }
+    case 'verify-full': {
+      break
+    }
   }
 
   return config
diff --git a/packages/pg-connection-string/test/parse.js b/packages/pg-connection-string/test/parse.js
index 59f16a62e..feb616a4f 100644
--- a/packages/pg-connection-string/test/parse.js
+++ b/packages/pg-connection-string/test/parse.js
@@ -258,19 +258,24 @@ describe('parse', function () {
   it('configuration parameter sslmode=prefer', function () {
     var connectionString = 'pg:///?sslmode=prefer'
     var subject = parse(connectionString)
-    subject.ssl.should.eql({})
+    subject.ssl.should.eql({
+      rejectUnauthorized: false,
+    })
   })
 
   it('configuration parameter sslmode=require', function () {
     var connectionString = 'pg:///?sslmode=require'
     var subject = parse(connectionString)
-    subject.ssl.should.eql({})
+    subject.ssl.should.eql({
+      rejectUnauthorized: false,
+    })
   })
 
   it('configuration parameter sslmode=verify-ca', function () {
     var connectionString = 'pg:///?sslmode=verify-ca'
     var subject = parse(connectionString)
-    subject.ssl.should.eql({})
+    subject.ssl.should.have.property('checkServerIdentity').that.is.a('function')
+    expect(subject.ssl.checkServerIdentity()).be.undefined
   })
 
   it('configuration parameter sslmode=verify-full', function () {
@@ -282,9 +287,9 @@ describe('parse', function () {
   it('configuration parameter ssl=true and sslmode=require still work with sslrootcert=/path/to/ca', function () {
     var connectionString = 'pg:///?ssl=true&sslrootcert=' + __dirname + '/example.ca&sslmode=require'
     var subject = parse(connectionString)
-    subject.ssl.should.eql({
-      ca: 'example ca\n',
-    })
+    subject.ssl.should.have.property('ca', 'example ca\n')
+    subject.ssl.should.have.property('checkServerIdentity').that.is.a('function')
+    expect(subject.ssl.checkServerIdentity()).be.undefined
   })
 
   it('allow other params like max, ...', function () {

From c3aebe7fd7c898f3fe30201cf2d4165997259b7e Mon Sep 17 00:00:00 2001
From: Patrick Malouin <p.malouin@gmail.com>
Date: Wed, 23 Feb 2022 19:59:35 -0500
Subject: [PATCH 2/9] Apply suggestions from code review

Co-authored-by: Charmander <~@charmander.me>
---
 packages/pg-connection-string/README.md     | 8 ++++----
 packages/pg-connection-string/test/parse.js | 4 ++--
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/packages/pg-connection-string/README.md b/packages/pg-connection-string/README.md
index 90dbd8678..d90bc03f8 100644
--- a/packages/pg-connection-string/README.md
+++ b/packages/pg-connection-string/README.md
@@ -87,11 +87,11 @@ Query parameters follow a `?` character, including the following special query p
  * `host=<host>` - sets `host` property, overriding the URL's host
  * `encoding=<encoding>` - sets the `client_encoding` property
  * `ssl=1`, `ssl=true`, `ssl=0`, `ssl=false` - sets `ssl` to true or false, accordingly
- * `sslmode=<sslmode>`
+* `sslmode=<sslmode>`
    * `sslmode=disable` - sets `ssl` to false
-   * `sslmode=no-verify`, `sslmode=prefer`, - sets `ssl` to `{ rejectUnauthorized: false }`
-   * `sslmode=require`, - sets `ssl` to `{ rejectUnauthorized: false }` unless `sslrootcert` is specified, in which case it behaves like `verify-ca`
-   * `sslmode=verify-ca` - sets `ssl` to `{ checkServerIdentity: no-op}` (verify CA, but not server identity)
+   * `sslmode=no-verify`, `sslmode=prefer` - sets `ssl` to `{ rejectUnauthorized: false }`
+   * `sslmode=require` - sets `ssl` to `{ rejectUnauthorized: false }` unless `sslrootcert` is specified, in which case it behaves like `verify-ca`
+   * `sslmode=verify-ca` - sets `ssl` to `{ checkServerIdentity: no-op }` (verify CA, but not server identity)
    * `sslmode=verify-full` - sets `ssl` to `{}` (verify CA and server identity)
  * `sslcert=<filename>` - reads data from the given file and includes the result as `ssl.cert`
  * `sslkey=<filename>` - reads data from the given file and includes the result as `ssl.key`
diff --git a/packages/pg-connection-string/test/parse.js b/packages/pg-connection-string/test/parse.js
index feb616a4f..d66425c35 100644
--- a/packages/pg-connection-string/test/parse.js
+++ b/packages/pg-connection-string/test/parse.js
@@ -275,7 +275,7 @@ describe('parse', function () {
     var connectionString = 'pg:///?sslmode=verify-ca'
     var subject = parse(connectionString)
     subject.ssl.should.have.property('checkServerIdentity').that.is.a('function')
-    expect(subject.ssl.checkServerIdentity()).be.undefined
+    expect(subject.ssl.checkServerIdentity()).to.be.undefined
   })
 
   it('configuration parameter sslmode=verify-full', function () {
@@ -289,7 +289,7 @@ describe('parse', function () {
     var subject = parse(connectionString)
     subject.ssl.should.have.property('ca', 'example ca\n')
     subject.ssl.should.have.property('checkServerIdentity').that.is.a('function')
-    expect(subject.ssl.checkServerIdentity()).be.undefined
+    expect(subject.ssl.checkServerIdentity()).to.be.undefined
   })
 
   it('allow other params like max, ...', function () {

From 8d56f865a8e9b8c7aa801e8a5357ad119e63976f Mon Sep 17 00:00:00 2001
From: "Herman J. Radtke III" <herman@hermanradtke.com>
Date: Tue, 8 Apr 2025 21:08:51 -0400
Subject: [PATCH 3/9] introduce sslcompat=libpq flag to support libpq sslmode
 semantics

---
 packages/pg-connection-string/README.md     |  7 ++-
 packages/pg-connection-string/index.js      | 63 ++++++++++++++-------
 packages/pg-connection-string/test/parse.js | 46 ++++++++++++---
 3 files changed, 86 insertions(+), 30 deletions(-)

diff --git a/packages/pg-connection-string/README.md b/packages/pg-connection-string/README.md
index d90bc03f8..38f659f60 100644
--- a/packages/pg-connection-string/README.md
+++ b/packages/pg-connection-string/README.md
@@ -87,12 +87,17 @@ Query parameters follow a `?` character, including the following special query p
  * `host=<host>` - sets `host` property, overriding the URL's host
  * `encoding=<encoding>` - sets the `client_encoding` property
  * `ssl=1`, `ssl=true`, `ssl=0`, `ssl=false` - sets `ssl` to true or false, accordingly
-* `sslmode=<sslmode>`
+ * `sslcompat=libpq` - use libpq semantics for `sslmode`
+ * `sslmode=<sslmode>` when `sslcompat=libpq`
    * `sslmode=disable` - sets `ssl` to false
    * `sslmode=no-verify`, `sslmode=prefer` - sets `ssl` to `{ rejectUnauthorized: false }`
    * `sslmode=require` - sets `ssl` to `{ rejectUnauthorized: false }` unless `sslrootcert` is specified, in which case it behaves like `verify-ca`
    * `sslmode=verify-ca` - sets `ssl` to `{ checkServerIdentity: no-op }` (verify CA, but not server identity)
    * `sslmode=verify-full` - sets `ssl` to `{}` (verify CA and server identity)
+ * `sslmode=<sslmode>` when `sslcompat` is not set
+   * `sslmode=disable` - sets `ssl` to false
+   * `sslmode=no-verify` - sets `ssl` to `{ rejectUnauthorized: false }`
+   * `sslmode=prefer`, `sslmode=require`, `sslmode=verify-ca`, `sslmode=verify-full` - sets `ssl` to true
  * `sslcert=<filename>` - reads data from the given file and includes the result as `ssl.cert`
  * `sslkey=<filename>` - reads data from the given file and includes the result as `ssl.key`
  * `sslrootcert=<filename>` - reads data from the given file and includes the result as `ssl.ca`
diff --git a/packages/pg-connection-string/index.js b/packages/pg-connection-string/index.js
index 5a4d59bbf..73f207cc9 100644
--- a/packages/pg-connection-string/index.js
+++ b/packages/pg-connection-string/index.js
@@ -87,31 +87,50 @@ function parse(str) {
     config.ssl.ca = fs.readFileSync(config.sslrootcert).toString()
   }
 
-  switch (config.sslmode) {
-    case 'disable': {
-      config.ssl = false
-      break
-    }
-    case 'prefer':
-    case 'no-verify': {
-      config.ssl.rejectUnauthorized = false
-      break
-    }
-    case 'require': {
-      if (config.sslrootcert) {
-        // If a root CA is specified, behavior of `sslmode=require` will be the same as that of `verify-ca`
-        config.ssl.checkServerIdentity = function () {}
-      } else {
+  if (config.sslcompat === 'libpq') {
+    switch (config.sslmode) {
+      case 'disable': {
+        config.ssl = false
+        break
+      }
+      case 'prefer':
+      case 'no-verify': {
         config.ssl.rejectUnauthorized = false
+        break
+      }
+      case 'require': {
+        if (config.sslrootcert) {
+          // If a root CA is specified, behavior of `sslmode=require` will be the same as that of `verify-ca`
+          config.ssl.checkServerIdentity = function () {}
+        } else {
+          config.ssl.rejectUnauthorized = false
+        }
+        break
+      }
+      case 'verify-ca': {
+        config.ssl.checkServerIdentity = function () {}
+        break
+      }
+      case 'verify-full': {
+        break
       }
-      break
-    }
-    case 'verify-ca': {
-      config.ssl.checkServerIdentity = function () {}
-      break
     }
-    case 'verify-full': {
-      break
+  } else {
+    switch (config.sslmode) {
+      case 'disable': {
+        config.ssl = false
+        break
+      }
+      case 'prefer':
+      case 'require':
+      case 'verify-ca':
+      case 'verify-full': {
+        break
+      }
+      case 'no-verify': {
+        config.ssl.rejectUnauthorized = false
+        break
+      }
     }
   }
 
diff --git a/packages/pg-connection-string/test/parse.js b/packages/pg-connection-string/test/parse.js
index d66425c35..fe9fec23a 100644
--- a/packages/pg-connection-string/test/parse.js
+++ b/packages/pg-connection-string/test/parse.js
@@ -1,6 +1,7 @@
 'use strict'
 
 var chai = require('chai')
+var expect = chai.expect
 chai.should()
 
 var parse = require('../').parse
@@ -255,29 +256,52 @@ describe('parse', function () {
     subject.ssl.should.eql(false)
   })
 
-  it('configuration parameter sslmode=prefer', function () {
-    var connectionString = 'pg:///?sslmode=prefer'
+  it('configuration parameter sslmode=prefer with libpq compatibility', function () {
+    var connectionString = 'pg:///?sslmode=prefer&sslcompat=libpq'
     var subject = parse(connectionString)
     subject.ssl.should.eql({
       rejectUnauthorized: false,
     })
   })
 
-  it('configuration parameter sslmode=require', function () {
-    var connectionString = 'pg:///?sslmode=require'
+  it('configuration parameter sslmode=require with libpq compatibility', function () {
+    var connectionString = 'pg:///?sslmode=require&sslcompat=libpq'
     var subject = parse(connectionString)
     subject.ssl.should.eql({
       rejectUnauthorized: false,
     })
   })
 
-  it('configuration parameter sslmode=verify-ca', function () {
-    var connectionString = 'pg:///?sslmode=verify-ca'
+  it('configuration parameter sslmode=verify-ca with libpq compatibility', function () {
+    var connectionString = 'pg:///?sslmode=verify-ca&sslcompat=libpq'
     var subject = parse(connectionString)
     subject.ssl.should.have.property('checkServerIdentity').that.is.a('function')
     expect(subject.ssl.checkServerIdentity()).to.be.undefined
   })
 
+  it('configuration parameter sslmode=prefer with libpq compatibility', function () {
+    var connectionString = 'pg:///?sslmode=prefer&sslcompat=libpq'
+    var subject = parse(connectionString)
+    subject.ssl.should.eql({
+      rejectUnauthorized: false,
+    })
+  })
+
+  it('configuration parameter sslmode=require with libpq compatibility', function () {
+    var connectionString = 'pg:///?sslmode=require&sslcompat=libpq'
+    var subject = parse(connectionString)
+    subject.ssl.should.eql({
+      rejectUnauthorized: false,
+    })
+  })
+
+  it('configuration parameter sslmode=verify-ca with libpq compatibility', function () {
+    var connectionString = 'pg:///?sslmode=verify-ca&sslcompat=libpq'
+    var subject = parse(connectionString)
+    subject.ssl.should.have.property('checkServerIdentity').that.is.a('function')
+    expect(subject.ssl.checkServerIdentity()).be.undefined
+  })
+
   it('configuration parameter sslmode=verify-full', function () {
     var connectionString = 'pg:///?sslmode=verify-full'
     var subject = parse(connectionString)
@@ -287,9 +311,17 @@ describe('parse', function () {
   it('configuration parameter ssl=true and sslmode=require still work with sslrootcert=/path/to/ca', function () {
     var connectionString = 'pg:///?ssl=true&sslrootcert=' + __dirname + '/example.ca&sslmode=require'
     var subject = parse(connectionString)
+    subject.ssl.should.eql({
+      ca: 'example ca\n',
+    })
+  })
+
+  it('configuration parameter ssl=true and sslmode=require still work with sslrootcert=/path/to/ca with libpq compatibility', function () {
+    var connectionString = 'pg:///?ssl=true&sslrootcert=' + __dirname + '/example.ca&sslmode=require&sslcompat=libpq'
+    var subject = parse(connectionString)
     subject.ssl.should.have.property('ca', 'example ca\n')
     subject.ssl.should.have.property('checkServerIdentity').that.is.a('function')
-    expect(subject.ssl.checkServerIdentity()).to.be.undefined
+    expect(subject.ssl.checkServerIdentity()).be.undefined
   })
 
   it('allow other params like max, ...', function () {

From 116459377639955f2bb5123280159525a50ab96b Mon Sep 17 00:00:00 2001
From: "Herman J. Radtke III" <herman@hermanradtke.com>
Date: Wed, 9 Apr 2025 17:44:26 -0400
Subject: [PATCH 4/9] document behavior of verify-ca for libpq compat

---
 packages/pg-connection-string/README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/pg-connection-string/README.md b/packages/pg-connection-string/README.md
index 38f659f60..2f1275ae0 100644
--- a/packages/pg-connection-string/README.md
+++ b/packages/pg-connection-string/README.md
@@ -92,7 +92,7 @@ Query parameters follow a `?` character, including the following special query p
    * `sslmode=disable` - sets `ssl` to false
    * `sslmode=no-verify`, `sslmode=prefer` - sets `ssl` to `{ rejectUnauthorized: false }`
    * `sslmode=require` - sets `ssl` to `{ rejectUnauthorized: false }` unless `sslrootcert` is specified, in which case it behaves like `verify-ca`
-   * `sslmode=verify-ca` - sets `ssl` to `{ checkServerIdentity: no-op }` (verify CA, but not server identity)
+   * `sslmode=verify-ca` - sets `ssl` to `{ checkServerIdentity: no-op }` (verify CA, but not server identity). This verifies the presented certificate against the effective CA, i.e. the one specified in sslrootcert or the system CA if sslrootcert was not specified.
    * `sslmode=verify-full` - sets `ssl` to `{}` (verify CA and server identity)
  * `sslmode=<sslmode>` when `sslcompat` is not set
    * `sslmode=disable` - sets `ssl` to false

From f16737e3ce92a3fff44c6aa876880e74408659d3 Mon Sep 17 00:00:00 2001
From: "Herman J. Radtke III" <herman@hermanradtke.com>
Date: Wed, 9 Apr 2025 17:46:03 -0400
Subject: [PATCH 5/9] remove sslmode=no-verify from libpq compat

---
 packages/pg-connection-string/README.md | 2 +-
 packages/pg-connection-string/index.js  | 3 +--
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/packages/pg-connection-string/README.md b/packages/pg-connection-string/README.md
index 2f1275ae0..60947ea1b 100644
--- a/packages/pg-connection-string/README.md
+++ b/packages/pg-connection-string/README.md
@@ -90,7 +90,7 @@ Query parameters follow a `?` character, including the following special query p
  * `sslcompat=libpq` - use libpq semantics for `sslmode`
  * `sslmode=<sslmode>` when `sslcompat=libpq`
    * `sslmode=disable` - sets `ssl` to false
-   * `sslmode=no-verify`, `sslmode=prefer` - sets `ssl` to `{ rejectUnauthorized: false }`
+   * `sslmode=prefer` - sets `ssl` to `{ rejectUnauthorized: false }`
    * `sslmode=require` - sets `ssl` to `{ rejectUnauthorized: false }` unless `sslrootcert` is specified, in which case it behaves like `verify-ca`
    * `sslmode=verify-ca` - sets `ssl` to `{ checkServerIdentity: no-op }` (verify CA, but not server identity). This verifies the presented certificate against the effective CA, i.e. the one specified in sslrootcert or the system CA if sslrootcert was not specified.
    * `sslmode=verify-full` - sets `ssl` to `{}` (verify CA and server identity)
diff --git a/packages/pg-connection-string/index.js b/packages/pg-connection-string/index.js
index 73f207cc9..c1fe58277 100644
--- a/packages/pg-connection-string/index.js
+++ b/packages/pg-connection-string/index.js
@@ -93,8 +93,7 @@ function parse(str) {
         config.ssl = false
         break
       }
-      case 'prefer':
-      case 'no-verify': {
+      case 'prefer': {
         config.ssl.rejectUnauthorized = false
         break
       }

From 165b5c6e05dadbbea9175a312ccdb4cbaf28c500 Mon Sep 17 00:00:00 2001
From: "Herman J. Radtke III" <herman@hermanradtke.com>
Date: Tue, 8 Apr 2025 21:17:10 -0400
Subject: [PATCH 6/9] require verify-ca to specify sslrootcert when sslmode is
 libpq compat

---
 packages/pg-connection-string/README.md     | 11 ++--
 packages/pg-connection-string/index.js      |  5 ++
 packages/pg-connection-string/test/parse.js | 58 +++++++++++++--------
 3 files changed, 48 insertions(+), 26 deletions(-)

diff --git a/packages/pg-connection-string/README.md b/packages/pg-connection-string/README.md
index 60947ea1b..52d01e87a 100644
--- a/packages/pg-connection-string/README.md
+++ b/packages/pg-connection-string/README.md
@@ -88,18 +88,21 @@ Query parameters follow a `?` character, including the following special query p
  * `encoding=<encoding>` - sets the `client_encoding` property
  * `ssl=1`, `ssl=true`, `ssl=0`, `ssl=false` - sets `ssl` to true or false, accordingly
  * `sslcompat=libpq` - use libpq semantics for `sslmode`
+ * `sslmode=<sslmode>` when `sslcompat` is not set
+   * `sslmode=disable` - sets `ssl` to false
+   * `sslmode=no-verify` - sets `ssl` to `{ rejectUnauthorized: false }`
+   * `sslmode=prefer`, `sslmode=require`, `sslmode=verify-ca`, `sslmode=verify-full` - sets `ssl` to true
  * `sslmode=<sslmode>` when `sslcompat=libpq`
    * `sslmode=disable` - sets `ssl` to false
    * `sslmode=prefer` - sets `ssl` to `{ rejectUnauthorized: false }`
    * `sslmode=require` - sets `ssl` to `{ rejectUnauthorized: false }` unless `sslrootcert` is specified, in which case it behaves like `verify-ca`
    * `sslmode=verify-ca` - sets `ssl` to `{ checkServerIdentity: no-op }` (verify CA, but not server identity). This verifies the presented certificate against the effective CA, i.e. the one specified in sslrootcert or the system CA if sslrootcert was not specified.
    * `sslmode=verify-full` - sets `ssl` to `{}` (verify CA and server identity)
- * `sslmode=<sslmode>` when `sslcompat` is not set
-   * `sslmode=disable` - sets `ssl` to false
-   * `sslmode=no-verify` - sets `ssl` to `{ rejectUnauthorized: false }`
-   * `sslmode=prefer`, `sslmode=require`, `sslmode=verify-ca`, `sslmode=verify-full` - sets `ssl` to true
  * `sslcert=<filename>` - reads data from the given file and includes the result as `ssl.cert`
  * `sslkey=<filename>` - reads data from the given file and includes the result as `ssl.key`
  * `sslrootcert=<filename>` - reads data from the given file and includes the result as `ssl.ca`
 
 A bare relative URL, such as `salesdata`, will indicate a database name while leaving other properties empty.
+
+> [!CAUTION]
+> Choosing an sslmode other than verify-full has serious security implications. Please read https://www.postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-SSLMODE-STATEMENTS to understand the trade-offs.
diff --git a/packages/pg-connection-string/index.js b/packages/pg-connection-string/index.js
index c1fe58277..d10442c54 100644
--- a/packages/pg-connection-string/index.js
+++ b/packages/pg-connection-string/index.js
@@ -107,6 +107,11 @@ function parse(str) {
         break
       }
       case 'verify-ca': {
+        if (!config.ssl.ca) {
+          throw new Error(
+            'SECURITY WARNING: Using sslmode=verify-ca requires specifying a CA with sslrootcert. If a public CA is used, verify-ca allows connections to a server that somebody else may have registered with the CA, making you vulnerable to Man-in-the-Middle attacks. Either specify a custom CA certificate with sslrootcert parameter or use sslmode=verify-full for proper security.'
+          )
+        }
         config.ssl.checkServerIdentity = function () {}
         break
       }
diff --git a/packages/pg-connection-string/test/parse.js b/packages/pg-connection-string/test/parse.js
index fe9fec23a..ce4fa7faa 100644
--- a/packages/pg-connection-string/test/parse.js
+++ b/packages/pg-connection-string/test/parse.js
@@ -256,27 +256,42 @@ describe('parse', function () {
     subject.ssl.should.eql(false)
   })
 
-  it('configuration parameter sslmode=prefer with libpq compatibility', function () {
-    var connectionString = 'pg:///?sslmode=prefer&sslcompat=libpq'
+  it('configuration parameter sslmode=prefer', function () {
+    var connectionString = 'pg:///?sslmode=prefer'
     var subject = parse(connectionString)
-    subject.ssl.should.eql({
-      rejectUnauthorized: false,
-    })
+    subject.ssl.should.eql({})
   })
 
-  it('configuration parameter sslmode=require with libpq compatibility', function () {
-    var connectionString = 'pg:///?sslmode=require&sslcompat=libpq'
+  it('configuration parameter sslmode=require', function () {
+    var connectionString = 'pg:///?sslmode=require'
+    var subject = parse(connectionString)
+    subject.ssl.should.eql({})
+  })
+
+  it('configuration parameter sslmode=verify-ca', function () {
+    var connectionString = 'pg:///?sslmode=verify-ca'
+    var subject = parse(connectionString)
+    subject.ssl.should.eql({})
+  })
+
+  it('configuration parameter sslmode=verify-full', function () {
+    var connectionString = 'pg:///?sslmode=verify-full'
+    var subject = parse(connectionString)
+    subject.ssl.should.eql({})
+  })
+
+  it('configuration parameter ssl=true and sslmode=require still work with sslrootcert=/path/to/ca', function () {
+    var connectionString = 'pg:///?ssl=true&sslrootcert=' + __dirname + '/example.ca&sslmode=require'
     var subject = parse(connectionString)
     subject.ssl.should.eql({
-      rejectUnauthorized: false,
+      ca: 'example ca\n',
     })
   })
 
-  it('configuration parameter sslmode=verify-ca with libpq compatibility', function () {
-    var connectionString = 'pg:///?sslmode=verify-ca&sslcompat=libpq'
+  it('configuration parameter sslmode=disable with libpq compatibility', function () {
+    var connectionString = 'pg:///?sslmode=disable&sslcompat=libpq'
     var subject = parse(connectionString)
-    subject.ssl.should.have.property('checkServerIdentity').that.is.a('function')
-    expect(subject.ssl.checkServerIdentity()).to.be.undefined
+    subject.ssl.should.eql(false)
   })
 
   it('configuration parameter sslmode=prefer with libpq compatibility', function () {
@@ -297,25 +312,24 @@ describe('parse', function () {
 
   it('configuration parameter sslmode=verify-ca with libpq compatibility', function () {
     var connectionString = 'pg:///?sslmode=verify-ca&sslcompat=libpq'
+    expect(function () {
+      parse(connectionString)
+    }).to.throw()
+  })
+
+  it('configuration parameter sslmode=verify-ca and sslrootcert with libpq compatibility', function () {
+    var connectionString = 'pg:///?sslmode=verify-ca&sslcompat=libpq&sslrootcert=' + __dirname + '/example.ca'
     var subject = parse(connectionString)
     subject.ssl.should.have.property('checkServerIdentity').that.is.a('function')
     expect(subject.ssl.checkServerIdentity()).be.undefined
   })
 
-  it('configuration parameter sslmode=verify-full', function () {
-    var connectionString = 'pg:///?sslmode=verify-full'
+  it('configuration parameter sslmode=verify-full with libpq compatibility', function () {
+    var connectionString = 'pg:///?sslmode=verify-full&sslcompat=libpq'
     var subject = parse(connectionString)
     subject.ssl.should.eql({})
   })
 
-  it('configuration parameter ssl=true and sslmode=require still work with sslrootcert=/path/to/ca', function () {
-    var connectionString = 'pg:///?ssl=true&sslrootcert=' + __dirname + '/example.ca&sslmode=require'
-    var subject = parse(connectionString)
-    subject.ssl.should.eql({
-      ca: 'example ca\n',
-    })
-  })
-
   it('configuration parameter ssl=true and sslmode=require still work with sslrootcert=/path/to/ca with libpq compatibility', function () {
     var connectionString = 'pg:///?ssl=true&sslrootcert=' + __dirname + '/example.ca&sslmode=require&sslcompat=libpq'
     var subject = parse(connectionString)

From ab4d18546008fb8e59c0d8f7ec5ec3028c7649e7 Mon Sep 17 00:00:00 2001
From: "Herman J. Radtke III" <herman@hermanradtke.com>
Date: Thu, 17 Apr 2025 05:58:26 -0400
Subject: [PATCH 7/9] Update packages/pg-connection-string/README.md

Co-authored-by: Patrick Malouin <p.malouin@gmail.com>
---
 packages/pg-connection-string/README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/pg-connection-string/README.md b/packages/pg-connection-string/README.md
index 52d01e87a..e1bee241e 100644
--- a/packages/pg-connection-string/README.md
+++ b/packages/pg-connection-string/README.md
@@ -96,7 +96,7 @@ Query parameters follow a `?` character, including the following special query p
    * `sslmode=disable` - sets `ssl` to false
    * `sslmode=prefer` - sets `ssl` to `{ rejectUnauthorized: false }`
    * `sslmode=require` - sets `ssl` to `{ rejectUnauthorized: false }` unless `sslrootcert` is specified, in which case it behaves like `verify-ca`
-   * `sslmode=verify-ca` - sets `ssl` to `{ checkServerIdentity: no-op }` (verify CA, but not server identity). This verifies the presented certificate against the effective CA, i.e. the one specified in sslrootcert or the system CA if sslrootcert was not specified.
+   * `sslmode=verify-ca` - sets `ssl` to `{ checkServerIdentity: no-op }` (verify CA, but not server identity). This verifies the presented certificate against the effective CA specified in sslrootcert.
    * `sslmode=verify-full` - sets `ssl` to `{}` (verify CA and server identity)
  * `sslcert=<filename>` - reads data from the given file and includes the result as `ssl.cert`
  * `sslkey=<filename>` - reads data from the given file and includes the result as `ssl.key`

From 7aa88ad4b3a99ef9bca6d88a5d9edd45953fe8de Mon Sep 17 00:00:00 2001
From: "Herman J. Radtke III" <herman@hermanradtke.com>
Date: Thu, 17 Apr 2025 06:14:16 -0400
Subject: [PATCH 8/9] add useLibpqCompat parsing option

---
 packages/pg-connection-string/index.d.ts    |  7 ++-
 packages/pg-connection-string/index.js      |  8 ++-
 packages/pg-connection-string/test/parse.js | 57 +++++++++++++++++++++
 3 files changed, 69 insertions(+), 3 deletions(-)

diff --git a/packages/pg-connection-string/index.d.ts b/packages/pg-connection-string/index.d.ts
index d68cc23d3..88618ba92 100644
--- a/packages/pg-connection-string/index.d.ts
+++ b/packages/pg-connection-string/index.d.ts
@@ -1,6 +1,11 @@
 import { ClientConfig } from 'pg'
 
-export function parse(connectionString: string): ConnectionOptions
+export function parse(connectionString: string, options: Options): ConnectionOptions
+
+export interface Options {
+  // Use libpq semantics when interpreting the connection string
+  useLibpqCompat?: boolean
+}
 
 export interface ConnectionOptions {
   host: string | null
diff --git a/packages/pg-connection-string/index.js b/packages/pg-connection-string/index.js
index d10442c54..39ac3c613 100644
--- a/packages/pg-connection-string/index.js
+++ b/packages/pg-connection-string/index.js
@@ -5,7 +5,7 @@
 //MIT License
 
 //parses a connection string
-function parse(str) {
+function parse(str, options = {}) {
   //unix socket
   if (str.charAt(0) === '/') {
     const config = str.split(' ')
@@ -87,7 +87,11 @@ function parse(str) {
     config.ssl.ca = fs.readFileSync(config.sslrootcert).toString()
   }
 
-  if (config.sslcompat === 'libpq') {
+  if (options.useLibpqCompat && config.sslcompat) {
+    throw new Error('Both useLibpqCompat and sslcompat are set. Please use only one of them.')
+  }
+
+  if (config.sslcompat === 'libpq' || options.useLibpqCompat) {
     switch (config.sslmode) {
       case 'disable': {
         config.ssl = false
diff --git a/packages/pg-connection-string/test/parse.js b/packages/pg-connection-string/test/parse.js
index ce4fa7faa..e184b693b 100644
--- a/packages/pg-connection-string/test/parse.js
+++ b/packages/pg-connection-string/test/parse.js
@@ -338,6 +338,63 @@ describe('parse', function () {
     expect(subject.ssl.checkServerIdentity()).be.undefined
   })
 
+  it('configuration parameter sslmode=disable with useLibpqCompat option', function () {
+    var connectionString = 'pg:///?sslmode=disable'
+    var subject = parse(connectionString, { useLibpqCompat: true })
+    subject.ssl.should.eql(false)
+  })
+
+  it('configuration parameter sslmode=prefer with useLibpqCompat option', function () {
+    var connectionString = 'pg:///?sslmode=prefer'
+    var subject = parse(connectionString, { useLibpqCompat: true })
+    subject.ssl.should.eql({
+      rejectUnauthorized: false,
+    })
+  })
+
+  it('configuration parameter sslmode=require with useLibpqCompat option', function () {
+    var connectionString = 'pg:///?sslmode=require'
+    var subject = parse(connectionString, { useLibpqCompat: true })
+    subject.ssl.should.eql({
+      rejectUnauthorized: false,
+    })
+  })
+
+  it('configuration parameter sslmode=verify-ca with useLibpqCompat option', function () {
+    var connectionString = 'pg:///?sslmode=verify-ca'
+    expect(function () {
+      parse(connectionString, { useLibpqCompat: true })
+    }).to.throw()
+  })
+
+  it('configuration parameter sslmode=verify-ca and sslrootcert with useLibpqCompat option', function () {
+    var connectionString = 'pg:///?sslmode=verify-ca&sslrootcert=' + __dirname + '/example.ca'
+    var subject = parse(connectionString, { useLibpqCompat: true })
+    subject.ssl.should.have.property('checkServerIdentity').that.is.a('function')
+    expect(subject.ssl.checkServerIdentity()).be.undefined
+  })
+
+  it('configuration parameter sslmode=verify-full with useLibpqCompat option', function () {
+    var connectionString = 'pg:///?sslmode=verify-full'
+    var subject = parse(connectionString, { useLibpqCompat: true })
+    subject.ssl.should.eql({})
+  })
+
+  it('configuration parameter ssl=true and sslmode=require still work with sslrootcert=/path/to/ca with useLibpqCompat option', function () {
+    var connectionString = 'pg:///?ssl=true&sslrootcert=' + __dirname + '/example.ca&sslmode=require'
+    var subject = parse(connectionString, { useLibpqCompat: true })
+    subject.ssl.should.have.property('ca', 'example ca\n')
+    subject.ssl.should.have.property('checkServerIdentity').that.is.a('function')
+    expect(subject.ssl.checkServerIdentity()).be.undefined
+  })
+
+  it('does not allow sslcompat query parameter and useLibpqCompat option at the same time', function () {
+    var connectionString = 'pg:///?sslcompat=libpq'
+    expect(function () {
+      parse(connectionString, { useLibpqCompat: true })
+    }).to.throw()
+  })
+
   it('allow other params like max, ...', function () {
     var subject = parse('pg://myhost/db?max=18&min=4')
     subject.max.should.equal('18')

From 89161f388009e2295cfe02ec79cb508bc7d12169 Mon Sep 17 00:00:00 2001
From: "Herman J. Radtke III" <herman@hermanradtke.com>
Date: Thu, 17 Apr 2025 06:14:44 -0400
Subject: [PATCH 9/9] rename sslcompat=libpq to uselibpqcompat=true

---
 packages/pg-connection-string/README.md     |  2 +-
 packages/pg-connection-string/index.js      |  6 ++--
 packages/pg-connection-string/test/parse.js | 31 +++++++++++----------
 3 files changed, 20 insertions(+), 19 deletions(-)

diff --git a/packages/pg-connection-string/README.md b/packages/pg-connection-string/README.md
index e1bee241e..26d52bff7 100644
--- a/packages/pg-connection-string/README.md
+++ b/packages/pg-connection-string/README.md
@@ -87,7 +87,7 @@ Query parameters follow a `?` character, including the following special query p
  * `host=<host>` - sets `host` property, overriding the URL's host
  * `encoding=<encoding>` - sets the `client_encoding` property
  * `ssl=1`, `ssl=true`, `ssl=0`, `ssl=false` - sets `ssl` to true or false, accordingly
- * `sslcompat=libpq` - use libpq semantics for `sslmode`
+ * `uselibpqcompat=true` - use libpq semantics
  * `sslmode=<sslmode>` when `sslcompat` is not set
    * `sslmode=disable` - sets `ssl` to false
    * `sslmode=no-verify` - sets `ssl` to `{ rejectUnauthorized: false }`
diff --git a/packages/pg-connection-string/index.js b/packages/pg-connection-string/index.js
index 39ac3c613..d40ec1252 100644
--- a/packages/pg-connection-string/index.js
+++ b/packages/pg-connection-string/index.js
@@ -87,11 +87,11 @@ function parse(str, options = {}) {
     config.ssl.ca = fs.readFileSync(config.sslrootcert).toString()
   }
 
-  if (options.useLibpqCompat && config.sslcompat) {
-    throw new Error('Both useLibpqCompat and sslcompat are set. Please use only one of them.')
+  if (options.useLibpqCompat && config.uselibpqcompat) {
+    throw new Error('Both useLibpqCompat and uselibpqcompat are set. Please use only one of them.')
   }
 
-  if (config.sslcompat === 'libpq' || options.useLibpqCompat) {
+  if (config.uselibpqcompat === 'true' || options.useLibpqCompat) {
     switch (config.sslmode) {
       case 'disable': {
         config.ssl = false
diff --git a/packages/pg-connection-string/test/parse.js b/packages/pg-connection-string/test/parse.js
index e184b693b..12f64ab49 100644
--- a/packages/pg-connection-string/test/parse.js
+++ b/packages/pg-connection-string/test/parse.js
@@ -288,50 +288,51 @@ describe('parse', function () {
     })
   })
 
-  it('configuration parameter sslmode=disable with libpq compatibility', function () {
-    var connectionString = 'pg:///?sslmode=disable&sslcompat=libpq'
+  it('configuration parameter sslmode=disable with uselibpqcompat query param', function () {
+    var connectionString = 'pg:///?sslmode=disable&uselibpqcompat=true'
     var subject = parse(connectionString)
     subject.ssl.should.eql(false)
   })
 
-  it('configuration parameter sslmode=prefer with libpq compatibility', function () {
-    var connectionString = 'pg:///?sslmode=prefer&sslcompat=libpq'
+  it('configuration parameter sslmode=prefer with uselibpqcompat query param', function () {
+    var connectionString = 'pg:///?sslmode=prefer&uselibpqcompat=true'
     var subject = parse(connectionString)
     subject.ssl.should.eql({
       rejectUnauthorized: false,
     })
   })
 
-  it('configuration parameter sslmode=require with libpq compatibility', function () {
-    var connectionString = 'pg:///?sslmode=require&sslcompat=libpq'
+  it('configuration parameter sslmode=require with uselibpqcompat query param', function () {
+    var connectionString = 'pg:///?sslmode=require&uselibpqcompat=true'
     var subject = parse(connectionString)
     subject.ssl.should.eql({
       rejectUnauthorized: false,
     })
   })
 
-  it('configuration parameter sslmode=verify-ca with libpq compatibility', function () {
-    var connectionString = 'pg:///?sslmode=verify-ca&sslcompat=libpq'
+  it('configuration parameter sslmode=verify-ca with uselibpqcompat query param', function () {
+    var connectionString = 'pg:///?sslmode=verify-ca&uselibpqcompat=true'
     expect(function () {
       parse(connectionString)
     }).to.throw()
   })
 
-  it('configuration parameter sslmode=verify-ca and sslrootcert with libpq compatibility', function () {
-    var connectionString = 'pg:///?sslmode=verify-ca&sslcompat=libpq&sslrootcert=' + __dirname + '/example.ca'
+  it('configuration parameter sslmode=verify-ca and sslrootcert with uselibpqcompat query param', function () {
+    var connectionString = 'pg:///?sslmode=verify-ca&uselibpqcompat=true&sslrootcert=' + __dirname + '/example.ca'
     var subject = parse(connectionString)
     subject.ssl.should.have.property('checkServerIdentity').that.is.a('function')
     expect(subject.ssl.checkServerIdentity()).be.undefined
   })
 
-  it('configuration parameter sslmode=verify-full with libpq compatibility', function () {
-    var connectionString = 'pg:///?sslmode=verify-full&sslcompat=libpq'
+  it('configuration parameter sslmode=verify-full with uselibpqcompat query param', function () {
+    var connectionString = 'pg:///?sslmode=verify-full&uselibpqcompat=true'
     var subject = parse(connectionString)
     subject.ssl.should.eql({})
   })
 
-  it('configuration parameter ssl=true and sslmode=require still work with sslrootcert=/path/to/ca with libpq compatibility', function () {
-    var connectionString = 'pg:///?ssl=true&sslrootcert=' + __dirname + '/example.ca&sslmode=require&sslcompat=libpq'
+  it('configuration parameter ssl=true and sslmode=require still work with sslrootcert=/path/to/ca with uselibpqcompat query param', function () {
+    var connectionString =
+      'pg:///?ssl=true&sslrootcert=' + __dirname + '/example.ca&sslmode=require&uselibpqcompat=true'
     var subject = parse(connectionString)
     subject.ssl.should.have.property('ca', 'example ca\n')
     subject.ssl.should.have.property('checkServerIdentity').that.is.a('function')
@@ -389,7 +390,7 @@ describe('parse', function () {
   })
 
   it('does not allow sslcompat query parameter and useLibpqCompat option at the same time', function () {
-    var connectionString = 'pg:///?sslcompat=libpq'
+    var connectionString = 'pg:///?uselibpqcompat=true'
     expect(function () {
       parse(connectionString, { useLibpqCompat: true })
     }).to.throw()