Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

whoosh

Package Overview
Dependencies
Maintainers
1
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

whoosh - npm Package Compare versions

Comparing version 1.1.3 to 1.2.0

4

CHANGELOG.md
# Change Log
## 1.2.0
### Added
- Support for keyboard-interactive. Thanks [janjaali](https://github.com/janjaali)
## 1.1.3

@@ -4,0 +8,0 @@ ### Changed

205

index.js

@@ -1,123 +0,134 @@

var Client = require('ssh2').Client
var debug = require('debug')('whoosh:client')
var ssh2Debug = require('debug')('whoosh:ssh2')
var _ = require('lodash')
var Readable = require('stream').Readable
var format = require('util').format
var Client = require('ssh2').Client;
var debug = require('debug')('whoosh:client');
var ssh2Debug = require('debug')('whoosh:ssh2');
var _ = require('lodash');
var Readable = require('stream').Readable;
var format = require('util').format;
module.exports = {
connect: function(config, next) {
connect: function(config, next) {
var connection = new Client()
var once = _.once(next)
var connectionUrl = format('%s@%s:%s', config.username, config.hostname, config.port)
var disconnected = true
var disconnecting = false
var connection = new Client();
var once = _.once(next);
var connectionUrl = format('%s@%s:%s', config.username, config.hostname, config.port);
var disconnected = true;
var disconnecting = false;
debug('Connecting to server %s', connectionUrl)
connection.connect(_.defaults(config, { debug: ssh2Debug }))
debug('Connecting to server %s', connectionUrl);
connection.connect(_.defaults(config, { debug: ssh2Debug }));
connection.on('ready', function() {
debug('Connected to server %s', connectionUrl)
disconnected = false
connection.on('ready', function() {
debug('Connected to server %s', connectionUrl);
disconnected = false;
connection.sftp(function(err, sftp) {
connection.sftp(function(err, sftp) {
if (err) {
connection.end()
return next(err)
}
if (err) {
connection.end();
return next(err);
}
sftp = _.extend(sftp, {
getContent: function(remotePath, options, next) {
if (arguments.length === 2) return sftp.getContent(remotePath, {}, arguments[1])
sftp = _.extend(sftp, {
getContent: function(remotePath, options, next) {
if (arguments.length === 2) return sftp.getContent(remotePath, {}, arguments[1]);
var once = _.once(next)
var content = ''
var once = _.once(next);
var content = '';
debug('Creating read stream to %s/%s', connectionUrl, remotePath)
var readStream = sftp.createReadStream(remotePath, options)
var before = new Date().getTime()
debug('Creating read stream to %s/%s', connectionUrl, remotePath);
var readStream = sftp.createReadStream(remotePath, options);
var before = new Date().getTime();
readStream.on('data', function(chunk) {
content += chunk
}).on('end', function() {
var duration = new Date().getTime() - before
var bytes = countBytes(content)
readStream.on('data', function(chunk) {
content += chunk;
}).on('end', function() {
var duration = new Date().getTime() - before;
var bytes = countBytes(content);
debug('Downloaded %d bytes from %s/%s in %dms', bytes, connectionUrl, remotePath, duration)
once(null, content, { bytes: bytes, duration: duration })
}).on('error', once)
},
putContent: function(content, remotePath, options, next) {
if (arguments.length === 3) return sftp.putContent(content, remotePath, {}, arguments[2])
debug('Downloaded %d bytes from %s/%s in %dms', bytes, connectionUrl, remotePath, duration);
once(null, content, { bytes: bytes, duration: duration });
}).on('error', once);
},
putContent: function(content, remotePath, options, next) {
if (arguments.length === 3) return sftp.putContent(content, remotePath, {}, arguments[2]);
var once = _.once(next)
var once = _.once(next);
debug('Creating write stream to %s/%s', connectionUrl, remotePath)
var writeStream = sftp.createWriteStream(remotePath)
var before = new Date().getTime()
debug('Creating write stream to %s/%s', connectionUrl, remotePath);
var writeStream = sftp.createWriteStream(remotePath);
var before = new Date().getTime();
writeStream.on('close', function() {
var duration = new Date().getTime() - before
var bytes = countBytes(content)
writeStream.on('close', function() {
var duration = new Date().getTime() - before;
var bytes = countBytes(content);
debug('Uploaded %d bytes to %s/%s in %sms', bytes, connectionUrl, remotePath, duration)
once(null, { bytes: bytes, duration: duration })
}).on('error', once)
debug('Uploaded %d bytes to %s/%s in %sms', bytes, connectionUrl, remotePath, duration);
once(null, { bytes: bytes, duration: duration });
}).on('error', once);
var readStream = new Readable()
readStream.push(content)
readStream.push(null)
readStream.pipe(writeStream)
},
exists: function(remotePath, next) {
sftp.stat(remotePath, function(err, stat) {
if (err && err.code !== 2) return next(err)
return next(null, !!stat)
})
},
disconnect: function(next) {
next = next || function () {};
if (!sftp.isConnected()) return next()
disconnecting = true
sftp.end()
connection.end()
connection.once('close', next)
},
isConnected: function(next) {
var connected = !disconnected && !disconnecting
return next && next(null, connected) || connected
}
})
var readStream = new Readable();
readStream.push(content);
readStream.push(null);
readStream.pipe(writeStream);
},
exists: function(remotePath, next) {
sftp.stat(remotePath, function(err, stat) {
if (err && err.code !== 2) return next(err);
return next(null, !!stat);
});
},
disconnect: function(next) {
next = next || function () {};
if (!sftp.isConnected()) return next();
disconnecting = true;
sftp.end();
connection.end();
connection.once('close', next);
},
isConnected: function(next) {
var connected = !disconnected && !disconnecting;
return next && next(null, connected) || connected;
},
});
once(null, sftp)
})
})
once(null, sftp);
});
});
connection.on('error', function(err) {
debug('Received error from connection: %s:%s. Original error was: ', config.hostname, config.port, err.message)
once(err)
})
connection.on('keyboard-interactive', function (name, instructions, lang, prompts, finish) {
var responses = _.map(prompts, function(entry) {
var challenge = _.find(config.challenges, function(challenge) {
return challenge.pattern.test(entry.prompt);
});
if (challenge) return challenge.response;
debug('No response for challenge: %s', entry.prompt);
return '';
});
finish(responses);
});
connection.on('end', function() {
debug('Connection to %s:%s ended', config.hostname, config.port)
disconnect()
})
connection.on('error', function(err) {
debug('Received error from connection: %s:%s. Original error was: ', config.hostname, config.port, err.message);
once(err);
});
connection.on('close', function() {
debug('Connection to %s:%s closed', config.hostname, config.port)
disconnect()
})
connection.on('end', function() {
debug('Connection to %s:%s ended', config.hostname, config.port);
disconnect();
});
function disconnect() {
disconnected = true
disconnecting = false
}
connection.on('close', function() {
debug('Connection to %s:%s closed', config.hostname, config.port);
disconnect();
});
function disconnect() {
disconnected = true;
disconnecting = false;
}
}
},
};
function countBytes(content) {
return Buffer.isBuffer(content) ? content.length : Buffer.byteLength(content)
return Buffer.isBuffer(content) ? content.length : Buffer.byteLength(content);
}
{
"name": "whoosh",
"version": "1.1.3",
"version": "1.2.0",
"description": "A streaming sftp client",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -118,3 +118,24 @@ # Whoosh

### keyboard interactive
If the server requires keyboard interactive authentication you can configure this as follows...
```js
Whoosh.connect({
hostname: 'sftp.example.com',
port: 22,
tryKeyboard: true,
challenges: [
{
pattern: /^Username:$/,
response: 'fred'
},
{
pattern: /^Password:$/,
response: 'secret'
}
]
}, (err, client) => {
// profit :)
})
```
The exact challenge patterns will be server dependent.

@@ -1,250 +0,250 @@

var assert = require('assert')
var _ = require('lodash')
var async = require('async')
var fs = require('fs-extra')
var crypto = require('crypto')
var Whoosh = require('..')
var assert = require('assert');
var _ = require('lodash');
var async = require('async');
var fs = require('fs-extra');
var crypto = require('crypto');
var Whoosh = require('..');
describe('whoosh', function() {
this.slow(undefined)
this.timeout(10000)
this.slow(undefined);
this.timeout(10000);
beforeEach(nuke)
beforeEach(nuke);
after(nuke)
after(nuke);
function nuke(next) {
async.series([
fs.remove.bind(fs, getLocalPath()),
fs.mkdirp.bind(fs, getLocalPath()),
fs.chmod.bind(fs, getLocalPath(), '0777')
], next)
}
function nuke(next) {
async.series([
fs.remove.bind(fs, getLocalPath()),
fs.mkdirp.bind(fs, getLocalPath()),
fs.chmod.bind(fs, getLocalPath(), '0777'),
], next);
}
var config = {
hostname: 'localhost',
port: 10022,
username: 'fred',
password: 'password'
}
var config = {
hostname: 'localhost',
port: 10022,
username: 'fred',
password: 'password',
};
it('should report connection errors', function(done) {
Whoosh.connect(_.defaults({ hostname: 'this-server-should-not-resolve-12asdf32'}, config), function(err, whoosh) {
assert.ok(err, 'Connection error was not reported')
assert.ok(/getaddrinfo ENOTFOUND/.test(err.message))
done()
})
})
it('should report connection errors', function(done) {
Whoosh.connect(_.defaults({ hostname: 'this-server-should-not-resolve-12asdf32'}, config), function(err, whoosh) {
assert.ok(err, 'Connection error was not reported');
assert.ok(/getaddrinfo ENOTFOUND/.test(err.message));
done();
});
});
it('should report connection errors', function(done) {
Whoosh.connect(_.defaults({ password: 'bad'}, config), function(err, whoosh) {
assert.ok(err, 'Connection error was not reported')
assert.equal(err.message, 'All configured authentication methods failed')
done()
})
})
it('should report connection errors', function(done) {
Whoosh.connect(_.defaults({ password: 'bad'}, config), function(err, whoosh) {
assert.ok(err, 'Connection error was not reported');
assert.equal(err.message, 'All configured authentication methods failed');
done();
});
});
it('should connect successfully', function(done) {
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err)
whoosh.stat('.', function(err, stats) {
assert.ifError(err)
assert.ok(stats)
whoosh.disconnect(done)
})
})
})
it('should connect successfully', function(done) {
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err);
whoosh.stat('.', function(err, stats) {
assert.ifError(err);
assert.ok(stats);
whoosh.disconnect(done);
});
});
});
it('should upload text content', function(done) {
var title = this.test.title
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err)
whoosh.putContent('test message', getRemotePath(title), function(err, stats) {
assert.ifError(err)
assert.equal('test message', fs.readFileSync(getLocalPath(title)).toString())
assert.equal(stats.bytes, 12)
assert.ok(stats.duration > 0)
whoosh.disconnect(done)
})
})
})
it('should upload text content', function(done) {
var title = this.test.title;
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err);
whoosh.putContent('test message', getRemotePath(title), function(err, stats) {
assert.ifError(err);
assert.equal('test message', fs.readFileSync(getLocalPath(title)).toString());
assert.equal(stats.bytes, 12);
assert.ok(stats.duration > 0);
whoosh.disconnect(done);
});
});
});
it('should download text content', function(done) {
var title = this.test.title
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err)
fs.writeFileSync(getLocalPath(title), 'test message')
whoosh.getContent(getRemotePath(title), function(err, content, stats) {
assert.ifError(err)
assert.equal('test message', content)
assert.equal(stats.bytes, 12)
assert.ok(stats.duration > 0)
whoosh.disconnect(done)
})
})
})
it('should download text content', function(done) {
var title = this.test.title;
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err);
fs.writeFileSync(getLocalPath(title), 'test message');
whoosh.getContent(getRemotePath(title), function(err, content, stats) {
assert.ifError(err);
assert.equal('test message', content);
assert.equal(stats.bytes, 12);
assert.ok(stats.duration > 0);
whoosh.disconnect(done);
});
});
});
it('should upload binary content', function(done) {
var title = this.test.title
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err)
whoosh.putContent(new Buffer('test message'), getRemotePath(title), function(err, stats) {
assert.ifError(err)
assert.equal('test message', fs.readFileSync(getLocalPath(title)).toString())
assert.equal(stats.bytes, 12)
whoosh.disconnect(done)
})
})
})
it('should upload binary content', function(done) {
var title = this.test.title;
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err);
whoosh.putContent(new Buffer('test message'), getRemotePath(title), function(err, stats) {
assert.ifError(err);
assert.equal('test message', fs.readFileSync(getLocalPath(title)).toString());
assert.equal(stats.bytes, 12);
whoosh.disconnect(done);
});
});
});
it('should download binary content', function(done) {
var title = this.test.title
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err)
fs.writeFileSync(getLocalPath(title), new Buffer('test message'))
whoosh.getContent(getRemotePath(title), function(err, content) {
assert.ifError(err)
assert.equal('test message', content)
whoosh.disconnect(done)
})
})
})
it('should download binary content', function(done) {
var title = this.test.title;
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err);
fs.writeFileSync(getLocalPath(title), new Buffer('test message'));
whoosh.getContent(getRemotePath(title), function(err, content) {
assert.ifError(err);
assert.equal('test message', content);
whoosh.disconnect(done);
});
});
});
it('should support multiple serial operations', function(done) {
var title = this.test.title
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err)
async.series({
a: whoosh.putContent.bind(whoosh, 'test message 1', getRemotePath(title + '_1')),
b: whoosh.putContent.bind(whoosh, 'test message 2', getRemotePath(title + '_2')),
c: whoosh.putContent.bind(whoosh, 'test message 3', getRemotePath(title + '_3')),
list: whoosh.readdir.bind(whoosh, getRemotePath())
}, function(err, results) {
assert.ifError(err)
assert.equal(results.list.length, 3)
whoosh.disconnect(done)
})
})
})
it('should support multiple serial operations', function(done) {
var title = this.test.title;
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err);
async.series({
a: whoosh.putContent.bind(whoosh, 'test message 1', getRemotePath(title + '_1')),
b: whoosh.putContent.bind(whoosh, 'test message 2', getRemotePath(title + '_2')),
c: whoosh.putContent.bind(whoosh, 'test message 3', getRemotePath(title + '_3')),
list: whoosh.readdir.bind(whoosh, getRemotePath()),
}, function(err, results) {
assert.ifError(err);
assert.equal(results.list.length, 3);
whoosh.disconnect(done);
});
});
});
it('should upload large files', function(done) {
var title = this.test.title
var content = crypto.pseudoRandomBytes(1024 * 1024).toString('hex')
it('should upload large files', function(done) {
var title = this.test.title;
var content = crypto.pseudoRandomBytes(1024 * 1024).toString('hex');
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err)
whoosh.putContent(content, getRemotePath(title), function(err) {
assert.ifError(err)
assert.equal(content, fs.readFileSync(getLocalPath(title)).toString())
whoosh.disconnect(done)
})
})
})
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err);
whoosh.putContent(content, getRemotePath(title), function(err) {
assert.ifError(err);
assert.equal(content, fs.readFileSync(getLocalPath(title)).toString());
whoosh.disconnect(done);
});
});
});
it('should upload a lot of files', function(done) {
var title = this.test.title
var content = crypto.pseudoRandomBytes(1024).toString('hex')
it('should upload a lot of files', function(done) {
var title = this.test.title;
var content = crypto.pseudoRandomBytes(1024).toString('hex');
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err)
async.timesLimit(1000, 50, function(index, next) {
whoosh.putContent(content, getRemotePath(title + '_' + index), next)
}, function(err) {
assert.ifError(err)
whoosh.readdir(getRemotePath(), function(err, list) {
assert.ifError(err)
assert.equal(list.length, 1000)
assert.ok(!_.find(list, function(stat) {
return stat.attrs.size !== content.length
}), 'File was corrupted during upload')
whoosh.disconnect(done)
})
})
})
})
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err);
async.timesLimit(1000, 50, function(index, next) {
whoosh.putContent(content, getRemotePath(title + '_' + index), next);
}, function(err) {
assert.ifError(err);
whoosh.readdir(getRemotePath(), function(err, list) {
assert.ifError(err);
assert.equal(list.length, 1000);
assert.ok(!_.find(list, function(stat) {
return stat.attrs.size !== content.length;
}), 'File was corrupted during upload');
whoosh.disconnect(done);
});
});
});
});
it('should tolerate repeated disconnects', function(done) {
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err)
async.times(10, function(index, next) {
whoosh.disconnect(next)
}, done)
})
})
it('should tolerate repeated disconnects', function(done) {
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err);
async.times(10, function(index, next) {
whoosh.disconnect(next);
}, done);
});
});
it('should report connection state (sync)', function(done) {
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err)
assert.ok(whoosh.isConnected())
whoosh.disconnect(function() {
assert.ok(!whoosh.isConnected())
done()
})
})
})
it('should report connection state (sync)', function(done) {
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err);
assert.ok(whoosh.isConnected());
whoosh.disconnect(function() {
assert.ok(!whoosh.isConnected());
done();
});
});
});
it('should report connection state (async)', function(done) {
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err)
async.series({
shouldBeConnected: whoosh.isConnected,
meh: whoosh.disconnect,
shouldBeDisconnected: whoosh.isConnected
}, function(err, results) {
assert.ifError(err)
assert.ok(results.shouldBeConnected)
assert.ok(!results.shouldBeDisconnected)
done()
})
})
})
it('should report connection state (async)', function(done) {
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err);
async.series({
shouldBeConnected: whoosh.isConnected,
meh: whoosh.disconnect,
shouldBeDisconnected: whoosh.isConnected,
}, function(err, results) {
assert.ifError(err);
assert.ok(results.shouldBeConnected);
assert.ok(!results.shouldBeDisconnected);
done();
});
});
});
it('should report if a file exists', function(done) {
var title = this.test.title
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err)
whoosh.putContent('test message', getRemotePath(title), function(err, stats) {
assert.ifError(err)
whoosh.exists(getRemotePath(title), function(err, exists) {
assert.ifError(err)
assert.equal(exists, true)
whoosh.disconnect(done)
})
})
})
})
it('should report if a file exists', function(done) {
var title = this.test.title;
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err);
whoosh.putContent('test message', getRemotePath(title), function(err, stats) {
assert.ifError(err);
whoosh.exists(getRemotePath(title), function(err, exists) {
assert.ifError(err);
assert.equal(exists, true);
whoosh.disconnect(done);
});
});
});
});
it('should report if a file does not exist', function(done) {
var title = this.test.title
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err)
whoosh.exists(getRemotePath(title), function(err, exists) {
assert.ifError(err)
assert.equal(exists, false)
whoosh.disconnect(done)
})
})
})
it('should report if a file does not exist', function(done) {
var title = this.test.title;
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err);
whoosh.exists(getRemotePath(title), function(err, exists) {
assert.ifError(err);
assert.equal(exists, false);
whoosh.disconnect(done);
});
});
});
it('should not disconnect after checking if a non existent file exists', function(done) {
var title = this.test.title
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err)
whoosh.exists(getRemotePath(title), function(err, exists) {
assert.ifError(err)
assert.ok(whoosh.isConnected())
whoosh.disconnect(done)
})
})
})
it('should not disconnect after checking if a non existent file exists', function(done) {
var title = this.test.title;
Whoosh.connect(config, function(err, whoosh) {
assert.ifError(err);
whoosh.exists(getRemotePath(title), function(err, exists) {
assert.ifError(err);
assert.ok(whoosh.isConnected());
whoosh.disconnect(done);
});
});
});
function getRemotePath(filename) {
return 'files/uploads/' + (filename ? filename.replace(/\W/g, '_') : '')
}
function getRemotePath(filename) {
return 'files/uploads/' + (filename ? filename.replace(/\W/g, '_') : '');
}
function getLocalPath(filename) {
return __dirname + '/volumes/sftp/home/fred/' + getRemotePath(filename)
}
function getLocalPath(filename) {
return __dirname + '/volumes/sftp/home/fred/' + getRemotePath(filename);
}
})
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc