Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bb8745b

Browse files
authored
Fix SASL to bubble up errors, enable SASL tests in CI, and add informative empty SASL password message (#2901)
* Enable SASL tests in GitHub actions CI * Add SASL test to ensure that client password is a string * Fix SASL error handling to emit and bubble up errors * Add informative error when SASL password is empty string
1 parent f82f39c commit bb8745b

File tree

5 files changed

+94
-7
lines changed

5 files changed

+94
-7
lines changed

.github/workflows/ci.yml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ jobs:
1515
POSTGRES_USER: postgres
1616
POSTGRES_PASSWORD: postgres
1717
POSTGRES_DB: ci_db_test
18+
POSTGRES_HOST_AUTH_METHOD: 'md5'
1819
ports:
1920
- 5432:5432
2021
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
@@ -23,7 +24,19 @@ jobs:
2324
node: ['10', '12', '14', '16', '18']
2425
os: [ubuntu-latest, windows-latest, macos-latest]
2526
name: Node.js ${{ matrix.node }} (${{ matrix.os }})
27+
env:
28+
PGUSER: postgres
29+
PGHOST: localhost
30+
PGPASSWORD: postgres
31+
PGDATABASE: ci_db_test
32+
PGTESTNOSSL: 'true'
33+
SCRAM_TEST_PGUSER: scram_test
34+
SCRAM_TEST_PGPASSWORD: test4scram
2635
steps:
36+
- run: |
37+
psql \
38+
-c "SET password_encryption = 'scram-sha-256'" \
39+
-c "CREATE ROLE scram_test LOGIN PASSWORD 'test4scram'"
2740
- uses: actions/checkout@v3
2841
with:
2942
persist-credentials: false
@@ -34,4 +47,4 @@ jobs:
3447
cache: yarn
3548
- run: yarn install
3649
# TODO(bmc): get ssl tests working in ci
37-
- run: PGTESTNOSSL=true PGUSER=postgres PGPASSWORD=postgres PGDATABASE=ci_db_test yarn test
50+
- run: yarn test

packages/pg/lib/client.js

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -247,19 +247,31 @@ class Client extends EventEmitter {
247247

248248
_handleAuthSASL(msg) {
249249
this._checkPgPass(() => {
250-
this.saslSession = sasl.startSession(msg.mechanisms)
251-
this.connection.sendSASLInitialResponseMessage(this.saslSession.mechanism, this.saslSession.response)
250+
try {
251+
this.saslSession = sasl.startSession(msg.mechanisms)
252+
this.connection.sendSASLInitialResponseMessage(this.saslSession.mechanism, this.saslSession.response)
253+
} catch (err) {
254+
this.connection.emit('error', err)
255+
}
252256
})
253257
}
254258

255259
_handleAuthSASLContinue(msg) {
256-
sasl.continueSession(this.saslSession, this.password, msg.data)
257-
this.connection.sendSCRAMClientFinalMessage(this.saslSession.response)
260+
try {
261+
sasl.continueSession(this.saslSession, this.password, msg.data)
262+
this.connection.sendSCRAMClientFinalMessage(this.saslSession.response)
263+
} catch (err) {
264+
this.connection.emit('error', err)
265+
}
258266
}
259267

260268
_handleAuthSASLFinal(msg) {
261-
sasl.finalizeSession(this.saslSession, msg.data)
262-
this.saslSession = null
269+
try {
270+
sasl.finalizeSession(this.saslSession, msg.data)
271+
this.saslSession = null
272+
} catch (err) {
273+
this.connection.emit('error', err)
274+
}
263275
}
264276

265277
_handleBackendKeyData(msg) {

packages/pg/lib/sasl.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ function continueSession(session, password, serverData) {
2323
if (typeof password !== 'string') {
2424
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a string')
2525
}
26+
if (password === '') {
27+
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a non-empty string')
28+
}
2629
if (typeof serverData !== 'string') {
2730
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: serverData must be a string')
2831
}

packages/pg/test/integration/client/sasl-scram-tests.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,24 @@ suite.testAsync('sasl/scram fails when password is wrong', async () => {
7373
)
7474
assert.ok(usingSasl, 'Should be using SASL for authentication')
7575
})
76+
77+
suite.testAsync('sasl/scram fails when password is empty', async () => {
78+
const client = new pg.Client({
79+
...config,
80+
// We use a password function here so the connection defaults do not
81+
// override the empty string value with one from process.env.PGPASSWORD
82+
password: () => '',
83+
})
84+
let usingSasl = false
85+
client.connection.once('authenticationSASL', () => {
86+
usingSasl = true
87+
})
88+
await assert.rejects(
89+
() => client.connect(),
90+
{
91+
message: 'SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a non-empty string',
92+
},
93+
'Error code should be for a password error'
94+
)
95+
assert.ok(usingSasl, 'Should be using SASL for authentication')
96+
})

packages/pg/test/unit/client/sasl-scram-tests.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,44 @@ test('sasl/scram', function () {
8080
)
8181
})
8282

83+
test('fails when client password is not a string', function () {
84+
for(const badPasswordValue of [null, undefined, 123, new Date(), {}]) {
85+
assert.throws(
86+
function () {
87+
sasl.continueSession(
88+
{
89+
message: 'SASLInitialResponse',
90+
clientNonce: 'a',
91+
},
92+
badPasswordValue,
93+
'r=1,i=1'
94+
)
95+
},
96+
{
97+
message: 'SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a string',
98+
}
99+
)
100+
}
101+
})
102+
103+
test('fails when client password is an empty string', function () {
104+
assert.throws(
105+
function () {
106+
sasl.continueSession(
107+
{
108+
message: 'SASLInitialResponse',
109+
clientNonce: 'a',
110+
},
111+
'',
112+
'r=1,i=1'
113+
)
114+
},
115+
{
116+
message: 'SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a non-empty string',
117+
}
118+
)
119+
})
120+
83121
test('fails when iteration is missing in server message', function () {
84122
assert.throws(
85123
function () {

0 commit comments

Comments
 (0)