@node-cli/secret 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Arno Versini
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # Node CLI secret package
2
+
3
+ ![npm](https://img.shields.io/npm/v/@node-cli/secret?label=version&logo=npm)
4
+
5
+ > Secret is a command line tool that can encode or decode a file with a password.
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ > npm install --global @node-cli/secret
11
+ ```
12
+
13
+ ## Examples
14
+
15
+ NOTE: The password is not stored anywhere, it is only used to encrypt or decrypt the file and will be prompted each time.
16
+
17
+ ### Encrypt a file
18
+
19
+ ```sh
20
+ > secret --encrypt README.md README.md.enc
21
+ ```
22
+
23
+ ### Decrypt a file
24
+
25
+ ```sh
26
+ > secret --decrypt README.md.enc README.md
27
+ ```
28
+
29
+ ### Get help
30
+
31
+ ```sh
32
+ > search --help
33
+ ```
34
+
35
+ ## License
36
+
37
+ MIT © Arno Versini
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @file Automatically generated by barrelsby.
3
+ */
4
+
5
+ export * from "./parse";
6
+ export * from "./secret";
7
+ export * from "./utilities";
@@ -0,0 +1,15 @@
1
+ export type Flags = {
2
+ decrypt?: boolean;
3
+ encrypt?: boolean;
4
+ help?: boolean;
5
+ version?: boolean;
6
+ };
7
+ export type Parameters = {
8
+ input?: string;
9
+ output?: string;
10
+ };
11
+ export type Configuration = {
12
+ flags?: Flags;
13
+ parameters?: Parameters;
14
+ };
15
+ export declare const config: Configuration;
package/dist/parse.js ADDED
@@ -0,0 +1,60 @@
1
+ import kleur from "kleur";
2
+ import { parser } from "@node-cli/parser";
3
+ /* istanbul ignore next */ export const config = parser({
4
+ meta: import.meta,
5
+ examples: [
6
+ {
7
+ command: 'secret -e "my-file.txt" "my-file.txt.enc"',
8
+ comment: '## Encrypt the file "my-file.txt" and save the result in\n ## "my-file.txt.enc" - password will be prompted'
9
+ },
10
+ {
11
+ command: 'secret -d "my-file.txt.enc" "my-file.txt"',
12
+ comment: `## Decrypt the file "my-file.txt.enc" and save the\n ## result in "my-file.txt" - password will be prompted`
13
+ }
14
+ ],
15
+ flags: {
16
+ decrypt: {
17
+ shortFlag: "d",
18
+ description: `Decrypt a password protected file`,
19
+ type: "boolean"
20
+ },
21
+ encrypt: {
22
+ shortFlag: "e",
23
+ description: `Encrypt a file with a password`,
24
+ type: "boolean"
25
+ },
26
+ help: {
27
+ shortFlag: "h",
28
+ description: "Display help instructions",
29
+ type: "boolean"
30
+ },
31
+ version: {
32
+ shortFlag: "v",
33
+ description: "Output the current version",
34
+ type: "boolean"
35
+ }
36
+ },
37
+ parameters: {
38
+ input: {
39
+ description: "The file to encrypt or decrypt"
40
+ },
41
+ output: {
42
+ description: "The file to create in order to save the result of the encryption or decryption"
43
+ }
44
+ },
45
+ restrictions: [
46
+ {
47
+ exit: 1,
48
+ message: ()=>kleur.red(`\nError: one of --encrypt or --decrypt option must be provided.`),
49
+ test: (x)=>x.encrypt === false && x.decrypt === false
50
+ },
51
+ {
52
+ exit: 1,
53
+ message: ()=>kleur.red(`\nError: either --encrypt or --decrypt option must be provided, but not both.`),
54
+ test: (x)=>x.encrypt === true && x.decrypt === true
55
+ }
56
+ ],
57
+ usage: "secret [options] [input] [output]"
58
+ });
59
+
60
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/parse.ts"],"sourcesContent":["import kleur from \"kleur\";\nimport { parser } from \"@node-cli/parser\";\n\nexport type Flags = {\n\tdecrypt?: boolean;\n\tencrypt?: boolean;\n\thelp?: boolean;\n\tversion?: boolean;\n};\n\nexport type Parameters = {\n\tinput?: string;\n\toutput?: string;\n};\n\nexport type Configuration = {\n\tflags?: Flags;\n\tparameters?: Parameters;\n};\n\n/* istanbul ignore next */\nexport const config: Configuration = parser({\n\tmeta: import.meta,\n\texamples: [\n\t\t{\n\t\t\tcommand: 'secret -e \"my-file.txt\" \"my-file.txt.enc\"',\n\t\t\tcomment:\n\t\t\t\t'## Encrypt the file \"my-file.txt\" and save the result in\\n ## \"my-file.txt.enc\" - password will be prompted',\n\t\t},\n\t\t{\n\t\t\tcommand: 'secret -d \"my-file.txt.enc\" \"my-file.txt\"',\n\t\t\tcomment: `## Decrypt the file \"my-file.txt.enc\" and save the\\n ## result in \"my-file.txt\" - password will be prompted`,\n\t\t},\n\t],\n\tflags: {\n\t\tdecrypt: {\n\t\t\tshortFlag: \"d\",\n\t\t\tdescription: `Decrypt a password protected file`,\n\t\t\ttype: \"boolean\",\n\t\t},\n\t\tencrypt: {\n\t\t\tshortFlag: \"e\",\n\t\t\tdescription: `Encrypt a file with a password`,\n\t\t\ttype: \"boolean\",\n\t\t},\n\t\thelp: {\n\t\t\tshortFlag: \"h\",\n\t\t\tdescription: \"Display help instructions\",\n\t\t\ttype: \"boolean\",\n\t\t},\n\t\tversion: {\n\t\t\tshortFlag: \"v\",\n\t\t\tdescription: \"Output the current version\",\n\t\t\ttype: \"boolean\",\n\t\t},\n\t},\n\tparameters: {\n\t\tinput: {\n\t\t\tdescription: \"The file to encrypt or decrypt\",\n\t\t},\n\t\toutput: {\n\t\t\tdescription:\n\t\t\t\t\"The file to create in order to save the result of the encryption or decryption\",\n\t\t},\n\t},\n\trestrictions: [\n\t\t{\n\t\t\texit: 1,\n\t\t\tmessage: () =>\n\t\t\t\tkleur.red(\n\t\t\t\t\t`\\nError: one of --encrypt or --decrypt option must be provided.`\n\t\t\t\t),\n\t\t\ttest: (x: { encrypt: boolean; decrypt: boolean }) =>\n\t\t\t\tx.encrypt === false && x.decrypt === false,\n\t\t},\n\t\t{\n\t\t\texit: 1,\n\t\t\tmessage: () =>\n\t\t\t\tkleur.red(\n\t\t\t\t\t`\\nError: either --encrypt or --decrypt option must be provided, but not both.`\n\t\t\t\t),\n\t\t\ttest: (x: { encrypt: boolean; decrypt: boolean }) =>\n\t\t\t\tx.encrypt === true && x.decrypt === true,\n\t\t},\n\t],\n\n\tusage: \"secret [options] [input] [output]\",\n});\n"],"names":["kleur","parser","config","meta","examples","command","comment","flags","decrypt","shortFlag","description","type","encrypt","help","version","parameters","input","output","restrictions","exit","message","red","test","x","usage"],"mappings":"AAAA,OAAOA,WAAW,QAAQ;AAC1B,SAASC,MAAM,QAAQ,mBAAmB;AAmB1C,wBAAwB,GACxB,OAAO,MAAMC,SAAwBD,OAAO;IAC3CE,MAAM;IACNC,UAAU;QACT;YACCC,SAAS;YACTC,SACC;QACF;QACA;YACCD,SAAS;YACTC,SAAS,CAAC,8GAA8G,CAAC;QAC1H;KACA;IACDC,OAAO;QACNC,SAAS;YACRC,WAAW;YACXC,aAAa,CAAC,iCAAiC,CAAC;YAChDC,MAAM;QACP;QACAC,SAAS;YACRH,WAAW;YACXC,aAAa,CAAC,8BAA8B,CAAC;YAC7CC,MAAM;QACP;QACAE,MAAM;YACLJ,WAAW;YACXC,aAAa;YACbC,MAAM;QACP;QACAG,SAAS;YACRL,WAAW;YACXC,aAAa;YACbC,MAAM;QACP;IACD;IACAI,YAAY;QACXC,OAAO;YACNN,aAAa;QACd;QACAO,QAAQ;YACPP,aACC;QACF;IACD;IACAQ,cAAc;QACb;YACCC,MAAM;YACNC,SAAS,IACRpB,MAAMqB,IACL,CAAC,+DAA+D,CAAC;YAEnEC,MAAM,CAACC,IACNA,EAAEX,YAAY,SAASW,EAAEf,YAAY;QACvC;QACA;YACCW,MAAM;YACNC,SAAS,IACRpB,MAAMqB,IACL,CAAC,6EAA6E,CAAC;YAEjFC,MAAM,CAACC,IACNA,EAAEX,YAAY,QAAQW,EAAEf,YAAY;QACtC;KACA;IAEDgB,OAAO;AACR,GAAG"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/secret.js ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+ /* istanbul ignore file */ import { displayConfirmation, displayPromptWithPassword, logger, processFileWithPassword, shouldContinue } from "./utilities.js";
3
+ import { config } from "./parse.js";
4
+ import fs from "fs-extra";
5
+ import path from "node:path";
6
+ const ENCRYPT = "encrypt";
7
+ const DECRYPT = "decrypt";
8
+ /**
9
+ * Caching the "action" for future usage (encrypt or decrypt).
10
+ */ const actionName = config.flags.encrypt ? ENCRYPT : DECRYPT;
11
+ /**
12
+ * Extracting the input and output files.
13
+ */ let inputFile, outputFile, outputFileExists = false;
14
+ if (Object.entries(config.parameters).length > 0) {
15
+ inputFile = config.parameters["0"];
16
+ outputFile = config.parameters["1"];
17
+ if (!fs.existsSync(inputFile)) {
18
+ logger.printErrorsAndExit([
19
+ `File "${inputFile}" does not exist!`
20
+ ], 1);
21
+ }
22
+ if (fs.existsSync(outputFile)) {
23
+ outputFileExists = true;
24
+ }
25
+ }
26
+ if (outputFileExists) {
27
+ const goodToGo = await displayConfirmation(`The file ${outputFile} already exists, overwrite it?`);
28
+ shouldContinue(goodToGo);
29
+ }
30
+ const password = await displayPromptWithPassword(`Enter password to ${actionName} the file`);
31
+ try {
32
+ await processFileWithPassword({
33
+ encode: config.flags.encrypt,
34
+ input: inputFile,
35
+ output: outputFile,
36
+ password
37
+ });
38
+ logger.log();
39
+ logger.info(`File ${path.basename(inputFile)} was ${actionName}ed.`);
40
+ if (outputFileExists) {
41
+ logger.info(`The result was saved in the file ${outputFile}`);
42
+ }
43
+ } catch (error) {
44
+ logger.error(error);
45
+ }
46
+
47
+ //# sourceMappingURL=secret.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/secret.ts"],"sourcesContent":["#!/usr/bin/env node\n/* istanbul ignore file */\n\nimport {\n\tdisplayConfirmation,\n\tdisplayPromptWithPassword,\n\tlogger,\n\tprocessFileWithPassword,\n\tshouldContinue,\n} from \"./utilities.js\";\n\nimport { config } from \"./parse.js\";\nimport fs from \"fs-extra\";\nimport path from \"node:path\";\n\nconst ENCRYPT = \"encrypt\";\nconst DECRYPT = \"decrypt\";\n\n/**\n * Caching the \"action\" for future usage (encrypt or decrypt).\n */\nconst actionName = config.flags.encrypt ? ENCRYPT : DECRYPT;\n\n/**\n * Extracting the input and output files.\n */\nlet inputFile: string,\n\toutputFile: string,\n\toutputFileExists: boolean = false;\nif (Object.entries(config.parameters).length > 0) {\n\tinputFile = config.parameters[\"0\"];\n\toutputFile = config.parameters[\"1\"];\n\tif (!fs.existsSync(inputFile)) {\n\t\tlogger.printErrorsAndExit([`File \"${inputFile}\" does not exist!`], 1);\n\t}\n\tif (fs.existsSync(outputFile)) {\n\t\toutputFileExists = true;\n\t}\n}\n\nif (outputFileExists) {\n\tconst goodToGo = await displayConfirmation(\n\t\t`The file ${outputFile} already exists, overwrite it?`\n\t);\n\tshouldContinue(goodToGo);\n}\n\nconst password = await displayPromptWithPassword(\n\t`Enter password to ${actionName} the file`\n);\n\ntry {\n\tawait processFileWithPassword({\n\t\tencode: config.flags.encrypt,\n\t\tinput: inputFile,\n\t\toutput: outputFile,\n\t\tpassword,\n\t});\n\tlogger.log();\n\tlogger.info(`File ${path.basename(inputFile)} was ${actionName}ed.`);\n\tif (outputFileExists) {\n\t\tlogger.info(`The result was saved in the file ${outputFile}`);\n\t}\n} catch (error) {\n\tlogger.error(error);\n}\n"],"names":["displayConfirmation","displayPromptWithPassword","logger","processFileWithPassword","shouldContinue","config","fs","path","ENCRYPT","DECRYPT","actionName","flags","encrypt","inputFile","outputFile","outputFileExists","Object","entries","parameters","length","existsSync","printErrorsAndExit","goodToGo","password","encode","input","output","log","info","basename","error"],"mappings":";AACA,wBAAwB,GAExB,SACCA,mBAAmB,EACnBC,yBAAyB,EACzBC,MAAM,EACNC,uBAAuB,EACvBC,cAAc,QACR,iBAAiB;AAExB,SAASC,MAAM,QAAQ,aAAa;AACpC,OAAOC,QAAQ,WAAW;AAC1B,OAAOC,UAAU,YAAY;AAE7B,MAAMC,UAAU;AAChB,MAAMC,UAAU;AAEhB;;CAEC,GACD,MAAMC,aAAaL,OAAOM,MAAMC,UAAUJ,UAAUC;AAEpD;;CAEC,GACD,IAAII,WACHC,YACAC,mBAA4B;AAC7B,IAAIC,OAAOC,QAAQZ,OAAOa,YAAYC,SAAS,GAAG;IACjDN,YAAYR,OAAOa,UAAU,CAAC,IAAI;IAClCJ,aAAaT,OAAOa,UAAU,CAAC,IAAI;IACnC,IAAI,CAACZ,GAAGc,WAAWP,YAAY;QAC9BX,OAAOmB,mBAAmB;YAAC,CAAC,MAAM,EAAER,UAAU,iBAAiB,CAAC;SAAC,EAAE;IACpE;IACA,IAAIP,GAAGc,WAAWN,aAAa;QAC9BC,mBAAmB;IACpB;AACD;AAEA,IAAIA,kBAAkB;IACrB,MAAMO,WAAW,MAAMtB,oBACtB,CAAC,SAAS,EAAEc,WAAW,8BAA8B,CAAC;IAEvDV,eAAekB;AAChB;AAEA,MAAMC,WAAW,MAAMtB,0BACtB,CAAC,kBAAkB,EAAES,WAAW,SAAS,CAAC;AAG3C,IAAI;IACH,MAAMP,wBAAwB;QAC7BqB,QAAQnB,OAAOM,MAAMC;QACrBa,OAAOZ;QACPa,QAAQZ;QACRS;IACD;IACArB,OAAOyB;IACPzB,OAAO0B,KAAK,CAAC,KAAK,EAAErB,KAAKsB,SAAShB,WAAW,KAAK,EAAEH,WAAW,GAAG,CAAC;IACnE,IAAIK,kBAAkB;QACrBb,OAAO0B,KAAK,CAAC,iCAAiC,EAAEd,WAAW,CAAC;IAC7D;AACD,EAAE,OAAOgB,OAAO;IACf5B,OAAO4B,MAAMA;AACd"}
@@ -0,0 +1,53 @@
1
+ import { Logger } from "@node-cli/logger";
2
+ export declare const logger: Logger;
3
+ /**
4
+ * Create an hexadecimal hash from a given string. The default
5
+ * algorithm is md5 but it can be changed to anything that
6
+ * crypto.createHash allows.
7
+ * @param {String} string the string to hash
8
+ * @param {String} [algorithm='md5'] the algorithm to use or hashing
9
+ * @return {String} the hashed string in hexa format
10
+ */
11
+ export declare const createHash: (string: string, algorithm?: string) => string;
12
+ /**
13
+ * Encrypts a string or a buffer using AES-256-CTR
14
+ * algorithm.
15
+ * @param {String} password a unique password
16
+ * @param {String} data a string to encrypt
17
+ * @return {String} the encrypted data in hexa
18
+ * encoding, followed by a dollar sign ($) and by a
19
+ * unique random initialization vector.
20
+ */
21
+ export declare const encrypt: (password: string, data: string) => string;
22
+ /**
23
+ * Decrypts a string that was encrypted using the
24
+ * AES-256-CRT algorithm via `encrypt`. It expects
25
+ * the encrypted string to have the corresponding
26
+ * initialization vector appended at the end, after
27
+ * a dollar sign ($) - which was done via the
28
+ * corresponding `encrypt` method.
29
+ * @param {String} password a unique password
30
+ * @param {String} data a string to decrypt
31
+ * @return {String} the decrypted data
32
+ */
33
+ export declare const decrypt: (password: string, data: string) => string;
34
+ /**
35
+ * Process a file with a given password. The file can be
36
+ * encoded or decoded depending on the `encode` flag.
37
+ * @param {Boolean} encode whether to encode or decode the file
38
+ * @param {String} input the input file path
39
+ * @param {String} [output] the output file path
40
+ * @param {String} password the password to use
41
+ * @return {Promise} a promise that resolves when
42
+ * the file has been processed.
43
+ */
44
+ export type ProcessFileOptions = {
45
+ encode: boolean;
46
+ input: string;
47
+ output?: string;
48
+ password: string;
49
+ };
50
+ export declare const processFileWithPassword: (options: ProcessFileOptions) => Promise<void>;
51
+ export declare const displayConfirmation: (message: string) => Promise<any>;
52
+ export declare const displayPromptWithPassword: (message: string) => Promise<any>;
53
+ export declare const shouldContinue: (goodToGo: boolean) => boolean;
@@ -0,0 +1,116 @@
1
+ import { Logger } from "@node-cli/logger";
2
+ import crypto from "node:crypto";
3
+ import fs from "fs-extra";
4
+ import inquirer from "inquirer";
5
+ export const logger = new Logger({
6
+ boring: process.env.NODE_ENV === "test"
7
+ });
8
+ const HEX = "hex";
9
+ const UTF8 = "utf8";
10
+ const DEFAULT_CRYPTO_ALGO = "aes-256-ctr";
11
+ const DEFAULT_HASH_ALGO = "md5";
12
+ const DEFAULT_BYTES_FOR_IV = 16;
13
+ const DEFAULT_FILE_ENCODING = UTF8;
14
+ /**
15
+ * Create an hexadecimal hash from a given string. The default
16
+ * algorithm is md5 but it can be changed to anything that
17
+ * crypto.createHash allows.
18
+ * @param {String} string the string to hash
19
+ * @param {String} [algorithm='md5'] the algorithm to use or hashing
20
+ * @return {String} the hashed string in hexa format
21
+ */ export const createHash = (string, algorithm = DEFAULT_HASH_ALGO)=>{
22
+ return crypto.createHash(algorithm).update(string, UTF8).digest(HEX);
23
+ };
24
+ /**
25
+ * Encrypts a string or a buffer using AES-256-CTR
26
+ * algorithm.
27
+ * @param {String} password a unique password
28
+ * @param {String} data a string to encrypt
29
+ * @return {String} the encrypted data in hexa
30
+ * encoding, followed by a dollar sign ($) and by a
31
+ * unique random initialization vector.
32
+ */ export const encrypt = (password, data)=>{
33
+ // Ensure that the initialization vector (IV) is random.
34
+ const iv = crypto.randomBytes(DEFAULT_BYTES_FOR_IV);
35
+ // Hash the given password (result is always the same).
36
+ const key = createHash(password);
37
+ // Create a cipher.
38
+ const cipher = crypto.createCipheriv(DEFAULT_CRYPTO_ALGO, key, iv);
39
+ // Encrypt the data using the newly created cipher.
40
+ const encrypted = cipher.update(data, UTF8, HEX) + cipher.final(HEX);
41
+ /*
42
+ * Append the IV at the end of the encrypted data
43
+ * to reuse it for decryption (IV is not a key,
44
+ * it can be public).
45
+ */ return `${encrypted}$${iv.toString(HEX)}`;
46
+ };
47
+ /**
48
+ * Decrypts a string that was encrypted using the
49
+ * AES-256-CRT algorithm via `encrypt`. It expects
50
+ * the encrypted string to have the corresponding
51
+ * initialization vector appended at the end, after
52
+ * a dollar sign ($) - which was done via the
53
+ * corresponding `encrypt` method.
54
+ * @param {String} password a unique password
55
+ * @param {String} data a string to decrypt
56
+ * @return {String} the decrypted data
57
+ */ export const decrypt = (password, data)=>{
58
+ // Extract encrypted data and initialization vector (IV).
59
+ const [encrypted, ivHex] = data.split("$");
60
+ // Create a buffer out of the raw hex IV
61
+ const iv = Buffer.from(ivHex, HEX);
62
+ // Hash the given password (result is always the same).
63
+ const hash = createHash(password);
64
+ // Create a cipher.
65
+ const decipher = crypto.createDecipheriv(DEFAULT_CRYPTO_ALGO, hash, iv);
66
+ // Return the decrypted data using the newly created cipher.
67
+ return decipher.update(encrypted, HEX, UTF8) + decipher.final("utf8");
68
+ };
69
+ export const processFileWithPassword = async (options)=>{
70
+ const { encode , input , output , password } = options;
71
+ const fileProcessor = encode ? encrypt : decrypt;
72
+ const data = await fs.readFile(input, DEFAULT_FILE_ENCODING);
73
+ if (output) {
74
+ // Save data to output file
75
+ await fs.outputFile(output, fileProcessor(password, data));
76
+ } else {
77
+ // Print to stdout directly
78
+ logger.log(fileProcessor(password, data));
79
+ }
80
+ };
81
+ /* istanbul ignore next */ export const displayConfirmation = async (message)=>{
82
+ const questions = {
83
+ default: true,
84
+ message: message || "Do you want to continue?",
85
+ name: "goodToGo",
86
+ type: "confirm"
87
+ };
88
+ logger.log();
89
+ const answers = await inquirer.prompt(questions);
90
+ return answers.goodToGo;
91
+ };
92
+ /* istanbul ignore next */ export const displayPromptWithPassword = async (message)=>{
93
+ const questions = {
94
+ message: message,
95
+ name: "password",
96
+ type: "password",
97
+ validate (value) {
98
+ if (!value) {
99
+ return "Password cannot be empty...";
100
+ }
101
+ return true;
102
+ }
103
+ };
104
+ const answers = await inquirer.prompt(questions);
105
+ return answers.password;
106
+ };
107
+ /* istanbul ignore next */ export const shouldContinue = (goodToGo)=>{
108
+ if (!goodToGo) {
109
+ logger.log("\nBye then!");
110
+ // eslint-disable-next-line unicorn/no-process-exit
111
+ process.exit(0);
112
+ }
113
+ return true;
114
+ };
115
+
116
+ //# sourceMappingURL=utilities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utilities.ts"],"sourcesContent":["import { Logger } from \"@node-cli/logger\";\nimport crypto from \"node:crypto\";\nimport fs from \"fs-extra\";\nimport inquirer from \"inquirer\";\n\nexport const logger = new Logger({\n\tboring: process.env.NODE_ENV === \"test\",\n});\n\nconst HEX = \"hex\";\nconst UTF8 = \"utf8\";\n\nconst DEFAULT_CRYPTO_ALGO = \"aes-256-ctr\";\nconst DEFAULT_HASH_ALGO = \"md5\";\nconst DEFAULT_BYTES_FOR_IV = 16;\nconst DEFAULT_FILE_ENCODING = UTF8;\n\n/**\n * Create an hexadecimal hash from a given string. The default\n * algorithm is md5 but it can be changed to anything that\n * crypto.createHash allows.\n * @param {String} string the string to hash\n * @param {String} [algorithm='md5'] the algorithm to use or hashing\n * @return {String} the hashed string in hexa format\n */\nexport const createHash = (\n\tstring: string,\n\talgorithm: string = DEFAULT_HASH_ALGO\n): string => {\n\treturn crypto.createHash(algorithm).update(string, UTF8).digest(HEX);\n};\n\n/**\n * Encrypts a string or a buffer using AES-256-CTR\n * algorithm.\n * @param {String} password a unique password\n * @param {String} data a string to encrypt\n * @return {String} the encrypted data in hexa\n * encoding, followed by a dollar sign ($) and by a\n * unique random initialization vector.\n */\nexport const encrypt = (password: string, data: string): string => {\n\t// Ensure that the initialization vector (IV) is random.\n\tconst iv = crypto.randomBytes(DEFAULT_BYTES_FOR_IV);\n\t// Hash the given password (result is always the same).\n\tconst key = createHash(password);\n\t// Create a cipher.\n\tconst cipher = crypto.createCipheriv(DEFAULT_CRYPTO_ALGO, key, iv);\n\t// Encrypt the data using the newly created cipher.\n\tconst encrypted = cipher.update(data, UTF8, HEX) + cipher.final(HEX);\n\t/*\n\t * Append the IV at the end of the encrypted data\n\t * to reuse it for decryption (IV is not a key,\n\t * it can be public).\n\t */\n\treturn `${encrypted}$${iv.toString(HEX)}`;\n};\n\n/**\n * Decrypts a string that was encrypted using the\n * AES-256-CRT algorithm via `encrypt`. It expects\n * the encrypted string to have the corresponding\n * initialization vector appended at the end, after\n * a dollar sign ($) - which was done via the\n * corresponding `encrypt` method.\n * @param {String} password a unique password\n * @param {String} data a string to decrypt\n * @return {String} the decrypted data\n */\nexport const decrypt = (password: string, data: string): string => {\n\t// Extract encrypted data and initialization vector (IV).\n\tconst [encrypted, ivHex] = data.split(\"$\");\n\t// Create a buffer out of the raw hex IV\n\tconst iv = Buffer.from(ivHex, HEX);\n\t// Hash the given password (result is always the same).\n\tconst hash = createHash(password);\n\t// Create a cipher.\n\tconst decipher = crypto.createDecipheriv(DEFAULT_CRYPTO_ALGO, hash, iv);\n\t// Return the decrypted data using the newly created cipher.\n\treturn decipher.update(encrypted, HEX, UTF8) + decipher.final(\"utf8\");\n};\n\n/**\n * Process a file with a given password. The file can be\n * encoded or decoded depending on the `encode` flag.\n * @param {Boolean} encode whether to encode or decode the file\n * @param {String} input the input file path\n * @param {String} [output] the output file path\n * @param {String} password the password to use\n * @return {Promise} a promise that resolves when\n * the file has been processed.\n */\nexport type ProcessFileOptions = {\n\tencode: boolean;\n\tinput: string;\n\toutput?: string;\n\tpassword: string;\n};\nexport const processFileWithPassword = async (\n\toptions: ProcessFileOptions\n): Promise<void> => {\n\tconst { encode, input, output, password } = options;\n\tconst fileProcessor = encode ? encrypt : decrypt;\n\tconst data = await fs.readFile(input, DEFAULT_FILE_ENCODING);\n\n\tif (output) {\n\t\t// Save data to output file\n\t\tawait fs.outputFile(output, fileProcessor(password, data));\n\t} else {\n\t\t// Print to stdout directly\n\t\tlogger.log(fileProcessor(password, data));\n\t}\n};\n\n/* istanbul ignore next */\nexport const displayConfirmation = async (message: string) => {\n\tconst questions = {\n\t\tdefault: true,\n\t\tmessage: message || \"Do you want to continue?\",\n\t\tname: \"goodToGo\",\n\t\ttype: \"confirm\",\n\t};\n\tlogger.log();\n\tconst answers = await inquirer.prompt(questions);\n\treturn answers.goodToGo;\n};\n\n/* istanbul ignore next */\nexport const displayPromptWithPassword = async (message: string) => {\n\tconst questions = {\n\t\tmessage: message,\n\t\tname: \"password\",\n\t\ttype: \"password\",\n\t\tvalidate(value: string) {\n\t\t\tif (!value) {\n\t\t\t\treturn \"Password cannot be empty...\";\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\t};\n\tconst answers = await inquirer.prompt(questions);\n\treturn answers.password;\n};\n\n/* istanbul ignore next */\nexport const shouldContinue = (goodToGo: boolean) => {\n\tif (!goodToGo) {\n\t\tlogger.log(\"\\nBye then!\");\n\t\t// eslint-disable-next-line unicorn/no-process-exit\n\t\tprocess.exit(0);\n\t}\n\treturn true;\n};\n"],"names":["Logger","crypto","fs","inquirer","logger","boring","process","env","NODE_ENV","HEX","UTF8","DEFAULT_CRYPTO_ALGO","DEFAULT_HASH_ALGO","DEFAULT_BYTES_FOR_IV","DEFAULT_FILE_ENCODING","createHash","string","algorithm","update","digest","encrypt","password","data","iv","randomBytes","key","cipher","createCipheriv","encrypted","final","toString","decrypt","ivHex","split","Buffer","from","hash","decipher","createDecipheriv","processFileWithPassword","options","encode","input","output","fileProcessor","readFile","outputFile","log","displayConfirmation","message","questions","default","name","type","answers","prompt","goodToGo","displayPromptWithPassword","validate","value","shouldContinue","exit"],"mappings":"AAAA,SAASA,MAAM,QAAQ,mBAAmB;AAC1C,OAAOC,YAAY,cAAc;AACjC,OAAOC,QAAQ,WAAW;AAC1B,OAAOC,cAAc,WAAW;AAEhC,OAAO,MAAMC,SAAS,IAAIJ,OAAO;IAChCK,QAAQC,QAAQC,IAAIC,aAAa;AAClC,GAAG;AAEH,MAAMC,MAAM;AACZ,MAAMC,OAAO;AAEb,MAAMC,sBAAsB;AAC5B,MAAMC,oBAAoB;AAC1B,MAAMC,uBAAuB;AAC7B,MAAMC,wBAAwBJ;AAE9B;;;;;;;CAOC,GACD,OAAO,MAAMK,aAAa,CACzBC,QACAC,YAAoBL,iBAAiB;IAErC,OAAOX,OAAOc,WAAWE,WAAWC,OAAOF,QAAQN,MAAMS,OAAOV;AACjE,EAAE;AAEF;;;;;;;;CAQC,GACD,OAAO,MAAMW,UAAU,CAACC,UAAkBC;IACzC,wDAAwD;IACxD,MAAMC,KAAKtB,OAAOuB,YAAYX;IAC9B,uDAAuD;IACvD,MAAMY,MAAMV,WAAWM;IACvB,mBAAmB;IACnB,MAAMK,SAASzB,OAAO0B,eAAehB,qBAAqBc,KAAKF;IAC/D,mDAAmD;IACnD,MAAMK,YAAYF,OAAOR,OAAOI,MAAMZ,MAAMD,OAAOiB,OAAOG,MAAMpB;IAChE;;;;EAIC,GACD,OAAO,CAAC,EAAEmB,UAAU,CAAC,EAAEL,GAAGO,SAASrB,KAAK,CAAC;AAC1C,EAAE;AAEF;;;;;;;;;;CAUC,GACD,OAAO,MAAMsB,UAAU,CAACV,UAAkBC;IACzC,yDAAyD;IACzD,MAAM,CAACM,WAAWI,MAAM,GAAGV,KAAKW,MAAM;IACtC,wCAAwC;IACxC,MAAMV,KAAKW,OAAOC,KAAKH,OAAOvB;IAC9B,uDAAuD;IACvD,MAAM2B,OAAOrB,WAAWM;IACxB,mBAAmB;IACnB,MAAMgB,WAAWpC,OAAOqC,iBAAiB3B,qBAAqByB,MAAMb;IACpE,4DAA4D;IAC5D,OAAOc,SAASnB,OAAOU,WAAWnB,KAAKC,QAAQ2B,SAASR,MAAM;AAC/D,EAAE;AAkBF,OAAO,MAAMU,0BAA0B,OACtCC;IAEA,MAAM,EAAEC,OAAM,EAAEC,MAAK,EAAEC,OAAM,EAAEtB,SAAQ,EAAE,GAAGmB;IAC5C,MAAMI,gBAAgBH,SAASrB,UAAUW;IACzC,MAAMT,OAAO,MAAMpB,GAAG2C,SAASH,OAAO5B;IAEtC,IAAI6B,QAAQ;QACX,2BAA2B;QAC3B,MAAMzC,GAAG4C,WAAWH,QAAQC,cAAcvB,UAAUC;IACrD,OAAO;QACN,2BAA2B;QAC3BlB,OAAO2C,IAAIH,cAAcvB,UAAUC;IACpC;AACD,EAAE;AAEF,wBAAwB,GACxB,OAAO,MAAM0B,sBAAsB,OAAOC;IACzC,MAAMC,YAAY;QACjBC,SAAS;QACTF,SAASA,WAAW;QACpBG,MAAM;QACNC,MAAM;IACP;IACAjD,OAAO2C;IACP,MAAMO,UAAU,MAAMnD,SAASoD,OAAOL;IACtC,OAAOI,QAAQE;AAChB,EAAE;AAEF,wBAAwB,GACxB,OAAO,MAAMC,4BAA4B,OAAOR;IAC/C,MAAMC,YAAY;QACjBD,SAASA;QACTG,MAAM;QACNC,MAAM;QACNK,UAASC,KAAa;YACrB,IAAI,CAACA,OAAO;gBACX,OAAO;YACR;YACA,OAAO;QACR;IACD;IACA,MAAML,UAAU,MAAMnD,SAASoD,OAAOL;IACtC,OAAOI,QAAQjC;AAChB,EAAE;AAEF,wBAAwB,GACxB,OAAO,MAAMuC,iBAAiB,CAACJ;IAC9B,IAAI,CAACA,UAAU;QACdpD,OAAO2C,IAAI;QACX,mDAAmD;QACnDzC,QAAQuD,KAAK;IACd;IACA,OAAO;AACR,EAAE"}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@node-cli/secret",
3
+ "version": "1.0.0",
4
+ "license": "MIT",
5
+ "author": "Arno Versini",
6
+ "description": "Secret is a CLI tool that can encode or decode a file with a password",
7
+ "type": "module",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": "./dist/secret.js",
10
+ "bin": {
11
+ "secret": "dist/secret.js"
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "node": ">=16",
17
+ "scripts": {
18
+ "build": "yarn run clean && yarn run build:types && yarn run build:js && yarn run build:barrel",
19
+ "build:barrel": "barrelsby --delete --directory dist --pattern \"**/*.d.ts\" --name \"index.d\"",
20
+ "build:js": "swc --source-maps --out-dir dist src",
21
+ "build:types": "tsc",
22
+ "clean": "rimraf dist types coverage",
23
+ "lint": "prettier --write \"src/*.ts\" && eslint --fix \"src/*.ts\"",
24
+ "test": "cross-env-shell NODE_OPTIONS=--experimental-vm-modules TZ=UTC jest",
25
+ "test:coverage": "npm run test -- --coverage",
26
+ "watch": "swc --watch --out-dir dist src"
27
+ },
28
+ "dependencies": {
29
+ "@node-cli/logger": ">=1.0.0",
30
+ "@node-cli/parser": ">=2.0.0",
31
+ "fs-extra": "11.1.1",
32
+ "inquirer": "9.2.6"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "gitHead": "9c4baafbf51504c8b9ec3c8434f06be73bd2b23f"
38
+ }