pg 8.5.1 → 8.7.3
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/LICENSE +1 -1
- package/README.md +5 -0
- package/lib/client.js +8 -0
- package/lib/connection.js +8 -1
- package/lib/index.js +2 -0
- package/lib/native/client.js +3 -0
- package/lib/sasl.js +110 -52
- package/package.json +7 -6
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -53,6 +53,11 @@ node-postgres's continued development has been made possible in part by generous
|
|
|
53
53
|
<a href="https://crate.io" target="_blank">
|
|
54
54
|
<img height="80" src="https://node-postgres.com/crate-io.png" />
|
|
55
55
|
</a>
|
|
56
|
+
|
|
57
|
+
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAABCAQAAAB0m0auAAAADElEQVR42mNkIBIAAABSAAI2VLqiAAAAAElFTkSuQmCC" />
|
|
58
|
+
<a href="https://www.eaze.com" target="_blank">
|
|
59
|
+
<img height="80" src="https://node-postgres.com/eaze.png" />
|
|
60
|
+
</a>
|
|
56
61
|
</div>
|
|
57
62
|
|
|
58
63
|
If you or your company are benefiting from node-postgres and would like to help keep the project financially sustainable [please consider supporting](https://github.com/sponsors/brianc) its development.
|
package/lib/client.js
CHANGED
package/lib/connection.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
var net = require('net')
|
|
4
4
|
var EventEmitter = require('events').EventEmitter
|
|
5
|
-
var util = require('util')
|
|
6
5
|
|
|
7
6
|
const { parse, serialize } = require('pg-protocol')
|
|
8
7
|
|
|
@@ -178,6 +177,14 @@ class Connection extends EventEmitter {
|
|
|
178
177
|
this._send(syncBuffer)
|
|
179
178
|
}
|
|
180
179
|
|
|
180
|
+
ref() {
|
|
181
|
+
this.stream.ref()
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
unref() {
|
|
185
|
+
this.stream.unref()
|
|
186
|
+
}
|
|
187
|
+
|
|
181
188
|
end() {
|
|
182
189
|
// 0x58 = 'X'
|
|
183
190
|
this._ending = true
|
package/lib/index.js
CHANGED
|
@@ -4,6 +4,7 @@ var Client = require('./client')
|
|
|
4
4
|
var defaults = require('./defaults')
|
|
5
5
|
var Connection = require('./connection')
|
|
6
6
|
var Pool = require('pg-pool')
|
|
7
|
+
const { DatabaseError } = require('pg-protocol')
|
|
7
8
|
|
|
8
9
|
const poolFactory = (Client) => {
|
|
9
10
|
return class BoundPool extends Pool {
|
|
@@ -21,6 +22,7 @@ var PG = function (clientConstructor) {
|
|
|
21
22
|
this._pools = []
|
|
22
23
|
this.Connection = Connection
|
|
23
24
|
this.types = require('pg-types')
|
|
25
|
+
this.DatabaseError = DatabaseError
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
if (typeof process.env.NODE_PG_FORCE_NATIVE !== 'undefined') {
|
package/lib/native/client.js
CHANGED
|
@@ -285,6 +285,9 @@ Client.prototype.cancel = function (query) {
|
|
|
285
285
|
}
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
+
Client.prototype.ref = function () {}
|
|
289
|
+
Client.prototype.unref = function () {}
|
|
290
|
+
|
|
288
291
|
Client.prototype.setTypeParser = function (oid, format, parseFn) {
|
|
289
292
|
return this._types.setTypeParser(oid, format, parseFn)
|
|
290
293
|
}
|
package/lib/sasl.js
CHANGED
|
@@ -20,19 +20,27 @@ function continueSession(session, password, serverData) {
|
|
|
20
20
|
if (session.message !== 'SASLInitialResponse') {
|
|
21
21
|
throw new Error('SASL: Last message was not SASLInitialResponse')
|
|
22
22
|
}
|
|
23
|
+
if (typeof password !== 'string') {
|
|
24
|
+
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a string')
|
|
25
|
+
}
|
|
26
|
+
if (typeof serverData !== 'string') {
|
|
27
|
+
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: serverData must be a string')
|
|
28
|
+
}
|
|
23
29
|
|
|
24
|
-
const sv =
|
|
30
|
+
const sv = parseServerFirstMessage(serverData)
|
|
25
31
|
|
|
26
32
|
if (!sv.nonce.startsWith(session.clientNonce)) {
|
|
27
33
|
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce does not start with client nonce')
|
|
34
|
+
} else if (sv.nonce.length === session.clientNonce.length) {
|
|
35
|
+
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce is too short')
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
var saltBytes = Buffer.from(sv.salt, 'base64')
|
|
31
39
|
|
|
32
40
|
var saltedPassword = Hi(password, saltBytes, sv.iteration)
|
|
33
41
|
|
|
34
|
-
var clientKey =
|
|
35
|
-
var storedKey =
|
|
42
|
+
var clientKey = hmacSha256(saltedPassword, 'Client Key')
|
|
43
|
+
var storedKey = sha256(clientKey)
|
|
36
44
|
|
|
37
45
|
var clientFirstMessageBare = 'n=*,r=' + session.clientNonce
|
|
38
46
|
var serverFirstMessage = 'r=' + sv.nonce + ',s=' + sv.salt + ',i=' + sv.iteration
|
|
@@ -41,12 +49,12 @@ function continueSession(session, password, serverData) {
|
|
|
41
49
|
|
|
42
50
|
var authMessage = clientFirstMessageBare + ',' + serverFirstMessage + ',' + clientFinalMessageWithoutProof
|
|
43
51
|
|
|
44
|
-
var clientSignature =
|
|
52
|
+
var clientSignature = hmacSha256(storedKey, authMessage)
|
|
45
53
|
var clientProofBytes = xorBuffers(clientKey, clientSignature)
|
|
46
54
|
var clientProof = clientProofBytes.toString('base64')
|
|
47
55
|
|
|
48
|
-
var serverKey =
|
|
49
|
-
var serverSignatureBytes =
|
|
56
|
+
var serverKey = hmacSha256(saltedPassword, 'Server Key')
|
|
57
|
+
var serverSignatureBytes = hmacSha256(serverKey, authMessage)
|
|
50
58
|
|
|
51
59
|
session.message = 'SASLResponse'
|
|
52
60
|
session.serverSignature = serverSignatureBytes.toString('base64')
|
|
@@ -57,54 +65,87 @@ function finalizeSession(session, serverData) {
|
|
|
57
65
|
if (session.message !== 'SASLResponse') {
|
|
58
66
|
throw new Error('SASL: Last message was not SASLResponse')
|
|
59
67
|
}
|
|
68
|
+
if (typeof serverData !== 'string') {
|
|
69
|
+
throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: serverData must be a string')
|
|
70
|
+
}
|
|
60
71
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
String(serverData)
|
|
64
|
-
.split(',')
|
|
65
|
-
.forEach(function (part) {
|
|
66
|
-
switch (part[0]) {
|
|
67
|
-
case 'v':
|
|
68
|
-
serverSignature = part.substr(2)
|
|
69
|
-
break
|
|
70
|
-
}
|
|
71
|
-
})
|
|
72
|
+
const { serverSignature } = parseServerFinalMessage(serverData)
|
|
72
73
|
|
|
73
74
|
if (serverSignature !== session.serverSignature) {
|
|
74
75
|
throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature does not match')
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
79
|
+
/**
|
|
80
|
+
* printable = %x21-2B / %x2D-7E
|
|
81
|
+
* ;; Printable ASCII except ",".
|
|
82
|
+
* ;; Note that any "printable" is also
|
|
83
|
+
* ;; a valid "value".
|
|
84
|
+
*/
|
|
85
|
+
function isPrintableChars(text) {
|
|
86
|
+
if (typeof text !== 'string') {
|
|
87
|
+
throw new TypeError('SASL: text must be a string')
|
|
88
|
+
}
|
|
89
|
+
return text
|
|
90
|
+
.split('')
|
|
91
|
+
.map((_, i) => text.charCodeAt(i))
|
|
92
|
+
.every((c) => (c >= 0x21 && c <= 0x2b) || (c >= 0x2d && c <= 0x7e))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* base64-char = ALPHA / DIGIT / "/" / "+"
|
|
97
|
+
*
|
|
98
|
+
* base64-4 = 4base64-char
|
|
99
|
+
*
|
|
100
|
+
* base64-3 = 3base64-char "="
|
|
101
|
+
*
|
|
102
|
+
* base64-2 = 2base64-char "=="
|
|
103
|
+
*
|
|
104
|
+
* base64 = *base64-4 [base64-3 / base64-2]
|
|
105
|
+
*/
|
|
106
|
+
function isBase64(text) {
|
|
107
|
+
return /^(?:[a-zA-Z0-9+/]{4})*(?:[a-zA-Z0-9+/]{2}==|[a-zA-Z0-9+/]{3}=)?$/.test(text)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function parseAttributePairs(text) {
|
|
111
|
+
if (typeof text !== 'string') {
|
|
112
|
+
throw new TypeError('SASL: attribute pairs text must be a string')
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return new Map(
|
|
116
|
+
text.split(',').map((attrValue) => {
|
|
117
|
+
if (!/^.=/.test(attrValue)) {
|
|
118
|
+
throw new Error('SASL: Invalid attribute pair entry')
|
|
94
119
|
}
|
|
120
|
+
const name = attrValue[0]
|
|
121
|
+
const value = attrValue.substring(2)
|
|
122
|
+
return [name, value]
|
|
95
123
|
})
|
|
124
|
+
)
|
|
125
|
+
}
|
|
96
126
|
|
|
127
|
+
function parseServerFirstMessage(data) {
|
|
128
|
+
const attrPairs = parseAttributePairs(data)
|
|
129
|
+
|
|
130
|
+
const nonce = attrPairs.get('r')
|
|
97
131
|
if (!nonce) {
|
|
98
132
|
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: nonce missing')
|
|
133
|
+
} else if (!isPrintableChars(nonce)) {
|
|
134
|
+
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: nonce must only contain printable characters')
|
|
99
135
|
}
|
|
100
|
-
|
|
136
|
+
const salt = attrPairs.get('s')
|
|
101
137
|
if (!salt) {
|
|
102
138
|
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: salt missing')
|
|
139
|
+
} else if (!isBase64(salt)) {
|
|
140
|
+
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: salt must be base64')
|
|
103
141
|
}
|
|
104
|
-
|
|
105
|
-
if (!
|
|
142
|
+
const iterationText = attrPairs.get('i')
|
|
143
|
+
if (!iterationText) {
|
|
106
144
|
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration missing')
|
|
145
|
+
} else if (!/^[1-9][0-9]*$/.test(iterationText)) {
|
|
146
|
+
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: invalid iteration count')
|
|
107
147
|
}
|
|
148
|
+
const iteration = parseInt(iterationText, 10)
|
|
108
149
|
|
|
109
150
|
return {
|
|
110
151
|
nonce,
|
|
@@ -113,31 +154,48 @@ function extractVariablesFromFirstServerMessage(data) {
|
|
|
113
154
|
}
|
|
114
155
|
}
|
|
115
156
|
|
|
157
|
+
function parseServerFinalMessage(serverData) {
|
|
158
|
+
const attrPairs = parseAttributePairs(serverData)
|
|
159
|
+
const serverSignature = attrPairs.get('v')
|
|
160
|
+
if (!serverSignature) {
|
|
161
|
+
throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature is missing')
|
|
162
|
+
} else if (!isBase64(serverSignature)) {
|
|
163
|
+
throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature must be base64')
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
serverSignature,
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
116
170
|
function xorBuffers(a, b) {
|
|
117
|
-
if (!Buffer.isBuffer(a))
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
return Buffer.from(
|
|
171
|
+
if (!Buffer.isBuffer(a)) {
|
|
172
|
+
throw new TypeError('first argument must be a Buffer')
|
|
173
|
+
}
|
|
174
|
+
if (!Buffer.isBuffer(b)) {
|
|
175
|
+
throw new TypeError('second argument must be a Buffer')
|
|
176
|
+
}
|
|
177
|
+
if (a.length !== b.length) {
|
|
178
|
+
throw new Error('Buffer lengths must match')
|
|
179
|
+
}
|
|
180
|
+
if (a.length === 0) {
|
|
181
|
+
throw new Error('Buffers cannot be empty')
|
|
182
|
+
}
|
|
183
|
+
return Buffer.from(a.map((_, i) => a[i] ^ b[i]))
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function sha256(text) {
|
|
187
|
+
return crypto.createHash('sha256').update(text).digest()
|
|
130
188
|
}
|
|
131
189
|
|
|
132
|
-
function
|
|
190
|
+
function hmacSha256(key, msg) {
|
|
133
191
|
return crypto.createHmac('sha256', key).update(msg).digest()
|
|
134
192
|
}
|
|
135
193
|
|
|
136
194
|
function Hi(password, saltBytes, iterations) {
|
|
137
|
-
var ui1 =
|
|
195
|
+
var ui1 = hmacSha256(password, Buffer.concat([saltBytes, Buffer.from([0, 0, 0, 1])]))
|
|
138
196
|
var ui = ui1
|
|
139
197
|
for (var i = 0; i < iterations - 1; i++) {
|
|
140
|
-
ui1 =
|
|
198
|
+
ui1 = hmacSha256(password, ui1)
|
|
141
199
|
ui = xorBuffers(ui, ui1)
|
|
142
200
|
}
|
|
143
201
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pg",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.7.3",
|
|
4
4
|
"description": "PostgreSQL client - pure javascript & libpq with the same API",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"database",
|
|
@@ -14,16 +14,17 @@
|
|
|
14
14
|
"homepage": "https://github.com/brianc/node-postgres",
|
|
15
15
|
"repository": {
|
|
16
16
|
"type": "git",
|
|
17
|
-
"url": "git://github.com/brianc/node-postgres.git"
|
|
17
|
+
"url": "git://github.com/brianc/node-postgres.git",
|
|
18
|
+
"directory": "packages/pg"
|
|
18
19
|
},
|
|
19
20
|
"author": "Brian Carlson <brian.m.carlson@gmail.com>",
|
|
20
21
|
"main": "./lib",
|
|
21
22
|
"dependencies": {
|
|
22
23
|
"buffer-writer": "2.0.0",
|
|
23
24
|
"packet-reader": "1.0.0",
|
|
24
|
-
"pg-connection-string": "^2.
|
|
25
|
-
"pg-pool": "^3.
|
|
26
|
-
"pg-protocol": "^1.
|
|
25
|
+
"pg-connection-string": "^2.5.0",
|
|
26
|
+
"pg-pool": "^3.5.1",
|
|
27
|
+
"pg-protocol": "^1.5.0",
|
|
27
28
|
"pg-types": "^2.1.0",
|
|
28
29
|
"pgpass": "1.x"
|
|
29
30
|
},
|
|
@@ -52,5 +53,5 @@
|
|
|
52
53
|
"engines": {
|
|
53
54
|
"node": ">= 8.0.0"
|
|
54
55
|
},
|
|
55
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "4fa7ee891a456168a75695ac026792136f16577f"
|
|
56
57
|
}
|