ssh2-sftp-client 7.0.4 → 7.1.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/README.md CHANGED
@@ -61,9 +61,9 @@ an SFTP client for node.js, a wrapper around [SSH2](https://github.com/mscdex/ss
61
61
 
62
62
  Documentation on the methods and available options in the underlying modules can be found on the [SSH2](https://github.com/mscdex/ssh2) project pages.
63
63
 
64
- Current stable release is **v7.0.4**.
64
+ Current stable release is **v7.1.0**.
65
65
 
66
- Code has been tested against Node versions 12.22.1, 14.17.0 and 16.2.0
66
+ Code has been tested against Node versions 12.22.6, 14.17.6 and 16.10.0
67
67
 
68
68
  Node versions < 10.x are not supported.
69
69
 
@@ -99,6 +99,8 @@ sftp.connect({
99
99
 
100
100
  - **Breaking Change** Expanded option handling for `get()` and `put()` methods. A number of use cases were identified where setting specific options on the read and write streams and the pipe operation are necessary. For example, disabling `autoClose` on streams or the `end` event in pipes. The `options` argument for `get()` and `put()` calls now supports properties for `readStreamOptions`, `writeStreamOptions` and `pipeOptions`. Note that options are only applied to streams created by the `get()` and `put()` methods. Streams passed into these methods are under the control of the client code and therefore cannot have options supplied in arguments to those streams (you would apply such options when you create the streams). Options are typically only necessary in special use cases. Most of the time, no options are required. However, if you are currently using options to either `put()` or `get()`, you will need to update your code to map these options to the new structure.
101
101
 
102
+ - **Breaking Change 7.1.0** A race condition was identified when using a put() call with a writeStream option of `autoClose: false`. In some situations, the promise would be resolved before the final close of the write stream. This could result in errors if you immediately attempt to access the uploaded file. To avoid this situatioin, the promise is now resolved once a `close` event is emitted. This means that setting `autoClose: false` can no longer be supported. The write stream for `put()` will autoClose once data writing has completed.
103
+
102
104
  - Improved event handling. A listener for a global error event is now defined to catch errors which occur in-between method calls i.e. connection lost in-between calls to the library methods. A new mechanism has also been added for removal of listeners when no longer required.
103
105
 
104
106
  # Documentation<a id="sec-5"></a>
@@ -497,11 +499,12 @@ Upload data from local system to remote server. If the `src` argument is a strin
497
499
  flags: 'w', // w - write and a - append
498
500
  encoding: null, // use null for binary files
499
501
  mode: 0o666, // mode to use for created file (rwx)
500
- autoClose: true // automatically close the write stream when finished
501
502
  }}
502
503
  ```
503
504
 
504
505
  The most common options to use are mode and encoding. The values shown above are the defaults. You do not have to set encoding to utf-8 for text files, null is fine for all file types. However, using utf-8 encoding for binary files will often result in data corruption.
506
+
507
+ Note that you cannot set `autoClose: false` for `writeStreamOptions`. If you attempt to set this property to false, it will be ignored. This is necessary to avoid a race condition which may exist when setting `autoClose` to false on the writeStream. As there is no easy way to access the writeStream once the promise has been resolved, setting this to autoClose false is not terribly useful as there is no easy way to manually close the stream after the promise has been resolved.
505
508
 
506
509
  2. Example Use
507
510
 
@@ -1356,3 +1359,5 @@ Thanks to the following for their contributions -
1356
1359
  - **anton-erofeev:** Documentation fix
1357
1360
  - **Ladislav Jacho:** Contributed solution explanation for connections hanging when transferring larger files.
1358
1361
  - **Emma Milner:** Contributed fix for put() bug
1362
+ - **Witni Davis:** Contributed PR to fix put() RCE when using 'finish' rather than 'close' to resolve promise
1363
+ - **Maik Marschner:** Contributed fix for connect() not returning sftp object. Also included test to check for this regression in future.
package/README.org CHANGED
@@ -9,9 +9,9 @@ convenience abstraction as well as a Promise based API.
9
9
  Documentation on the methods and available options in the underlying modules can
10
10
  be found on the [[https://github.com/mscdex/ssh2][SSH2]] project pages.
11
11
 
12
- Current stable release is *v7.0.4*.
12
+ Current stable release is *v7.1.0*.
13
13
 
14
- Code has been tested against Node versions 12.22.1, 14.17.0 and 16.2.0
14
+ Code has been tested against Node versions 12.22.6, 14.17.6 and 16.10.0
15
15
 
16
16
  Node versions < 10.x are not supported.
17
17
 
@@ -62,6 +62,14 @@ npm install ssh2-sftp-client
62
62
  currently using options to either ~put()~ or ~get()~, you will need to update
63
63
  your code to map these options to the new structure.
64
64
 
65
+ - *Breaking Change 7.1.0* A race condition was identified when using a put()
66
+ call with a writeStream option of ~autoClose: false~. In some situations, the
67
+ promise would be resolved before the final close of the write stream. This
68
+ could result in errors if you immediately attempt to access the uploaded
69
+ file. To avoid this situatioin, the promise is now resolved once a ~close~
70
+ event is emitted. This means that setting ~autoClose: false~ can no longer be
71
+ supported. The write stream for ~put()~ will autoClose once data writing has completed.
72
+
65
73
  - Improved event handling. A listener for a global error event is now defined to
66
74
  catch errors which occur in-between method calls i.e. connection lost
67
75
  in-between calls to the library methods. A new mechanism has also been added
@@ -546,7 +554,6 @@ option value. For example, you might use the following to set ~writeStream~ opti
546
554
  flags: 'w', // w - write and a - append
547
555
  encoding: null, // use null for binary files
548
556
  mode: 0o666, // mode to use for created file (rwx)
549
- autoClose: true // automatically close the write stream when finished
550
557
  }}
551
558
  #+end_src
552
559
 
@@ -555,6 +562,14 @@ the defaults. You do not have to set encoding to utf-8 for text files, null is
555
562
  fine for all file types. However, using utf-8 encoding for binary files will
556
563
  often result in data corruption.
557
564
 
565
+ Note that you cannot set ~autoClose: false~ for ~writeStreamOptions~. If you
566
+ attempt to set this property to false, it will be ignored. This is necessary to
567
+ avoid a race condition which may exist when setting ~autoClose~ to false on the
568
+ writeStream. As there is no easy way to access the writeStream once the promise
569
+ has been resolved, setting this to autoClose false is not terribly useful as
570
+ there is no easy way to manually close the stream after the promise has been
571
+ resolved.
572
+
558
573
  **** Example Use
559
574
 
560
575
  #+begin_src javascript
@@ -1765,3 +1780,7 @@ Thanks to the following for their contributions -
1765
1780
  - Ladislav Jacho :: Contributed solution explanation for connections hanging
1766
1781
  when transferring larger files.
1767
1782
  - Emma Milner :: Contributed fix for put() bug
1783
+ - Witni Davis :: Contributed PR to fix put() RCE when using 'finish' rather than
1784
+ 'close' to resolve promise
1785
+ - Maik Marschner :: Contributed fix for connect() not returning sftp object.
1786
+ Also included test to check for this regression in future.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ssh2-sftp-client",
3
- "version": "7.0.4",
3
+ "version": "7.1.0",
4
4
  "description": "ssh2 sftp client for node",
5
5
  "main": "src/index.js",
6
6
  "repository": {
@@ -32,10 +32,10 @@
32
32
  "dependencies": {
33
33
  "concat-stream": "^2.0.0",
34
34
  "promise-retry": "^2.0.1",
35
- "ssh2": "^1.4.0"
35
+ "ssh2": "^1.5.0"
36
36
  },
37
37
  "devDependencies": {
38
- "chai": "^4.2.0",
38
+ "chai": "^4.3.4",
39
39
  "chai-as-promised": "^7.1.1",
40
40
  "chai-subset": "^1.6.0",
41
41
  "checksum": "^1.0.0",
@@ -45,11 +45,11 @@
45
45
  "eslint-plugin-mocha": "^9.0.0",
46
46
  "eslint-plugin-node": "^11.1.0",
47
47
  "eslint-plugin-promise": "^5.1.0",
48
- "eslint-plugin-unicorn": "^35.0.0",
49
- "mocha": "^9.0.2",
48
+ "eslint-plugin-unicorn": "^36.0.0",
49
+ "mocha": "^9.1.2",
50
50
  "moment": "^2.29.1",
51
51
  "nyc": "^15.1.0",
52
- "prettier": "^2.3.2",
52
+ "prettier": "^2.4.1",
53
53
  "through2": "^4.0.2",
54
54
  "winston": "^3.3.3"
55
55
  }
package/src/index.js CHANGED
@@ -114,7 +114,7 @@ class SftpClient {
114
114
  *
115
115
  * @param {Object} config - an SFTP configuration object
116
116
  *
117
- * @return {Promise} which will resolve to an sftp client object
117
+ * @return {Promise<Object>} which will resolve to an sftp client object
118
118
  *
119
119
  */
120
120
  getConnection(config) {
@@ -173,7 +173,7 @@ class SftpClient {
173
173
  *
174
174
  * @param {Object} config - an SFTP configuration object
175
175
  *
176
- * @return {Promise} which will resolve to an sftp client object
176
+ * @return {Promise<Object>} which will resolve to an sftp client object
177
177
  *
178
178
  */
179
179
  async connect(config) {
@@ -204,7 +204,7 @@ class SftpClient {
204
204
  minTimeout: config.retry_minTimeout || 1000,
205
205
  }
206
206
  );
207
- await this.getSftpChannel();
207
+ return this.getSftpChannel();
208
208
  } catch (err) {
209
209
  this.debugMsg(`connect: Error ${err.message}`);
210
210
  this._resetEventFlags();
@@ -221,7 +221,7 @@ class SftpClient {
221
221
  * Returns undefined if the path does not exists.
222
222
  *
223
223
  * @param {String} remotePath - remote path, may be relative
224
- * @returns {Promise} - remote absolute path or undefined
224
+ * @returns {Promise<String>} - remote absolute path or ''
225
225
  */
226
226
  realPath(remotePath) {
227
227
  return new Promise((resolve, reject) => {
@@ -249,6 +249,13 @@ class SftpClient {
249
249
  });
250
250
  }
251
251
 
252
+ /**
253
+ * @async
254
+ *
255
+ * Return the current workding directory path
256
+ *
257
+ * @returns {Promise<String>} - current remote working directory
258
+ */
252
259
  cwd() {
253
260
  return this.realPath('.');
254
261
  }
@@ -257,7 +264,7 @@ class SftpClient {
257
264
  * Retrieves attributes for path
258
265
  *
259
266
  * @param {String} remotePath - a string containing the path to a file
260
- * @return {Promise} stats - attributes info
267
+ * @return {Promise<Object>} stats - attributes info
261
268
  */
262
269
  async stat(remotePath) {
263
270
  const _stat = (aPath) => {
@@ -323,7 +330,7 @@ class SftpClient {
323
330
  *
324
331
  * @param {string} remotePath - path to the object on the sftp server.
325
332
  *
326
- * @return {Promise} returns false if object does not exist. Returns type of
333
+ * @return {Promise<Boolean|String>} returns false if object does not exist. Returns type of
327
334
  * object if it does
328
335
  */
329
336
  async exists(remotePath) {
@@ -382,8 +389,7 @@ class SftpClient {
382
389
  *
383
390
  * @param {String} remotePath - path to remote directory
384
391
  * @param {RegExp} pattern - regular expression to match filenames
385
- * @returns {Promise} array of file description objects
386
- * @throws {Error}
392
+ * @returns {Promise<Array>} array of file description objects
387
393
  */
388
394
  list(remotePath, pattern = /.*/) {
389
395
  return new Promise((resolve, reject) => {
@@ -449,7 +455,7 @@ class SftpClient {
449
455
  * @param {Object} options - options object with supported properties of readStreamOptions,
450
456
  * writeStreamOptions and pipeOptions.
451
457
  *
452
- * @return {Promise}
458
+ * @return {Promise<String|Stream|Buffer>}
453
459
  */
454
460
  get(
455
461
  remotePath,
@@ -549,7 +555,7 @@ class SftpClient {
549
555
  * @param {String} remotePath
550
556
  * @param {String} localPath
551
557
  * @param {Object} options
552
- * @return {Promise} the result of downloading the file
558
+ * @return {Promise<String>} the result of downloading the file
553
559
  */
554
560
  async fastGet(remotePath, localPath, options) {
555
561
  try {
@@ -569,7 +575,7 @@ class SftpClient {
569
575
  err.code = errorCode.badPath;
570
576
  throw err;
571
577
  }
572
- await new Promise((resolve, reject) => {
578
+ let rslt = await new Promise((resolve, reject) => {
573
579
  if (haveConnection(this, 'fastGet', reject)) {
574
580
  this.debugMsg(
575
581
  `fastGet -> remote: ${remotePath} local: ${localPath} `,
@@ -587,6 +593,7 @@ class SftpClient {
587
593
  }).finally(() => {
588
594
  removeTempListeners(this, 'fastGet');
589
595
  });
596
+ return rslt;
590
597
  } catch (err) {
591
598
  this._resetEventFlags();
592
599
  throw fmtError(err, 'fastGet');
@@ -604,7 +611,7 @@ class SftpClient {
604
611
  * @param {String} localPath
605
612
  * @param {String} remotePath
606
613
  * @param {Object} options
607
- * @return {Promise} the result of downloading the file
614
+ * @return {Promise<String>} the result of downloading the file
608
615
  */
609
616
  fastPut(localPath, remotePath, options) {
610
617
  this.debugMsg(`fastPut -> local ${localPath} remote ${remotePath}`);
@@ -664,12 +671,16 @@ class SftpClient {
664
671
  * @param {Object} options - options used for read, write stream and pipe configuration
665
672
  * value supported by node. Allowed properties are readStreamOptions,
666
673
  * writeStreamOptions and pipeOptions.
667
- * @return {Promise}
674
+ * @return {Promise<String>}
668
675
  */
669
676
  put(
670
677
  localSrc,
671
678
  remotePath,
672
- options = { readStreamOptions: {}, writeStreamOptions: {}, pipeOptions: {} }
679
+ options = {
680
+ readStreamOptions: {},
681
+ writeStreamOptions: { autoClose: true },
682
+ pipeOptions: {},
683
+ }
673
684
  ) {
674
685
  let wtr, rdr;
675
686
 
@@ -691,13 +702,15 @@ class SftpClient {
691
702
  addTempListeners(this, 'put', reject);
692
703
  wtr = this.sftp.createWriteStream(
693
704
  remotePath,
694
- options.writeStreamOptions ? options.writeStreamOptions : {}
705
+ options.writeStreamOptions
706
+ ? { ...options.writeStreamOptions, autoClose: true }
707
+ : {}
695
708
  );
696
709
  wtr.once('error', (err) => {
697
710
  this.debugMsg(`put: write stream error ${err.message}`);
698
711
  reject(fmtError(`${err.message} ${remotePath}`, 'put', err.code));
699
712
  });
700
- wtr.once('finish', () => {
713
+ wtr.once('close', () => {
701
714
  this.debugMsg('put: promise resolved');
702
715
  resolve(`Uploaded data stream to ${remotePath}`);
703
716
  });
@@ -741,13 +754,6 @@ class SftpClient {
741
754
  ) {
742
755
  rdr.destroy();
743
756
  }
744
- if (
745
- wtr &&
746
- options.writeStreamOptions &&
747
- options.writeStreamOptions.autoClose === false
748
- ) {
749
- wtr.destroy();
750
- }
751
757
  });
752
758
  }
753
759
 
@@ -757,9 +763,8 @@ class SftpClient {
757
763
  * @param {Buffer|stream} input
758
764
  * @param {String} remotePath
759
765
  * @param {Object} options
760
- * @return {Promise}
766
+ * @return {Promise<String>}
761
767
  */
762
-
763
768
  async append(input, remotePath, options = {}) {
764
769
  const fileType = await this.exists(remotePath);
765
770
  if (fileType && fileType === 'd') {
@@ -807,7 +812,7 @@ class SftpClient {
807
812
  *
808
813
  * @param {string} remotePath - remote directory path.
809
814
  * @param {boolean} recursive - if true, recursively create directories
810
- * @return {Promise}
815
+ * @return {Promise<String>}
811
816
  */
812
817
  async mkdir(remotePath, recursive = false) {
813
818
  const _mkdir = (p) => {
@@ -873,7 +878,7 @@ class SftpClient {
873
878
  * @param {string} remotePath - path to directory to be removed
874
879
  * @param {boolean} recursive - if true, remove directories/files in target
875
880
  * directory
876
- * @return {Promise}
881
+ * @return {Promise<String>}
877
882
  */
878
883
  async rmdir(remotePath, recursive = false) {
879
884
  const _rmdir = (p) => {
@@ -926,7 +931,7 @@ class SftpClient {
926
931
  * @param {string} remotePath - path to the file to delete
927
932
  * @param {boolean} notFoundOK - if true, ignore errors for missing target.
928
933
  * Default is false.
929
- * @return {Promise} with string 'Successfully deleted file' once resolved
934
+ * @return {Promise<String>} with string 'Successfully deleted file' once resolved
930
935
  *
931
936
  */
932
937
  delete(remotePath, notFoundOK = false) {
@@ -963,7 +968,7 @@ class SftpClient {
963
968
  * @param {string} fromPath - path to the file to be renamed.
964
969
  * @param {string} toPath - path to the new name.
965
970
  *
966
- * @return {Promise}
971
+ * @return {Promise<String>}
967
972
  *
968
973
  */
969
974
  rename(fromPath, toPath) {
@@ -1000,7 +1005,7 @@ class SftpClient {
1000
1005
  * @param {string} fromPath - path to the file to be renamed.
1001
1006
  * @param {string} toPath - path the new name.
1002
1007
  *
1003
- * @return {Promise}
1008
+ * @return {Promise<String>}
1004
1009
  *
1005
1010
  */
1006
1011
  posixRename(fromPath, toPath) {
@@ -1036,7 +1041,7 @@ class SftpClient {
1036
1041
  * @param {string} remotePath - path to the remote target object.
1037
1042
  * @param {number | string} mode - the new octal mode to set
1038
1043
  *
1039
- * @return {Promise}
1044
+ * @return {Promise<String>}
1040
1045
  */
1041
1046
  chmod(remotePath, mode) {
1042
1047
  return new Promise((resolve, reject) => {
@@ -1064,8 +1069,7 @@ class SftpClient {
1064
1069
  * @param {String} dstDir - remote destination directory
1065
1070
  * @param {RegExp} filter - (Optional) a regular expression used to select
1066
1071
  * files and directories to upload
1067
- * @returns {String}
1068
- * @throws {Error}
1072
+ * @returns {Promise<String>}
1069
1073
  */
1070
1074
  async uploadDir(srcDir, dstDir, filter = /.*/) {
1071
1075
  try {
@@ -1124,8 +1128,7 @@ class SftpClient {
1124
1128
  * @param {String} dstDir - local destination directory
1125
1129
  * @param {RegExp} filter - (Optional) a regular expression used to select
1126
1130
  * files and directories to upload
1127
- * @returns {Promise}
1128
- * @throws {Error}
1131
+ * @returns {Promise<String>}
1129
1132
  */
1130
1133
  async downloadDir(srcDir, dstDir, filter = /.*/) {
1131
1134
  try {
@@ -1176,6 +1179,7 @@ class SftpClient {
1176
1179
  *
1177
1180
  * End the SFTP connection
1178
1181
  *
1182
+ * @returns {Promise<Boolean>}
1179
1183
  */
1180
1184
  end() {
1181
1185
  let endCloseHandler;