imapflow 1.2.4 → 1.2.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.
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +15 -0
- package/lib/commands/append.js +2 -7
- package/lib/commands/copy.js +2 -7
- package/lib/commands/create.js +8 -17
- package/lib/commands/delete.js +2 -7
- package/lib/commands/expunge.js +2 -7
- package/lib/commands/move.js +2 -7
- package/lib/commands/quota.js +2 -7
- package/lib/commands/rename.js +2 -7
- package/lib/commands/search.js +2 -7
- package/lib/commands/select.js +2 -6
- package/lib/commands/store.js +2 -7
- package/lib/commands/subscribe.js +2 -7
- package/lib/commands/unsubscribe.js +2 -8
- package/lib/imap-flow.d.ts +4 -2
- package/lib/imap-flow.js +68 -63
- package/lib/tools.js +26 -15
- package/package.json +5 -2
- package/test/commands-integration-test.js +8216 -0
- package/test/connection-edge-cases-test.js +518 -0
- package/test/jp-decoder-test.js +199 -0
- package/test/limited-passthrough-test.js +258 -0
- package/test/memory-leak-test.js +667 -0
- package/test/proxy-connection-test.js +541 -0
- package/test/search-compiler-test.js +820 -50
- package/test/tools-test.js +992 -0
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const proxyquire = require('proxyquire').noCallThru();
|
|
4
|
+
|
|
5
|
+
// Mock socket object
|
|
6
|
+
const createMockSocket = () => ({
|
|
7
|
+
on: () => {},
|
|
8
|
+
write: () => {},
|
|
9
|
+
end: () => {},
|
|
10
|
+
destroy: () => {}
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
// Mock logger
|
|
14
|
+
const createMockLogger = () => {
|
|
15
|
+
const logs = { info: [], error: [] };
|
|
16
|
+
return {
|
|
17
|
+
info: msg => logs.info.push(msg),
|
|
18
|
+
error: msg => logs.error.push(msg),
|
|
19
|
+
_logs: logs
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// ============================================
|
|
24
|
+
// HTTP Proxy Tests
|
|
25
|
+
// ============================================
|
|
26
|
+
|
|
27
|
+
module.exports['Proxy Connection: HTTP proxy success'] = async test => {
|
|
28
|
+
const mockSocket = createMockSocket();
|
|
29
|
+
const logger = createMockLogger();
|
|
30
|
+
|
|
31
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
32
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': (url, port, host, cb) => {
|
|
33
|
+
cb(null, mockSocket);
|
|
34
|
+
},
|
|
35
|
+
socks: { SocksClient: {} },
|
|
36
|
+
dns: { promises: { resolve: async () => ['127.0.0.1'] } },
|
|
37
|
+
net: { isIP: () => true }
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const socket = await proxyConnection(logger, 'http://proxy.example.com:8080', '192.168.1.1', 993);
|
|
41
|
+
|
|
42
|
+
test.equal(socket, mockSocket);
|
|
43
|
+
test.equal(logger._logs.info.length, 1);
|
|
44
|
+
test.ok(logger._logs.info[0].msg.includes('HTTP proxy'));
|
|
45
|
+
test.done();
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
module.exports['Proxy Connection: HTTPS proxy success'] = async test => {
|
|
49
|
+
const mockSocket = createMockSocket();
|
|
50
|
+
const logger = createMockLogger();
|
|
51
|
+
|
|
52
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
53
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': (url, port, host, cb) => {
|
|
54
|
+
cb(null, mockSocket);
|
|
55
|
+
},
|
|
56
|
+
socks: { SocksClient: {} },
|
|
57
|
+
dns: { promises: { resolve: async () => ['127.0.0.1'] } },
|
|
58
|
+
net: { isIP: () => true }
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const socket = await proxyConnection(logger, 'https://proxy.example.com:8080', '192.168.1.1', 993);
|
|
62
|
+
|
|
63
|
+
test.equal(socket, mockSocket);
|
|
64
|
+
test.equal(logger._logs.info.length, 1);
|
|
65
|
+
test.done();
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
module.exports['Proxy Connection: HTTP proxy with password hides it in logs'] = async test => {
|
|
69
|
+
const mockSocket = createMockSocket();
|
|
70
|
+
const logger = createMockLogger();
|
|
71
|
+
|
|
72
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
73
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': (url, port, host, cb) => {
|
|
74
|
+
cb(null, mockSocket);
|
|
75
|
+
},
|
|
76
|
+
socks: { SocksClient: {} },
|
|
77
|
+
dns: { promises: { resolve: async () => ['127.0.0.1'] } },
|
|
78
|
+
net: { isIP: () => true }
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
await proxyConnection(logger, 'http://user:secret123@proxy.example.com:8080', '192.168.1.1', 993);
|
|
82
|
+
|
|
83
|
+
test.equal(logger._logs.info.length, 1);
|
|
84
|
+
test.ok(!logger._logs.info[0].proxyUrl.includes('secret123'));
|
|
85
|
+
test.ok(logger._logs.info[0].proxyUrl.includes('(hidden)'));
|
|
86
|
+
test.done();
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
module.exports['Proxy Connection: HTTP proxy failure'] = async test => {
|
|
90
|
+
const logger = createMockLogger();
|
|
91
|
+
const testError = new Error('Connection refused');
|
|
92
|
+
|
|
93
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
94
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': (url, port, host, cb) => {
|
|
95
|
+
cb(testError);
|
|
96
|
+
},
|
|
97
|
+
socks: { SocksClient: {} },
|
|
98
|
+
dns: { promises: { resolve: async () => ['127.0.0.1'] } },
|
|
99
|
+
net: { isIP: () => true }
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
await proxyConnection(logger, 'http://proxy.example.com:8080', '192.168.1.1', 993);
|
|
104
|
+
test.ok(false, 'Should have thrown');
|
|
105
|
+
} catch (err) {
|
|
106
|
+
test.equal(err, testError);
|
|
107
|
+
test.equal(logger._logs.error.length, 1);
|
|
108
|
+
test.ok(logger._logs.error[0].msg.includes('Failed'));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
test.done();
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
module.exports['Proxy Connection: HTTP proxy failure hides password'] = async test => {
|
|
115
|
+
const logger = createMockLogger();
|
|
116
|
+
|
|
117
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
118
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': (url, port, host, cb) => {
|
|
119
|
+
cb(new Error('Failed'));
|
|
120
|
+
},
|
|
121
|
+
socks: { SocksClient: {} },
|
|
122
|
+
dns: { promises: { resolve: async () => ['127.0.0.1'] } },
|
|
123
|
+
net: { isIP: () => true }
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
await proxyConnection(logger, 'http://user:secret@proxy.example.com:8080', '192.168.1.1', 993);
|
|
128
|
+
} catch (err) {
|
|
129
|
+
// Error expected - we just need to verify logging
|
|
130
|
+
err.expected = true;
|
|
131
|
+
test.ok(!logger._logs.error[0].proxyUrl.includes('secret'));
|
|
132
|
+
test.ok(logger._logs.error[0].proxyUrl.includes('(hidden)'));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
test.done();
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// ============================================
|
|
139
|
+
// SOCKS Proxy Tests
|
|
140
|
+
// ============================================
|
|
141
|
+
|
|
142
|
+
module.exports['Proxy Connection: SOCKS5 proxy success'] = async test => {
|
|
143
|
+
const mockSocket = createMockSocket();
|
|
144
|
+
const logger = createMockLogger();
|
|
145
|
+
|
|
146
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
147
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': () => {},
|
|
148
|
+
socks: {
|
|
149
|
+
SocksClient: {
|
|
150
|
+
createConnection: async opts => {
|
|
151
|
+
test.equal(opts.proxy.type, 5);
|
|
152
|
+
test.equal(opts.command, 'connect');
|
|
153
|
+
return { socket: mockSocket };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
dns: { promises: { resolve: async () => ['127.0.0.1'] } },
|
|
158
|
+
net: { isIP: () => true }
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const socket = await proxyConnection(logger, 'socks5://proxy.example.com:1080', '192.168.1.1', 993);
|
|
162
|
+
|
|
163
|
+
test.equal(socket, mockSocket);
|
|
164
|
+
test.equal(logger._logs.info.length, 1);
|
|
165
|
+
test.ok(logger._logs.info[0].msg.includes('SOCKS proxy'));
|
|
166
|
+
test.done();
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
module.exports['Proxy Connection: SOCKS proxy (defaults to SOCKS5)'] = async test => {
|
|
170
|
+
const mockSocket = createMockSocket();
|
|
171
|
+
const logger = createMockLogger();
|
|
172
|
+
|
|
173
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
174
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': () => {},
|
|
175
|
+
socks: {
|
|
176
|
+
SocksClient: {
|
|
177
|
+
createConnection: async opts => {
|
|
178
|
+
test.equal(opts.proxy.type, 5); // Default to SOCKS5
|
|
179
|
+
return { socket: mockSocket };
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
dns: { promises: { resolve: async () => ['127.0.0.1'] } },
|
|
184
|
+
net: { isIP: () => true }
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
await proxyConnection(logger, 'socks://proxy.example.com:1080', '192.168.1.1', 993);
|
|
188
|
+
test.done();
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
module.exports['Proxy Connection: SOCKS4 proxy'] = async test => {
|
|
192
|
+
const mockSocket = createMockSocket();
|
|
193
|
+
const logger = createMockLogger();
|
|
194
|
+
|
|
195
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
196
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': () => {},
|
|
197
|
+
socks: {
|
|
198
|
+
SocksClient: {
|
|
199
|
+
createConnection: async opts => {
|
|
200
|
+
test.equal(opts.proxy.type, 4);
|
|
201
|
+
return { socket: mockSocket };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
dns: { promises: { resolve: async () => ['127.0.0.1'] } },
|
|
206
|
+
net: { isIP: () => true }
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
await proxyConnection(logger, 'socks4://proxy.example.com:1080', '192.168.1.1', 993);
|
|
210
|
+
test.done();
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
module.exports['Proxy Connection: SOCKS4a proxy'] = async test => {
|
|
214
|
+
const mockSocket = createMockSocket();
|
|
215
|
+
const logger = createMockLogger();
|
|
216
|
+
|
|
217
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
218
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': () => {},
|
|
219
|
+
socks: {
|
|
220
|
+
SocksClient: {
|
|
221
|
+
createConnection: async opts => {
|
|
222
|
+
test.equal(opts.proxy.type, 4);
|
|
223
|
+
return { socket: mockSocket };
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
dns: { promises: { resolve: async () => ['127.0.0.1'] } },
|
|
228
|
+
net: { isIP: () => true }
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
await proxyConnection(logger, 'socks4a://proxy.example.com:1080', '192.168.1.1', 993);
|
|
232
|
+
test.done();
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
module.exports['Proxy Connection: SOCKS proxy with authentication'] = async test => {
|
|
236
|
+
const mockSocket = createMockSocket();
|
|
237
|
+
const logger = createMockLogger();
|
|
238
|
+
|
|
239
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
240
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': () => {},
|
|
241
|
+
socks: {
|
|
242
|
+
SocksClient: {
|
|
243
|
+
createConnection: async opts => {
|
|
244
|
+
test.equal(opts.proxy.userId, 'testuser');
|
|
245
|
+
test.equal(opts.proxy.password, 'testpass');
|
|
246
|
+
return { socket: mockSocket };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
dns: { promises: { resolve: async () => ['127.0.0.1'] } },
|
|
251
|
+
net: { isIP: () => true }
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
await proxyConnection(logger, 'socks5://testuser:testpass@proxy.example.com:1080', '192.168.1.1', 993);
|
|
255
|
+
test.done();
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
module.exports['Proxy Connection: SOCKS proxy hides password in logs'] = async test => {
|
|
259
|
+
const mockSocket = createMockSocket();
|
|
260
|
+
const logger = createMockLogger();
|
|
261
|
+
|
|
262
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
263
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': () => {},
|
|
264
|
+
socks: {
|
|
265
|
+
SocksClient: {
|
|
266
|
+
createConnection: async () => ({ socket: mockSocket })
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
dns: { promises: { resolve: async () => ['127.0.0.1'] } },
|
|
270
|
+
net: { isIP: () => true }
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
await proxyConnection(logger, 'socks5://user:secretpass@proxy.example.com:1080', '192.168.1.1', 993);
|
|
274
|
+
|
|
275
|
+
test.ok(!logger._logs.info[0].proxyUrl.includes('secretpass'));
|
|
276
|
+
test.ok(logger._logs.info[0].proxyUrl.includes('(hidden)'));
|
|
277
|
+
test.done();
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
module.exports['Proxy Connection: SOCKS proxy default port'] = async test => {
|
|
281
|
+
const mockSocket = createMockSocket();
|
|
282
|
+
const logger = createMockLogger();
|
|
283
|
+
|
|
284
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
285
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': () => {},
|
|
286
|
+
socks: {
|
|
287
|
+
SocksClient: {
|
|
288
|
+
createConnection: async opts => {
|
|
289
|
+
test.equal(opts.proxy.port, 1080); // Default SOCKS port
|
|
290
|
+
return { socket: mockSocket };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
dns: { promises: { resolve: async () => ['127.0.0.1'] } },
|
|
295
|
+
net: { isIP: () => true }
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
await proxyConnection(logger, 'socks5://proxy.example.com', '192.168.1.1', 993);
|
|
299
|
+
test.done();
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
module.exports['Proxy Connection: SOCKS proxy failure'] = async test => {
|
|
303
|
+
const logger = createMockLogger();
|
|
304
|
+
const testError = new Error('SOCKS connection failed');
|
|
305
|
+
|
|
306
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
307
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': () => {},
|
|
308
|
+
socks: {
|
|
309
|
+
SocksClient: {
|
|
310
|
+
createConnection: async () => {
|
|
311
|
+
throw testError;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
dns: { promises: { resolve: async () => ['127.0.0.1'] } },
|
|
316
|
+
net: { isIP: () => true }
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
try {
|
|
320
|
+
await proxyConnection(logger, 'socks5://proxy.example.com:1080', '192.168.1.1', 993);
|
|
321
|
+
test.ok(false, 'Should have thrown');
|
|
322
|
+
} catch (err) {
|
|
323
|
+
test.equal(err, testError);
|
|
324
|
+
test.equal(logger._logs.error.length, 1);
|
|
325
|
+
test.ok(logger._logs.error[0].msg.includes('Failed'));
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
test.done();
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
module.exports['Proxy Connection: SOCKS proxy failure hides password'] = async test => {
|
|
332
|
+
const logger = createMockLogger();
|
|
333
|
+
|
|
334
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
335
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': () => {},
|
|
336
|
+
socks: {
|
|
337
|
+
SocksClient: {
|
|
338
|
+
createConnection: async () => {
|
|
339
|
+
throw new Error('Failed');
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
dns: { promises: { resolve: async () => ['127.0.0.1'] } },
|
|
344
|
+
net: { isIP: () => true }
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
await proxyConnection(logger, 'socks5://user:secret@proxy.example.com:1080', '192.168.1.1', 993);
|
|
349
|
+
} catch (err) {
|
|
350
|
+
// Error expected - we just need to verify logging
|
|
351
|
+
err.expected = true;
|
|
352
|
+
test.ok(!logger._logs.error[0].proxyUrl.includes('secret'));
|
|
353
|
+
test.ok(logger._logs.error[0].proxyUrl.includes('(hidden)'));
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
test.done();
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
// ============================================
|
|
360
|
+
// DNS Resolution Tests
|
|
361
|
+
// ============================================
|
|
362
|
+
|
|
363
|
+
module.exports['Proxy Connection: Resolves hostname to IP'] = async test => {
|
|
364
|
+
const mockSocket = createMockSocket();
|
|
365
|
+
const logger = createMockLogger();
|
|
366
|
+
let resolvedHost = null;
|
|
367
|
+
|
|
368
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
369
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': (url, port, host, cb) => {
|
|
370
|
+
resolvedHost = host;
|
|
371
|
+
cb(null, mockSocket);
|
|
372
|
+
},
|
|
373
|
+
socks: { SocksClient: {} },
|
|
374
|
+
dns: {
|
|
375
|
+
promises: {
|
|
376
|
+
resolve: async hostname => {
|
|
377
|
+
if (hostname === 'mail.example.com') {
|
|
378
|
+
return ['93.184.216.34'];
|
|
379
|
+
}
|
|
380
|
+
return [];
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
net: { isIP: host => /^\d+\.\d+\.\d+\.\d+$/.test(host) }
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
await proxyConnection(logger, 'http://proxy.example.com:8080', 'mail.example.com', 993);
|
|
388
|
+
|
|
389
|
+
test.equal(resolvedHost, '93.184.216.34');
|
|
390
|
+
test.done();
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
module.exports['Proxy Connection: Skips DNS for IP addresses'] = async test => {
|
|
394
|
+
const mockSocket = createMockSocket();
|
|
395
|
+
const logger = createMockLogger();
|
|
396
|
+
let dnsResolveCalled = false;
|
|
397
|
+
|
|
398
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
399
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': (url, port, host, cb) => {
|
|
400
|
+
cb(null, mockSocket);
|
|
401
|
+
},
|
|
402
|
+
socks: { SocksClient: {} },
|
|
403
|
+
dns: {
|
|
404
|
+
promises: {
|
|
405
|
+
resolve: async () => {
|
|
406
|
+
dnsResolveCalled = true;
|
|
407
|
+
return ['127.0.0.1'];
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
net: { isIP: () => true } // Pretend it's already an IP
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
await proxyConnection(logger, 'http://proxy.example.com:8080', '192.168.1.1', 993);
|
|
415
|
+
|
|
416
|
+
test.equal(dnsResolveCalled, false);
|
|
417
|
+
test.done();
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
module.exports['Proxy Connection: SOCKS resolves proxy hostname'] = async test => {
|
|
421
|
+
const mockSocket = createMockSocket();
|
|
422
|
+
const logger = createMockLogger();
|
|
423
|
+
let proxyHostResolved = null;
|
|
424
|
+
|
|
425
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
426
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': () => {},
|
|
427
|
+
socks: {
|
|
428
|
+
SocksClient: {
|
|
429
|
+
createConnection: async opts => {
|
|
430
|
+
proxyHostResolved = opts.proxy.host;
|
|
431
|
+
return { socket: mockSocket };
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
},
|
|
435
|
+
dns: {
|
|
436
|
+
promises: {
|
|
437
|
+
resolve: async hostname => {
|
|
438
|
+
if (hostname === 'proxy.example.com') {
|
|
439
|
+
return ['10.0.0.1'];
|
|
440
|
+
}
|
|
441
|
+
return ['127.0.0.1'];
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
net: { isIP: host => /^\d+\.\d+\.\d+\.\d+$/.test(host) }
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
await proxyConnection(logger, 'socks5://proxy.example.com:1080', '192.168.1.1', 993);
|
|
449
|
+
|
|
450
|
+
test.equal(proxyHostResolved, '10.0.0.1');
|
|
451
|
+
test.done();
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
// ============================================
|
|
455
|
+
// Edge Cases
|
|
456
|
+
// ============================================
|
|
457
|
+
|
|
458
|
+
module.exports['Proxy Connection: Unknown protocol returns undefined'] = async test => {
|
|
459
|
+
const logger = createMockLogger();
|
|
460
|
+
|
|
461
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
462
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': () => {},
|
|
463
|
+
socks: { SocksClient: {} },
|
|
464
|
+
dns: { promises: { resolve: async () => ['127.0.0.1'] } },
|
|
465
|
+
net: { isIP: () => true }
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
const result = await proxyConnection(logger, 'ftp://proxy.example.com:21', '192.168.1.1', 993);
|
|
469
|
+
|
|
470
|
+
test.equal(result, undefined);
|
|
471
|
+
test.done();
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
module.exports['Proxy Connection: HTTP proxy with no socket returned'] = async test => {
|
|
475
|
+
const logger = createMockLogger();
|
|
476
|
+
|
|
477
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
478
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': (url, port, host, cb) => {
|
|
479
|
+
cb(null, null); // No socket
|
|
480
|
+
},
|
|
481
|
+
socks: { SocksClient: {} },
|
|
482
|
+
dns: { promises: { resolve: async () => ['127.0.0.1'] } },
|
|
483
|
+
net: { isIP: () => true }
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
const socket = await proxyConnection(logger, 'http://proxy.example.com:8080', '192.168.1.1', 993);
|
|
487
|
+
|
|
488
|
+
test.equal(socket, null);
|
|
489
|
+
// No log when socket is null
|
|
490
|
+
test.equal(logger._logs.info.length, 0);
|
|
491
|
+
test.done();
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
module.exports['Proxy Connection: DNS returns empty result'] = async test => {
|
|
495
|
+
const mockSocket = createMockSocket();
|
|
496
|
+
const logger = createMockLogger();
|
|
497
|
+
let usedHost = null;
|
|
498
|
+
|
|
499
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
500
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': (url, port, host, cb) => {
|
|
501
|
+
usedHost = host;
|
|
502
|
+
cb(null, mockSocket);
|
|
503
|
+
},
|
|
504
|
+
socks: { SocksClient: {} },
|
|
505
|
+
dns: {
|
|
506
|
+
promises: {
|
|
507
|
+
resolve: async () => [] // Empty result
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
net: { isIP: () => false }
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
await proxyConnection(logger, 'http://proxy.example.com:8080', 'mail.example.com', 993);
|
|
514
|
+
|
|
515
|
+
// Should keep original hostname when DNS returns empty
|
|
516
|
+
test.equal(usedHost, 'mail.example.com');
|
|
517
|
+
test.done();
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
module.exports['Proxy Connection: SOCKS with username only'] = async test => {
|
|
521
|
+
const mockSocket = createMockSocket();
|
|
522
|
+
const logger = createMockLogger();
|
|
523
|
+
|
|
524
|
+
const { proxyConnection } = proxyquire('../lib/proxy-connection', {
|
|
525
|
+
'nodemailer/lib/smtp-connection/http-proxy-client': () => {},
|
|
526
|
+
socks: {
|
|
527
|
+
SocksClient: {
|
|
528
|
+
createConnection: async opts => {
|
|
529
|
+
test.equal(opts.proxy.userId, 'testuser');
|
|
530
|
+
test.equal(opts.proxy.password, ''); // Empty string from URL parsing
|
|
531
|
+
return { socket: mockSocket };
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
},
|
|
535
|
+
dns: { promises: { resolve: async () => ['127.0.0.1'] } },
|
|
536
|
+
net: { isIP: () => true }
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
await proxyConnection(logger, 'socks5://testuser@proxy.example.com:1080', '192.168.1.1', 993);
|
|
540
|
+
test.done();
|
|
541
|
+
};
|