From 881d75c33824e3b5e719abe25eacc90998861b74 Mon Sep 17 00:00:00 2001 From: steelbrain Date: Tue, 26 Jul 2022 14:53:05 +0300 Subject: [PATCH 1/2] :new: Separate private key and private key path --- README.md | 20 ++++++++++---------- src/index.ts | 31 ++++++++++++++++++++----------- test/main-test.ts | 2 +- test/validation-test.ts | 20 +++++++++++++++++--- 4 files changed, 48 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 49e8537..c2759e1 100644 --- a/README.md +++ b/README.md @@ -15,17 +15,16 @@ const ssh = new NodeSSH() ssh.connect({ host: 'localhost', username: 'steel', - privateKey: '/home/steel/.ssh/id_rsa' + privateKeyPath: '/home/steel/.ssh/id_rsa' +}) + +// or with inline privateKey + +ssh.connect({ + host: 'localhost', + username: 'steel', + privateKey: Buffer.from('...') }) -/* - Or - ssh.connect({ - host: 'localhost', - username: 'steel', - privateKey: fs.readFileSync('/home/steel/.ssh/id_rsa', 'utf8') - }) - if you want to use the raw string as private key - */ .then(function() { // Local, Remote ssh.putFile('/home/steel/Lab/localPath/fileName', '/home/steel/Lab/remotePath/fileName').then(function() { @@ -110,6 +109,7 @@ declare type Config = ConnectConfig & { port?: number; username?: string; password?: string; + privateKeyPath?: string; privateKey?: string; passphrase?: string; tryKeyboard?: boolean; diff --git a/src/index.ts b/src/index.ts index ffdceeb..4d95d38 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,6 +14,7 @@ import { Prompt, Stats, TransferOptions } from 'ssh2-streams' export type Config = ConnectConfig & { password?: string privateKey?: string + privateKeyPath?: string tryKeyboard?: boolean onKeyboardInteractive?: ( name: string, @@ -161,25 +162,33 @@ export class NodeSSH { throw new AssertionError({ message: 'Either config.host or config.sock must be provided' }) } - if (config.privateKey != null) { - invariant(typeof config.privateKey === 'string', 'config.privateKey must be a valid string') + if (config.privateKey != null || config.privateKeyPath != null) { + if (config.privateKey != null) { + invariant(typeof config.privateKey === 'string', 'config.privateKey must be a valid string') + invariant( + config.privateKeyPath == null, + 'config.privateKeyPath must not be specified when config.privateKey is specified', + ) + } else if (config.privateKeyPath != null) { + invariant(typeof config.privateKeyPath === 'string', 'config.privateKeyPath must be a valid string') + invariant( + config.privateKey == null, + 'config.privateKey must not be specified when config.privateKeyPath is specified', + ) + } + invariant( config.passphrase == null || typeof config.passphrase === 'string', - 'config.passphrase must be a valid string', + 'config.passphrase must be null or a valid string', ) - if ( - !( - (config.privateKey.includes('BEGIN') && config.privateKey.includes('KEY')) || - config.privateKey.includes('PuTTY-User-Key-File-2') - ) - ) { + if (config.privateKeyPath != null) { // Must be an fs path try { - config.privateKey = await readFile(config.privateKey) + config.privateKey = await readFile(config.privateKeyPath) } catch (err) { if (err != null && err.code === 'ENOENT') { - throw new AssertionError({ message: 'config.privateKey does not exist at given fs path' }) + throw new AssertionError({ message: 'config.privateKeyPath does not exist at given fs path' }) } throw err } diff --git a/test/main-test.ts b/test/main-test.ts index 3976bd8..4b0d674 100644 --- a/test/main-test.ts +++ b/test/main-test.ts @@ -53,7 +53,7 @@ async function connectWithPrivateKey(port, client) { host: '127.0.0.1', port, username: 'steel', - privateKey: PRIVATE_KEY_PATH, + privateKeyPath: PRIVATE_KEY_PATH, }) } async function connectWithInlinePrivateKey(port, client) { diff --git a/test/validation-test.ts b/test/validation-test.ts index 2a46735..4873786 100644 --- a/test/validation-test.ts +++ b/test/validation-test.ts @@ -313,10 +313,24 @@ test('throws if privateKey is a file and does not exist', async function (t) { await normalizeConfig({ username: 'asd', host: 'localhost', - privateKey: keyPath, + privateKeyPath: keyPath, }) }, - `config.privateKey does not exist at given fs path`, + `config.privateKeyPath does not exist at given fs path`, + ) +}) +test('throws if privateKey is specified and so is privateKeyPath', async function (t) { + await throwsAsync( + t, + async function () { + await normalizeConfig({ + username: 'asd', + host: 'localhost', + privateKey: 'x', + privateKeyPath: 'y', + }) + }, + `config.privateKeyPath must not be specified when config.privateKey is specified`, ) }) test('does not throw if privateKey is valid', async function (t) { @@ -348,7 +362,7 @@ test('does not throw if privateKey is valid', async function (t) { await normalizeConfig({ username: 'asd', host: 'localhost', - privateKey: PRIVATE_KEY_PATH, + privateKeyPath: PRIVATE_KEY_PATH, }) }, 'connect ECONNREFUSED 127.0.0.1:22', From ba6554bfada0860630fad625042151d20bd2bef6 Mon Sep 17 00:00:00 2001 From: steelbrain Date: Tue, 26 Jul 2022 14:54:29 +0300 Subject: [PATCH 2/2] :memo: Document changes in CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fca8359..b2567be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +#### Upcoming + +- BREAKING: `privateKey` config parameter no longer accepts file system paths. Please use the new `privateKeyPath` parameter instead + #### 12.0.5 - Fixed manifest config for Typescript typings. #431 (Thanks @Sikarii)