staticrypt/cli/index.js

160 wiersze
5.0 KiB
JavaScript
Czysty Zwykły widok Historia

#!/usr/bin/env node
"use strict";
// check node version before anything else
const nodeVersion = process.versions.node.split(".");
if (nodeVersion[0] < 16) {
console.log("ERROR: Node version 16 or higher is required.");
process.exit(1);
}
2022-11-21 21:46:45 +00:00
// parse .env file into process.env
2023-04-19 09:18:27 +00:00
require("dotenv").config();
2022-11-21 21:46:45 +00:00
const fs = require("fs");
const cryptoEngine = require("../lib/cryptoEngine.js");
const codec = require("../lib/codec.js");
const { generateRandomSalt } = cryptoEngine;
2023-04-19 07:57:12 +00:00
const { encodeWithHashedPassword } = codec.init(cryptoEngine);
2023-04-19 09:18:27 +00:00
const {
parseCommandLineArguments,
buildStaticryptJS,
isOptionSetByUser,
genFile,
getFileContent,
getValidatedSalt,
2023-04-19 09:18:27 +00:00
getValidatedPassword,
getConfig,
writeConfig,
} = require("./helpers.js");
// parse arguments
const yargs = parseCommandLineArguments();
const namedArgs = yargs.argv;
async function runStatiCrypt() {
2023-03-30 16:43:40 +00:00
const hasSaltFlag = isOptionSetByUser("s", yargs);
const hasShareFlag = isOptionSetByUser("share", yargs);
2023-03-30 16:45:47 +00:00
const positionalArguments = namedArgs._;
2023-04-19 07:47:05 +00:00
// require at least one positional argument unless some specific flags are passed
if (!hasShareFlag && !(hasSaltFlag && !namedArgs.salt)) {
2023-03-30 16:43:40 +00:00
if (positionalArguments.length === 0) {
console.log("ERROR: Invalid number of arguments. Please provide an input file.\n");
2023-03-30 16:43:40 +00:00
yargs.showHelp();
process.exit(1);
}
}
// get config file
const configPath = namedArgs.config.toLowerCase() === "false" ? null : "./" + namedArgs.config;
const config = getConfig(configPath);
// if the 's' flag is passed without parameter, generate a salt, display & exit
2023-03-30 16:43:40 +00:00
if (hasSaltFlag && !namedArgs.salt) {
const generatedSalt = generateRandomSalt();
// show salt
console.log(generatedSalt);
// write to config file if it doesn't exist
if (!config.salt) {
config.salt = generatedSalt;
writeConfig(configPath, config);
}
process.exit(0);
}
// get the salt & password
const salt = getValidatedSalt(namedArgs, config);
const password = await getValidatedPassword(namedArgs.password, namedArgs.short);
// display the share link with the hashed password if the --share flag is set
2023-03-30 16:43:40 +00:00
if (hasShareFlag) {
const url = namedArgs.share || "";
2023-04-19 07:57:12 +00:00
const hashedPassword = await cryptoEngine.hashPassword(password, salt);
console.log(url + "#staticrypt_pwd=" + hashedPassword);
process.exit(0);
}
// write salt to config file
if (config.salt !== salt) {
config.salt = salt;
2023-04-03 11:47:10 +00:00
writeConfig(configPath, config);
}
const isRememberEnabled = namedArgs.remember !== "false";
2023-04-19 07:47:05 +00:00
const baseTemplateData = {
is_remember_enabled: JSON.stringify(isRememberEnabled),
js_staticrypt: buildStaticryptJS(),
template_button: namedArgs.templateButton,
template_error: namedArgs.templateError,
template_instructions: namedArgs.templateInstructions,
template_placeholder: namedArgs.templatePlaceholder,
template_remember: namedArgs.templateRemember,
template_title: namedArgs.templateTitle,
template_color_primary: namedArgs.templateColorPrimary,
template_color_secondary: namedArgs.templateColorSecondary,
};
2023-04-19 07:57:12 +00:00
const hashedPassword = await cryptoEngine.hashPassword(password, salt);
2023-04-19 07:47:05 +00:00
2023-04-19 09:18:27 +00:00
positionalArguments.forEach((path) =>
encodeAndGenerateFile(path.toString(), hashedPassword, salt, baseTemplateData, isRememberEnabled, namedArgs)
);
}
async function encodeAndGenerateFile(path, hashedPassword, salt, baseTemplateData, isRememberEnabled, namedArgs) {
// if the path is a directory, get into it and process all files
if (fs.statSync(path).isDirectory()) {
if (!namedArgs.recursive) {
2023-04-19 09:18:27 +00:00
console.log(
"ERROR: The path '" +
path +
"' is a directory. Use the -r|--recursive flag to process all files in the directory."
);
2023-04-19 07:47:05 +00:00
// just return instead of exiting the process, that way all other files can be processed
return;
}
2023-04-19 07:47:05 +00:00
2023-04-19 09:18:27 +00:00
fs.readdirSync(path).forEach((filePath) => {
const fullPath = `${path}/${filePath}`;
2023-04-19 07:47:05 +00:00
encodeAndGenerateFile(fullPath, hashedPassword, salt, baseTemplateData, isRememberEnabled, namedArgs);
});
return;
2023-04-19 07:47:05 +00:00
}
// get the file content
const contents = getFileContent(path);
// encrypt input
const encryptedMsg = await encodeWithHashedPassword(contents, hashedPassword);
const staticryptConfig = {
encryptedMsg,
isRememberEnabled,
rememberDurationInDays: namedArgs.remember,
salt,
};
const templateData = {
...baseTemplateData,
staticrypt_config: staticryptConfig,
};
2023-04-19 09:18:27 +00:00
const outputFilepath = namedArgs.directory.replace(/\/+$/, "") + "/" + path;
genFile(templateData, outputFilepath, namedArgs.template);
}
runStatiCrypt();