imapflow 1.7.7 → 2.0.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/CHANGELOG.md +27 -0
- package/README.md +8 -2
- package/dist/cjs/charsets.d.ts +1 -0
- package/dist/cjs/charsets.js +294 -0
- package/dist/cjs/commands/append.d.ts +22 -0
- package/dist/cjs/commands/append.js +151 -0
- package/dist/cjs/commands/authenticate.d.ts +24 -0
- package/dist/cjs/commands/authenticate.js +223 -0
- package/dist/cjs/commands/capability.d.ts +8 -0
- package/dist/cjs/commands/capability.js +32 -0
- package/dist/cjs/commands/close.d.ts +8 -0
- package/dist/cjs/commands/close.js +39 -0
- package/dist/cjs/commands/compress.d.ts +8 -0
- package/dist/cjs/commands/compress.js +56 -0
- package/dist/cjs/commands/copy.d.ts +13 -0
- package/dist/cjs/commands/copy.js +44 -0
- package/dist/cjs/commands/copyuid-parser.d.ts +11 -0
- package/dist/cjs/commands/copyuid-parser.js +32 -0
- package/dist/cjs/commands/create.d.ts +11 -0
- package/dist/cjs/commands/create.js +80 -0
- package/dist/cjs/commands/delete.d.ts +11 -0
- package/dist/cjs/commands/delete.js +40 -0
- package/dist/cjs/commands/enable.d.ts +9 -0
- package/dist/cjs/commands/enable.js +61 -0
- package/dist/cjs/commands/esearch-parser.d.ts +17 -0
- package/dist/cjs/commands/esearch-parser.js +91 -0
- package/dist/cjs/commands/expunge.d.ts +12 -0
- package/dist/cjs/commands/expunge.js +60 -0
- package/dist/cjs/commands/fetch.d.ts +30 -0
- package/dist/cjs/commands/fetch.js +241 -0
- package/dist/cjs/commands/id.d.ts +10 -0
- package/dist/cjs/commands/id.js +80 -0
- package/dist/cjs/commands/idle.d.ts +9 -0
- package/dist/cjs/commands/idle.js +347 -0
- package/dist/cjs/commands/list.d.ts +16 -0
- package/dist/cjs/commands/list.js +518 -0
- package/dist/cjs/commands/login.d.ts +11 -0
- package/dist/cjs/commands/login.js +42 -0
- package/dist/cjs/commands/logout.d.ts +8 -0
- package/dist/cjs/commands/logout.js +47 -0
- package/dist/cjs/commands/move.d.ts +13 -0
- package/dist/cjs/commands/move.js +57 -0
- package/dist/cjs/commands/namespace.d.ts +25 -0
- package/dist/cjs/commands/namespace.js +139 -0
- package/dist/cjs/commands/noop.d.ts +8 -0
- package/dist/cjs/commands/noop.js +22 -0
- package/dist/cjs/commands/quota.d.ts +10 -0
- package/dist/cjs/commands/quota.js +119 -0
- package/dist/cjs/commands/rename.d.ts +12 -0
- package/dist/cjs/commands/rename.js +48 -0
- package/dist/cjs/commands/search.d.ts +15 -0
- package/dist/cjs/commands/search.js +228 -0
- package/dist/cjs/commands/select.d.ts +25 -0
- package/dist/cjs/commands/select.js +250 -0
- package/dist/cjs/commands/starttls.d.ts +8 -0
- package/dist/cjs/commands/starttls.js +30 -0
- package/dist/cjs/commands/status-fields.d.ts +14 -0
- package/dist/cjs/commands/status-fields.js +61 -0
- package/dist/cjs/commands/status.d.ts +12 -0
- package/dist/cjs/commands/status.js +108 -0
- package/dist/cjs/commands/store.d.ts +19 -0
- package/dist/cjs/commands/store.js +93 -0
- package/dist/cjs/commands/subscribe.d.ts +9 -0
- package/dist/cjs/commands/subscribe.js +31 -0
- package/dist/cjs/commands/unsubscribe.d.ts +9 -0
- package/dist/cjs/commands/unsubscribe.js +31 -0
- package/dist/cjs/connection-deadline.d.ts +49 -0
- package/dist/cjs/connection-deadline.js +91 -0
- package/dist/cjs/errors.d.ts +83 -0
- package/dist/cjs/errors.js +13 -0
- package/dist/cjs/handler/imap-compiler.d.ts +24 -0
- package/dist/cjs/handler/imap-compiler.js +285 -0
- package/dist/cjs/handler/imap-formal-syntax.d.ts +28 -0
- package/dist/cjs/handler/imap-formal-syntax.js +121 -0
- package/dist/cjs/handler/imap-handler.d.ts +9 -0
- package/dist/cjs/handler/imap-handler.js +10 -0
- package/dist/cjs/handler/imap-parser.d.ts +16 -0
- package/dist/cjs/handler/imap-parser.js +90 -0
- package/dist/cjs/handler/imap-stream.d.ts +181 -0
- package/dist/cjs/handler/imap-stream.js +446 -0
- package/dist/cjs/handler/limits.d.ts +25 -0
- package/dist/cjs/handler/limits.js +51 -0
- package/dist/cjs/handler/parser-instance.d.ts +68 -0
- package/dist/cjs/handler/parser-instance.js +223 -0
- package/dist/cjs/handler/token-parser.d.ts +91 -0
- package/dist/cjs/handler/token-parser.js +673 -0
- package/dist/cjs/handler/types.d.ts +91 -0
- package/dist/cjs/handler/types.js +4 -0
- package/dist/cjs/imap-commands.d.ts +16 -0
- package/dist/cjs/imap-commands.js +74 -0
- package/dist/cjs/imap-flow.d.ts +676 -0
- package/dist/cjs/imap-flow.js +3949 -0
- package/dist/cjs/jp-decoder.d.ts +12 -0
- package/dist/cjs/jp-decoder.js +79 -0
- package/dist/cjs/limited-passthrough.d.ts +25 -0
- package/dist/cjs/limited-passthrough.js +54 -0
- package/dist/cjs/logger.d.ts +3 -0
- package/dist/cjs/logger.js +11 -0
- package/dist/cjs/package-info.d.ts +3 -0
- package/dist/cjs/package-info.js +7 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/proxy-connection.d.ts +33 -0
- package/dist/cjs/proxy-connection.js +392 -0
- package/dist/cjs/search-compiler.d.ts +34 -0
- package/dist/cjs/search-compiler.js +476 -0
- package/dist/cjs/special-use.d.ts +22 -0
- package/dist/cjs/special-use.js +911 -0
- package/dist/cjs/tools.d.ts +427 -0
- package/dist/cjs/tools.js +1496 -0
- package/{lib/imap-flow.d.ts → dist/cjs/types.d.ts} +386 -516
- package/dist/cjs/types.js +5 -0
- package/dist/esm/charsets.d.ts +1 -0
- package/{lib → dist/esm}/charsets.js +1 -6
- package/dist/esm/commands/append.d.ts +22 -0
- package/{lib → dist/esm}/commands/append.js +22 -52
- package/dist/esm/commands/authenticate.d.ts +24 -0
- package/{lib → dist/esm}/commands/authenticate.js +62 -87
- package/dist/esm/commands/capability.d.ts +8 -0
- package/{lib → dist/esm}/commands/capability.js +6 -9
- package/dist/esm/commands/close.d.ts +8 -0
- package/{lib → dist/esm}/commands/close.js +6 -10
- package/dist/esm/commands/compress.d.ts +8 -0
- package/{lib → dist/esm}/commands/compress.js +7 -11
- package/dist/esm/commands/copy.d.ts +13 -0
- package/{lib → dist/esm}/commands/copy.js +12 -20
- package/dist/esm/commands/copyuid-parser.d.ts +11 -0
- package/{lib → dist/esm}/commands/copyuid-parser.js +9 -15
- package/dist/esm/commands/create.d.ts +11 -0
- package/{lib → dist/esm}/commands/create.js +13 -27
- package/dist/esm/commands/delete.d.ts +11 -0
- package/{lib → dist/esm}/commands/delete.js +9 -14
- package/dist/esm/commands/enable.d.ts +9 -0
- package/{lib → dist/esm}/commands/enable.js +23 -30
- package/dist/esm/commands/esearch-parser.d.ts +17 -0
- package/dist/esm/commands/esearch-parser.js +88 -0
- package/dist/esm/commands/expunge.d.ts +12 -0
- package/{lib → dist/esm}/commands/expunge.js +17 -22
- package/dist/esm/commands/fetch.d.ts +30 -0
- package/{lib → dist/esm}/commands/fetch.js +32 -64
- package/dist/esm/commands/id.d.ts +10 -0
- package/{lib → dist/esm}/commands/id.js +17 -23
- package/dist/esm/commands/idle.d.ts +9 -0
- package/{lib → dist/esm}/commands/idle.js +47 -81
- package/dist/esm/commands/list.d.ts +16 -0
- package/{lib → dist/esm}/commands/list.js +56 -121
- package/dist/esm/commands/login.d.ts +11 -0
- package/{lib → dist/esm}/commands/login.js +10 -15
- package/dist/esm/commands/logout.d.ts +8 -0
- package/{lib → dist/esm}/commands/logout.js +9 -11
- package/dist/esm/commands/move.d.ts +13 -0
- package/{lib → dist/esm}/commands/move.js +13 -21
- package/dist/esm/commands/namespace.d.ts +25 -0
- package/{lib → dist/esm}/commands/namespace.js +34 -44
- package/dist/esm/commands/noop.d.ts +8 -0
- package/{lib → dist/esm}/commands/noop.js +6 -7
- package/dist/esm/commands/quota.d.ts +10 -0
- package/{lib → dist/esm}/commands/quota.js +18 -36
- package/dist/esm/commands/rename.d.ts +12 -0
- package/{lib → dist/esm}/commands/rename.js +10 -15
- package/dist/esm/commands/search.d.ts +15 -0
- package/{lib → dist/esm}/commands/search.js +36 -135
- package/dist/esm/commands/select.d.ts +25 -0
- package/{lib → dist/esm}/commands/select.js +33 -64
- package/dist/esm/commands/starttls.d.ts +8 -0
- package/{lib → dist/esm}/commands/starttls.js +6 -8
- package/dist/esm/commands/status-fields.d.ts +14 -0
- package/{lib → dist/esm}/commands/status-fields.js +5 -16
- package/dist/esm/commands/status.d.ts +12 -0
- package/{lib → dist/esm}/commands/status.js +18 -29
- package/dist/esm/commands/store.d.ts +19 -0
- package/{lib → dist/esm}/commands/store.js +24 -37
- package/dist/esm/commands/subscribe.d.ts +9 -0
- package/{lib → dist/esm}/commands/subscribe.js +8 -12
- package/dist/esm/commands/unsubscribe.d.ts +9 -0
- package/{lib → dist/esm}/commands/unsubscribe.js +8 -12
- package/dist/esm/connection-deadline.d.ts +49 -0
- package/{lib → dist/esm}/connection-deadline.js +14 -25
- package/dist/esm/errors.d.ts +83 -0
- package/dist/esm/errors.js +9 -0
- package/dist/esm/handler/imap-compiler.d.ts +24 -0
- package/{lib → dist/esm}/handler/imap-compiler.js +22 -80
- package/dist/esm/handler/imap-formal-syntax.d.ts +28 -0
- package/dist/esm/handler/imap-formal-syntax.js +117 -0
- package/dist/esm/handler/imap-handler.d.ts +9 -0
- package/dist/esm/handler/imap-handler.js +9 -0
- package/dist/esm/handler/imap-parser.d.ts +16 -0
- package/{lib → dist/esm}/handler/imap-parser.js +31 -44
- package/dist/esm/handler/imap-stream.d.ts +181 -0
- package/{lib → dist/esm}/handler/imap-stream.js +29 -121
- package/dist/esm/handler/limits.d.ts +25 -0
- package/{lib → dist/esm}/handler/limits.js +13 -22
- package/dist/esm/handler/parser-instance.d.ts +68 -0
- package/{lib → dist/esm}/handler/parser-instance.js +19 -47
- package/dist/esm/handler/token-parser.d.ts +91 -0
- package/{lib → dist/esm}/handler/token-parser.js +71 -155
- package/dist/esm/handler/types.d.ts +91 -0
- package/dist/esm/handler/types.js +3 -0
- package/dist/esm/imap-commands.d.ts +16 -0
- package/dist/esm/imap-commands.js +67 -0
- package/dist/esm/imap-flow.d.ts +676 -0
- package/{lib → dist/esm}/imap-flow.js +785 -1802
- package/dist/esm/jp-decoder.d.ts +12 -0
- package/{lib → dist/esm}/jp-decoder.js +6 -21
- package/dist/esm/limited-passthrough.d.ts +25 -0
- package/{lib → dist/esm}/limited-passthrough.js +7 -20
- package/dist/esm/logger.d.ts +3 -0
- package/dist/esm/logger.js +4 -0
- package/dist/esm/package-info.d.ts +3 -0
- package/dist/esm/package-info.js +4 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/proxy-connection.d.ts +33 -0
- package/{lib → dist/esm}/proxy-connection.js +56 -127
- package/dist/esm/search-compiler.d.ts +34 -0
- package/{lib → dist/esm}/search-compiler.js +54 -110
- package/dist/esm/special-use.d.ts +22 -0
- package/dist/esm/special-use.js +907 -0
- package/dist/esm/tools.d.ts +427 -0
- package/dist/esm/tools.js +1446 -0
- package/dist/esm/types.d.ts +828 -0
- package/dist/esm/types.js +4 -0
- package/package.json +60 -20
- package/.gitattributes +0 -1
- package/.github/CODE_OF_CONDUCT.md +0 -76
- package/.github/FUNDING.yml +0 -4
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
- package/.github/contributing.md +0 -17
- package/.github/workflows/release.yaml +0 -36
- package/.github/workflows/stale.yml +0 -29
- package/.github/workflows/test.yml +0 -51
- package/.ncurc.js +0 -4
- package/.prettierignore +0 -4
- package/.prettierrc.js +0 -8
- package/.release-please-manifest.json +0 -3
- package/CLAUDE.md +0 -104
- package/Gruntfile.js +0 -23
- package/eslint.config.js +0 -45
- package/lib/handler/imap-formal-syntax.js +0 -189
- package/lib/handler/imap-handler.js +0 -17
- package/lib/imap-commands.js +0 -45
- package/lib/logger.js +0 -5
- package/lib/special-use.js +0 -923
- package/lib/tools.js +0 -1612
- package/release-please-config.json +0 -10
- package/test/authentication-test.js +0 -101
- package/test/auto-idle-test.js +0 -470
- package/test/bodystructure-test.js +0 -899
- package/test/charsets-test.js +0 -161
- package/test/commands-branches-test.js +0 -1095
- package/test/commands-integration-test.js +0 -11124
- package/test/commands-test.js +0 -73
- package/test/connection-edge-cases-test.js +0 -1828
- package/test/connection-test.js +0 -162
- package/test/copyuid-parser-test.js +0 -173
- package/test/fetch-generator-test.js +0 -218
- package/test/fixtures/fake-timers.js +0 -115
- package/test/fixtures/serialized-mimetorture.js +0 -2738
- package/test/fixtures/test-client.js +0 -57
- package/test/fixtures/test-tls.js +0 -8
- package/test/handler-branches-test.js +0 -310
- package/test/idle-polling-test.js +0 -518
- package/test/imap-compiler-test.js +0 -809
- package/test/imap-flow-compress-test.js +0 -166
- package/test/imap-flow-coverage-test.js +0 -612
- package/test/imap-flow-fetch-download-test.js +0 -873
- package/test/imap-flow-internals-test.js +0 -725
- package/test/imap-flow-methods-test.js +0 -889
- package/test/imap-flow-proxy-paths-test.js +0 -366
- package/test/imap-flow-secure-test.js +0 -573
- package/test/imap-flow-server-test.js +0 -1474
- package/test/imap-formal-syntax-test.js +0 -293
- package/test/imap-parser-test.js +0 -1474
- package/test/imap-stream-edge-cases-test.js +0 -666
- package/test/imap-stream-test.js +0 -177
- package/test/imapflow-test.js +0 -258
- package/test/integration/README.md +0 -52
- package/test/integration/dovecot-test.conf +0 -27
- package/test/integration/rev2-live-test.js +0 -431
- package/test/integration/run-rev2-tests.sh +0 -75
- package/test/integration-test.js +0 -83
- package/test/jp-decoder-test.js +0 -304
- package/test/limited-passthrough-test.js +0 -299
- package/test/memory-cleanup-test.js +0 -144
- package/test/memory-leak-test.js +0 -667
- package/test/parser-limits-test.js +0 -292
- package/test/proxy-connection-test.js +0 -738
- package/test/reliability-improvements-test.js +0 -548
- package/test/search-compiler-test.js +0 -1300
- package/test/search-test.js +0 -329
- package/test/special-use-test.js +0 -418
- package/test/starttls-injection-test.js +0 -181
- package/test/tag-correlation-test.js +0 -333
- package/test/timer-policy-test.js +0 -227
- package/test/token-parser-test.js +0 -456
- package/test/tools-test.js +0 -2013
- package/test/unhandled-rejection-test.js +0 -593
|
@@ -1,573 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// End-to-end secure-transport tests: a successful STARTTLS upgrade and a direct
|
|
4
|
-
// TLS connection, both against in-process mock servers using a self-signed cert.
|
|
5
|
-
// These cover upgradeToSTARTTLS()'s success path and the TLS branches of connect().
|
|
6
|
-
|
|
7
|
-
const net = require('net');
|
|
8
|
-
const tls = require('tls');
|
|
9
|
-
const { ImapFlow } = require('../lib/imap-flow');
|
|
10
|
-
const { cert, key } = require('./fixtures/test-tls');
|
|
11
|
-
|
|
12
|
-
const CAPS = 'IMAP4rev1 ID ENABLE NAMESPACE';
|
|
13
|
-
|
|
14
|
-
// Shared per-connection IMAP line handler used by both the plaintext and the
|
|
15
|
-
// upgraded TLS phases. Returns responses for the minimal session commands.
|
|
16
|
-
// `caps` is the full capability list to advertise for this phase - the STARTTLS
|
|
17
|
-
// test uses different pre- and post-TLS sets to prove the client discards the
|
|
18
|
-
// plaintext capabilities and re-fetches them over TLS (RFC 9051 6.2.1).
|
|
19
|
-
const handleLine = (sock, line, onStartTls, caps) => {
|
|
20
|
-
caps = caps || `${CAPS} STARTTLS`;
|
|
21
|
-
let parts = line.split(' ');
|
|
22
|
-
let tag = parts[0];
|
|
23
|
-
let cmd = (parts[1] || '').toUpperCase();
|
|
24
|
-
switch (cmd) {
|
|
25
|
-
case 'CAPABILITY':
|
|
26
|
-
sock.write(`* CAPABILITY ${caps}\r\n${tag} OK CAPABILITY done\r\n`);
|
|
27
|
-
break;
|
|
28
|
-
case 'STARTTLS':
|
|
29
|
-
sock.write(`${tag} OK Begin TLS\r\n`);
|
|
30
|
-
if (onStartTls) {
|
|
31
|
-
onStartTls();
|
|
32
|
-
}
|
|
33
|
-
break;
|
|
34
|
-
case 'ID':
|
|
35
|
-
sock.write(`* ID ("name" "mock" "version" "1")\r\n${tag} OK ID done\r\n`);
|
|
36
|
-
break;
|
|
37
|
-
case 'LOGIN':
|
|
38
|
-
sock.write(`${tag} OK LOGIN done\r\n`);
|
|
39
|
-
break;
|
|
40
|
-
case 'NAMESPACE':
|
|
41
|
-
sock.write(`* NAMESPACE (("" "/")) NIL NIL\r\n${tag} OK NAMESPACE done\r\n`);
|
|
42
|
-
break;
|
|
43
|
-
case 'ENABLE':
|
|
44
|
-
sock.write(`${tag} OK ENABLE done\r\n`);
|
|
45
|
-
break;
|
|
46
|
-
case 'COMPRESS':
|
|
47
|
-
sock.write(`${tag} NO not now\r\n`);
|
|
48
|
-
break;
|
|
49
|
-
case 'NOOP':
|
|
50
|
-
sock.write(`${tag} OK NOOP done\r\n`);
|
|
51
|
-
break;
|
|
52
|
-
case 'LOGOUT':
|
|
53
|
-
sock.write(`* BYE bye\r\n${tag} OK LOGOUT done\r\n`);
|
|
54
|
-
break;
|
|
55
|
-
default:
|
|
56
|
-
sock.write(`${tag} BAD unknown ${cmd}\r\n`);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const lineReader = (sock, onLine) => {
|
|
61
|
-
let buf = '';
|
|
62
|
-
let handler = data => {
|
|
63
|
-
buf += data.toString('binary');
|
|
64
|
-
let idx;
|
|
65
|
-
while ((idx = buf.indexOf('\r\n')) >= 0) {
|
|
66
|
-
let line = buf.slice(0, idx);
|
|
67
|
-
buf = buf.slice(idx + 2);
|
|
68
|
-
onLine(line);
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
sock.on('data', handler);
|
|
72
|
-
sock.on('error', () => {});
|
|
73
|
-
return () => sock.removeListener('data', handler);
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const listen = server => new Promise(resolve => server.listen(0, '127.0.0.1', () => resolve(server.address().port)));
|
|
77
|
-
|
|
78
|
-
// Builds a STARTTLS mock server. Defaults give the happy path; options tweak a
|
|
79
|
-
// phase without another copy of the upgrade scaffold:
|
|
80
|
-
// - preTlsCaps / postTlsCaps: capability list advertised before / after the upgrade
|
|
81
|
-
// - startTlsOk: tag => string, overrides the response line to the STARTTLS command
|
|
82
|
-
// - onTlsLine: (tlsSocket, line) => boolean, intercepts post-upgrade lines; return
|
|
83
|
-
// true when the line was handled
|
|
84
|
-
const createStartTlsServer = (opts = {}) =>
|
|
85
|
-
net.createServer(rawSocket => {
|
|
86
|
-
rawSocket.on('error', () => {});
|
|
87
|
-
|
|
88
|
-
let preTlsCaps = opts.preTlsCaps || `${CAPS} STARTTLS`;
|
|
89
|
-
let postTlsCaps = opts.postTlsCaps || CAPS;
|
|
90
|
-
|
|
91
|
-
let detachPlain;
|
|
92
|
-
detachPlain = lineReader(rawSocket, line => {
|
|
93
|
-
let parts = line.split(' ');
|
|
94
|
-
let tag = parts[0];
|
|
95
|
-
let cmd = (parts[1] || '').toUpperCase();
|
|
96
|
-
|
|
97
|
-
if (cmd === 'STARTTLS') {
|
|
98
|
-
rawSocket.write(opts.startTlsOk ? opts.startTlsOk(tag) : `${tag} OK Begin TLS\r\n`);
|
|
99
|
-
detachPlain();
|
|
100
|
-
let tlsSocket = new tls.TLSSocket(rawSocket, { isServer: true, key, cert });
|
|
101
|
-
tlsSocket.on('error', () => {});
|
|
102
|
-
lineReader(tlsSocket, l => {
|
|
103
|
-
if (opts.onTlsLine && opts.onTlsLine(tlsSocket, l)) {
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
handleLine(tlsSocket, l, null, postTlsCaps);
|
|
107
|
-
});
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
handleLine(rawSocket, line, null, preTlsCaps);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
rawSocket.write(`* OK [CAPABILITY ${preTlsCaps}] ready\r\n`);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// The client options shared by every STARTTLS test.
|
|
118
|
-
const makeStartTlsClient = (port, overrides = {}) =>
|
|
119
|
-
new ImapFlow({
|
|
120
|
-
host: '127.0.0.1',
|
|
121
|
-
port,
|
|
122
|
-
secure: false,
|
|
123
|
-
doSTARTTLS: true,
|
|
124
|
-
servername: 'localhost',
|
|
125
|
-
tls: { rejectUnauthorized: false },
|
|
126
|
-
disableAutoIdle: true,
|
|
127
|
-
disableCompression: true,
|
|
128
|
-
logger: false,
|
|
129
|
-
auth: { user: 'test', pass: 'secret' },
|
|
130
|
-
...overrides
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
// Every terminal upgrade path must leave no upgrade state behind
|
|
134
|
-
const assertUpgradeSettled = (test, client) => {
|
|
135
|
-
test.equal(client.upgrading, false, 'upgrading flag cleared');
|
|
136
|
-
test.equal(client._upgradeReject, null, 'upgrade rejector cleared');
|
|
137
|
-
test.equal(client.upgradeTimeout, null, 'upgrade timer cleared');
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
// ---------------------------------------------------------------------------
|
|
141
|
-
// STARTTLS happy path
|
|
142
|
-
// ---------------------------------------------------------------------------
|
|
143
|
-
|
|
144
|
-
module.exports['Secure: STARTTLS upgrade completes a session'] = async test => {
|
|
145
|
-
// post-TLS phase advertises a different capability set
|
|
146
|
-
let server = createStartTlsServer({
|
|
147
|
-
preTlsCaps: `${CAPS} STARTTLS PRETLS-ONLY`,
|
|
148
|
-
postTlsCaps: `${CAPS} POSTTLS-ONLY`
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
let port = await listen(server);
|
|
152
|
-
let client = makeStartTlsClient(port);
|
|
153
|
-
client.on('error', () => {});
|
|
154
|
-
|
|
155
|
-
await client.connect();
|
|
156
|
-
test.ok(client.secureConnection, 'connection upgraded to TLS');
|
|
157
|
-
test.ok(client.authenticated, 'authenticated over TLS');
|
|
158
|
-
test.ok(client.usable);
|
|
159
|
-
// RFC 9051 6.2.1: capabilities cached before STARTTLS MUST be discarded and
|
|
160
|
-
// re-fetched over the TLS channel
|
|
161
|
-
test.ok(client.capabilities.has('POSTTLS-ONLY'), 'post-TLS capabilities were re-fetched');
|
|
162
|
-
test.ok(!client.capabilities.has('PRETLS-ONLY'), 'pre-TLS capabilities were discarded');
|
|
163
|
-
|
|
164
|
-
await client.noop();
|
|
165
|
-
await client.logout();
|
|
166
|
-
client.close();
|
|
167
|
-
server.close();
|
|
168
|
-
test.done();
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
module.exports['Secure: STARTTLS discards capabilities even when the OK carries a CAPABILITY code'] = async test => {
|
|
172
|
-
// A server (or a MITM rewriting the plaintext stream) may stamp [CAPABILITY ...]
|
|
173
|
-
// on the STARTTLS OK itself. That marks the capability set as freshly updated, so
|
|
174
|
-
// a discard conditioned on "an update is still pending" would keep exactly the
|
|
175
|
-
// pre-TLS list an attacker controls - the list that then chooses the AUTH
|
|
176
|
-
// mechanism and answers LOGINDISABLED. RFC 9051 6.2.1 makes the discard mandatory.
|
|
177
|
-
let server = createStartTlsServer({
|
|
178
|
-
preTlsCaps: `${CAPS} STARTTLS PRETLS-ONLY`,
|
|
179
|
-
postTlsCaps: `${CAPS} POSTTLS-ONLY`,
|
|
180
|
-
// the OK itself carries a (pre-TLS, attacker-rewritable) CAPABILITY code
|
|
181
|
-
startTlsOk: tag => `${tag} OK [CAPABILITY ${CAPS} PRETLS-ONLY] Begin TLS\r\n`
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
let port = await listen(server);
|
|
185
|
-
let client = makeStartTlsClient(port);
|
|
186
|
-
client.on('error', () => {});
|
|
187
|
-
|
|
188
|
-
await client.connect();
|
|
189
|
-
test.ok(client.secureConnection, 'connection upgraded to TLS');
|
|
190
|
-
test.ok(client.capabilities.has('POSTTLS-ONLY'), 'post-TLS capabilities were re-fetched');
|
|
191
|
-
test.ok(!client.capabilities.has('PRETLS-ONLY'), 'pre-TLS capabilities were discarded');
|
|
192
|
-
|
|
193
|
-
await client.logout();
|
|
194
|
-
client.close();
|
|
195
|
-
server.close();
|
|
196
|
-
test.done();
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
module.exports['Secure: pre-TLS rawCapabilities do not survive a failed re-fetch'] = async test => {
|
|
200
|
-
// capabilities/authCapabilities are discarded at the upgrade, but rawCapabilities
|
|
201
|
-
// is public surface external consumers read. If the post-TLS CAPABILITY re-fetch
|
|
202
|
-
// fails, the pre-TLS list - the one an active attacker can rewrite - must not
|
|
203
|
-
// linger there either.
|
|
204
|
-
let server = createStartTlsServer({
|
|
205
|
-
preTlsCaps: `${CAPS} STARTTLS PRETLS-ONLY`,
|
|
206
|
-
postTlsCaps: `${CAPS} POSTTLS-ONLY`,
|
|
207
|
-
onTlsLine: (tlsSocket, line) => {
|
|
208
|
-
let parts = line.split(' ');
|
|
209
|
-
if ((parts[1] || '').toUpperCase() === 'CAPABILITY') {
|
|
210
|
-
// the re-fetch over TLS fails
|
|
211
|
-
tlsSocket.write(`${parts[0]} NO CAPABILITY not available\r\n`);
|
|
212
|
-
return true;
|
|
213
|
-
}
|
|
214
|
-
return false;
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
let port = await listen(server);
|
|
219
|
-
let client = makeStartTlsClient(port);
|
|
220
|
-
client.on('error', () => {});
|
|
221
|
-
|
|
222
|
-
await client.connect();
|
|
223
|
-
test.ok(client.secureConnection, 'connection upgraded to TLS');
|
|
224
|
-
test.ok(!client.capabilities.has('PRETLS-ONLY'), 'pre-TLS capabilities were discarded');
|
|
225
|
-
let raw = [].concat(client.rawCapabilities || []);
|
|
226
|
-
test.ok(!raw.some(entry => entry && /PRETLS-ONLY/.test(entry.value || entry)), 'pre-TLS rawCapabilities were discarded');
|
|
227
|
-
|
|
228
|
-
await client.logout();
|
|
229
|
-
client.close();
|
|
230
|
-
server.close();
|
|
231
|
-
test.done();
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
// Records every socket that went through configureSocket(), which is the single place the
|
|
235
|
-
// transport options (keepalive + inactivity watchdog) are applied.
|
|
236
|
-
const trackConfiguredSockets = client => {
|
|
237
|
-
let sockets = [];
|
|
238
|
-
let original = client.configureSocket.bind(client);
|
|
239
|
-
client.configureSocket = socket => {
|
|
240
|
-
sockets.push(socket);
|
|
241
|
-
return original(socket);
|
|
242
|
-
};
|
|
243
|
-
return sockets;
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
module.exports['Secure: STARTTLS session keeps the inactivity watchdog'] = async test => {
|
|
247
|
-
// Regression: the inactivity timer was armed on the plain socket only, while the timeout
|
|
248
|
-
// listener was attached to the TLS socket, so an upgraded session had no watchdog at all
|
|
249
|
-
// and a dead connection was never noticed.
|
|
250
|
-
let server = createStartTlsServer();
|
|
251
|
-
let port = await listen(server);
|
|
252
|
-
let client = makeStartTlsClient(port, { socketTimeout: 200 });
|
|
253
|
-
|
|
254
|
-
let errors = [];
|
|
255
|
-
client.on('error', err => errors.push(err));
|
|
256
|
-
let closed = new Promise(resolve => client.once('close', resolve));
|
|
257
|
-
let configured = trackConfiguredSockets(client);
|
|
258
|
-
|
|
259
|
-
await client.connect();
|
|
260
|
-
test.ok(client.secureConnection, 'connection upgraded to TLS');
|
|
261
|
-
test.equal(configured.length, 2, 'both the plain socket and the upgraded socket are configured');
|
|
262
|
-
test.equal(client.socket.timeout, 200, 'the upgraded socket carries the configured inactivity timeout');
|
|
263
|
-
test.equal(configured[0].timeout, 0, 'the superseded plain-socket timer is cleared, not left armed');
|
|
264
|
-
|
|
265
|
-
// No traffic from here on: the watchdog has to fire and take the connection down.
|
|
266
|
-
await closed;
|
|
267
|
-
|
|
268
|
-
test.ok(
|
|
269
|
-
errors.some(err => err.code === 'ETIMEOUT'),
|
|
270
|
-
'the inactivity watchdog reported a socket timeout'
|
|
271
|
-
);
|
|
272
|
-
test.ok(client.isClosed, 'the connection closed after the timeout');
|
|
273
|
-
|
|
274
|
-
client.close();
|
|
275
|
-
server.close();
|
|
276
|
-
test.done();
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
module.exports['Secure: STARTTLS leaves no upgrade state behind on success'] = async test => {
|
|
280
|
-
// Item 8: every terminal path of the upgrade runs through one settlement helper, so after
|
|
281
|
-
// a successful handshake no timer, rejector, flag or temporary handler survives.
|
|
282
|
-
let server = createStartTlsServer();
|
|
283
|
-
let port = await listen(server);
|
|
284
|
-
let client = makeStartTlsClient(port);
|
|
285
|
-
client.on('error', () => {});
|
|
286
|
-
|
|
287
|
-
await client.connect();
|
|
288
|
-
|
|
289
|
-
assertUpgradeSettled(test, client);
|
|
290
|
-
// The handshake-only handler is gone, leaving the generic socket error handler as the single
|
|
291
|
-
// error path (during the handshake it is the other way round, which is what prevents a
|
|
292
|
-
// handshake error from firing two handlers at once).
|
|
293
|
-
test.equal(client.socket.listenerCount('error'), 1, 'the TLS socket keeps exactly one error path');
|
|
294
|
-
|
|
295
|
-
await client.logout();
|
|
296
|
-
client.close();
|
|
297
|
-
server.close();
|
|
298
|
-
test.done();
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
module.exports['Secure: close() during a STARTTLS upgrade settles the upgrade'] = async test => {
|
|
302
|
-
// The server acknowledges STARTTLS but never performs a handshake. close() has to settle
|
|
303
|
-
// the pending upgrade instead of leaving the session promise dangling forever.
|
|
304
|
-
let server = net.createServer(rawSocket => {
|
|
305
|
-
rawSocket.on('error', () => {});
|
|
306
|
-
let detach = lineReader(rawSocket, line => {
|
|
307
|
-
handleLine(rawSocket, line, () => {
|
|
308
|
-
detach(); // silence: no ServerHello, no data at all
|
|
309
|
-
});
|
|
310
|
-
});
|
|
311
|
-
rawSocket.write(`* OK [CAPABILITY ${CAPS} STARTTLS] ready\r\n`);
|
|
312
|
-
});
|
|
313
|
-
let port = await listen(server);
|
|
314
|
-
let client = makeStartTlsClient(port);
|
|
315
|
-
client.on('error', () => {});
|
|
316
|
-
|
|
317
|
-
let connectResult = client.connect().then(
|
|
318
|
-
() => null,
|
|
319
|
-
err => err
|
|
320
|
-
);
|
|
321
|
-
|
|
322
|
-
// Wait until the upgrade is in flight, then close the connection under it.
|
|
323
|
-
while (!client.upgrading) {
|
|
324
|
-
await new Promise(resolve => setTimeout(resolve, 10));
|
|
325
|
-
}
|
|
326
|
-
// A late socket event would invoke the same settle helper the rejector exposes -
|
|
327
|
-
// capture it before close() consumes it
|
|
328
|
-
let settle = client._upgradeReject;
|
|
329
|
-
client.close();
|
|
330
|
-
|
|
331
|
-
let err = await connectResult;
|
|
332
|
-
test.ok(err, 'connect rejected rather than hanging on the abandoned upgrade');
|
|
333
|
-
test.ok(['ClosedAfterConnectText', 'ClosedAfterConnectTLS', 'NoConnection'].includes(err.code), `connect rejected with ${err.code}`);
|
|
334
|
-
assertUpgradeSettled(test, client);
|
|
335
|
-
|
|
336
|
-
// The late event lands after the upgrade already settled - it must be a no-op:
|
|
337
|
-
// a settled upgrade neither claims the error as a TLS failure nor re-arms any state
|
|
338
|
-
let lateErr = new Error('late socket error');
|
|
339
|
-
settle(lateErr);
|
|
340
|
-
test.ok(!lateErr.tlsFailed, 'the late error was not marked as a TLS upgrade failure');
|
|
341
|
-
assertUpgradeSettled(test, client);
|
|
342
|
-
|
|
343
|
-
server.close();
|
|
344
|
-
test.done();
|
|
345
|
-
};
|
|
346
|
-
|
|
347
|
-
module.exports['Secure: STARTTLS handshake failure rejects connect'] = async test => {
|
|
348
|
-
let server = createStartTlsServer();
|
|
349
|
-
let port = await listen(server);
|
|
350
|
-
// The tls option is erased entirely, not merely relaxed: certificate validation
|
|
351
|
-
// stays on, so the mock server's self-signed certificate must fail the handshake,
|
|
352
|
-
// and the upgrade builds its TLS options from the no-options fallback
|
|
353
|
-
let client = makeStartTlsClient(port, { tls: undefined });
|
|
354
|
-
client.on('error', () => {});
|
|
355
|
-
|
|
356
|
-
let err = await client.connect().then(
|
|
357
|
-
() => null,
|
|
358
|
-
connectErr => connectErr
|
|
359
|
-
);
|
|
360
|
-
|
|
361
|
-
test.ok(err, 'connect rejected on the failed handshake');
|
|
362
|
-
test.ok(err.tlsFailed, 'the error is marked as a TLS upgrade failure');
|
|
363
|
-
assertUpgradeSettled(test, client);
|
|
364
|
-
|
|
365
|
-
client.close();
|
|
366
|
-
server.close();
|
|
367
|
-
test.done();
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
// ---------------------------------------------------------------------------
|
|
371
|
-
// Direct TLS connection
|
|
372
|
-
// ---------------------------------------------------------------------------
|
|
373
|
-
|
|
374
|
-
module.exports['Secure: direct TLS connection completes a session'] = async test => {
|
|
375
|
-
let server = tls.createServer({ key, cert }, sock => {
|
|
376
|
-
sock.on('error', () => {});
|
|
377
|
-
lineReader(sock, line => handleLine(sock, line));
|
|
378
|
-
sock.write(`* OK [CAPABILITY ${CAPS}] ready\r\n`);
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
let port = await listen(server);
|
|
382
|
-
let client = new ImapFlow({
|
|
383
|
-
host: '127.0.0.1',
|
|
384
|
-
port,
|
|
385
|
-
secure: true,
|
|
386
|
-
servername: 'localhost',
|
|
387
|
-
tls: { rejectUnauthorized: false },
|
|
388
|
-
disableAutoIdle: true,
|
|
389
|
-
disableCompression: true,
|
|
390
|
-
logger: false,
|
|
391
|
-
auth: { user: 'test', pass: 'secret' }
|
|
392
|
-
});
|
|
393
|
-
client.on('error', () => {});
|
|
394
|
-
|
|
395
|
-
await client.connect();
|
|
396
|
-
test.ok(client.secureConnection);
|
|
397
|
-
test.ok(client.authenticated);
|
|
398
|
-
test.ok(client.tls, 'cipher info recorded');
|
|
399
|
-
|
|
400
|
-
await client.noop();
|
|
401
|
-
await client.logout();
|
|
402
|
-
client.close();
|
|
403
|
-
server.close();
|
|
404
|
-
test.done();
|
|
405
|
-
};
|
|
406
|
-
|
|
407
|
-
// ---------------------------------------------------------------------------
|
|
408
|
-
// STARTTLS required but unsupported -> fail closed
|
|
409
|
-
// ---------------------------------------------------------------------------
|
|
410
|
-
|
|
411
|
-
module.exports['Secure: doSTARTTLS=false skips an advertised upgrade'] = async test => {
|
|
412
|
-
let server = net.createServer(sock => {
|
|
413
|
-
sock.on('error', () => {});
|
|
414
|
-
lineReader(sock, line => handleLine(sock, line)); // advertises STARTTLS in CAPABILITY
|
|
415
|
-
sock.write(`* OK [CAPABILITY ${CAPS} STARTTLS] ready\r\n`);
|
|
416
|
-
});
|
|
417
|
-
let port = await listen(server);
|
|
418
|
-
let client = new ImapFlow({
|
|
419
|
-
host: '127.0.0.1',
|
|
420
|
-
port,
|
|
421
|
-
secure: false,
|
|
422
|
-
doSTARTTLS: false, // explicitly disabled -> stay plaintext
|
|
423
|
-
tls: { rejectUnauthorized: false },
|
|
424
|
-
disableAutoIdle: true,
|
|
425
|
-
disableCompression: true,
|
|
426
|
-
logger: false,
|
|
427
|
-
auth: { user: 'test', pass: 'secret' }
|
|
428
|
-
});
|
|
429
|
-
client.on('error', () => {});
|
|
430
|
-
|
|
431
|
-
await client.connect();
|
|
432
|
-
test.ok(!client.secureConnection, 'stayed on plaintext');
|
|
433
|
-
test.ok(client.authenticated);
|
|
434
|
-
|
|
435
|
-
await client.logout();
|
|
436
|
-
client.close();
|
|
437
|
-
server.close();
|
|
438
|
-
test.done();
|
|
439
|
-
};
|
|
440
|
-
|
|
441
|
-
module.exports['Secure: STARTTLS command rejected by server fails closed'] = async test => {
|
|
442
|
-
let server = net.createServer(sock => {
|
|
443
|
-
sock.on('error', () => {});
|
|
444
|
-
lineReader(sock, line => {
|
|
445
|
-
let parts = line.split(' ');
|
|
446
|
-
let tag = parts[0];
|
|
447
|
-
let cmd = (parts[1] || '').toUpperCase();
|
|
448
|
-
if (cmd === 'CAPABILITY') {
|
|
449
|
-
sock.write(`* CAPABILITY ${CAPS} STARTTLS\r\n${tag} OK done\r\n`);
|
|
450
|
-
} else if (cmd === 'STARTTLS') {
|
|
451
|
-
sock.write(`${tag} NO STARTTLS not available right now\r\n`);
|
|
452
|
-
} else {
|
|
453
|
-
sock.write(`${tag} OK ok\r\n`);
|
|
454
|
-
}
|
|
455
|
-
});
|
|
456
|
-
sock.write(`* OK [CAPABILITY ${CAPS} STARTTLS] ready\r\n`);
|
|
457
|
-
});
|
|
458
|
-
let port = await listen(server);
|
|
459
|
-
let client = new ImapFlow({
|
|
460
|
-
host: '127.0.0.1',
|
|
461
|
-
port,
|
|
462
|
-
secure: false,
|
|
463
|
-
doSTARTTLS: true,
|
|
464
|
-
tls: { rejectUnauthorized: false },
|
|
465
|
-
disableAutoIdle: true,
|
|
466
|
-
disableCompression: true,
|
|
467
|
-
logger: false,
|
|
468
|
-
auth: { user: 'test', pass: 'secret' }
|
|
469
|
-
});
|
|
470
|
-
client.on('error', () => {});
|
|
471
|
-
|
|
472
|
-
let err = null;
|
|
473
|
-
try {
|
|
474
|
-
await client.connect();
|
|
475
|
-
} catch (e) {
|
|
476
|
-
err = e;
|
|
477
|
-
}
|
|
478
|
-
test.ok(err, 'connect rejected when STARTTLS refused');
|
|
479
|
-
test.ok(err.tlsFailed);
|
|
480
|
-
|
|
481
|
-
client.close();
|
|
482
|
-
server.close();
|
|
483
|
-
test.done();
|
|
484
|
-
};
|
|
485
|
-
|
|
486
|
-
module.exports['Secure: STARTTLS handshake error rejects connect'] = async test => {
|
|
487
|
-
// Server acknowledges STARTTLS then sends non-TLS garbage so the client's TLS
|
|
488
|
-
// handshake fails, exercising the dedicated TLS-socket error handler.
|
|
489
|
-
let server = net.createServer(rawSocket => {
|
|
490
|
-
rawSocket.on('error', () => {});
|
|
491
|
-
let detach = lineReader(rawSocket, line => {
|
|
492
|
-
handleLine(rawSocket, line, () => {
|
|
493
|
-
detach();
|
|
494
|
-
// garbage instead of a TLS ServerHello
|
|
495
|
-
rawSocket.write(Buffer.from('this is definitely not a TLS handshake\r\n'.repeat(8)));
|
|
496
|
-
});
|
|
497
|
-
});
|
|
498
|
-
rawSocket.write(`* OK [CAPABILITY ${CAPS} STARTTLS] ready\r\n`);
|
|
499
|
-
});
|
|
500
|
-
let port = await listen(server);
|
|
501
|
-
let client = new ImapFlow({
|
|
502
|
-
host: '127.0.0.1',
|
|
503
|
-
port,
|
|
504
|
-
secure: false,
|
|
505
|
-
doSTARTTLS: true,
|
|
506
|
-
servername: 'localhost',
|
|
507
|
-
tls: { rejectUnauthorized: false },
|
|
508
|
-
disableAutoIdle: true,
|
|
509
|
-
disableCompression: true,
|
|
510
|
-
logger: false,
|
|
511
|
-
auth: { user: 'test', pass: 'secret' }
|
|
512
|
-
});
|
|
513
|
-
client.on('error', () => {});
|
|
514
|
-
|
|
515
|
-
let err = null;
|
|
516
|
-
try {
|
|
517
|
-
await client.connect();
|
|
518
|
-
} catch (e) {
|
|
519
|
-
err = e;
|
|
520
|
-
}
|
|
521
|
-
test.ok(err, 'connect rejected on TLS handshake failure');
|
|
522
|
-
test.ok(err.tlsFailed, 'flagged as a TLS failure');
|
|
523
|
-
|
|
524
|
-
client.close();
|
|
525
|
-
server.close();
|
|
526
|
-
test.done();
|
|
527
|
-
};
|
|
528
|
-
|
|
529
|
-
module.exports['Secure: STARTTLS required but not advertised throws'] = async test => {
|
|
530
|
-
let server = net.createServer(sock => {
|
|
531
|
-
sock.on('error', () => {});
|
|
532
|
-
lineReader(sock, line => {
|
|
533
|
-
let parts = line.split(' ');
|
|
534
|
-
let tag = parts[0];
|
|
535
|
-
let cmd = (parts[1] || '').toUpperCase();
|
|
536
|
-
if (cmd === 'CAPABILITY') {
|
|
537
|
-
// no STARTTLS advertised
|
|
538
|
-
sock.write(`* CAPABILITY ${CAPS}\r\n${tag} OK done\r\n`);
|
|
539
|
-
} else {
|
|
540
|
-
sock.write(`${tag} OK ok\r\n`);
|
|
541
|
-
}
|
|
542
|
-
});
|
|
543
|
-
sock.write(`* OK [CAPABILITY ${CAPS}] ready\r\n`);
|
|
544
|
-
});
|
|
545
|
-
|
|
546
|
-
let port = await listen(server);
|
|
547
|
-
let client = new ImapFlow({
|
|
548
|
-
host: '127.0.0.1',
|
|
549
|
-
port,
|
|
550
|
-
secure: false,
|
|
551
|
-
doSTARTTLS: true,
|
|
552
|
-
servername: 'localhost',
|
|
553
|
-
tls: { rejectUnauthorized: false },
|
|
554
|
-
disableAutoIdle: true,
|
|
555
|
-
disableCompression: true,
|
|
556
|
-
logger: false,
|
|
557
|
-
auth: { user: 'test', pass: 'secret' }
|
|
558
|
-
});
|
|
559
|
-
client.on('error', () => {});
|
|
560
|
-
|
|
561
|
-
let err = null;
|
|
562
|
-
try {
|
|
563
|
-
await client.connect();
|
|
564
|
-
} catch (e) {
|
|
565
|
-
err = e;
|
|
566
|
-
}
|
|
567
|
-
test.ok(err, 'connect rejected');
|
|
568
|
-
test.ok(err.tlsFailed, 'flagged as TLS failure');
|
|
569
|
-
|
|
570
|
-
client.close();
|
|
571
|
-
server.close();
|
|
572
|
-
test.done();
|
|
573
|
-
};
|