kopia lustrzana https://github.com/robinmoisson/staticrypt
add support for decrypting in the CLI (closes #112)
rodzic
3ca758eb0a
commit
5d99ce92ba
|
@ -5,3 +5,4 @@ node_modules
|
|||
.env
|
||||
encrypted/
|
||||
!example/encrypted/
|
||||
decrypted/
|
35
README.md
35
README.md
|
@ -29,7 +29,7 @@ You can then run it with `npx staticrypt ...`. You can also install globally wit
|
|||
**Encrypt a file:** encrypt `test.html` and create a `encrypted/test.html` file (use `-d my_directory` to change the output directory):
|
||||
|
||||
```bash
|
||||
staticrypt test.html -p MY_LONG_PASSWORD
|
||||
staticrypt test.html -p <long-password>
|
||||
|
||||
# or do not include the password if you want to be prompted for it:
|
||||
staticrypt test.html
|
||||
|
@ -45,22 +45,34 @@ staticrypt test.html
|
|||
**Encrypt multiple files at once** and put them in a `encrypted/` directory:
|
||||
|
||||
```bash
|
||||
# this will encrypt test_A.html, test_B.html and all files in the test/ directory
|
||||
staticrypt test_A.html test_B.html test/* -p MY_LONG_PASSWORD
|
||||
# => encrypted files are in encrypted/test_A.html, encrypted/test_B.html, encrypted/test/...
|
||||
# this will encrypt test_A.html and test_B.html
|
||||
staticrypt test_A.html test_B.html -p <long-password>
|
||||
# => encrypted files are in encrypted/test_A.html and encrypted/test_B.html
|
||||
|
||||
# you can also use the -r flag to recursively encrypt all files in a directory
|
||||
staticrypt dir_to_encrypt -p MY_LONG_PASSWORD -r
|
||||
staticrypt dir_to_encrypt -p <long-password> -r
|
||||
# => encrypted files are in encrypted/dir_to_encrypt/...
|
||||
|
||||
# if you don't want to include the directory name in the output path, you can use dir_to_encrypt/* instead
|
||||
staticrypt dir_to_encrypt/* -p <long-password> -r
|
||||
# => encrypted files are in encrypted/...
|
||||
```
|
||||
|
||||
**Encrypt a file and get a shareable link containing the hashed password** - you can include your file URL or leave blank:
|
||||
|
||||
```bash
|
||||
# you can also pass '--share' without specifying the URL to get the `#staticrypt_pwd=...`
|
||||
staticrypt test.html -p MY_LONG_PASSWORD --share https://example.com/encrypted.html
|
||||
staticrypt test.html -p <long-password> --share https://example.com/encrypted.html
|
||||
# => https://example.com/encrypted.html#staticrypt_pwd=5bfbf1343c7257cd7be23ecd74bb37fa2c76d041042654f358b6255baeab898f
|
||||
```
|
||||
|
||||
**Decrypt files you encrypted earlier** straight from the CLI by including the `--decrypt` flag. The `-r|--recursive` flag and output `-d|--directory` option work the same way as when encrypting (default name for the output directory is `decrypted`):
|
||||
|
||||
```bash
|
||||
staticrypt encrypted/test.html -p <long-password> --decrypt
|
||||
# => decrypted file is in decrypted/test.html
|
||||
```
|
||||
|
||||
**Pin the salt to use staticrypt in your CI in a build step** - if you want want the "Remember-me" or share features to work accross multiple pages or multiple successive deployment, the salt needs to stay the same ([see why](https://github.com/robinmoisson/staticrypt#why-does-staticrypt-create-a-config-file)). If you run StatiCrypt in a CI step, you can pin the salt in two ways:
|
||||
|
||||
```bash
|
||||
|
@ -69,7 +81,7 @@ staticrypt test.html -p MY_LONG_PASSWORD --share https://example.com/encrypted.h
|
|||
staticrypt --salt
|
||||
|
||||
# or hardcode the salt in the CI script command:
|
||||
staticrypt test.html -p MY_LONG_PASSWORD --salt 12345678901234567890123456789012
|
||||
staticrypt test.html -p <long-password> --salt 12345678901234567890123456789012
|
||||
```
|
||||
|
||||
### CLI Reference
|
||||
|
@ -83,9 +95,12 @@ The password argument is optional if `STATICRYPT_PASSWORD` is set in the environ
|
|||
--version Show version number [boolean]
|
||||
-c, --config Path to the config file. Set to "false" to
|
||||
disable.[string] [default: ".staticrypt.json"]
|
||||
-d, --directory Name of the directory where the encrypted
|
||||
files will be saved.
|
||||
[string] [default: "encrypted/"]
|
||||
-d, --directory Name of the directory where the generated
|
||||
files will be saved. If the '--decrypt' flag
|
||||
is set, default will be 'decrypted'.
|
||||
[string] [default: "encrypted"]
|
||||
--decrypt Include this flag to decrypt files instead of
|
||||
encrypt. [boolean] [default: false]
|
||||
-p, --password The password to encrypt your file with. Leave
|
||||
empty to be prompted for it. If
|
||||
STATICRYPT_PASSWORD is set in the env, we'll
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const path = require("path");
|
||||
const pathModule = require("path");
|
||||
const fs = require("fs");
|
||||
const readline = require("readline");
|
||||
|
||||
|
@ -6,7 +6,9 @@ const { generateRandomSalt, generateRandomString } = require("../lib/cryptoEngin
|
|||
const { renderTemplate } = require("../lib/formater.js");
|
||||
const Yargs = require("yargs");
|
||||
|
||||
const PASSWORD_TEMPLATE_DEFAULT_PATH = path.join(__dirname, "..", "lib", "password_template.html");
|
||||
const PASSWORD_TEMPLATE_DEFAULT_PATH = pathModule.join(__dirname, "..", "lib", "password_template.html");
|
||||
const OUTPUT_DIRECTORY_DEFAULT_PATH = "encrypted";
|
||||
exports.OUTPUT_DIRECTORY_DEFAULT_PATH = OUTPUT_DIRECTORY_DEFAULT_PATH;
|
||||
|
||||
/**
|
||||
* @param {string} message
|
||||
|
@ -71,16 +73,19 @@ function prompt(question) {
|
|||
});
|
||||
}
|
||||
|
||||
async function getValidatedPassword(passwordArgument, isShortAllowed) {
|
||||
const password = await getPassword(passwordArgument);
|
||||
|
||||
/**
|
||||
* @param {string} password
|
||||
* @param {boolean} isShortAllowed
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function validatePassword(password, isShortAllowed) {
|
||||
if (password.length < 14 && !isShortAllowed) {
|
||||
const shouldUseShort = await prompt(
|
||||
`WARNING: Your password is less than 14 characters (length: ${password.length})` +
|
||||
" and it's easy to try brute-forcing on public files. For better security we recommend using a longer one, for example: " +
|
||||
" and it's easy to try brute-forcing on public files, so we recommend using a longer one. Here's a generated one: " +
|
||||
generateRandomString(21) +
|
||||
"\nYou can hide this warning by increasing your password length or adding the '--short' flag." +
|
||||
" Do you want to use the short password? [y/N] "
|
||||
"\nDo you want to still want to use the shorter password? [y/N] "
|
||||
);
|
||||
|
||||
if (!shouldUseShort.match(/^\s*(y|yes)\s*$/i)) {
|
||||
|
@ -88,10 +93,8 @@ async function getValidatedPassword(passwordArgument, isShortAllowed) {
|
|||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
return password;
|
||||
}
|
||||
exports.getValidatedPassword = getValidatedPassword;
|
||||
exports.validatePassword = validatePassword;
|
||||
|
||||
/**
|
||||
* Get the config from the config file.
|
||||
|
@ -137,6 +140,7 @@ async function getPassword(passwordArgument) {
|
|||
// prompt the user for their password
|
||||
return prompt("Enter your long, unusual password: ");
|
||||
}
|
||||
exports.getPassword = getPassword;
|
||||
|
||||
/**
|
||||
* @param {string} filepath
|
||||
|
@ -200,8 +204,8 @@ function getSalt(namedArgs, config) {
|
|||
* @param {string} modulePath - path from staticrypt root directory
|
||||
*/
|
||||
function convertCommonJSToBrowserJS(modulePath) {
|
||||
const rootDirectory = path.join(__dirname, "..");
|
||||
const resolvedPath = path.join(rootDirectory, ...modulePath.split("/")) + ".js";
|
||||
const rootDirectory = pathModule.join(__dirname, "..");
|
||||
const resolvedPath = pathModule.join(rootDirectory, ...modulePath.split("/")) + ".js";
|
||||
|
||||
if (!fs.existsSync(resolvedPath)) {
|
||||
exitWithError(`could not find module to convert at path "${resolvedPath}"`);
|
||||
|
@ -262,20 +266,29 @@ function genFile(data, outputFilePath, templateFilePath) {
|
|||
|
||||
const renderedTemplate = renderTemplate(templateContents, data);
|
||||
|
||||
writeFile(outputFilePath, renderedTemplate);
|
||||
}
|
||||
exports.genFile = genFile;
|
||||
|
||||
/**
|
||||
* @param {string} filePath
|
||||
* @param {string} contents
|
||||
*/
|
||||
function writeFile(filePath, contents) {
|
||||
// create output directory if it does not exist
|
||||
const dirname = path.dirname(outputFilePath);
|
||||
const dirname = pathModule.dirname(filePath);
|
||||
if (!fs.existsSync(dirname)) {
|
||||
fs.mkdirSync(dirname, { recursive: true });
|
||||
}
|
||||
|
||||
try {
|
||||
fs.writeFileSync(outputFilePath, renderedTemplate);
|
||||
fs.writeFileSync(filePath, contents);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
exitWithError("could not generate output file");
|
||||
exitWithError(`could not write file at path "${filePath}"`);
|
||||
}
|
||||
}
|
||||
exports.genFile = genFile;
|
||||
exports.writeFile = writeFile;
|
||||
|
||||
/**
|
||||
* @param {string} templatePathParameter
|
||||
|
@ -287,6 +300,28 @@ function isCustomPasswordTemplateDefault(templatePathParameter) {
|
|||
}
|
||||
exports.isCustomPasswordTemplateDefault = isCustomPasswordTemplateDefault;
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @param {string} rootDirectory
|
||||
* @param {(fullPath: string, rootDirectoryFromArgument: string) => void} callback
|
||||
*/
|
||||
function recursivelyApplyCallbackToFiles(callback, path, rootDirectory = "") {
|
||||
const fullPath = pathModule.resolve(path);
|
||||
const fullRootDirectory = rootDirectory || pathModule.dirname(fullPath);
|
||||
|
||||
if (fs.statSync(fullPath).isDirectory()) {
|
||||
fs.readdirSync(fullPath).forEach((filePath) => {
|
||||
const fullFilePath = `${fullPath}/${filePath}`;
|
||||
|
||||
recursivelyApplyCallbackToFiles(callback, fullFilePath, fullRootDirectory);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
callback(fullPath, fullRootDirectory);
|
||||
}
|
||||
exports.recursivelyApplyCallbackToFiles = recursivelyApplyCallbackToFiles;
|
||||
|
||||
function parseCommandLineArguments() {
|
||||
return (
|
||||
Yargs.usage("Usage: staticrypt <filename> [<filename> ...] [options]")
|
||||
|
@ -299,8 +334,15 @@ function parseCommandLineArguments() {
|
|||
.option("d", {
|
||||
alias: "directory",
|
||||
type: "string",
|
||||
describe: "Name of the directory where the encrypted files will be saved.",
|
||||
default: "encrypted/",
|
||||
describe:
|
||||
"Name of the directory where the generated files will be saved. If the '--decrypt' flag is " +
|
||||
"set, default will be 'decrypted'.",
|
||||
default: OUTPUT_DIRECTORY_DEFAULT_PATH,
|
||||
})
|
||||
.option("decrypt", {
|
||||
type: "boolean",
|
||||
describe: "Include this flag to decrypt files instead of encrypt.",
|
||||
default: false,
|
||||
})
|
||||
.option("p", {
|
||||
alias: "password",
|
||||
|
|
126
cli/index.js
126
cli/index.js
|
@ -12,22 +12,28 @@ if (nodeVersion[0] < 16) {
|
|||
// parse .env file into process.env
|
||||
require("dotenv").config();
|
||||
|
||||
const pathModule = require("path");
|
||||
const fs = require("fs");
|
||||
|
||||
const cryptoEngine = require("../lib/cryptoEngine.js");
|
||||
const codec = require("../lib/codec.js");
|
||||
const { generateRandomSalt } = cryptoEngine;
|
||||
const { encodeWithHashedPassword } = codec.init(cryptoEngine);
|
||||
const { decode, encodeWithHashedPassword } = codec.init(cryptoEngine);
|
||||
const {
|
||||
parseCommandLineArguments,
|
||||
OUTPUT_DIRECTORY_DEFAULT_PATH,
|
||||
buildStaticryptJS,
|
||||
isOptionSetByUser,
|
||||
exitWithError,
|
||||
genFile,
|
||||
getFileContent,
|
||||
getValidatedSalt,
|
||||
getValidatedPassword,
|
||||
getConfig,
|
||||
getFileContent,
|
||||
getPassword,
|
||||
getValidatedSalt,
|
||||
isOptionSetByUser,
|
||||
parseCommandLineArguments,
|
||||
recursivelyApplyCallbackToFiles,
|
||||
validatePassword,
|
||||
writeConfig,
|
||||
writeFile,
|
||||
} = require("./helpers.js");
|
||||
|
||||
// parse arguments
|
||||
|
@ -67,23 +73,49 @@ async function runStatiCrypt() {
|
|||
writeConfig(configPath, config);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// get the salt & password
|
||||
const salt = getValidatedSalt(namedArgs, config);
|
||||
const password = await getValidatedPassword(namedArgs.password, namedArgs.short);
|
||||
const password = await getPassword(namedArgs.password);
|
||||
const hashedPassword = await cryptoEngine.hashPassword(password, salt);
|
||||
|
||||
// display the share link with the hashed password if the --share flag is set
|
||||
if (hasShareFlag) {
|
||||
await validatePassword(password, namedArgs.short);
|
||||
|
||||
const url = namedArgs.share || "";
|
||||
|
||||
const hashedPassword = await cryptoEngine.hashPassword(password, salt);
|
||||
|
||||
console.log(url + "#staticrypt_pwd=" + hashedPassword);
|
||||
process.exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// only process a directory if the --recursive flag is set
|
||||
const directoriesInArguments = positionalArguments.filter((path) => fs.statSync(path).isDirectory());
|
||||
if (directoriesInArguments.length > 0 && !namedArgs.recursive) {
|
||||
exitWithError(
|
||||
`'${directoriesInArguments[0].toString()}' is a directory. Use the -r|--recursive flag to process directories.`
|
||||
);
|
||||
}
|
||||
|
||||
// if asking for decryption, decrypt all the files
|
||||
if (namedArgs.decrypt) {
|
||||
const isOutputDirectoryDefault =
|
||||
namedArgs.directory === OUTPUT_DIRECTORY_DEFAULT_PATH && !isOptionSetByUser("d", yargs);
|
||||
const outputDirectory = isOutputDirectoryDefault ? "decrypted" : namedArgs.directory;
|
||||
|
||||
positionalArguments.forEach((path) => {
|
||||
recursivelyApplyCallbackToFiles((fullPath, fullRootDirectory) => {
|
||||
decodeAndGenerateFile(fullPath, fullRootDirectory, hashedPassword, salt, outputDirectory);
|
||||
}, path);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await validatePassword(password, namedArgs.short);
|
||||
|
||||
// write salt to config file
|
||||
if (config.salt !== salt) {
|
||||
config.salt = salt;
|
||||
|
@ -105,35 +137,55 @@ async function runStatiCrypt() {
|
|||
template_color_secondary: namedArgs.templateColorSecondary,
|
||||
};
|
||||
|
||||
const hashedPassword = await cryptoEngine.hashPassword(password, salt);
|
||||
|
||||
positionalArguments.forEach((path) =>
|
||||
encodeAndGenerateFile(path.toString(), hashedPassword, salt, baseTemplateData, isRememberEnabled, namedArgs)
|
||||
);
|
||||
// encode all the files
|
||||
positionalArguments.forEach((path) => {
|
||||
recursivelyApplyCallbackToFiles((fullPath, fullRootDirectory) => {
|
||||
encodeAndGenerateFile(
|
||||
fullPath,
|
||||
fullRootDirectory,
|
||||
hashedPassword,
|
||||
salt,
|
||||
baseTemplateData,
|
||||
isRememberEnabled,
|
||||
namedArgs
|
||||
);
|
||||
}, path);
|
||||
});
|
||||
}
|
||||
|
||||
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) {
|
||||
console.log(
|
||||
"ERROR: The path '" +
|
||||
path +
|
||||
"' is a directory. Use the -r|--recursive flag to process all files in the directory."
|
||||
);
|
||||
async function decodeAndGenerateFile(path, rootDirectoryFromArguments, hashedPassword, salt, outputDirectory) {
|
||||
// get the file content
|
||||
const encryptedFileContent = getFileContent(path);
|
||||
|
||||
// just return instead of exiting the process, that way all other files can be processed
|
||||
return;
|
||||
}
|
||||
// extract the cipher text from the encrypted file
|
||||
const cipherTextMatch = encryptedFileContent.match(/"staticryptEncryptedMsgUniqueVariableName":\s*"([^"]+)"/);
|
||||
|
||||
fs.readdirSync(path).forEach((filePath) => {
|
||||
const fullPath = `${path}/${filePath}`;
|
||||
|
||||
encodeAndGenerateFile(fullPath, hashedPassword, salt, baseTemplateData, isRememberEnabled, namedArgs);
|
||||
});
|
||||
return;
|
||||
if (!cipherTextMatch) {
|
||||
return console.log(`ERROR: could not extract cipher text from ${path}`);
|
||||
}
|
||||
|
||||
// decrypt input
|
||||
const { success, decoded } = await decode(cipherTextMatch[1], hashedPassword, salt);
|
||||
|
||||
if (!success) {
|
||||
return console.log(`ERROR: could not decrypt ${path}`);
|
||||
}
|
||||
|
||||
const relativePath = pathModule.relative(rootDirectoryFromArguments, path);
|
||||
const outputFilepath = outputDirectory + "/" + relativePath;
|
||||
|
||||
writeFile(outputFilepath, decoded);
|
||||
}
|
||||
|
||||
async function encodeAndGenerateFile(
|
||||
path,
|
||||
rootDirectoryFromArguments,
|
||||
hashedPassword,
|
||||
salt,
|
||||
baseTemplateData,
|
||||
isRememberEnabled,
|
||||
namedArgs
|
||||
) {
|
||||
// get the file content
|
||||
const contents = getFileContent(path);
|
||||
|
||||
|
@ -141,7 +193,7 @@ async function encodeAndGenerateFile(path, hashedPassword, salt, baseTemplateDat
|
|||
const encryptedMsg = await encodeWithHashedPassword(contents, hashedPassword);
|
||||
|
||||
const staticryptConfig = {
|
||||
encryptedMsg,
|
||||
staticryptEncryptedMsgUniqueVariableName: encryptedMsg,
|
||||
isRememberEnabled,
|
||||
rememberDurationInDays: namedArgs.remember,
|
||||
salt,
|
||||
|
@ -151,7 +203,9 @@ async function encodeAndGenerateFile(path, hashedPassword, salt, baseTemplateDat
|
|||
staticrypt_config: staticryptConfig,
|
||||
};
|
||||
|
||||
const outputFilepath = namedArgs.directory.replace(/\/+$/, "") + "/" + path;
|
||||
// remove the base path so that the actual output path is relative to the base path
|
||||
const relativePath = pathModule.relative(rootDirectoryFromArguments, path);
|
||||
const outputFilepath = namedArgs.directory + "/" + relativePath;
|
||||
|
||||
genFile(templateData, outputFilepath, namedArgs.template);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ const decode = codec.init(cryptoEngine).decode;
|
|||
* Initialize the staticrypt module, that exposes functions callbable by the password_template.
|
||||
*
|
||||
* @param {{
|
||||
* encryptedMsg: string,
|
||||
* staticryptEncryptedMsgUniqueVariableName: string,
|
||||
* isRememberEnabled: boolean,
|
||||
* rememberDurationInDays: number,
|
||||
* salt: string,
|
||||
|
@ -29,10 +29,10 @@ function init(staticryptConfig, templateConfig) {
|
|||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async function decryptAndReplaceHtml(hashedPassword) {
|
||||
const { encryptedMsg, salt } = staticryptConfig;
|
||||
const { staticryptEncryptedMsgUniqueVariableName, salt } = staticryptConfig;
|
||||
const { replaceHtmlCallback } = templateConfig;
|
||||
|
||||
const result = await decode(encryptedMsg, hashedPassword, salt);
|
||||
const result = await decode(staticryptEncryptedMsgUniqueVariableName, hashedPassword, salt);
|
||||
if (!result.success) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "staticrypt",
|
||||
"version": "3.2.0",
|
||||
"version": "3.3.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "staticrypt",
|
||||
"version": "3.2.0",
|
||||
"version": "3.3.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dotenv": "^16.0.3",
|
||||
|
@ -15,6 +15,9 @@
|
|||
"bin": {
|
||||
"staticrypt": "cli/index.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^2.8.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
|
@ -104,6 +107,21 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "2.8.7",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz",
|
||||
"integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"prettier": "bin-prettier.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
|
@ -248,6 +266,12 @@
|
|||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
|
||||
},
|
||||
"prettier": {
|
||||
"version": "2.8.7",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz",
|
||||
"integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==",
|
||||
"dev": true
|
||||
},
|
||||
"require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "staticrypt",
|
||||
"version": "3.2.0",
|
||||
"version": "3.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 long password and put it in a HTML file with a password prompt that can decrypted in-browser (client side).",
|
||||
"main": "index.js",
|
||||
"files": [
|
||||
|
@ -43,5 +43,8 @@
|
|||
"bugs": {
|
||||
"url": "https://github.com/robinmoisson/staticrypt/issues"
|
||||
},
|
||||
"homepage": "https://github.com/robinmoisson/staticrypt"
|
||||
"homepage": "https://github.com/robinmoisson/staticrypt",
|
||||
"devDependencies": {
|
||||
"prettier": "^2.8.7"
|
||||
}
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue