Skip to content

Commit

Permalink
handle passing password through env
Browse files Browse the repository at this point in the history
  • Loading branch information
robinmoisson committed Nov 21, 2022
1 parent 786ca7a commit ba24ae4
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 41 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
.vscode/
node_modules
.staticrypt.json
.env
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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]
Expand Down
65 changes: 64 additions & 1 deletion cli/helpers.js
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -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}
*/
Expand All @@ -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;
50 changes: 18 additions & 32 deletions cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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",
Expand Down Expand Up @@ -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;
Expand All @@ -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)) {
Expand All @@ -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;
Expand Down Expand Up @@ -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);
3 changes: 2 additions & 1 deletion example/example_encrypted.html
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@
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,
Expand Down Expand Up @@ -461,6 +461,7 @@
if (!hasDecrypted) {
document.getElementById("staticrypt_loading").classList.add("hidden");
document.getElementById("staticrypt_content").classList.remove("hidden");
document.getElementById("staticrypt-password").focus();
}
}

Expand Down
1 change: 1 addition & 0 deletions lib/password_template.html
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}

Expand Down
21 changes: 17 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -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": [
Expand All @@ -12,14 +12,14 @@
},
"dependencies": {
"crypto-js": "3.1.9-1",
"dotenv": "^16.0.3",
"yargs": ">=10.0.3"
},
"author": "Robin Moisson (https://github.com/robinmoisson)",
"contributors": [
"Aaron Coplan (https://github.com/AaronCoplan)"
],
"license": "MIT",
"devDependencies": {},
"scripts": {
"build": "bash ./scripts/build.sh",
"test": "echo \"Error: no test specified\" && exit 1"
Expand Down

0 comments on commit ba24ae4

Please sign in to comment.