pg 8.9.0 → 8.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  var EventEmitter = require('events').EventEmitter
4
4
  var utils = require('./utils')
5
- var sasl = require('./sasl')
6
- var pgPass = require('pgpass')
5
+ var sasl = require('./crypto/sasl')
7
6
  var TypeOverrides = require('./type-overrides')
8
7
 
9
8
  var ConnectionParameters = require('./connection-parameters')
10
9
  var Query = require('./query')
11
10
  var defaults = require('./defaults')
12
11
  var Connection = require('./connection')
12
+ const crypto = require('./crypto/utils')
13
13
 
14
14
  class Client extends EventEmitter {
15
15
  constructor(config) {
@@ -37,6 +37,7 @@ class Client extends EventEmitter {
37
37
  this._Promise = c.Promise || global.Promise
38
38
  this._types = new TypeOverrides(c.types)
39
39
  this._ending = false
40
+ this._ended = false
40
41
  this._connecting = false
41
42
  this._connected = false
42
43
  this._connectionError = false
@@ -132,6 +133,7 @@ class Client extends EventEmitter {
132
133
 
133
134
  clearTimeout(this.connectionTimeoutHandle)
134
135
  this._errorAllQueries(error)
136
+ this._ended = true
135
137
 
136
138
  if (!this._ending) {
137
139
  // if the connection is ended without us calling .end()
@@ -223,12 +225,17 @@ class Client extends EventEmitter {
223
225
  } else if (this.password !== null) {
224
226
  cb()
225
227
  } else {
226
- pgPass(this.connectionParameters, (pass) => {
227
- if (undefined !== pass) {
228
- this.connectionParameters.password = this.password = pass
229
- }
230
- cb()
231
- })
228
+ try {
229
+ const pgPass = require('pgpass')
230
+ pgPass(this.connectionParameters, (pass) => {
231
+ if (undefined !== pass) {
232
+ this.connectionParameters.password = this.password = pass
233
+ }
234
+ cb()
235
+ })
236
+ } catch (e) {
237
+ this.emit('error', e)
238
+ }
232
239
  }
233
240
  }
234
241
 
@@ -239,9 +246,13 @@ class Client extends EventEmitter {
239
246
  }
240
247
 
241
248
  _handleAuthMD5Password(msg) {
242
- this._checkPgPass(() => {
243
- const hashedPassword = utils.postgresMd5PasswordHash(this.user, this.password, msg.salt)
244
- this.connection.password(hashedPassword)
249
+ this._checkPgPass(async () => {
250
+ try {
251
+ const hashedPassword = await crypto.postgresMd5PasswordHash(this.user, this.password, msg.salt)
252
+ this.connection.password(hashedPassword)
253
+ } catch (e) {
254
+ this.emit('error', e)
255
+ }
245
256
  })
246
257
  }
247
258
 
@@ -256,9 +267,9 @@ class Client extends EventEmitter {
256
267
  })
257
268
  }
258
269
 
259
- _handleAuthSASLContinue(msg) {
270
+ async _handleAuthSASLContinue(msg) {
260
271
  try {
261
- sasl.continueSession(this.saslSession, this.password, msg.data)
272
+ await sasl.continueSession(this.saslSession, this.password, msg.data)
262
273
  this.connection.sendSCRAMClientFinalMessage(this.saslSession.response)
263
274
  } catch (err) {
264
275
  this.connection.emit('error', err)
@@ -454,35 +465,15 @@ class Client extends EventEmitter {
454
465
  return this._types.getTypeParser(oid, format)
455
466
  }
456
467
 
457
- // Ported from PostgreSQL 9.2.4 source code in src/interfaces/libpq/fe-exec.c
468
+ // escapeIdentifier and escapeLiteral moved to utility functions & exported
469
+ // on PG
470
+ // re-exported here for backwards compatibility
458
471
  escapeIdentifier(str) {
459
- return '"' + str.replace(/"/g, '""') + '"'
472
+ return utils.escapeIdentifier(str)
460
473
  }
461
474
 
462
- // Ported from PostgreSQL 9.2.4 source code in src/interfaces/libpq/fe-exec.c
463
475
  escapeLiteral(str) {
464
- var hasBackslash = false
465
- var escaped = "'"
466
-
467
- for (var i = 0; i < str.length; i++) {
468
- var c = str[i]
469
- if (c === "'") {
470
- escaped += c + c
471
- } else if (c === '\\') {
472
- escaped += c + c
473
- hasBackslash = true
474
- } else {
475
- escaped += c
476
- }
477
- }
478
-
479
- escaped += "'"
480
-
481
- if (hasBackslash === true) {
482
- escaped = ' E' + escaped
483
- }
484
-
485
- return escaped
476
+ return utils.escapeLiteral(str)
486
477
  }
487
478
 
488
479
  _pulseQueryQueue() {
@@ -603,7 +594,7 @@ class Client extends EventEmitter {
603
594
  this._ending = true
604
595
 
605
596
  // if we have never connected, then end is a noop, callback immediately
606
- if (!this.connection._connecting) {
597
+ if (!this.connection._connecting || this._ended) {
607
598
  if (cb) {
608
599
  cb()
609
600
  } else {
package/lib/connection.js CHANGED
@@ -4,6 +4,7 @@ var net = require('net')
4
4
  var EventEmitter = require('events').EventEmitter
5
5
 
6
6
  const { parse, serialize } = require('pg-protocol')
7
+ const { getStream, getSecureStream } = require('./stream')
7
8
 
8
9
  const flushBuffer = serialize.flush()
9
10
  const syncBuffer = serialize.sync()
@@ -15,7 +16,7 @@ class Connection extends EventEmitter {
15
16
  super()
16
17
  config = config || {}
17
18
 
18
- this.stream = config.stream || new net.Socket()
19
+ this.stream = config.stream || getStream(config.ssl)
19
20
  if (typeof this.stream === 'function') {
20
21
  this.stream = this.stream(config)
21
22
  }
@@ -79,7 +80,6 @@ class Connection extends EventEmitter {
79
80
  self.stream.end()
80
81
  return self.emit('error', new Error('There was an error establishing an SSL connection'))
81
82
  }
82
- var tls = require('tls')
83
83
  const options = {
84
84
  socket: self.stream,
85
85
  }
@@ -92,11 +92,12 @@ class Connection extends EventEmitter {
92
92
  }
93
93
  }
94
94
 
95
- if (net.isIP(host) === 0) {
95
+ var net = require('net')
96
+ if (net.isIP && net.isIP(host) === 0) {
96
97
  options.servername = host
97
98
  }
98
99
  try {
99
- self.stream = tls.connect(options)
100
+ self.stream = getSecureStream(options)
100
101
  } catch (err) {
101
102
  return self.emit('error', err)
102
103
  }
@@ -108,9 +109,6 @@ class Connection extends EventEmitter {
108
109
  }
109
110
 
110
111
  attachListeners(stream) {
111
- stream.on('end', () => {
112
- this.emit('end')
113
- })
114
112
  parse(stream, (msg) => {
115
113
  var eventName = msg.name === 'error' ? 'errorMessage' : msg.name
116
114
  if (this._emitMessage) {
@@ -1,5 +1,5 @@
1
1
  'use strict'
2
- const crypto = require('crypto')
2
+ const crypto = require('./utils')
3
3
 
4
4
  function startSession(mechanisms) {
5
5
  if (mechanisms.indexOf('SCRAM-SHA-256') === -1) {
@@ -16,7 +16,7 @@ function startSession(mechanisms) {
16
16
  }
17
17
  }
18
18
 
19
- function continueSession(session, password, serverData) {
19
+ async function continueSession(session, password, serverData) {
20
20
  if (session.message !== 'SASLInitialResponse') {
21
21
  throw new Error('SASL: Last message was not SASLInitialResponse')
22
22
  }
@@ -38,29 +38,22 @@ function continueSession(session, password, serverData) {
38
38
  throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce is too short')
39
39
  }
40
40
 
41
- var saltBytes = Buffer.from(sv.salt, 'base64')
42
-
43
- var saltedPassword = crypto.pbkdf2Sync(password, saltBytes, sv.iteration, 32, 'sha256')
44
-
45
- var clientKey = hmacSha256(saltedPassword, 'Client Key')
46
- var storedKey = sha256(clientKey)
47
-
48
41
  var clientFirstMessageBare = 'n=*,r=' + session.clientNonce
49
42
  var serverFirstMessage = 'r=' + sv.nonce + ',s=' + sv.salt + ',i=' + sv.iteration
50
-
51
43
  var clientFinalMessageWithoutProof = 'c=biws,r=' + sv.nonce
52
-
53
44
  var authMessage = clientFirstMessageBare + ',' + serverFirstMessage + ',' + clientFinalMessageWithoutProof
54
45
 
55
- var clientSignature = hmacSha256(storedKey, authMessage)
56
- var clientProofBytes = xorBuffers(clientKey, clientSignature)
57
- var clientProof = clientProofBytes.toString('base64')
58
-
59
- var serverKey = hmacSha256(saltedPassword, 'Server Key')
60
- var serverSignatureBytes = hmacSha256(serverKey, authMessage)
46
+ var saltBytes = Buffer.from(sv.salt, 'base64')
47
+ var saltedPassword = await crypto.deriveKey(password, saltBytes, sv.iteration)
48
+ var clientKey = await crypto.hmacSha256(saltedPassword, 'Client Key')
49
+ var storedKey = await crypto.sha256(clientKey)
50
+ var clientSignature = await crypto.hmacSha256(storedKey, authMessage)
51
+ var clientProof = xorBuffers(Buffer.from(clientKey), Buffer.from(clientSignature)).toString('base64')
52
+ var serverKey = await crypto.hmacSha256(saltedPassword, 'Server Key')
53
+ var serverSignatureBytes = await crypto.hmacSha256(serverKey, authMessage)
61
54
 
62
55
  session.message = 'SASLResponse'
63
- session.serverSignature = serverSignatureBytes.toString('base64')
56
+ session.serverSignature = Buffer.from(serverSignatureBytes).toString('base64')
64
57
  session.response = clientFinalMessageWithoutProof + ',p=' + clientProof
65
58
  }
66
59
 
@@ -186,14 +179,6 @@ function xorBuffers(a, b) {
186
179
  return Buffer.from(a.map((_, i) => a[i] ^ b[i]))
187
180
  }
188
181
 
189
- function sha256(text) {
190
- return crypto.createHash('sha256').update(text).digest()
191
- }
192
-
193
- function hmacSha256(key, msg) {
194
- return crypto.createHmac('sha256', key).update(msg).digest()
195
- }
196
-
197
182
  module.exports = {
198
183
  startSession,
199
184
  continueSession,
@@ -0,0 +1,37 @@
1
+ 'use strict'
2
+ // This file contains crypto utility functions for versions of Node.js < 15.0.0,
3
+ // which does not support the WebCrypto.subtle API.
4
+
5
+ const nodeCrypto = require('crypto')
6
+
7
+ function md5(string) {
8
+ return nodeCrypto.createHash('md5').update(string, 'utf-8').digest('hex')
9
+ }
10
+
11
+ // See AuthenticationMD5Password at https://www.postgresql.org/docs/current/static/protocol-flow.html
12
+ function postgresMd5PasswordHash(user, password, salt) {
13
+ var inner = md5(password + user)
14
+ var outer = md5(Buffer.concat([Buffer.from(inner), salt]))
15
+ return 'md5' + outer
16
+ }
17
+
18
+ function sha256(text) {
19
+ return nodeCrypto.createHash('sha256').update(text).digest()
20
+ }
21
+
22
+ function hmacSha256(key, msg) {
23
+ return nodeCrypto.createHmac('sha256', key).update(msg).digest()
24
+ }
25
+
26
+ async function deriveKey(password, salt, iterations) {
27
+ return nodeCrypto.pbkdf2Sync(password, salt, iterations, 32, 'sha256')
28
+ }
29
+
30
+ module.exports = {
31
+ postgresMd5PasswordHash,
32
+ randomBytes: nodeCrypto.randomBytes,
33
+ deriveKey,
34
+ sha256,
35
+ hmacSha256,
36
+ md5,
37
+ }
@@ -0,0 +1,92 @@
1
+ 'use strict'
2
+
3
+ const useLegacyCrypto = parseInt(process.versions && process.versions.node && process.versions.node.split('.')[0]) < 15
4
+ if (useLegacyCrypto) {
5
+ // We are on an old version of Node.js that requires legacy crypto utilities.
6
+ module.exports = require('./utils-legacy')
7
+ return
8
+ }
9
+
10
+ const nodeCrypto = require('crypto')
11
+
12
+ module.exports = {
13
+ postgresMd5PasswordHash,
14
+ randomBytes,
15
+ deriveKey,
16
+ sha256,
17
+ hmacSha256,
18
+ md5,
19
+ }
20
+
21
+ /**
22
+ * The Web Crypto API - grabbed from the Node.js library or the global
23
+ * @type Crypto
24
+ */
25
+ const webCrypto = nodeCrypto.webcrypto || globalThis.crypto
26
+ /**
27
+ * The SubtleCrypto API for low level crypto operations.
28
+ * @type SubtleCrypto
29
+ */
30
+ const subtleCrypto = webCrypto.subtle
31
+ const textEncoder = new TextEncoder()
32
+
33
+ /**
34
+ *
35
+ * @param {*} length
36
+ * @returns
37
+ */
38
+ function randomBytes(length) {
39
+ return webCrypto.getRandomValues(Buffer.alloc(length))
40
+ }
41
+
42
+ async function md5(string) {
43
+ try {
44
+ return nodeCrypto.createHash('md5').update(string, 'utf-8').digest('hex')
45
+ } catch (e) {
46
+ // `createHash()` failed so we are probably not in Node.js, use the WebCrypto API instead.
47
+ // Note that the MD5 algorithm on WebCrypto is not available in Node.js.
48
+ // This is why we cannot just use WebCrypto in all environments.
49
+ const data = typeof string === 'string' ? textEncoder.encode(string) : string
50
+ const hash = await subtleCrypto.digest('MD5', data)
51
+ return Array.from(new Uint8Array(hash))
52
+ .map((b) => b.toString(16).padStart(2, '0'))
53
+ .join('')
54
+ }
55
+ }
56
+
57
+ // See AuthenticationMD5Password at https://www.postgresql.org/docs/current/static/protocol-flow.html
58
+ async function postgresMd5PasswordHash(user, password, salt) {
59
+ var inner = await md5(password + user)
60
+ var outer = await md5(Buffer.concat([Buffer.from(inner), salt]))
61
+ return 'md5' + outer
62
+ }
63
+
64
+ /**
65
+ * Create a SHA-256 digest of the given data
66
+ * @param {Buffer} data
67
+ */
68
+ async function sha256(text) {
69
+ return await subtleCrypto.digest('SHA-256', text)
70
+ }
71
+
72
+ /**
73
+ * Sign the message with the given key
74
+ * @param {ArrayBuffer} keyBuffer
75
+ * @param {string} msg
76
+ */
77
+ async function hmacSha256(keyBuffer, msg) {
78
+ const key = await subtleCrypto.importKey('raw', keyBuffer, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'])
79
+ return await subtleCrypto.sign('HMAC', key, textEncoder.encode(msg))
80
+ }
81
+
82
+ /**
83
+ * Derive a key from the password and salt
84
+ * @param {string} password
85
+ * @param {Uint8Array} salt
86
+ * @param {number} iterations
87
+ */
88
+ async function deriveKey(password, salt, iterations) {
89
+ const key = await subtleCrypto.importKey('raw', textEncoder.encode(password), 'PBKDF2', false, ['deriveBits'])
90
+ const params = { name: 'PBKDF2', hash: 'SHA-256', salt: salt, iterations: iterations }
91
+ return await subtleCrypto.deriveBits(params, key, 32 * 8, ['deriveBits'])
92
+ }
package/lib/index.js CHANGED
@@ -5,6 +5,7 @@ var defaults = require('./defaults')
5
5
  var Connection = require('./connection')
6
6
  var Pool = require('pg-pool')
7
7
  const { DatabaseError } = require('pg-protocol')
8
+ const { escapeIdentifier, escapeLiteral } = require('./utils')
8
9
 
9
10
  const poolFactory = (Client) => {
10
11
  return class BoundPool extends Pool {
@@ -23,6 +24,8 @@ var PG = function (clientConstructor) {
23
24
  this.Connection = Connection
24
25
  this.types = require('pg-types')
25
26
  this.DatabaseError = DatabaseError
27
+ this.escapeIdentifier = escapeIdentifier
28
+ this.escapeLiteral = escapeLiteral
26
29
  }
27
30
 
28
31
  if (typeof process.env.NODE_PG_FORCE_NATIVE !== 'undefined') {
@@ -1,7 +1,13 @@
1
1
  'use strict'
2
2
 
3
3
  // eslint-disable-next-line
4
- var Native = require('pg-native')
4
+ var Native
5
+ try {
6
+ // Wrap this `require()` in a try-catch to avoid upstream bundlers from complaining that this might not be available since it is an optional import
7
+ Native = require('pg-native')
8
+ } catch (e) {
9
+ throw e
10
+ }
5
11
  var TypeOverrides = require('../type-overrides')
6
12
  var EventEmitter = require('events').EventEmitter
7
13
  var util = require('util')
package/lib/stream.js ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Get a socket stream compatible with the current runtime environment.
3
+ * @returns {Duplex}
4
+ */
5
+ module.exports.getStream = function getStream(ssl) {
6
+ const net = require('net')
7
+ if (typeof net.Socket === 'function') {
8
+ return new net.Socket()
9
+ } else {
10
+ const { CloudflareSocket } = require('pg-cloudflare')
11
+ return new CloudflareSocket(ssl)
12
+ }
13
+ }
14
+
15
+ /**
16
+ * Get a TLS secured socket, compatible with the current environment,
17
+ * using the socket and other settings given in `options`.
18
+ * @returns {Duplex}
19
+ */
20
+ module.exports.getSecureStream = function getSecureStream(options) {
21
+ var tls = require('tls')
22
+ if (tls.connect) {
23
+ return tls.connect(options)
24
+ } else {
25
+ options.socket.startTls(options)
26
+ return options.socket
27
+ }
28
+ }
package/lib/utils.js CHANGED
@@ -1,7 +1,5 @@
1
1
  'use strict'
2
2
 
3
- const crypto = require('crypto')
4
-
5
3
  const defaults = require('./defaults')
6
4
 
7
5
  function escapeElement(elementRepresentation) {
@@ -164,15 +162,34 @@ function normalizeQueryConfig(config, values, callback) {
164
162
  return config
165
163
  }
166
164
 
167
- const md5 = function (string) {
168
- return crypto.createHash('md5').update(string, 'utf-8').digest('hex')
165
+ // Ported from PostgreSQL 9.2.4 source code in src/interfaces/libpq/fe-exec.c
166
+ const escapeIdentifier = function (str) {
167
+ return '"' + str.replace(/"/g, '""') + '"'
169
168
  }
170
169
 
171
- // See AuthenticationMD5Password at https://www.postgresql.org/docs/current/static/protocol-flow.html
172
- const postgresMd5PasswordHash = function (user, password, salt) {
173
- var inner = md5(password + user)
174
- var outer = md5(Buffer.concat([Buffer.from(inner), salt]))
175
- return 'md5' + outer
170
+ const escapeLiteral = function (str) {
171
+ var hasBackslash = false
172
+ var escaped = "'"
173
+
174
+ for (var i = 0; i < str.length; i++) {
175
+ var c = str[i]
176
+ if (c === "'") {
177
+ escaped += c + c
178
+ } else if (c === '\\') {
179
+ escaped += c + c
180
+ hasBackslash = true
181
+ } else {
182
+ escaped += c
183
+ }
184
+ }
185
+
186
+ escaped += "'"
187
+
188
+ if (hasBackslash === true) {
189
+ escaped = ' E' + escaped
190
+ }
191
+
192
+ return escaped
176
193
  }
177
194
 
178
195
  module.exports = {
@@ -182,6 +199,6 @@ module.exports = {
182
199
  return prepareValue(value)
183
200
  },
184
201
  normalizeQueryConfig,
185
- postgresMd5PasswordHash,
186
- md5,
202
+ escapeIdentifier,
203
+ escapeLiteral,
187
204
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pg",
3
- "version": "8.9.0",
3
+ "version": "8.11.0",
4
4
  "description": "PostgreSQL client - pure javascript & libpq with the same API",
5
5
  "keywords": [
6
6
  "database",
@@ -22,17 +22,24 @@
22
22
  "dependencies": {
23
23
  "buffer-writer": "2.0.0",
24
24
  "packet-reader": "1.0.0",
25
- "pg-connection-string": "^2.5.0",
26
- "pg-pool": "^3.5.2",
25
+ "pg-connection-string": "^2.6.0",
26
+ "pg-pool": "^3.6.0",
27
27
  "pg-protocol": "^1.6.0",
28
28
  "pg-types": "^2.1.0",
29
29
  "pgpass": "1.x"
30
30
  },
31
31
  "devDependencies": {
32
+ "@cloudflare/workers-types": "^4.20230404.0",
32
33
  "async": "2.6.4",
33
34
  "bluebird": "3.5.2",
34
35
  "co": "4.6.0",
35
- "pg-copy-streams": "0.3.0"
36
+ "pg-copy-streams": "0.3.0",
37
+ "typescript": "^4.0.3",
38
+ "workerd": "^1.20230419.0",
39
+ "wrangler": "^2.16.0"
40
+ },
41
+ "optionalDependencies": {
42
+ "pg-cloudflare": "^1.1.0"
36
43
  },
37
44
  "peerDependencies": {
38
45
  "pg-native": ">=3.0.1"
@@ -53,5 +60,5 @@
53
60
  "engines": {
54
61
  "node": ">= 8.0.0"
55
62
  },
56
- "gitHead": "20a243e8b30926a348cafc44177e95345618f7bc"
63
+ "gitHead": "14b840e96e57fc0617b5c4758f6318f774148ee4"
57
64
  }