diff --git a/README.md b/README.md index d52eb9a..014f608 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ line = readlineSync.prompt([options]) ``` Readies readline for input from the user, putting the current `setPrompt` options on a new line, giving the user a new spot to write. -If `{noEchoBack: true}` is specified to `options`, echo back is avoided. It is used to hide the password which is typed by user on screen. *See [Note](#note) for security.* +If `{noEchoBack: true}` is specified to `options`, echo back is avoided. It is used to hide the password which is typed by user on screen. ### question @@ -50,7 +50,7 @@ line = readlineSync.question([query[, options]]) Displays the `query` to the user, and then returns the user's response after it has been typed. `query` is converted to string (i.e. `toString` method is called) before it is displayed every time. -If `{noEchoBack: true}` is specified to `options`, echo back is avoided. It is used to hide the password which is typed by user on screen. *See [Note](#note) for security.* +If `{noEchoBack: true}` is specified to `options`, echo back is avoided. It is used to hide the password which is typed by user on screen. ### setEncoding @@ -104,9 +104,8 @@ try { } ``` -+ If `options.noEchoBack` is used, the text that input by user is saved to temporary file (e.g. `/tmp/readline-sync.stdout`). This file is removed immediately after reading is done, but you have to be careful about it because this text is *plain*. Removing the file might fail, or the file might be peeped before it is removed. - ## Release History + * 2014-07-13 v0.4.3 fixed #6: Crypto input data. * 2014-07-12 v0.4.2 `setPrompt()` and `setEncoding()` return current value. * 2014-07-12 v0.4.1 `setPrompt()` and `question()` accept the value which is not string too (e.g. number, Date, Object, etc.). * 2014-07-12 v0.4.0 Add `options.noEchoBack`. diff --git a/lib/encrypt.js b/lib/encrypt.js new file mode 100644 index 0000000..b1e475b --- /dev/null +++ b/lib/encrypt.js @@ -0,0 +1,16 @@ +var algorithmCipher = 'aes-256-cbc', + cipher = require('crypto').createCipher(algorithmCipher, process.argv[2]), + stdin = process.stdin, + stdout = process.stdout, + crypted = ''; + +stdin.resume(); +stdin.setEncoding('utf8'); +stdin.on('data', function(d) { + crypted += cipher.update(d, 'utf8', 'hex'); +}); +stdin.on('end', function() { + stdout.write(crypted + cipher.final('hex'), 'binary', function() { + process.exit(0); + }); +}); diff --git a/lib/readline-sync.js b/lib/readline-sync.js index 6a41353..536c0f0 100644 --- a/lib/readline-sync.js +++ b/lib/readline-sync.js @@ -8,14 +8,18 @@ 'use strict'; -var promptText = '> ', - encoding = 'utf8', +var BUF_SIZE = 256, + ALGORITHM_CIPHER = 'aes-256-cbc', + ALGORITHM_HASH = 'sha256', + + promptText = '> ', + encoding = 'utf8', fs = require('fs'), stdin = process.stdin, stdout = process.stdout, buffer = new Buffer(BUF_SIZE), - useShell = true, print, tempdir; + useShell = true, print, tempdir, salt = 0; function _readlineSync(display, options) { var input = '', rsize, err; @@ -78,18 +82,29 @@ function _readlineShell(noEchoBack) { pathStdout = getTempfile('readline-sync.stdout'), pathStatus = getTempfile('readline-sync.status'), pathDone = getTempfile('readline-sync.done'), - optEchoBack = noEchoBack ? ' noechoback' : ''; + optEchoBack = noEchoBack ? ' noechoback' : '', + crypto = require('crypto'), shasum, decipher, password; + + shasum = crypto.createHash(ALGORITHM_HASH); + shasum.update('' + process.pid + (salt++) + Math.random()); + password = shasum.digest('hex'); + decipher = crypto.createDecipher(ALGORITHM_CIPHER, password); if (process.platform === 'win32') { // The quote (") is escaped by node before parsed by shell. Then use ENV{Q}. shellPath = 'cmd.exe'; args = ['/V:ON', '/S', '/C', - '%Q%' + __dirname + '\\read.bat%Q%' + optEchoBack + ' >%Q%' + pathStdout + - '%Q% & (echo !ERRORLEVEL!)>%Q%' + pathStatus + '%Q% & (echo 1)>%Q%' + pathDone + '%Q%']; + '%Q%' + __dirname + '\\read.bat%Q%' + optEchoBack + + ' |%Q%' + process.execPath + '%Q% %Q%' + __dirname + '\\encrypt.js%Q% %Q%' + password + '%Q%' + + ' >%Q%' + pathStdout + '%Q%' + + ' & (echo !ERRORLEVEL!)>%Q%' + pathStatus + '%Q% & (echo 1)>%Q%' + pathDone + '%Q%']; } else { shellPath = '/bin/sh'; - args = ['-c', '(' + shellPath + ' "' + __dirname + '/read.sh"' + optEchoBack + ') >"' + pathStdout + - '"; echo $? >"' + pathStatus + '"; echo 1 >"' + pathDone + '"']; + args = ['-c', + '(' + shellPath + ' "' + __dirname + '/read.sh"' + optEchoBack + ')' + + ' |"' + process.execPath + '" "' + __dirname + '/encrypt.js" "' + password + '"' + + ' >"' + pathStdout + '";' + + ' echo $? >"' + pathStatus + '"; echo 1 >"' + pathDone + '"']; } stdin.pause(); // re-start in child process @@ -99,7 +114,9 @@ function _readlineShell(noEchoBack) { if (fs.readFileSync(pathDone, {encoding: encoding}).trim() === '1') { break; } } if (fs.readFileSync(pathStatus, {encoding: encoding}).trim() === '0') { - shellStdout = fs.readFileSync(pathStdout, {encoding: encoding}); + shellStdout = + decipher.update(fs.readFileSync(pathStdout, {encoding: 'binary'}), 'hex', encoding) + + decipher.final(encoding); } fs.unlinkSync(pathStdout); diff --git a/package.json b/package.json index 4aa15a6..32d08fe 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "readline-sync", "description": "Synchronous Readline", - "version": "0.4.2", + "version": "0.4.3", "homepage": "https://github.com/anseki/readline-sync", "author": { "name": "anseki"