imapflow 1.0.169 → 1.0.171

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 CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.0.171](https://github.com/postalsys/imapflow/compare/v1.0.170...v1.0.171) (2024-12-05)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * Trying to re-publish as previous release attempt failed, no actual changes ([d882816](https://github.com/postalsys/imapflow/commit/d8828163e87067182c6011abda109a17baabac35))
9
+
10
+ ## [1.0.170](https://github.com/postalsys/imapflow/compare/v1.0.169...v1.0.170) (2024-12-05)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **uidvalidity:** Do not expect UIDVALIDITY to always exist ([7761653](https://github.com/postalsys/imapflow/commit/7761653e92965a09be8a872813363eb81f7f3d9a))
16
+
3
17
  ## [1.0.169](https://github.com/postalsys/imapflow/compare/v1.0.168...v1.0.169) (2024-11-08)
4
18
 
5
19
 
@@ -151,13 +151,12 @@ module.exports = async (connection, username, { accessToken, password }) => {
151
151
  }
152
152
 
153
153
  if (password) {
154
- if (connection.capabilities.has('AUTH=LOGIN')) {
155
- return await authLogin(connection, username, password);
156
- }
157
-
158
154
  if (connection.capabilities.has('AUTH=PLAIN')) {
159
155
  return await authPlain(connection, username, password);
160
156
  }
157
+ if (connection.capabilities.has('AUTH=LOGIN')) {
158
+ return await authLogin(connection, username, password);
159
+ }
161
160
  }
162
161
 
163
162
  throw new Error('Unsupported authentication mechanism');
@@ -40,7 +40,7 @@ module.exports = async (connection, path, options) => {
40
40
  extraArgs.push([
41
41
  { type: 'ATOM', value: 'QRESYNC' },
42
42
  [
43
- { type: 'ATOM', value: options.uidValidity.toString() },
43
+ { type: 'ATOM', value: options.uidValidity?.toString() },
44
44
  { type: 'ATOM', value: options.changedSince.toString() }
45
45
  ]
46
46
  ]);
package/lib/imap-flow.js CHANGED
@@ -119,8 +119,19 @@ class ImapFlow extends EventEmitter {
119
119
  * @param {Object} options IMAP connection options
120
120
  * @param {String} options.host Hostname of the IMAP server
121
121
  * @param {Number} options.port Port number for the IMAP server
122
- * @param {Boolean} [options.secure=false] Should the connection be established over TLS.
123
- * If `false` then connection is upgraded to TLS using STARTTLS extension before authentication
122
+ * @param {Boolean} [options.secure=false] Should the connection be established immediately and directly over TLS? Typically on port 993.
123
+ * @param {Boolean} [options.doSTARTTLS=undefined] Should the connection be established using STARTTLS?
124
+ * * If `true`, the connection is first established as unencrypted and then upgraded to TLS using STARTTLS, before authentication.
125
+ * If the server does not advertize the `STARTTLS` `CAPABILITY`, or the upgrade fails for other reasons, then the connection fails.
126
+ * Note: The combination `secure=true` (direct TLS) and `doSTARTTLS=true` is invalid.
127
+ * * If `false`, then STARTTLS will not be used, even if the server advertizes it in IMAP `CAPABILITY`.
128
+ * This helps with servers that have a broken TLS configuration.
129
+ * If `doSTARTTLS=false` and `secure=false`, then a plain unencrypted socket is used.
130
+ * Be sure to clearly warn the user about the consequences.
131
+ * * If `undefined` (default) and `secure=false` (default), the connection is upgraded using STARTTLS before authentication, /only if possible/ .
132
+ * If not possible, the connection will use an unencrypted plain socket.
133
+ * This can mean TLS is used under normal circumstances, but a serious attacker can force an unencrypted connection and steal passwords,
134
+ * called "downgrade attack". This can lead to a false sense of security. Be sure to warn the user.
124
135
  * @param {String} [options.servername] Servername for SNI (or when host is set to an IP address)
125
136
  * @param {Boolean} [options.disableCompression=false] if `true` then client does not try to use COMPRESS=DEFLATE extension
126
137
  * @param {Object} options.auth Authentication options. Authentication is requested automatically during <code>connect()</code>
@@ -589,7 +600,7 @@ class ImapFlow extends EventEmitter {
589
600
 
590
601
  if (err.responseStatus === 'NO' && txt.includes('Some of the requested messages no longer exist')) {
591
602
  // Treat as successful response
592
- this.log.warn({ msg: 'Partial Gmail response', cid: this.id, err });
603
+ this.log.warn({ msg: 'Partial FETCH response', cid: this.id, err });
593
604
  await new Promise(resolve => request.resolve({ response: parsed, next: resolve }));
594
605
  break;
595
606
  }
@@ -715,10 +726,7 @@ class ImapFlow extends EventEmitter {
715
726
  this.idRequested = await this.run('ID', this.clientInfo);
716
727
  }
717
728
 
718
- // try to use STARTTLS is possible
719
- if (!this.secureConnection) {
720
- await this.upgradeConnection();
721
- }
729
+ await this.upgradeToSTARTTLS();
722
730
 
723
731
  let authenticated = await this.authenticate();
724
732
  if (!authenticated) {
@@ -837,21 +845,47 @@ class ImapFlow extends EventEmitter {
837
845
  });
838
846
  }
839
847
 
840
- async upgradeConnection() {
848
+ _failSTARTTLS() {
849
+ if (this.options.doSTARTTLS === true) {
850
+ // STARTTLS configured as requirement
851
+ let err = new Error('Server does not support STARTTLS');
852
+ err.tlsFailed = true;
853
+ throw err;
854
+ } else {
855
+ // Opportunistic STARTTLS. But it's not possible right now.
856
+ // Attention: Could be a downgrade attack.
857
+ return false;
858
+ }
859
+ }
860
+
861
+ /**
862
+ * Tries to upgrade the connection to TLS using STARTTLS.
863
+ * @throws if STARTTLS is required, but not possible.
864
+ * @returns {boolean} true, if the connection is now protected by TLS, either direct TLS or STARTTLS.
865
+ */
866
+ async upgradeToSTARTTLS() {
867
+ if (this.options.doSTARTTLS === true && this.options.secure === true) {
868
+ throw new Error('Misconfiguration: Cannot set both secure=true for TLS and doSTARTTLS=true for STARTTLS.');
869
+ }
870
+
841
871
  if (this.secureConnection) {
842
- // already secure
872
+ // Already using direct TLS. No need for STARTTLS.
843
873
  return true;
844
874
  }
845
875
 
846
- if (!this.capabilities.has('STARTTLS')) {
847
- // can not upgrade
876
+ if (this.options.doSTARTTLS === false) {
877
+ // STARTTLS explictly disabled by config
848
878
  return false;
849
879
  }
850
880
 
881
+ if (!this.capabilities.has('STARTTLS')) {
882
+ return this._failSTARTTLS();
883
+ }
884
+
851
885
  this.expectCapabilityUpdate = true;
852
886
  let canUpgrade = await this.run('STARTTLS');
853
887
  if (!canUpgrade) {
854
- return;
888
+ return this._failSTARTTLS();
855
889
  }
856
890
 
857
891
  this.socket.unpipe(this.streamer);
@@ -875,6 +909,7 @@ class ImapFlow extends EventEmitter {
875
909
  }
876
910
  setImmediate(() => this.close());
877
911
  this.upgrading = false;
912
+ err.tlsFailed = true;
878
913
  reject(err);
879
914
  });
880
915
 
@@ -884,6 +919,7 @@ class ImapFlow extends EventEmitter {
884
919
  }
885
920
  setImmediate(() => this.close());
886
921
  let err = new Error('Failed to upgrade connection in required time');
922
+ err.tlsFailed = true;
887
923
  err.code = 'UPGRADE_TIMEOUT';
888
924
  reject(err);
889
925
  }, UPGRADE_TIMEOUT);
package/lib/tools.js CHANGED
@@ -390,7 +390,11 @@ module.exports = {
390
390
  }
391
391
  }
392
392
 
393
- map.id = map.emailId || createHash('md5').update([path, mailbox.uidValidity.toString(), map.uid.toString()].join(':')).digest('hex');
393
+ map.id =
394
+ map.emailId ||
395
+ createHash('md5')
396
+ .update([path, mailbox.uidValidity?.toString() || '', map.uid.toString()].join(':'))
397
+ .digest('hex');
394
398
  }
395
399
 
396
400
  if (map.flags) {
package/lib/types.d.ts CHANGED
@@ -8,8 +8,19 @@ declare module "imapflow" {
8
8
  * @param options - IMAP connection options
9
9
  * @param options.host - Hostname of the IMAP server
10
10
  * @param options.port - Port number for the IMAP server
11
- * @param [options.secure = false] - Should the connection be established over TLS.
12
- * If `false` then connection is upgraded to TLS using STARTTLS extension before authentication
11
+ * @param [options.secure = false] - Should the connection be established immediately and directly over TLS? Typically on port 993.
12
+ * @param [options.doSTARTTLS] - Should the connection be established using STARTTLS?
13
+ * * If `true`, the connection is first established as unencrypted and then upgraded to TLS using STARTTLS, before authentication.
14
+ * If the server does not advertize the `STARTTLS` `CAPABILITY`, or the upgrade fails for other reasons, then the connection fails.
15
+ * Note: The combination `secure=true` (direct TLS) and `doSTARTTLS=true` is invalid.
16
+ * * If `false`, then STARTTLS will not be used, even if the server advertizes it in IMAP `CAPABILITY`.
17
+ * This helps with servers that have a broken TLS configuration.
18
+ * If `doSTARTTLS=false` and `secure=false`, then a plain unencrypted socket is used.
19
+ * Be sure to clearly warn the user about the consequences.
20
+ * * If `undefined` (default) and `secure=false` (default), the connection is upgraded using STARTTLS before authentication, /only if possible/ .
21
+ * If not possible, the connection will use an unencrypted plain socket.
22
+ * This can mean TLS is used under normal circumstances, but a serious attacker can force an unencrypted connection and steal passwords,
23
+ * called "downgrade attack". This can lead to a false sense of security. Be sure to warn the user.
13
24
  * @param [options.servername] - Servername for SNI (or when host is set to an IP address)
14
25
  * @param [options.disableCompression = false] - if `true` then client does not try to use COMPRESS=DEFLATE extension
15
26
  * @param options.auth - Authentication options. Authentication is requested automatically during <code>connect()</code>
@@ -41,6 +52,7 @@ declare module "imapflow" {
41
52
  host: string;
42
53
  port: number;
43
54
  secure?: boolean;
55
+ doSTARTTLS?: boolean;
44
56
  servername?: string;
45
57
  disableCompression?: boolean;
46
58
  auth: {
@@ -122,6 +134,11 @@ declare module "imapflow" {
122
134
  * If `true` then in addition of sending data to logger, ImapFlow emits 'log' events with the same data
123
135
  */
124
136
  emitLogs: boolean;
137
+ /**
138
+ * Tries to upgrade the connection to TLS using STARTTLS.
139
+ * @returns true, if the connection is now protected by TLS, either direct TLS or STARTTLS.
140
+ */
141
+ upgradeToSTARTTLS(): boolean;
125
142
  /**
126
143
  * Initiates a connection against IMAP server. Throws if anything goes wrong. This is something you have to call before you can run any IMAP commands
127
144
  * @example
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imapflow",
3
- "version": "1.0.169",
3
+ "version": "1.0.171",
4
4
  "description": "IMAP Client for Node",
5
5
  "main": "./lib/imap-flow.js",
6
6
  "scripts": {
@@ -32,7 +32,7 @@
32
32
  "@babel/eslint-plugin": "7.25.9",
33
33
  "@babel/plugin-syntax-class-properties": "7.12.13",
34
34
  "@babel/preset-env": "7.26.0",
35
- "@types/node": "22.9.0",
35
+ "@types/node": "22.10.1",
36
36
  "eslint": "8.57.0",
37
37
  "eslint-config-nodemailer": "1.2.0",
38
38
  "eslint-config-prettier": "9.1.0",
@@ -40,7 +40,7 @@
40
40
  "grunt-cli": "1.5.0",
41
41
  "grunt-contrib-nodeunit": "5.0.0",
42
42
  "grunt-eslint": "24.3.0",
43
- "imapflow-jsdoc-template": "3.4.0-imapflow.1",
43
+ "imapflow-jsdoc-template": "3.4.0-imapflow.2",
44
44
  "jsdoc": "3.6.11",
45
45
  "st": "3.0.1",
46
46
  "tsd-jsdoc": "2.5.0"
@@ -49,9 +49,9 @@
49
49
  "encoding-japanese": "2.2.0",
50
50
  "iconv-lite": "0.6.3",
51
51
  "libbase64": "1.3.0",
52
- "libmime": "5.3.5",
53
- "libqp": "2.1.0",
54
- "mailsplit": "5.4.0",
52
+ "libmime": "5.3.6",
53
+ "libqp": "2.1.1",
54
+ "mailsplit": "5.4.2",
55
55
  "nodemailer": "6.9.16",
56
56
  "pino": "9.5.0",
57
57
  "socks": "2.8.3"