Haraka 3.1.4 → 3.1.6

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.
Files changed (55) hide show
  1. package/.prettierignore +1 -1
  2. package/{Changes.md → CHANGELOG.md} +34 -0
  3. package/CONTRIBUTORS.md +26 -26
  4. package/README.md +68 -93
  5. package/SECURITY.md +178 -0
  6. package/bin/haraka +7 -14
  7. package/config/plugins +0 -3
  8. package/docs/Connection.md +126 -39
  9. package/docs/CoreConfig.md +92 -74
  10. package/docs/HAProxy.md +41 -25
  11. package/docs/Logging.md +68 -38
  12. package/docs/Outbound.md +124 -179
  13. package/docs/Plugins.md +38 -59
  14. package/docs/Transaction.md +78 -83
  15. package/docs/Tutorial.md +122 -209
  16. package/docs/plugins/aliases.md +1 -141
  17. package/docs/plugins/auth/auth_ldap.md +2 -39
  18. package/docs/plugins/max_unrecognized_commands.md +4 -18
  19. package/docs/plugins/process_title.md +3 -3
  20. package/docs/plugins/reseed_rng.md +11 -13
  21. package/docs/plugins/tls.md +7 -7
  22. package/docs/plugins/toobusy.md +10 -4
  23. package/docs/tutorials/SettingUpOutbound.md +40 -48
  24. package/endpoint.js +32 -2
  25. package/outbound/hmail.js +3 -2
  26. package/outbound/index.js +3 -0
  27. package/package.json +21 -34
  28. package/plugins/queue/smtp_forward.js +4 -4
  29. package/run_tests +3 -15
  30. package/server.js +17 -7
  31. package/smtp_client.js +8 -6
  32. package/test/connection.js +234 -0
  33. package/test/endpoint.js +32 -4
  34. package/test/host_pool.js +57 -31
  35. package/test/logger.js +75 -135
  36. package/test/outbound/bounce_net_errors.js +87 -131
  37. package/test/outbound/bounce_rfc3464.js +177 -254
  38. package/test/outbound/hmail.js +19 -0
  39. package/test/outbound/index.js +189 -0
  40. package/test/outbound/queue.js +92 -0
  41. package/test/plugins/auth/auth_base.js +39 -44
  42. package/test/plugins/auth/auth_vpopmaild.js +8 -9
  43. package/test/plugins/queue/smtp_forward.js +953 -183
  44. package/test/plugins/rcpt_to.host_list_base.js +58 -93
  45. package/test/plugins/rcpt_to.in_host_list.js +126 -175
  46. package/test/plugins/record_envelope_addresses.js +8 -8
  47. package/test/plugins/status.js +10 -10
  48. package/test/plugins/tls.js +9 -19
  49. package/test/plugins/xclient.js +75 -110
  50. package/test/plugins.js +10 -13
  51. package/test/rfc1869.js +50 -70
  52. package/test/server.js +438 -421
  53. package/test/smtp_client.js +1192 -218
  54. package/test/tls_socket.js +242 -0
  55. package/tls_socket.js +18 -22
@@ -152,6 +152,195 @@ describe('outbound', () => {
152
152
  })
153
153
  assert.ok(true)
154
154
  })
155
+
156
+ it('waits for drain when stream backpressure is applied', async () => {
157
+ const todo = {
158
+ queue_time: Date.now(),
159
+ domain: 'example.com',
160
+ rcpt_to: [],
161
+ mail_from: {},
162
+ notes: {},
163
+ uuid: 'u1',
164
+ }
165
+ let drained = false
166
+
167
+ await new Promise((resolve) => {
168
+ const ws = {
169
+ write() {
170
+ return false
171
+ },
172
+ once(event, cb) {
173
+ assert.equal(event, 'drain')
174
+ setImmediate(() => {
175
+ drained = true
176
+ cb()
177
+ resolve()
178
+ })
179
+ },
180
+ }
181
+ outbound.build_todo(todo, ws, () => {})
182
+ })
183
+
184
+ assert.equal(drained, true)
185
+ })
186
+ })
187
+
188
+ describe('send_trans_email', () => {
189
+ const queueDir = path.resolve('test', 'test-queue')
190
+
191
+ beforeEach(() => {
192
+ process.env.HARAKA_TEST_DIR = path.resolve('test')
193
+ fs.mkdirSync(queueDir, { recursive: true })
194
+ })
195
+
196
+ afterEach(() => {
197
+ delete process.env.HARAKA_TEST_DIR
198
+ try {
199
+ for (const f of fs.readdirSync(queueDir)) {
200
+ fs.unlinkSync(path.join(queueDir, f))
201
+ }
202
+ } catch (ignore) {}
203
+ })
204
+
205
+ // Regression test for haraka/Haraka#3551:
206
+ // When dkim_verify (data_post) pipes the message_stream and DKIMVerifyStream
207
+ // fires its callback early via process.nextTick (no DKIM-Signature found),
208
+ // the chain runs synchronously into process_delivery → pipe() while the
209
+ // first pipe is still in flight. pre_send_trans_email_respond must yield
210
+ // (via setImmediate) before opening a new pipe.
211
+ it('yields to setImmediate before opening process_delivery pipes', async () => {
212
+ const stream = require('node:stream')
213
+ const Transaction = require('../../transaction')
214
+ const Address = require('address-rfc2821').Address
215
+ const outbound = require('../../outbound')
216
+ const plugins = require('../../plugins')
217
+
218
+ const txn = Transaction.createTransaction()
219
+ const origRunHooks = plugins.run_hooks
220
+ try {
221
+ txn.mail_from = new Address('<from@example.com>')
222
+ txn.rcpt_to = [new Address('<to@example.com>')]
223
+ txn.message_stream.add_line(Buffer.from('From: from@example.com\r\n'))
224
+ txn.message_stream.add_line(Buffer.from('To: to@example.com\r\n'))
225
+ txn.message_stream.add_line(Buffer.from('\r\n'))
226
+ txn.message_stream.add_line(Buffer.from('body\r\n'))
227
+ await new Promise((r) => txn.message_stream.add_line_end(r))
228
+
229
+ // Start a pipe on the message_stream and fire a synchronous callback
230
+ // before it drains — this models what dkim_verify does.
231
+ const verifierFiredCb = new Promise((resolve) => {
232
+ let scheduled = false
233
+ const verifier = new stream.Writable({
234
+ write(_chunk, _enc, cb) {
235
+ if (!scheduled) {
236
+ scheduled = true
237
+ process.nextTick(resolve)
238
+ }
239
+ cb()
240
+ },
241
+ })
242
+ txn.message_stream.pipe(verifier)
243
+ })
244
+ await verifierFiredCb
245
+
246
+ // Now invoke send_trans_email — its pre_send_trans_email_respond
247
+ // should yield (await setImmediate) before calling process_delivery,
248
+ // letting the verifier pipe drain so the new pipe can succeed.
249
+ await new Promise((resolve, reject) => {
250
+ // Stub the heavy bits: we only care that the chain doesn't throw
251
+ // "Cannot pipe while currently piping" before queuing happens.
252
+ plugins.run_hooks = (hook, obj) => {
253
+ if (hook === 'pre_send_trans_email') {
254
+ // Mimic empty-hook synchronous callback (no plugins)
255
+ obj.pre_send_trans_email_respond(constants.cont).catch(reject)
256
+ } else {
257
+ origRunHooks.call(plugins, hook, obj)
258
+ }
259
+ }
260
+
261
+ outbound.send_trans_email(txn, (retval) => {
262
+ if (retval === constants.ok) resolve()
263
+ else reject(new Error(`unexpected retval ${retval}`))
264
+ })
265
+ })
266
+ } finally {
267
+ plugins.run_hooks = origRunHooks
268
+ txn.message_stream.destroy()
269
+ }
270
+ })
271
+
272
+ it('adds missing Message-Id/Date and prepends Received before queueing', async () => {
273
+ process.env.HARAKA_TEST_DIR = path.resolve('test')
274
+ const Address = require('address-rfc2821').Address
275
+ const outbound = require('../../outbound')
276
+ const plugins = require('../../plugins')
277
+
278
+ const added = []
279
+ const leading = []
280
+ const queued = []
281
+ const transaction = {
282
+ uuid: 'txn-add-headers',
283
+ header: {
284
+ get_all(_name) {
285
+ return []
286
+ },
287
+ get() {
288
+ return null
289
+ },
290
+ },
291
+ rcpt_to: [new Address('<user@example.com>')],
292
+ notes: {},
293
+ add_header(name, value) {
294
+ added.push([name, value])
295
+ },
296
+ remove_header() {},
297
+ add_leading_header(name, value) {
298
+ leading.push([name, value])
299
+ },
300
+ results: {
301
+ add() {},
302
+ },
303
+ }
304
+
305
+ const originalRunHooks = plugins.run_hooks
306
+ const originalProcessDelivery = outbound.process_delivery
307
+ const originalPush = outbound.delivery_queue.push
308
+ outbound.delivery_queue.push = (hmail) => {
309
+ queued.push(hmail)
310
+ }
311
+ outbound.process_delivery = async (_okPaths, _todo, hmails) => {
312
+ hmails.push({ queued: true })
313
+ }
314
+ plugins.run_hooks = (hook, conn) => {
315
+ if (hook === 'pre_send_trans_email') {
316
+ conn.pre_send_trans_email_respond(constants.cont)
317
+ }
318
+ }
319
+
320
+ try {
321
+ const result = await new Promise((resolve) => {
322
+ outbound.send_trans_email(transaction, (retval, msg) => resolve({ retval, msg }))
323
+ })
324
+
325
+ assert.equal(result.retval, constants.ok)
326
+ assert.match(result.msg, /Message Queued/)
327
+ assert.equal(queued.length, 1)
328
+ assert.equal(
329
+ added.some(([name]) => name === 'Message-Id'),
330
+ true,
331
+ )
332
+ assert.equal(
333
+ added.some(([name]) => name === 'Date'),
334
+ true,
335
+ )
336
+ assert.equal(leading[0][0], 'Received')
337
+ } finally {
338
+ plugins.run_hooks = originalRunHooks
339
+ outbound.process_delivery = originalProcessDelivery
340
+ outbound.delivery_queue.push = originalPush
341
+ delete process.env.HARAKA_TEST_DIR
342
+ }
343
+ })
155
344
  })
156
345
 
157
346
  describe('timer_queue', () => {
@@ -230,4 +230,96 @@ describe('outbound/queue', () => {
230
230
  }
231
231
  })
232
232
  })
233
+
234
+ describe('queue maintenance', () => {
235
+ it('delete_dot_files removes leftover dot files only', async () => {
236
+ const tmpDir = path.join(os.tmpdir(), `haraka-dot-clean-${Date.now()}`)
237
+ fs.mkdirSync(tmpDir, { recursive: true })
238
+ const dotName = `${qfile.platformDOT}leftover`
239
+ const normalName = 'keep-me'
240
+ fs.writeFileSync(path.join(tmpDir, dotName), 'x')
241
+ fs.writeFileSync(path.join(tmpDir, normalName), 'x')
242
+
243
+ const originalQueueDir = queue.queue_dir
244
+ queue.queue_dir = tmpDir
245
+ try {
246
+ await queue.delete_dot_files()
247
+ assert.equal(fs.existsSync(path.join(tmpDir, dotName)), false)
248
+ assert.equal(fs.existsSync(path.join(tmpDir, normalName)), true)
249
+ } finally {
250
+ queue.queue_dir = originalQueueDir
251
+ fs.rmSync(tmpDir, { recursive: true, force: true })
252
+ }
253
+ })
254
+
255
+ it('_add_hmail pushes immediate items and schedules delayed ones', () => {
256
+ const originalPush = queue.delivery_queue.push
257
+ const originalAdd = queue.temp_fail_queue.add
258
+ const pushed = []
259
+ const delayed = []
260
+ let delayedCb
261
+
262
+ queue.delivery_queue.push = (item) => pushed.push(item)
263
+ queue.temp_fail_queue.add = (id, ms, cb) => {
264
+ delayed.push([id, ms])
265
+ delayedCb = cb
266
+ }
267
+ queue.cur_time = new Date()
268
+
269
+ const immediate = { filename: 'a', next_process: queue.cur_time - 1 }
270
+ const future = { filename: 'b', next_process: queue.cur_time.getTime() + 1000 }
271
+
272
+ try {
273
+ queue._add_hmail(immediate)
274
+ queue._add_hmail(future)
275
+ assert.equal(pushed.length, 1)
276
+ assert.equal(delayed.length, 1)
277
+ assert.equal(delayed[0][0], 'b')
278
+ delayedCb()
279
+ assert.equal(pushed.length, 2)
280
+ } finally {
281
+ queue.delivery_queue.push = originalPush
282
+ queue.temp_fail_queue.add = originalAdd
283
+ }
284
+ })
285
+
286
+ it('scan_queue_pids returns unique pids from queue files', async () => {
287
+ populateTestQueue()
288
+ const originalQueueDir = queue.queue_dir
289
+ queue.queue_dir = testQueueDir
290
+
291
+ try {
292
+ const pids = await queue.scan_queue_pids()
293
+ assert.ok(Array.isArray(pids))
294
+ assert.equal(pids.length >= 1, true)
295
+ } finally {
296
+ queue.queue_dir = originalQueueDir
297
+ clearTestQueue()
298
+ }
299
+ })
300
+
301
+ it('scan_queue_pids throws when queue dir cannot be read', async () => {
302
+ const originalQueueDir = queue.queue_dir
303
+ const badPath = path.join(os.tmpdir(), `queue-not-dir-${Date.now()}`)
304
+ fs.writeFileSync(badPath, 'x')
305
+ queue.queue_dir = badPath
306
+ try {
307
+ await assert.rejects(() => queue.scan_queue_pids())
308
+ } finally {
309
+ queue.queue_dir = originalQueueDir
310
+ fs.rmSync(badPath, { force: true })
311
+ }
312
+ })
313
+
314
+ it('delete_dot_files handles readdir errors without throwing', async () => {
315
+ const originalQueueDir = queue.queue_dir
316
+ queue.queue_dir = path.join(os.tmpdir(), `missing-dot-${Date.now()}`)
317
+ try {
318
+ await queue.delete_dot_files()
319
+ assert.ok(true)
320
+ } finally {
321
+ queue.queue_dir = originalQueueDir
322
+ }
323
+ })
324
+ })
233
325
  })
@@ -1,11 +1,12 @@
1
1
  'use strict'
2
2
  const assert = require('node:assert')
3
+ const { describe, it, beforeEach } = require('node:test')
3
4
 
4
5
  const { Address } = require('address-rfc2821')
5
6
  const fixtures = require('haraka-test-fixtures')
6
7
  const utils = require('haraka-utils')
7
8
 
8
- const _set_up = (done) => {
9
+ const _set_up = () => {
9
10
  this.plugin = new fixtures.plugin('auth/auth_base')
10
11
 
11
12
  this.plugin.get_plain_passwd = (user, cb) => {
@@ -15,11 +16,9 @@ const _set_up = (done) => {
15
16
 
16
17
  this.connection = fixtures.connection.createConnection()
17
18
  this.connection.capabilities = null
18
-
19
- done()
20
19
  }
21
20
 
22
- const _set_up_2 = (done) => {
21
+ const _set_up_2 = () => {
23
22
  this.plugin = new fixtures.plugin('auth/auth_base')
24
23
 
25
24
  this.plugin.get_plain_passwd = (user, connection, cb) => {
@@ -30,11 +29,9 @@ const _set_up_2 = (done) => {
30
29
 
31
30
  this.connection = fixtures.connection.createConnection()
32
31
  this.connection.capabilities = null
33
-
34
- done()
35
32
  }
36
33
 
37
- const _set_up_custom_pwcb_opts = (done) => {
34
+ const _set_up_custom_pwcb_opts = () => {
38
35
  this.plugin = new fixtures.plugin('auth/auth_base')
39
36
 
40
37
  this.plugin.check_plain_passwd = (connection, user, passwd, pwok_cb) => {
@@ -63,15 +60,13 @@ const _set_up_custom_pwcb_opts = (done) => {
63
60
  this.connection.notes.resp_strings.push([code, msg])
64
61
  return cb()
65
62
  }
66
-
67
- done()
68
63
  }
69
64
 
70
65
  describe('auth_base', () => {
71
66
  describe('hook_capabilities', () => {
72
67
  beforeEach(_set_up)
73
68
 
74
- it('no TLS, no auth', (done) => {
69
+ it('no TLS, no auth', (t, done) => {
75
70
  this.plugin.hook_capabilities((rc, msg) => {
76
71
  assert.equal(undefined, rc)
77
72
  assert.equal(undefined, msg)
@@ -80,7 +75,7 @@ describe('auth_base', () => {
80
75
  }, this.connection)
81
76
  })
82
77
 
83
- it('with TLS, auth is offered', (done) => {
78
+ it('with TLS, auth is offered', (t, done) => {
84
79
  this.connection.tls.enabled = true
85
80
  this.connection.capabilities = []
86
81
  this.plugin.hook_capabilities((rc, msg) => {
@@ -97,13 +92,13 @@ describe('auth_base', () => {
97
92
  describe('get_plain_passwd', () => {
98
93
  beforeEach(_set_up)
99
94
 
100
- it('get_plain_passwd, no result', (done) => {
95
+ it('get_plain_passwd, no result', (t, done) => {
101
96
  this.plugin.get_plain_passwd('user', (pass) => {
102
97
  assert.equal(pass, null)
103
98
  done()
104
99
  })
105
100
  })
106
- it('get_plain_passwd, test user', (done) => {
101
+ it('get_plain_passwd, test user', (t, done) => {
107
102
  this.plugin.get_plain_passwd('test', (pass) => {
108
103
  assert.equal(pass, 'testpass')
109
104
  done()
@@ -114,21 +109,21 @@ describe('auth_base', () => {
114
109
  describe('check_plain_passwd', () => {
115
110
  beforeEach(_set_up)
116
111
 
117
- it('valid password', (done) => {
112
+ it('valid password', (t, done) => {
118
113
  this.plugin.check_plain_passwd(this.connection, 'test', 'testpass', (pass) => {
119
114
  assert.equal(pass, true)
120
115
  done()
121
116
  })
122
117
  })
123
118
 
124
- it('wrong password', (done) => {
119
+ it('wrong password', (t, done) => {
125
120
  this.plugin.check_plain_passwd(this.connection, 'test', 'test1pass', (pass) => {
126
121
  assert.equal(pass, false)
127
122
  done()
128
123
  })
129
124
  })
130
125
 
131
- it('null password', (done) => {
126
+ it('null password', (t, done) => {
132
127
  this.plugin.check_plain_passwd(this.connection, 'test', null, (pass) => {
133
128
  assert.equal(pass, false)
134
129
  done()
@@ -139,7 +134,7 @@ describe('auth_base', () => {
139
134
  describe('select_auth_method', () => {
140
135
  beforeEach(_set_up)
141
136
 
142
- it('no auth methods yield no result', (done) => {
137
+ it('no auth methods yield no result', (t, done) => {
143
138
  this.plugin.select_auth_method(
144
139
  (code) => {
145
140
  assert.equal(code, null)
@@ -151,7 +146,7 @@ describe('auth_base', () => {
151
146
  )
152
147
  })
153
148
 
154
- it('invalid AUTH method, no result', (done) => {
149
+ it('invalid AUTH method, no result', (t, done) => {
155
150
  this.connection.notes.allowed_auth_methods = ['PLAIN', 'LOGIN', 'CRAM-MD5']
156
151
  this.plugin.select_auth_method(
157
152
  (code) => {
@@ -164,7 +159,7 @@ describe('auth_base', () => {
164
159
  )
165
160
  })
166
161
 
167
- it('valid AUTH method, valid attempt', (done) => {
162
+ it('valid AUTH method, valid attempt', (t, done) => {
168
163
  const method = `PLAIN ${utils.base64('discard\0test\0testpass')}`
169
164
  this.connection.notes.allowed_auth_methods = ['PLAIN', 'LOGIN']
170
165
  this.plugin.select_auth_method(
@@ -182,7 +177,7 @@ describe('auth_base', () => {
182
177
  describe('auth_plain', () => {
183
178
  beforeEach(_set_up)
184
179
 
185
- it('params type=string returns OK', (done) => {
180
+ it('params type=string returns OK', (t, done) => {
186
181
  this.plugin.auth_plain(
187
182
  (rc) => {
188
183
  assert.equal(rc, OK)
@@ -194,7 +189,7 @@ describe('auth_base', () => {
194
189
  )
195
190
  })
196
191
 
197
- it('params type=empty array, returns OK', (done) => {
192
+ it('params type=empty array, returns OK', (t, done) => {
198
193
  this.plugin.auth_plain(
199
194
  (rc) => {
200
195
  assert.equal(rc, OK)
@@ -206,7 +201,7 @@ describe('auth_base', () => {
206
201
  )
207
202
  })
208
203
 
209
- it('params type=array, successful auth', (done) => {
204
+ it('params type=array, successful auth', (t, done) => {
210
205
  const method = utils.base64('discard\0test\0testpass')
211
206
  this.plugin.auth_plain(
212
207
  (rc) => {
@@ -219,7 +214,7 @@ describe('auth_base', () => {
219
214
  )
220
215
  })
221
216
 
222
- it('params type=with two line login', (done) => {
217
+ it('params type=with two line login', (t, done) => {
223
218
  this.plugin.auth_plain(
224
219
  (rc) => {
225
220
  assert.equal(this.connection.notes.auth_plain_asked_login, true)
@@ -235,7 +230,7 @@ describe('auth_base', () => {
235
230
  describe('check_user', () => {
236
231
  beforeEach(_set_up_2)
237
232
 
238
- it('bad auth', (done) => {
233
+ it('bad auth', (t, done) => {
239
234
  const credentials = ['matt', 'ttam']
240
235
  this.plugin.check_user(
241
236
  (code) => {
@@ -250,7 +245,7 @@ describe('auth_base', () => {
250
245
  )
251
246
  })
252
247
 
253
- it('good auth', (done) => {
248
+ it('good auth', (t, done) => {
254
249
  const credentials = ['test', 'testpass']
255
250
  this.plugin.check_user(
256
251
  (code) => {
@@ -269,7 +264,7 @@ describe('auth_base', () => {
269
264
  describe('check_user_custom_opts', () => {
270
265
  beforeEach(_set_up_custom_pwcb_opts)
271
266
 
272
- it('legacyok_nomessage', (done) => {
267
+ it('legacyok_nomessage', (t, done) => {
273
268
  this.plugin.check_user(
274
269
  (code, msg) => {
275
270
  assert.equal(code, OK)
@@ -283,7 +278,7 @@ describe('auth_base', () => {
283
278
  )
284
279
  })
285
280
 
286
- it('legacyfail_nomessage', (done) => {
281
+ it('legacyfail_nomessage', (t, done) => {
287
282
  this.plugin.check_user(
288
283
  (code, msg) => {
289
284
  assert.equal(code, OK)
@@ -297,7 +292,7 @@ describe('auth_base', () => {
297
292
  )
298
293
  })
299
294
 
300
- it('legacyok_message', (done) => {
295
+ it('legacyok_message', (t, done) => {
301
296
  this.plugin.check_user(
302
297
  (code, msg) => {
303
298
  assert.equal(code, OK)
@@ -311,7 +306,7 @@ describe('auth_base', () => {
311
306
  )
312
307
  })
313
308
 
314
- it('legacyfail_message', (done) => {
309
+ it('legacyfail_message', (t, done) => {
315
310
  this.plugin.check_user(
316
311
  (code, msg) => {
317
312
  assert.equal(code, OK)
@@ -325,7 +320,7 @@ describe('auth_base', () => {
325
320
  )
326
321
  })
327
322
 
328
- it('newok', (done) => {
323
+ it('newok', (t, done) => {
329
324
  this.plugin.check_user(
330
325
  (code, msg) => {
331
326
  assert.equal(code, OK)
@@ -339,7 +334,7 @@ describe('auth_base', () => {
339
334
  )
340
335
  })
341
336
 
342
- it('newfail', (done) => {
337
+ it('newfail', (t, done) => {
343
338
  this.plugin.check_user(
344
339
  (code, msg) => {
345
340
  assert.equal(code, OK)
@@ -357,7 +352,7 @@ describe('auth_base', () => {
357
352
  describe('auth_notes_are_set', () => {
358
353
  beforeEach(_set_up_2)
359
354
 
360
- it('bad auth: no notes should be set', (done) => {
355
+ it('bad auth: no notes should be set', (t, done) => {
361
356
  const credentials = ['matt', 'ttam']
362
357
  this.plugin.check_user(
363
358
  (code) => {
@@ -371,7 +366,7 @@ describe('auth_base', () => {
371
366
  )
372
367
  })
373
368
 
374
- it('good auth: dont store password', (done) => {
369
+ it('good auth: dont store password', (t, done) => {
375
370
  const creds = ['test', 'testpass']
376
371
  this.plugin.blankout_password = true
377
372
  this.plugin.check_user(
@@ -386,7 +381,7 @@ describe('auth_base', () => {
386
381
  )
387
382
  })
388
383
 
389
- it('good auth: store password (default)', (done) => {
384
+ it('good auth: store password (default)', (t, done) => {
390
385
  const creds = ['test', 'testpass']
391
386
  this.plugin.check_user(
392
387
  (code) => {
@@ -404,7 +399,7 @@ describe('auth_base', () => {
404
399
  describe('hook_unrecognized_command', () => {
405
400
  beforeEach(_set_up)
406
401
 
407
- it('AUTH type FOO', (done) => {
402
+ it('AUTH type FOO', (t, done) => {
408
403
  const params = ['AUTH', 'FOO']
409
404
  this.connection.notes.allowed_auth_methods = ['PLAIN', 'LOGIN']
410
405
  this.plugin.hook_unrecognized_command(
@@ -418,7 +413,7 @@ describe('auth_base', () => {
418
413
  )
419
414
  })
420
415
 
421
- it('AUTH PLAIN', (done) => {
416
+ it('AUTH PLAIN', (t, done) => {
422
417
  const params = ['AUTH', 'PLAIN', utils.base64('discard\0test\0testpass')]
423
418
  this.connection.notes.allowed_auth_methods = ['PLAIN', 'LOGIN']
424
419
  this.plugin.hook_unrecognized_command(
@@ -432,7 +427,7 @@ describe('auth_base', () => {
432
427
  )
433
428
  })
434
429
 
435
- it('AUTH PLAIN, authenticating', (done) => {
430
+ it('AUTH PLAIN, authenticating', (t, done) => {
436
431
  this.connection.notes.allowed_auth_methods = ['PLAIN', 'LOGIN']
437
432
  this.connection.notes.authenticating = true
438
433
  this.connection.notes.auth_method = 'PLAIN'
@@ -451,7 +446,7 @@ describe('auth_base', () => {
451
446
  describe('auth_login', () => {
452
447
  beforeEach(_set_up)
453
448
 
454
- it('AUTH LOGIN', (done) => {
449
+ it('AUTH LOGIN', (t, done) => {
455
450
  const params = ['AUTH', 'LOGIN']
456
451
  this.connection.notes.allowed_auth_methods = ['PLAIN', 'LOGIN']
457
452
  this.plugin.hook_unrecognized_command(
@@ -484,7 +479,7 @@ describe('auth_base', () => {
484
479
  )
485
480
  })
486
481
 
487
- it('AUTH LOGIN <username>', (done) => {
482
+ it('AUTH LOGIN <username>', (t, done) => {
488
483
  const params = ['AUTH', 'LOGIN', utils.base64('test')]
489
484
  this.connection.notes.allowed_auth_methods = ['PLAIN', 'LOGIN']
490
485
  this.plugin.hook_unrecognized_command(
@@ -509,7 +504,7 @@ describe('auth_base', () => {
509
504
  )
510
505
  })
511
506
 
512
- it('AUTH LOGIN <username>, bad protocol', (done) => {
507
+ it('AUTH LOGIN <username>, bad protocol', (t, done) => {
513
508
  const params = ['AUTH', 'LOGIN', utils.base64('test')]
514
509
  this.connection.notes.allowed_auth_methods = ['PLAIN', 'LOGIN']
515
510
  this.plugin.hook_unrecognized_command(
@@ -535,7 +530,7 @@ describe('auth_base', () => {
535
530
  )
536
531
  })
537
532
 
538
- it('AUTH LOGIN, reauthentication', (done) => {
533
+ it('AUTH LOGIN, reauthentication', (t, done) => {
539
534
  const params = ['AUTH', 'LOGIN', utils.base64('test')]
540
535
  this.connection.notes.allowed_auth_methods = ['PLAIN', 'LOGIN']
541
536
  this.plugin.hook_unrecognized_command(
@@ -583,7 +578,7 @@ describe('auth_base', () => {
583
578
  describe('constrain_sender', () => {
584
579
  beforeEach(_set_up)
585
580
 
586
- it('constrain_sender, domain match', (done) => {
581
+ it('constrain_sender, domain match', (t, done) => {
587
582
  this.mfrom = new Address('user@example.com')
588
583
  this.connection.results.add({ name: 'auth' }, { user: 'user@example.com' })
589
584
  this.plugin.constrain_sender(
@@ -596,7 +591,7 @@ describe('auth_base', () => {
596
591
  )
597
592
  })
598
593
 
599
- it('constrain_sender, domain mismatch', (done) => {
594
+ it('constrain_sender, domain mismatch', (t, done) => {
600
595
  this.mfrom = new Address('user@example.net')
601
596
  this.connection.results.add({ name: 'auth' }, { user: 'user@example.com' })
602
597
  this.plugin.constrain_sender(
@@ -609,7 +604,7 @@ describe('auth_base', () => {
609
604
  [this.mfrom],
610
605
  )
611
606
  })
612
- it('constrain_sender, no domain', (done) => {
607
+ it('constrain_sender, no domain', (t, done) => {
613
608
  this.mfrom = new Address('user@example.com')
614
609
  this.connection.results.add({ name: 'auth' }, { user: 'user' })
615
610
  this.plugin.constrain_sender(