nodemailer 6.6.2 → 6.7.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 +18 -0
- package/README.md +2 -0
- package/SECURITY.txt +22 -0
- package/lib/ses-transport/index.js +2 -1
- package/lib/shared/index.js +116 -18
- package/lib/smtp-connection/index.js +37 -15
- package/package.json +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 6.7.0 2021-10-11
|
|
4
|
+
|
|
5
|
+
- Updated DNS resolving logic. If there are multiple responses for a A/AAAA record, then loop these randomly instead of only caching the first one
|
|
6
|
+
|
|
7
|
+
## 6.6.5 2021-09-23
|
|
8
|
+
|
|
9
|
+
- Replaced Object.values() and Array.flat() with polyfills to allow using Nodemailer in Node v6+
|
|
10
|
+
|
|
11
|
+
## 6.6.4 2021-09-22
|
|
12
|
+
|
|
13
|
+
- Better compatibility with IPv6-only SMTP hosts (oxzi)
|
|
14
|
+
- Fix ses verify for sdk v3 (hannesvdvreken)
|
|
15
|
+
- Added SECURITY.txt for contact info
|
|
16
|
+
|
|
17
|
+
## 6.6.3 2021-07-14
|
|
18
|
+
|
|
19
|
+
- Do not show passwords in SMTP transaction logs. All passwords used in logging are replaced by `"/* secret */"`
|
|
20
|
+
|
|
3
21
|
## 6.6.1 2021-05-23
|
|
4
22
|
|
|
5
23
|
- Fixed address formatting issue where newlines in an email address, if provided via address object, were not properly removed. Reported by tmazeika (#1289)
|
package/README.md
CHANGED
|
@@ -10,6 +10,8 @@ See [nodemailer.com](https://nodemailer.com/) for documentation and terms.
|
|
|
10
10
|
|
|
11
11
|
## Having an issue?
|
|
12
12
|
|
|
13
|
+
> Nodemailer supports all Node.js versions starting from Node.js@v6.0.0. Existing test suite does not support such old Node.js versions so all features are not actually tested. From time to time some regression bugs might occur because of this.
|
|
14
|
+
|
|
13
15
|
#### First review the docs
|
|
14
16
|
|
|
15
17
|
Documentation for Nodemailer can be found at [nodemailer.com](https://nodemailer.com/about/).
|
package/SECURITY.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
-----BEGIN PGP SIGNED MESSAGE-----
|
|
2
|
+
Hash: SHA256
|
|
3
|
+
|
|
4
|
+
Contact: mailto:andris@reinman.eu
|
|
5
|
+
Encryption: https://keys.openpgp.org/vks/v1/by-fingerprint/5D952A46E1D8C931F6364E01DC6C83F4D584D364
|
|
6
|
+
Preferred-Languages: en, et
|
|
7
|
+
-----BEGIN PGP SIGNATURE-----
|
|
8
|
+
|
|
9
|
+
iQIzBAEBCAAdFiEEXZUqRuHYyTH2Nk4B3GyD9NWE02QFAmFDnUgACgkQ3GyD9NWE
|
|
10
|
+
02RqUA/+MM3afmRYq874C7wp+uN6dTMCvUX5g5zqBZ2yKpFr46L+PYvM7o8TMm5h
|
|
11
|
+
hmLT2I1zZmi+xezOL3zHFizaw0tKkZIz9cWl3Jrgs0FLp0zOsSz1xucp9Q2tYM/Q
|
|
12
|
+
vbiP6ys0gbim4tkDGRmZOEiO23s0BuRnmHt7vZg210O+D105Yd8/Ohzbj6PSLBO5
|
|
13
|
+
W1tA7Xw5t0FQ14NNH5+MKyDIKoCX12n0FmrC6qLTXeojf291UgKhCUPda3LIGTmx
|
|
14
|
+
mTXz0y68149Mw+JikRCYP8HfGRY9eA4XZrYXF7Bl2T9OJpKD3JAH+69P3xBw19Gn
|
|
15
|
+
Csaw3twu8P1bxoVGjY4KRrBOp68W8TwZYjWVWbqY6oV8hb/JfrMxa+kaSxRuloFs
|
|
16
|
+
oL6+phrDSPTWdOj2LlEDBJbPOMeDFzIlsBBcJ/JHCEHTvlHl7LoWr3YuWce9PUwl
|
|
17
|
+
4r3JUovvaeuJxLgC0vu3WCB3Jeocsl3SreqNkrVc1IjvkSomn3YGm5nCNAd/2F0V
|
|
18
|
+
exCGRk/8wbkSjAY38GwQ8K/VuFsefWN3L9sVwIMAMu88KFCAN+GzVFiwvyIXehF5
|
|
19
|
+
eogP9mIXzdQ5YReQjUjApOzGz54XnDyv9RJ3sdvMHosLP+IOg+0q5t9agWv6aqSR
|
|
20
|
+
2HzCpiQnH/gmM5NS0AU4Koq/L7IBeLu1B8+61/+BiHgZJJmPdgU=
|
|
21
|
+
=BUZr
|
|
22
|
+
-----END PGP SIGNATURE-----
|
|
@@ -321,7 +321,7 @@ class SESTransport extends EventEmitter {
|
|
|
321
321
|
Destinations: ['invalid@invalid']
|
|
322
322
|
};
|
|
323
323
|
const cb = err => {
|
|
324
|
-
if (err && err.code !== 'InvalidParameterValue') {
|
|
324
|
+
if (err && (err.code || err.Code) !== 'InvalidParameterValue') {
|
|
325
325
|
return callback(err);
|
|
326
326
|
}
|
|
327
327
|
return callback(null, true);
|
|
@@ -335,6 +335,7 @@ class SESTransport extends EventEmitter {
|
|
|
335
335
|
|
|
336
336
|
if (typeof ses.send === 'function' && aws.SendRawEmailCommand) {
|
|
337
337
|
// v3 API
|
|
338
|
+
sesMessage.RawMessage.Data = Buffer.from(sesMessage.RawMessage.Data);
|
|
338
339
|
ses.send(new aws.SendRawEmailCommand(sesMessage), cb);
|
|
339
340
|
} else {
|
|
340
341
|
// v2 API
|
package/lib/shared/index.js
CHANGED
|
@@ -8,10 +8,26 @@ const fs = require('fs');
|
|
|
8
8
|
const fetch = require('../fetch');
|
|
9
9
|
const dns = require('dns');
|
|
10
10
|
const net = require('net');
|
|
11
|
+
const os = require('os');
|
|
11
12
|
|
|
12
13
|
const DNS_TTL = 5 * 60 * 1000;
|
|
13
14
|
|
|
15
|
+
const networkInterfaces = (module.exports.networkInterfaces = os.networkInterfaces());
|
|
16
|
+
|
|
14
17
|
const resolver = (family, hostname, callback) => {
|
|
18
|
+
const familySupported =
|
|
19
|
+
// crux that replaces Object.values(networkInterfaces) as Object.values is not supported in nodejs v6
|
|
20
|
+
Object.keys(networkInterfaces)
|
|
21
|
+
.map(key => networkInterfaces[key])
|
|
22
|
+
// crux that replaces .flat() as it is not supported in older Node versions (v10 and older)
|
|
23
|
+
.reduce((acc, val) => acc.concat(val), [])
|
|
24
|
+
.filter(i => !i.internal)
|
|
25
|
+
.filter(i => i.family === 'IPv' + family).length > 0;
|
|
26
|
+
|
|
27
|
+
if (!familySupported) {
|
|
28
|
+
return callback(null, []);
|
|
29
|
+
}
|
|
30
|
+
|
|
15
31
|
dns['resolve' + family](hostname, (err, addresses) => {
|
|
16
32
|
if (err) {
|
|
17
33
|
switch (err.code) {
|
|
@@ -30,16 +46,45 @@ const resolver = (family, hostname, callback) => {
|
|
|
30
46
|
};
|
|
31
47
|
|
|
32
48
|
const dnsCache = (module.exports.dnsCache = new Map());
|
|
49
|
+
|
|
50
|
+
const formatDNSValue = (value, extra) => {
|
|
51
|
+
if (!value) {
|
|
52
|
+
return Object.assign({}, extra || {});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return Object.assign(
|
|
56
|
+
{
|
|
57
|
+
servername: value.servername,
|
|
58
|
+
host:
|
|
59
|
+
!value.addresses || !value.addresses.length
|
|
60
|
+
? null
|
|
61
|
+
: value.addresses.length === 1
|
|
62
|
+
? value.addresses[0]
|
|
63
|
+
: value.addresses[Math.floor(Math.random() * value.addresses.length)]
|
|
64
|
+
},
|
|
65
|
+
extra || {}
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
33
69
|
module.exports.resolveHostname = (options, callback) => {
|
|
34
70
|
options = options || {};
|
|
35
71
|
|
|
72
|
+
if (!options.host && options.servername) {
|
|
73
|
+
options.host = options.servername;
|
|
74
|
+
}
|
|
75
|
+
|
|
36
76
|
if (!options.host || net.isIP(options.host)) {
|
|
37
77
|
// nothing to do here
|
|
38
78
|
let value = {
|
|
39
|
-
|
|
79
|
+
addresses: [options.host],
|
|
40
80
|
servername: options.servername || false
|
|
41
81
|
};
|
|
42
|
-
return callback(
|
|
82
|
+
return callback(
|
|
83
|
+
null,
|
|
84
|
+
formatDNSValue(value, {
|
|
85
|
+
cached: false
|
|
86
|
+
})
|
|
87
|
+
);
|
|
43
88
|
}
|
|
44
89
|
|
|
45
90
|
let cached;
|
|
@@ -47,11 +92,12 @@ module.exports.resolveHostname = (options, callback) => {
|
|
|
47
92
|
if (dnsCache.has(options.host)) {
|
|
48
93
|
cached = dnsCache.get(options.host);
|
|
49
94
|
if (!cached.expires || cached.expires >= Date.now()) {
|
|
50
|
-
return callback(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
95
|
+
return callback(
|
|
96
|
+
null,
|
|
97
|
+
formatDNSValue(cached.value, {
|
|
98
|
+
cached: true
|
|
99
|
+
})
|
|
100
|
+
);
|
|
55
101
|
}
|
|
56
102
|
}
|
|
57
103
|
|
|
@@ -59,40 +105,68 @@ module.exports.resolveHostname = (options, callback) => {
|
|
|
59
105
|
if (err) {
|
|
60
106
|
if (cached) {
|
|
61
107
|
// ignore error, use expired value
|
|
62
|
-
return callback(
|
|
108
|
+
return callback(
|
|
109
|
+
null,
|
|
110
|
+
formatDNSValue(cached.value, {
|
|
111
|
+
cached: true,
|
|
112
|
+
error: err
|
|
113
|
+
})
|
|
114
|
+
);
|
|
63
115
|
}
|
|
64
116
|
return callback(err);
|
|
65
117
|
}
|
|
118
|
+
|
|
66
119
|
if (addresses && addresses.length) {
|
|
67
120
|
let value = {
|
|
68
|
-
|
|
121
|
+
addresses,
|
|
69
122
|
servername: options.servername || options.host
|
|
70
123
|
};
|
|
124
|
+
|
|
71
125
|
dnsCache.set(options.host, {
|
|
72
126
|
value,
|
|
73
127
|
expires: Date.now() + DNS_TTL
|
|
74
128
|
});
|
|
75
|
-
|
|
129
|
+
|
|
130
|
+
return callback(
|
|
131
|
+
null,
|
|
132
|
+
formatDNSValue(value, {
|
|
133
|
+
cached: false
|
|
134
|
+
})
|
|
135
|
+
);
|
|
76
136
|
}
|
|
77
137
|
|
|
78
138
|
resolver(6, options.host, (err, addresses) => {
|
|
79
139
|
if (err) {
|
|
80
140
|
if (cached) {
|
|
81
141
|
// ignore error, use expired value
|
|
82
|
-
return callback(
|
|
142
|
+
return callback(
|
|
143
|
+
null,
|
|
144
|
+
formatDNSValue(cached.value, {
|
|
145
|
+
cached: true,
|
|
146
|
+
error: err
|
|
147
|
+
})
|
|
148
|
+
);
|
|
83
149
|
}
|
|
84
150
|
return callback(err);
|
|
85
151
|
}
|
|
152
|
+
|
|
86
153
|
if (addresses && addresses.length) {
|
|
87
154
|
let value = {
|
|
88
|
-
|
|
155
|
+
addresses,
|
|
89
156
|
servername: options.servername || options.host
|
|
90
157
|
};
|
|
158
|
+
|
|
91
159
|
dnsCache.set(options.host, {
|
|
92
160
|
value,
|
|
93
161
|
expires: Date.now() + DNS_TTL
|
|
94
162
|
});
|
|
95
|
-
|
|
163
|
+
|
|
164
|
+
return callback(
|
|
165
|
+
null,
|
|
166
|
+
formatDNSValue(value, {
|
|
167
|
+
cached: false
|
|
168
|
+
})
|
|
169
|
+
);
|
|
96
170
|
}
|
|
97
171
|
|
|
98
172
|
try {
|
|
@@ -100,30 +174,54 @@ module.exports.resolveHostname = (options, callback) => {
|
|
|
100
174
|
if (err) {
|
|
101
175
|
if (cached) {
|
|
102
176
|
// ignore error, use expired value
|
|
103
|
-
return callback(
|
|
177
|
+
return callback(
|
|
178
|
+
null,
|
|
179
|
+
formatDNSValue(cached.value, {
|
|
180
|
+
cached: true,
|
|
181
|
+
error: err
|
|
182
|
+
})
|
|
183
|
+
);
|
|
104
184
|
}
|
|
105
185
|
return callback(err);
|
|
106
186
|
}
|
|
107
187
|
|
|
108
188
|
if (!address && cached) {
|
|
109
189
|
// nothing was found, fallback to cached value
|
|
110
|
-
return callback(
|
|
190
|
+
return callback(
|
|
191
|
+
null,
|
|
192
|
+
formatDNSValue(cached.value, {
|
|
193
|
+
cached: true
|
|
194
|
+
})
|
|
195
|
+
);
|
|
111
196
|
}
|
|
112
197
|
|
|
113
198
|
let value = {
|
|
114
|
-
|
|
199
|
+
addresses: address ? [address] : [options.host],
|
|
115
200
|
servername: options.servername || options.host
|
|
116
201
|
};
|
|
202
|
+
|
|
117
203
|
dnsCache.set(options.host, {
|
|
118
204
|
value,
|
|
119
205
|
expires: Date.now() + DNS_TTL
|
|
120
206
|
});
|
|
121
|
-
|
|
207
|
+
|
|
208
|
+
return callback(
|
|
209
|
+
null,
|
|
210
|
+
formatDNSValue(value, {
|
|
211
|
+
cached: false
|
|
212
|
+
})
|
|
213
|
+
);
|
|
122
214
|
});
|
|
123
215
|
} catch (err) {
|
|
124
216
|
if (cached) {
|
|
125
217
|
// ignore error, use expired value
|
|
126
|
-
return callback(
|
|
218
|
+
return callback(
|
|
219
|
+
null,
|
|
220
|
+
formatDNSValue(cached.value, {
|
|
221
|
+
cached: true,
|
|
222
|
+
error: err
|
|
223
|
+
})
|
|
224
|
+
);
|
|
127
225
|
}
|
|
128
226
|
return callback(err);
|
|
129
227
|
}
|
|
@@ -261,12 +261,12 @@ class SMTPConnection extends EventEmitter {
|
|
|
261
261
|
tnx: 'dns',
|
|
262
262
|
source: opts.host,
|
|
263
263
|
resolved: resolved.host,
|
|
264
|
-
cached: !!resolved.
|
|
264
|
+
cached: !!resolved.cached
|
|
265
265
|
},
|
|
266
266
|
'Resolved %s as %s [cache %s]',
|
|
267
267
|
opts.host,
|
|
268
268
|
resolved.host,
|
|
269
|
-
resolved.
|
|
269
|
+
resolved.cached ? 'hit' : 'miss'
|
|
270
270
|
);
|
|
271
271
|
Object.keys(resolved).forEach(key => {
|
|
272
272
|
if (key.charAt(0) !== '_' && resolved[key]) {
|
|
@@ -299,12 +299,12 @@ class SMTPConnection extends EventEmitter {
|
|
|
299
299
|
tnx: 'dns',
|
|
300
300
|
source: opts.host,
|
|
301
301
|
resolved: resolved.host,
|
|
302
|
-
cached: !!resolved.
|
|
302
|
+
cached: !!resolved.cached
|
|
303
303
|
},
|
|
304
304
|
'Resolved %s as %s [cache %s]',
|
|
305
305
|
opts.host,
|
|
306
306
|
resolved.host,
|
|
307
|
-
resolved.
|
|
307
|
+
resolved.cached ? 'hit' : 'miss'
|
|
308
308
|
);
|
|
309
309
|
Object.keys(resolved).forEach(key => {
|
|
310
310
|
if (key.charAt(0) !== '_' && resolved[key]) {
|
|
@@ -332,12 +332,12 @@ class SMTPConnection extends EventEmitter {
|
|
|
332
332
|
tnx: 'dns',
|
|
333
333
|
source: opts.host,
|
|
334
334
|
resolved: resolved.host,
|
|
335
|
-
cached: !!resolved.
|
|
335
|
+
cached: !!resolved.cached
|
|
336
336
|
},
|
|
337
337
|
'Resolved %s as %s [cache %s]',
|
|
338
338
|
opts.host,
|
|
339
339
|
resolved.host,
|
|
340
|
-
resolved.
|
|
340
|
+
resolved.cached ? 'hit' : 'miss'
|
|
341
341
|
);
|
|
342
342
|
Object.keys(resolved).forEach(key => {
|
|
343
343
|
if (key.charAt(0) !== '_' && resolved[key]) {
|
|
@@ -548,6 +548,16 @@ class SMTPConnection extends EventEmitter {
|
|
|
548
548
|
'\u0000' +
|
|
549
549
|
this._auth.credentials.pass,
|
|
550
550
|
'utf-8'
|
|
551
|
+
).toString('base64'),
|
|
552
|
+
// log entry without passwords
|
|
553
|
+
'AUTH PLAIN ' +
|
|
554
|
+
Buffer.from(
|
|
555
|
+
//this._auth.user+'\u0000'+
|
|
556
|
+
'\u0000' + // skip authorization identity as it causes problems with some servers
|
|
557
|
+
this._auth.credentials.user +
|
|
558
|
+
'\u0000' +
|
|
559
|
+
'/* secret */',
|
|
560
|
+
'utf-8'
|
|
551
561
|
).toString('base64')
|
|
552
562
|
);
|
|
553
563
|
return;
|
|
@@ -945,8 +955,9 @@ class SMTPConnection extends EventEmitter {
|
|
|
945
955
|
* Send a command to the server, append \r\n
|
|
946
956
|
*
|
|
947
957
|
* @param {String} str String to be sent to the server
|
|
958
|
+
* @param {String} logStr Optional string to be used for logging instead of the actual string
|
|
948
959
|
*/
|
|
949
|
-
_sendCommand(str) {
|
|
960
|
+
_sendCommand(str, logStr) {
|
|
950
961
|
if (this._destroyed) {
|
|
951
962
|
// Connection already closed, can't send any more data
|
|
952
963
|
return;
|
|
@@ -961,7 +972,7 @@ class SMTPConnection extends EventEmitter {
|
|
|
961
972
|
{
|
|
962
973
|
tnx: 'client'
|
|
963
974
|
},
|
|
964
|
-
(str || '').toString().replace(/\r?\n$/, '')
|
|
975
|
+
(logStr || str || '').toString().replace(/\r?\n$/, '')
|
|
965
976
|
);
|
|
966
977
|
}
|
|
967
978
|
|
|
@@ -1420,18 +1431,21 @@ class SMTPConnection extends EventEmitter {
|
|
|
1420
1431
|
|
|
1421
1432
|
// Decode from base64
|
|
1422
1433
|
let base64decoded = Buffer.from(challengeString, 'base64').toString('ascii'),
|
|
1423
|
-
|
|
1434
|
+
hmacMD5 = crypto.createHmac('md5', this._auth.credentials.pass);
|
|
1424
1435
|
|
|
1425
|
-
|
|
1436
|
+
hmacMD5.update(base64decoded);
|
|
1426
1437
|
|
|
1427
|
-
let
|
|
1428
|
-
let prepended = this._auth.credentials.user + ' ' + hex_hmac;
|
|
1438
|
+
let prepended = this._auth.credentials.user + ' ' + hmacMD5.digest('hex');
|
|
1429
1439
|
|
|
1430
1440
|
this._responseActions.push(str => {
|
|
1431
1441
|
this._actionAUTH_CRAM_MD5_PASS(str, callback);
|
|
1432
1442
|
});
|
|
1433
1443
|
|
|
1434
|
-
this._sendCommand(
|
|
1444
|
+
this._sendCommand(
|
|
1445
|
+
Buffer.from(prepended).toString('base64'),
|
|
1446
|
+
// hidden hash for logs
|
|
1447
|
+
Buffer.from(this._auth.credentials.user + ' /* secret */').toString('base64')
|
|
1448
|
+
);
|
|
1435
1449
|
}
|
|
1436
1450
|
|
|
1437
1451
|
/**
|
|
@@ -1476,7 +1490,11 @@ class SMTPConnection extends EventEmitter {
|
|
|
1476
1490
|
this._actionAUTHComplete(str, callback);
|
|
1477
1491
|
});
|
|
1478
1492
|
|
|
1479
|
-
this._sendCommand(
|
|
1493
|
+
this._sendCommand(
|
|
1494
|
+
Buffer.from((this._auth.credentials.pass || '').toString(), 'utf-8').toString('base64'),
|
|
1495
|
+
// Hidden pass for logs
|
|
1496
|
+
Buffer.from('/* secret */', 'utf-8').toString('base64')
|
|
1497
|
+
);
|
|
1480
1498
|
}
|
|
1481
1499
|
|
|
1482
1500
|
/**
|
|
@@ -1706,7 +1724,11 @@ class SMTPConnection extends EventEmitter {
|
|
|
1706
1724
|
this._responseActions.push(str => {
|
|
1707
1725
|
this._actionAUTHComplete(str, isRetry, callback);
|
|
1708
1726
|
});
|
|
1709
|
-
this._sendCommand(
|
|
1727
|
+
this._sendCommand(
|
|
1728
|
+
'AUTH XOAUTH2 ' + this._auth.oauth2.buildXOAuth2Token(accessToken),
|
|
1729
|
+
// Hidden for logs
|
|
1730
|
+
'AUTH XOAUTH2 ' + this._auth.oauth2.buildXOAuth2Token('/* secret */')
|
|
1731
|
+
);
|
|
1710
1732
|
});
|
|
1711
1733
|
}
|
|
1712
1734
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodemailer",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.7.0",
|
|
4
4
|
"description": "Easy as cake e-mail sending from your Node.js applications",
|
|
5
5
|
"main": "lib/nodemailer.js",
|
|
6
6
|
"scripts": {
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
},
|
|
21
21
|
"homepage": "https://nodemailer.com/",
|
|
22
22
|
"devDependencies": {
|
|
23
|
+
"@aws-sdk/client-ses": "3.36.0",
|
|
24
|
+
"aws-sdk": "2.1004.0",
|
|
23
25
|
"bunyan": "1.8.15",
|
|
24
26
|
"chai": "4.3.4",
|
|
25
27
|
"eslint-config-nodemailer": "1.2.0",
|
|
@@ -31,11 +33,11 @@
|
|
|
31
33
|
"libbase64": "1.2.1",
|
|
32
34
|
"libmime": "5.0.0",
|
|
33
35
|
"libqp": "1.1.0",
|
|
34
|
-
"mocha": "9.
|
|
36
|
+
"mocha": "9.1.2",
|
|
35
37
|
"nodemailer-ntlm-auth": "1.0.1",
|
|
36
38
|
"proxy": "1.0.2",
|
|
37
39
|
"proxy-test-server": "1.0.0",
|
|
38
|
-
"sinon": "11.1.
|
|
40
|
+
"sinon": "11.1.2",
|
|
39
41
|
"smtp-server": "3.9.0"
|
|
40
42
|
},
|
|
41
43
|
"engines": {
|