kopia lustrzana https://github.com/robinmoisson/staticrypt
rename CLI args, save output to new dir (closes #166)
rodzic
92691a1994
commit
f8f11067ea
143
cli/helpers.js
143
cli/helpers.js
|
@ -1,7 +1,8 @@
|
|||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const readline = require('readline');
|
||||
|
||||
const { generateRandomSalt } = require("../lib/cryptoEngine.js");
|
||||
const path = require("path");
|
||||
const {renderTemplate} = require("../lib/formater.js");
|
||||
const Yargs = require("yargs");
|
||||
|
||||
|
@ -51,24 +52,46 @@ function isOptionSetByUser(option, yargs) {
|
|||
exports.isOptionSetByUser = isOptionSetByUser;
|
||||
|
||||
/**
|
||||
* Get the password from the command arguments
|
||||
* Prompts the user for input on the CLI.
|
||||
*
|
||||
* @param {string[]} positionalArguments
|
||||
* @returns {string}
|
||||
* @param {string} question
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function getPassword(positionalArguments) {
|
||||
let password = process.env.STATICRYPT_PASSWORD;
|
||||
const hasEnvPassword = password !== undefined && password !== "";
|
||||
function prompt (question) {
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
});
|
||||
|
||||
return new Promise((resolve) => {
|
||||
return rl.question(question, (answer) => {
|
||||
rl.close();
|
||||
return resolve(answer);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the password from the command arguments or environment variables.
|
||||
*
|
||||
* @param {string} passwordArgument - password from the command line
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function getPassword(passwordArgument) {
|
||||
// try to get the password from the environment variable
|
||||
const envPassword = process.env.STATICRYPT_PASSWORD;
|
||||
const hasEnvPassword = envPassword !== undefined && envPassword !== "";
|
||||
if (hasEnvPassword) {
|
||||
return password;
|
||||
return envPassword;
|
||||
}
|
||||
|
||||
if (positionalArguments.length < 2) {
|
||||
exitWithError("missing password, please provide an argument or set the STATICRYPT_PASSWORD environment variable in the environment or .env file");
|
||||
// try to get the password from the command line arguments
|
||||
if (passwordArgument !== null) {
|
||||
return passwordArgument;
|
||||
}
|
||||
|
||||
return positionalArguments[1].toString();
|
||||
// prompt the user for their password
|
||||
return prompt('Enter your long, unusual password: ');
|
||||
}
|
||||
exports.getPassword = getPassword;
|
||||
|
||||
|
@ -140,11 +163,12 @@ exports.convertCommonJSToBrowserJS = convertCommonJSToBrowserJS;
|
|||
* @param {string} errorName
|
||||
* @returns {string}
|
||||
*/
|
||||
function readFile(filePath, errorName = file) {
|
||||
function readFile(filePath, errorName = "file") {
|
||||
try {
|
||||
return fs.readFileSync(filePath, "utf8");
|
||||
} catch (e) {
|
||||
exitWithError(`could not read ${errorName}!`);
|
||||
console.error(e);
|
||||
exitWithError(`could not read ${errorName} at path "${filePath}"`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,10 +184,17 @@ function genFile(data, outputFilePath, templateFilePath) {
|
|||
|
||||
const renderedTemplate = renderTemplate(templateContents, data);
|
||||
|
||||
// create output directory if it does not exist
|
||||
const dirname = path.dirname(outputFilePath);
|
||||
if (!fs.existsSync(dirname)) {
|
||||
fs.mkdirSync(dirname, { recursive: true });
|
||||
}
|
||||
|
||||
try {
|
||||
fs.writeFileSync(outputFilePath, renderedTemplate);
|
||||
} catch (e) {
|
||||
exitWithError("could not generate output file!");
|
||||
console.error(e);
|
||||
exitWithError("could not generate output file");
|
||||
}
|
||||
}
|
||||
exports.genFile = genFile;
|
||||
|
@ -179,64 +210,32 @@ function isCustomPasswordTemplateDefault(templatePathParameter) {
|
|||
exports.isCustomPasswordTemplateDefault = isCustomPasswordTemplateDefault;
|
||||
|
||||
function parseCommandLineArguments() {
|
||||
return Yargs.usage("Usage: staticrypt <filename> [<password>] [options]")
|
||||
return Yargs.usage("Usage: staticrypt <filename> [options]")
|
||||
.option("c", {
|
||||
alias: "config",
|
||||
type: "string",
|
||||
describe: 'Path to the config file. Set to "false" to disable.',
|
||||
default: ".staticrypt.json",
|
||||
})
|
||||
.option("decrypt-button", {
|
||||
type: "string",
|
||||
describe: 'Label to use for the decrypt button. Default: "DECRYPT".',
|
||||
default: "DECRYPT",
|
||||
})
|
||||
.option("f", {
|
||||
alias: "file-template",
|
||||
type: "string",
|
||||
describe: "Path to custom HTML template with password prompt.",
|
||||
default: PASSWORD_TEMPLATE_DEFAULT_PATH,
|
||||
})
|
||||
.option("i", {
|
||||
alias: "instructions",
|
||||
type: "string",
|
||||
describe: "Special instructions to display to the user.",
|
||||
default: "",
|
||||
})
|
||||
.option("label-error", {
|
||||
type: "string",
|
||||
describe: "Error message to display on entering wrong password.",
|
||||
default: "Bad password!",
|
||||
})
|
||||
.option("noremember", {
|
||||
type: "boolean",
|
||||
describe: 'Set this flag to remove the "Remember me" checkbox.',
|
||||
default: false,
|
||||
})
|
||||
.option("o", {
|
||||
alias: "output",
|
||||
type: "string",
|
||||
describe: "File name/path for the generated encrypted file.",
|
||||
describe: "Name of the directory where the encrypted files will be saved.",
|
||||
default: "encrypted/",
|
||||
})
|
||||
.option("p", {
|
||||
alias: "password",
|
||||
type: "string",
|
||||
describe: "The password to encrypt your file with.",
|
||||
default: null,
|
||||
})
|
||||
.option("passphrase-placeholder", {
|
||||
type: "string",
|
||||
describe: "Placeholder to use for the password input.",
|
||||
default: "Password",
|
||||
})
|
||||
.option("r", {
|
||||
alias: "remember",
|
||||
.option("remember", {
|
||||
type: "number",
|
||||
describe:
|
||||
'Expiration in days of the "Remember me" checkbox that will save the (salted + hashed) password ' +
|
||||
'in localStorage when entered by the user. Default: "0", no expiration.',
|
||||
'in localStorage when entered by the user. Set to "false" to hide the box. Default: "0", no expiration.',
|
||||
default: 0,
|
||||
})
|
||||
.option("remember-label", {
|
||||
type: "string",
|
||||
describe: 'Label to use for the "Remember me" checkbox.',
|
||||
default: "Remember me",
|
||||
})
|
||||
// do not give a default option to this parameter - we want to see when the flag is included with no
|
||||
// value and when it's not included at all
|
||||
.option("s", {
|
||||
|
@ -261,7 +260,37 @@ function parseCommandLineArguments() {
|
|||
default: false,
|
||||
})
|
||||
.option("t", {
|
||||
alias: "title",
|
||||
alias: "template",
|
||||
type: "string",
|
||||
describe: "Path to custom HTML template with password prompt.",
|
||||
default: PASSWORD_TEMPLATE_DEFAULT_PATH,
|
||||
})
|
||||
.option("template-button", {
|
||||
type: "string",
|
||||
describe: 'Label to use for the decrypt button. Default: "DECRYPT".',
|
||||
default: "DECRYPT",
|
||||
})
|
||||
.option("template-instructions", {
|
||||
type: "string",
|
||||
describe: "Special instructions to display to the user.",
|
||||
default: "",
|
||||
})
|
||||
.option("template-error", {
|
||||
type: "string",
|
||||
describe: "Error message to display on entering wrong password.",
|
||||
default: "Bad password!",
|
||||
})
|
||||
.option("template-placeholder", {
|
||||
type: "string",
|
||||
describe: "Placeholder to use for the password input.",
|
||||
default: "Password",
|
||||
})
|
||||
.option("template-remember", {
|
||||
type: "string",
|
||||
describe: 'Label to use for the "Remember me" checkbox.',
|
||||
default: "Remember me",
|
||||
})
|
||||
.option("template-title", {
|
||||
type: "string",
|
||||
describe: "Title for the output HTML page.",
|
||||
default: "Protected Page",
|
||||
|
|
26
cli/index.js
26
cli/index.js
|
@ -27,18 +27,18 @@ async function runStatiCrypt() {
|
|||
|
||||
// validate the number of arguments
|
||||
const positionalArguments = namedArgs._;
|
||||
if (positionalArguments.length > 2 || positionalArguments.length === 0) {
|
||||
if (positionalArguments.length === 0) {
|
||||
yargs.showHelp();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// parse input
|
||||
const inputFilepath = positionalArguments[0].toString(),
|
||||
password = getPassword(positionalArguments);
|
||||
password = await getPassword(namedArgs.password);
|
||||
|
||||
if (password.length < 16 && !namedArgs.short) {
|
||||
console.log(
|
||||
`WARNING: Your password is less than 16 characters (length: ${password.length}). Brute-force attacks are easy to `
|
||||
`\nWARNING: Your password is less than 16 characters (length: ${password.length}). Brute-force attacks are easy to `
|
||||
+ `try on public files, and you are most safe when using a long password.\n\n`
|
||||
+ `👉️ Here's a strong generated password you could use: `
|
||||
+ generateRandomString(21)
|
||||
|
@ -88,25 +88,23 @@ async function runStatiCrypt() {
|
|||
const encryptedMessage = await encode(contents, password, salt);
|
||||
|
||||
const data = {
|
||||
decrypt_button: namedArgs.decryptButton,
|
||||
decrypt_button: namedArgs.templateButton,
|
||||
encrypted: encryptedMessage,
|
||||
instructions: namedArgs.instructions,
|
||||
is_remember_enabled: namedArgs.noremember ? "false" : "true",
|
||||
instructions: namedArgs.templateInstructions,
|
||||
is_remember_enabled: namedArgs.remember === "false" ? "false" : "true",
|
||||
js_codec: convertCommonJSToBrowserJS("lib/codec"),
|
||||
js_crypto_engine: convertCommonJSToBrowserJS("lib/cryptoEngine"),
|
||||
label_error: namedArgs.labelError,
|
||||
passphrase_placeholder: namedArgs.passphrasePlaceholder,
|
||||
label_error: namedArgs.templateError,
|
||||
passphrase_placeholder: namedArgs.templatePlaceholder,
|
||||
remember_duration_in_days: namedArgs.remember,
|
||||
remember_me: namedArgs.rememberLabel,
|
||||
remember_me: namedArgs.templateRemember,
|
||||
salt: salt,
|
||||
title: namedArgs.title,
|
||||
title: namedArgs.templateTitle,
|
||||
};
|
||||
|
||||
const outputFilepath = namedArgs.output !== null
|
||||
? namedArgs.output
|
||||
: inputFilepath.replace(/\.html$/, "") + "_encrypted.html";
|
||||
const outputFilepath = namedArgs.output.replace(/\/+$/, '') + "/" + inputFilepath;
|
||||
|
||||
genFile(data, outputFilepath, namedArgs.f);
|
||||
genFile(data, outputFilepath, namedArgs.template);
|
||||
}
|
||||
|
||||
runStatiCrypt();
|
||||
|
|
Ładowanie…
Reference in New Issue