From dc9c152a87ec51a1f647447268917243d2eab1fd Mon Sep 17 00:00:00 2001
From: Alex Burley <alex.burley@tray.io>
Date: Mon, 24 Feb 2020 10:09:53 +0000
Subject: [PATCH] Accept the maxVersion and minVersion properties in connection
 ssl option

fixes #2301
closes #2304
---
 Changes.md                                    |  1 +
 Readme.md                                     |  4 ++
 lib/Connection.js                             |  4 +-
 .../test-connection-ssl-max-version-accept.js | 42 ++++++++++++++++++
 .../test-connection-ssl-max-version-reject.js | 44 +++++++++++++++++++
 .../test-connection-ssl-min-version-accept.js | 42 ++++++++++++++++++
 .../test-connection-ssl-min-version-reject.js | 44 +++++++++++++++++++
 7 files changed, 180 insertions(+), 1 deletion(-)
 create mode 100644 test/unit/connection/test-connection-ssl-max-version-accept.js
 create mode 100644 test/unit/connection/test-connection-ssl-max-version-reject.js
 create mode 100644 test/unit/connection/test-connection-ssl-min-version-accept.js
 create mode 100644 test/unit/connection/test-connection-ssl-min-version-reject.js

diff --git a/Changes.md b/Changes.md
index a0ea3528a..077764d2d 100644
--- a/Changes.md
+++ b/Changes.md
@@ -6,6 +6,7 @@ you spot any mistakes.
 
 ## HEAD
 
+* Accept the `maxVersion` and `minVersion` properties in connection `ssl` option #2301 #2304
 * Support Node.js 14.x
 * Support Node.js 15.x
 * Support Node.js 16.x
diff --git a/Readme.md b/Readme.md
index 4726bc154..c83c14766 100644
--- a/Readme.md
+++ b/Readme.md
@@ -280,6 +280,10 @@ following options:
 * `ciphers`: The ciphers to use to use in the SSL handshake instead of the default ones for Node.js. This
   is passed as the `ciphers` option for [`tls.createSecureContext()`] call (or underlying [`crypto.createCredentials()`]
   if using Node.js below 0.12).
+* `maxVersion`: This is passed as the `maxVersion` option for the underlying [`tls.createSecureContext()`]
+  call.
+* `minVersion`: This is passed as the `minVersion` option for the underlying [`tls.createSecureContext()`]
+  call.
 * `key`: This is passed as the `key` option for [`tls.createSecureContext()`] call (or underlying
   [`crypto.createCredentials()`] if using Node.js below 0.12).
 * `passphrase`: This is passed as the `passphrase` option for [`tls.createSecureContext()`] call (or
diff --git a/lib/Connection.js b/lib/Connection.js
index 6802255dd..76fcbb049 100644
--- a/lib/Connection.js
+++ b/lib/Connection.js
@@ -306,7 +306,7 @@ if (tls.TLSSocket) {
       });
 
       // cleartext <-> protocol
-      secureSocket.pipe(connection._protocol);
+      secureSocket.pipe(connection._protocol, { end: false });
       connection._protocol.on('data', function(data) {
         secureSocket.write(data);
       });
@@ -465,6 +465,8 @@ function createSecureContext (config, cb) {
       cert       : config.ssl.cert,
       ciphers    : config.ssl.ciphers,
       key        : config.ssl.key,
+      maxVersion : config.ssl.maxVersion,
+      minVersion : config.ssl.minVersion,
       passphrase : config.ssl.passphrase
     });
   } catch (err) {
diff --git a/test/unit/connection/test-connection-ssl-max-version-accept.js b/test/unit/connection/test-connection-ssl-max-version-accept.js
new file mode 100644
index 000000000..f2e7419f1
--- /dev/null
+++ b/test/unit/connection/test-connection-ssl-max-version-accept.js
@@ -0,0 +1,42 @@
+var assert = require('assert');
+var common = require('../../common');
+var tls    = require('tls');
+
+if (!tls.createSecureContext) {
+  common.skipTest('node ' + process.version + ' does not support tls.createSecureContext()');
+}
+
+if (!tls.DEFAULT_MAX_VERSION) {
+  common.skipTest('node ' + process.version + ' does not support tls maxVersion');
+}
+
+var server = common.createFakeServer({
+  ssl: {
+    maxVersion : tls.DEFAULT_MAX_VERSION,
+    minVersion : tls.DEFAULT_MAX_VERSION
+  }
+});
+
+server.listen(0, function (err) {
+  assert.ifError(err);
+
+  var connection = common.createConnection({
+    port : server.port(),
+    ssl  : {
+      ca         : common.getSSLConfig().ca,
+      maxVersion : tls.DEFAULT_MAX_VERSION
+    }
+  });
+
+  connection.connect(function (err) {
+    assert.ifError(err);
+    connection.destroy();
+    server.destroy();
+  });
+});
+
+server.on('connection', function (incomingConnection) {
+  incomingConnection.handshake({
+    serverCapabilities1: common.ClientConstants.CLIENT_SSL
+  });
+});
diff --git a/test/unit/connection/test-connection-ssl-max-version-reject.js b/test/unit/connection/test-connection-ssl-max-version-reject.js
new file mode 100644
index 000000000..3660fa847
--- /dev/null
+++ b/test/unit/connection/test-connection-ssl-max-version-reject.js
@@ -0,0 +1,44 @@
+var assert = require('assert');
+var common = require('../../common');
+var tls    = require('tls');
+
+if (!tls.createSecureContext) {
+  common.skipTest('node ' + process.version + ' does not support tls.createSecureContext()');
+}
+
+if (!tls.DEFAULT_MAX_VERSION) {
+  common.skipTest('node ' + process.version + ' does not support tls maxVersion');
+}
+
+var server = common.createFakeServer({
+  ssl: {
+    maxVersion : tls.DEFAULT_MAX_VERSION,
+    minVersion : tls.DEFAULT_MAX_VERSION
+  }
+});
+
+server.listen(0, function (err) {
+  assert.ifError(err);
+
+  var connection = common.createConnection({
+    port : server.port(),
+    ssl  : {
+      ca         : common.getSSLConfig().ca,
+      maxVersion : tls.DEFAULT_MIN_VERSION
+    }
+  });
+
+  connection.connect(function (err) {
+    assert.ok(err);
+    assert.strictEqual(err.code, 'HANDSHAKE_SSL_ERROR');
+    assert.strictEqual(err.fatal, true);
+    connection.destroy();
+    server.destroy();
+  });
+});
+
+server.on('connection', function (incomingConnection) {
+  incomingConnection.handshake({
+    serverCapabilities1: common.ClientConstants.CLIENT_SSL
+  });
+});
diff --git a/test/unit/connection/test-connection-ssl-min-version-accept.js b/test/unit/connection/test-connection-ssl-min-version-accept.js
new file mode 100644
index 000000000..a7e06fbe7
--- /dev/null
+++ b/test/unit/connection/test-connection-ssl-min-version-accept.js
@@ -0,0 +1,42 @@
+var assert = require('assert');
+var common = require('../../common');
+var tls    = require('tls');
+
+if (!tls.createSecureContext) {
+  common.skipTest('node ' + process.version + ' does not support tls.createSecureContext()');
+}
+
+if (!tls.DEFAULT_MIN_VERSION) {
+  common.skipTest('node ' + process.version + ' does not support tls minVersion');
+}
+
+var server = common.createFakeServer({
+  ssl: {
+    maxVersion : tls.DEFAULT_MIN_VERSION,
+    minVersion : tls.DEFAULT_MIN_VERSION
+  }
+});
+
+server.listen(0, function (err) {
+  assert.ifError(err);
+
+  var connection = common.createConnection({
+    port : server.port(),
+    ssl  : {
+      ca         : common.getSSLConfig().ca,
+      minVersion : tls.DEFAULT_MIN_VERSION
+    }
+  });
+
+  connection.connect(function (err) {
+    assert.ifError(err);
+    connection.destroy();
+    server.destroy();
+  });
+});
+
+server.on('connection', function (incomingConnection) {
+  incomingConnection.handshake({
+    serverCapabilities1: common.ClientConstants.CLIENT_SSL
+  });
+});
diff --git a/test/unit/connection/test-connection-ssl-min-version-reject.js b/test/unit/connection/test-connection-ssl-min-version-reject.js
new file mode 100644
index 000000000..ea88fe3be
--- /dev/null
+++ b/test/unit/connection/test-connection-ssl-min-version-reject.js
@@ -0,0 +1,44 @@
+var assert = require('assert');
+var common = require('../../common');
+var tls    = require('tls');
+
+if (!tls.createSecureContext) {
+  common.skipTest('node ' + process.version + ' does not support tls.createSecureContext()');
+}
+
+if (!tls.DEFAULT_MIN_VERSION) {
+  common.skipTest('node ' + process.version + ' does not support tls minVersion');
+}
+
+var server = common.createFakeServer({
+  ssl: {
+    maxVersion : tls.DEFAULT_MIN_VERSION,
+    minVersion : tls.DEFAULT_MIN_VERSION
+  }
+});
+
+server.listen(0, function (err) {
+  assert.ifError(err);
+
+  var connection = common.createConnection({
+    port : server.port(),
+    ssl  : {
+      ca         : common.getSSLConfig().ca,
+      minVersion : tls.DEFAULT_MAX_VERSION
+    }
+  });
+
+  connection.connect(function (err) {
+    assert.ok(err);
+    assert.strictEqual(err.code, 'HANDSHAKE_SSL_ERROR');
+    assert.strictEqual(err.fatal, true);
+    connection.destroy();
+    server.destroy();
+  });
+});
+
+server.on('connection', function (incomingConnection) {
+  incomingConnection.handshake({
+    serverCapabilities1: common.ClientConstants.CLIENT_SSL
+  });
+});