handle passing password through env

pull/147/head v2.3.0
robinmoisson 2022-11-21 22:46:45 +01:00
rodzic 786ca7a681
commit ba24ae42c0
8 zmienionych plików z 115 dodań i 41 usunięć

1
.gitignore vendored
Wyświetl plik

@ -2,3 +2,4 @@
.vscode/
node_modules
.staticrypt.json
.env

Wyświetl plik

@ -22,6 +22,13 @@ Staticrypt is available through npm as a CLI, install with `npm install -g stati
staticrypt test.html MY_PASSPHRASE
```
**Encrypt a file with the passphrase in an environment variable:** set your passphrase in the `STATICRYPT_PASSWORD` environment variable ([`.env` files](https://www.npmjs.com/package/dotenv#usage) are supported):
```bash
# the passphrase is in the STATICRYPT_PASSWORD env variable
staticrypt test.html
```
**Encrypt a file and get a shareble link containing the hashed password** - you can include your file URL or leave blank:
```bash
@ -43,7 +50,9 @@ find . -type f -name "*.html" -not -name "*_encrypted.html" -exec staticrypt {}
### CLI Reference
Usage: staticrypt <filename> <passphrase> [options]
The passphrase argument is optional if `STATICRYPT_PASSWORD` is set in the environment or `.env` file.
Usage: staticrypt <filename> [<passphrase>] [options]
Options:
--help Show help [boolean]

Wyświetl plik

@ -1,3 +1,11 @@
const fs = require("fs");
const cryptoEngine = require("../lib/cryptoEngine/cryptojsEngine");
const { generateRandomSalt } = cryptoEngine;
/**
* @param {string} message
*/
function exitEarly(message) {
console.log(message);
process.exit(1);
@ -12,7 +20,7 @@ exports.exitEarly = exitEarly;
*
* From https://github.com/yargs/yargs/issues/513#issuecomment-221412008
*
* @param option
* @param {string} option
* @param yargs
* @returns {boolean}
*/
@ -36,3 +44,58 @@ function isOptionSetByUser(option, yargs) {
return false;
}
exports.isOptionSetByUser = isOptionSetByUser;
/**
* Get the password from the command arguments
*
* @param {string[]} positionalArguments
* @returns {string}
*/
function getPassword(positionalArguments) {
let password = process.env.STATICRYPT_PASSWORD;
const hasEnvPassword = password !== undefined && password !== "";
if (hasEnvPassword) {
return password;
}
if (positionalArguments.length < 2) {
exitEarly("Missing password: please provide an argument or set the STATICRYPT_PASSWORD environment variable in the environment or .env file");
}
return positionalArguments[1];
}
exports.getPassword = getPassword;
/**
* @param {string} filepath
* @returns {string}
*/
function getFileContent(filepath) {
try {
return fs.readFileSync(filepath, "utf8");
} catch (e) {
exitEarly("Failure: input file does not exist!");
}
}
exports.getFileContent = getFileContent;
/**
* @param {object} namedArgs
* @param {object} config
* @returns {string}
*/
function getSalt(namedArgs, config) {
// either a salt was provided by the user through the flag --salt
if (!!namedArgs.salt) {
return String(namedArgs.salt).toLowerCase();
}
// or try to read the salt from config file
if (config.salt) {
return config.salt;
}
return generateRandomSalt();
}
exports.getSalt = getSalt;

Wyświetl plik

@ -5,10 +5,14 @@
const fs = require("fs");
const path = require("path");
const Yargs = require("yargs");
// parse .env file into process.env
require('dotenv').config();
const cryptoEngine = require("../lib/cryptoEngine/cryptojsEngine");
const codec = require("../lib/codec");
const { convertCommonJSToBrowserJS, genFile} = require("../lib/formater");
const { exitEarly, isOptionSetByUser } = require("./helpers");
const { exitEarly, isOptionSetByUser, getPassword, getFileContent, getSalt} = require("./helpers");
const { generateRandomSalt } = cryptoEngine;
const { encode } = codec.init(cryptoEngine);
@ -19,7 +23,7 @@ const SCRIPT_TAG =
SCRIPT_URL +
'" integrity="sha384-lp4k1VRKPU9eBnPePjnJ9M2RF3i7PC30gXs70+elCVfgwLwx1tv5+ctxdtwxqZa7" crossorigin="anonymous"></script>';
const yargs = Yargs.usage("Usage: staticrypt <filename> <passphrase> [options]")
const yargs = Yargs.usage("Usage: staticrypt <filename> [<passphrase>] [options]")
.option("c", {
alias: "config",
type: "string",
@ -117,11 +121,16 @@ if (isOptionSetByUser("s", yargs) && !namedArgs.salt) {
}
// validate the number of arguments
if (namedArgs._.length !== 2) {
const positionalArguments = namedArgs._;
if (positionalArguments.length > 2 || positionalArguments.length === 0) {
Yargs.showHelp();
process.exit(1);
}
// parse input
const inputFilepath = positionalArguments[0].toString(),
password = getPassword(positionalArguments);
// get config file
const isUsingconfigFile = namedArgs.config.toLowerCase() !== "false";
const configPath = "./" + namedArgs.config;
@ -130,22 +139,8 @@ if (isUsingconfigFile && fs.existsSync(configPath)) {
config = JSON.parse(fs.readFileSync(configPath, "utf8"));
}
/**
* Get the salt to use
*/
let salt;
// either a salt was provided by the user through the flag --salt
if (!!namedArgs.salt) {
salt = String(namedArgs.salt).toLowerCase();
}
// or we try to read the salt from config file
else if (!!config.salt) {
salt = config.salt;
}
// or we generate a salt
else {
salt = generateRandomSalt();
}
// get the salt
const salt = getSalt(namedArgs, config);
// validate the salt
if (salt.length !== 32 || /[^a-f0-9]/.test(salt)) {
@ -161,28 +156,19 @@ if (isUsingconfigFile && config.salt !== salt) {
fs.writeFileSync(configPath, JSON.stringify(config, null, 4));
}
// parse input
const input = namedArgs._[0].toString(),
passphrase = namedArgs._[1].toString();
// display the share link with the hashed password if the --share flag is set
if (isOptionSetByUser("share", yargs)) {
const url = namedArgs.share || "";
const hashedPassphrase = cryptoEngine.hashPassphrase(passphrase, salt);
const hashedPassphrase = cryptoEngine.hashPassphrase(password, salt);
console.log(url + "?staticrypt_pwd=" + hashedPassphrase);
}
// get the file content
let contents;
try {
contents = fs.readFileSync(input, "utf8");
} catch (e) {
exitEarly("Failure: input file does not exist!");
}
const contents = getFileContent(inputFilepath);
// encrypt input
const encryptedMessage = encode(contents, passphrase, salt);
const encryptedMessage = encode(contents, password, salt);
// create crypto-js tag (embedded or not)
let cryptoTag = SCRIPT_TAG;
@ -218,6 +204,6 @@ const data = {
const outputFilePath = namedArgs.output !== null
? namedArgs.output
: input.replace(/\.html$/, "") + "_encrypted.html";
: inputFilepath.replace(/\.html$/, "") + "_encrypted.html";
genFile(data, outputFilePath, namedArgs.f);

Wyświetl plik

@ -349,7 +349,7 @@ exports.init = init;
var decode = codec.init(cryptoEngine).decode;
// variables to be filled when generating the file
var encryptedMsg = '56dea7d61318f40686a20af181fb5d14ce025164d56770ddc61e4078def3d7fa69239fbb3c84b1fe941343cf41100ba6U2FsdGVkX1+4r6JJlpkeR9aAipKLcWURUBxGgPpqDEAR7vAJu1wnAztHaNch1IemKHPOJ+Oy9Mi30xuAfl14RUbSn/nIpReEzVNWVdTQsetFPs+uSDdlayJF1I9CwSkmp0MnQB+d85QWh3xDQpdZ/+fWaRdXPIhhvavpkThFAeJatSezVbA2Xx4CNJWfzc2ekin7uoEAizHk+qUAefRCbA==',
var encryptedMsg = '30d59eebfa6443549af9f78e49525e458bd5d4ee783cb2fc5a61882f62769d8ec467ba45c6c10554321b4ff338b9baacU2FsdGVkX1/M666zWrcMkpw3mxNzMVp8DMjVCvC7R6wGFtZMgA17LV7KuFWo9vqiCoqz52rvljIuZ65Uq5xFJxKGZAAhXfiKrfNnlhcoEsqrdLH/RVRqC2VKPwlM+6OyMd2GB5FV79kWJW9xlnrUjpokIbq7OqIfKooQ8hSKsJFqHNJFYzLNuGMtvERC23jgSFXSKoCACBPXsSgqCdrxbQ==',
salt = 'b93bbaf35459951c47721d1f3eaeb5b9',
labelError = 'Bad password!',
isRememberEnabled = true,
@ -461,6 +461,7 @@ exports.init = init;
if (!hasDecrypted) {
document.getElementById("staticrypt_loading").classList.add("hidden");
document.getElementById("staticrypt_content").classList.remove("hidden");
document.getElementById("staticrypt-password").focus();
}
}

Wyświetl plik

@ -329,6 +329,7 @@
if (!hasDecrypted) {
document.getElementById("staticrypt_loading").classList.add("hidden");
document.getElementById("staticrypt_content").classList.remove("hidden");
document.getElementById("staticrypt-password").focus();
}
}

21
package-lock.json wygenerowano
Wyświetl plik

@ -1,21 +1,21 @@
{
"name": "staticrypt",
"version": "2.1.1",
"version": "2.3.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "staticrypt",
"version": "2.1.1",
"version": "2.3.0",
"license": "MIT",
"dependencies": {
"crypto-js": "3.1.9-1",
"dotenv": "^16.0.3",
"yargs": ">=10.0.3"
},
"bin": {
"staticrypt": "cli/index.js"
},
"devDependencies": {}
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
@ -70,6 +70,14 @@
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.9-1.tgz",
"integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg="
},
"node_modules/dotenv": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
"integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==",
"engines": {
"node": ">=12"
}
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@ -223,6 +231,11 @@
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.9-1.tgz",
"integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg="
},
"dotenv": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
"integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ=="
},
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",

Wyświetl plik

@ -1,6 +1,6 @@
{
"name": "staticrypt",
"version": "2.2.1",
"version": "2.3.0",
"description": "Based on the [crypto-js](https://github.com/brix/crypto-js) library, StatiCrypt uses AES-256 to encrypt your input with your passphrase and put it in a HTML file with a password prompt that can decrypted in-browser (client side).",
"main": "index.js",
"files": [
@ -12,6 +12,7 @@
},
"dependencies": {
"crypto-js": "3.1.9-1",
"dotenv": "^16.0.3",
"yargs": ">=10.0.3"
},
"author": "Robin Moisson (https://github.com/robinmoisson)",
@ -19,7 +20,6 @@
"Aaron Coplan (https://github.com/AaronCoplan)"
],
"license": "MIT",
"devDependencies": {},
"scripts": {
"build": "bash ./scripts/build.sh",
"test": "echo \"Error: no test specified\" && exit 1"