diff --git a/src/utils/processors.ts b/src/utils/processors.ts index da11fe36..0efc65d3 100644 --- a/src/utils/processors.ts +++ b/src/utils/processors.ts @@ -23,11 +23,23 @@ import * as errors from '../errors'; * When SIGINT is received this will return undefined */ async function promptPassword(): Promise { - const { password } = await prompts({ - name: 'password', - type: 'password', - message: 'Please enter the password', - }); + // If `isTTY` is `undefined` then stdin has been piped into and `prompts` will not process it properly + if (process.stdin.setRawMode == null) return; + let cancelled = false; + const { password } = await prompts( + { + name: 'password', + type: 'password', + message: 'Please enter the password', + }, + { + onCancel: () => { + cancelled = true; + }, + }, + ); + // If cancelled then we just return undefined + if (cancelled) return; return password; } @@ -37,28 +49,36 @@ async function promptPassword(): Promise { * When SIGINT is received this will return undefined */ async function promptNewPassword(): Promise { - let password: string | undefined; + // If `isTTY` is `undefined` then stdin has been piped into and `prompts` will not process it properly + if (process.stdin.setRawMode == null) return; while (true) { - ({ password } = await prompts({ - name: 'password', - type: 'password', - message: 'Enter new password', - })); - // If undefined, then SIGINT was sent, return undefined - if (password == null) return; - const { passwordConfirm } = await prompts({ - name: 'passwordConfirm', - type: 'password', - message: 'Confirm new password', - }); - // If undefined, then SIGINT was sent, return undefined - if (passwordConfirm == null) return; - // Compare the passwords are the same - if (password === passwordConfirm) break; + let cancelled = false; + const { password, passwordConfirm } = await prompts( + [ + { + name: 'password', + type: 'password', + message: 'Enter new password', + }, + { + name: 'passwordConfirm', + type: 'password', + message: 'Confirm new password', + }, + ], + { + onCancel: () => { + cancelled = true; + }, + }, + ); + // If cancelled then we just return undefined + if (cancelled) return; + // Confirm that the passwords are the same + if (password === passwordConfirm) return password; // Interactive message process.stderr.write('Passwords do not match!\n'); } - return password; } /** diff --git a/tests/agent/start.test.ts b/tests/agent/start.test.ts index 3151c6ac..732ee776 100644 --- a/tests/agent/start.test.ts +++ b/tests/agent/start.test.ts @@ -1036,110 +1036,4 @@ describe('start', () => { globalThis.defaultTimeout * 2, ); }); - test('prompts for password twice when creating state', async () => { - const polykeyPath = path.join(dataDir, 'polykey'); - await fs.promises.mkdir(polykeyPath); - - // Starting with missing directory prompts a new password - await testUtils.pkExpect({ - args: [ - 'agent', - 'start', - '--node-path', - path.join(dataDir, 'polykey'), - '--client-host', - '127.0.0.1', - '--agent-host', - '127.0.0.1', - '--workers', - 'none', - '--seed-nodes', - '', - '--verbose', - '--format', - 'json', - '--password-ops-limit', - 'min', - '--password-mem-limit', - 'min', - ], - expect: (expectChain) => { - expectChain.expect(/Enter new password/); - expectChain.sendline('password'); - expectChain.wait(/Confirm new password/); - expectChain.sendEof(); - return expectChain; - }, - }); - }); - test( - 'prompts for password once with existing state', - async () => { - const password = 'abc123'; - const agentProcess1 = await testUtils.pkSpawn( - [ - 'agent', - 'start', - '--client-host', - '127.0.0.1', - '--agent-host', - '127.0.0.1', - '--workers', - 'none', - '--seed-nodes', - '', - '--verbose', - ], - { - env: { - PK_NODE_PATH: path.join(dataDir, 'polykey'), - PK_PASSWORD: password, - PK_PASSWORD_OPS_LIMIT: 'min', - PK_PASSWORD_MEM_LIMIT: 'min', - }, - cwd: dataDir, - }, - logger, - ); - const rlOut = readline.createInterface(agentProcess1.stdout!); - await new Promise((resolve, reject) => { - rlOut.once('line', resolve); - rlOut.once('close', () => reject(Error('closed early'))); - }); - agentProcess1.kill('SIGHUP'); - - // Starting again on existing state - await testUtils.pkExpect({ - args: [ - 'agent', - 'start', - '--node-path', - path.join(dataDir, 'polykey'), - '--client-host', - '127.0.0.1', - '--agent-host', - '127.0.0.1', - '--workers', - 'none', - '--seed-nodes', - '', - '--verbose', - '--format', - 'json', - '--password-ops-limit', - 'min', - '--password-mem-limit', - 'min', - ], - expect: (expectChain) => { - expectChain.expect(/Please enter the password/); - expectChain.sendline('password'); - expectChain.wait(/Creating PolykeyAgent/); - expectChain.sendEof(); - return expectChain; - }, - }); - }, - globalThis.defaultTimeout * 2, - ); }); diff --git a/tests/bootstrap.test.ts b/tests/bootstrap.test.ts index 6a2e53b6..fd550019 100644 --- a/tests/bootstrap.test.ts +++ b/tests/bootstrap.test.ts @@ -1,4 +1,3 @@ -import type { IChain } from 'nexpect'; import path from 'path'; import fs from 'fs'; import readline from 'readline'; @@ -303,29 +302,4 @@ describe('bootstrap', () => { }, globalThis.defaultTimeout * 2, ); - test( - 'bootstraps node state prompts for password twice ', - async () => { - const password = 'password'; - const passwordPath = path.join(dataDir, 'password'); - await fs.promises.writeFile(passwordPath, password); - await testUtils.pkExpect({ - args: ['bootstrap', '--verbose'], - env: { - PK_NODE_PATH: path.join(dataDir, 'polykey'), - PK_PASSWORD_OPS_LIMIT: 'min', - PK_PASSWORD_MEM_LIMIT: 'min', - }, - cwd: dataDir, - expect: (expectChain: IChain) => { - expectChain.expect(/Enter new password/); - expectChain.sendline('password'); - expectChain.wait(/Confirm new password/); - expectChain.sendEof(); - return expectChain; - }, - }); - }, - globalThis.defaultTimeout * 2, - ); });