yamlock 0.2.3 → 0.2.4

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/README.md CHANGED
@@ -54,6 +54,9 @@ yamlock encrypt config.json --key "$YAMLOCK_KEY" --paths "db.password,api.token"
54
54
  # Inspect CLI metadata
55
55
  yamlock version
56
56
  yamlock algorithms
57
+
58
+ # Generate a random key for YAMLOCK_KEY
59
+ yamlock keygen --length 64 --format base64
57
60
  ```
58
61
 
59
62
  The CLI detects YAML (`.yaml`/`.yml`) and JSON extensions automatically and writes the file back in the same format.
@@ -61,6 +64,7 @@ The CLI detects YAML (`.yaml`/`.yml`) and JSON extensions automatically and writ
61
64
  Options of note:
62
65
  - `--output <file>` writes the result to a separate file instead of overwriting the input.
63
66
  - `--paths <path1,path2>` targets only the specified fields (dot/bracket notation like `db.password` or `users[0].token`).
67
+ - Command `keygen` produces a random key and shows how to store it (shell export or `.env`).
64
68
  - Commands `version` and `algorithms` print the installed CLI version and the list of supported ciphers respectively.
65
69
 
66
70
  ### Node.js API
package/dist/cli/cli.js CHANGED
@@ -3,6 +3,7 @@ import { readFileSync, writeFileSync } from 'node:fs';
3
3
  import { resolve, extname } from 'node:path';
4
4
  import { exit } from 'node:process';
5
5
  import { createRequire } from 'node:module';
6
+ import { randomBytes } from 'node:crypto';
6
7
 
7
8
  import yaml from 'js-yaml';
8
9
 
@@ -12,10 +13,14 @@ import { listSupportedAlgorithms } from '../crypto/utils.js';
12
13
  const require = createRequire(import.meta.url);
13
14
  const packageJson = require('../../package.json');
14
15
 
15
- const HELP_TEXT = `
16
+ const BANNER = `
16
17
  ░█░█░█▀█░█▄░▄█░█░░░█▀█░█▀▀░█░█░
17
18
  ░░█░░█▀█░█░▀░█░█░░░█░█░█░░░█▀▄░
18
- ░░▀░░▀░▀░▀░░░▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░
19
+ ░░▀░░▀░▀░▀░░░▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░`;
20
+
21
+ function getHelpText() {
22
+ return `${BANNER}
23
+ Version: ${packageJson.version}
19
24
 
20
25
  Usage:
21
26
  yamlock <command> [options]
@@ -25,13 +30,17 @@ Commands:
25
30
  decrypt <file> Decrypt string values in the given YAML/JSON file.
26
31
  version Print the yamlock CLI version.
27
32
  algorithms Print the list of supported cipher algorithms.
33
+ keygen Generate a random YAMLOCK_KEY.
28
34
 
29
35
  Options:
30
36
  -k, --key <value> Encryption key (or use YAMLOCK_KEY env).
31
37
  -a, --algorithm <value> Cipher algorithm (default: aes-256-cbc).
32
38
  -o, --output <file> Write the result to a different file (otherwise overwrites the input file).
33
39
  -p, --paths <p1,p2> Comma-separated list of field paths to process (dot/bracket notation).
40
+ --length <bytes> (keygen) Number of random bytes to generate (default: 32).
41
+ --format <hex|base64> (keygen) Output format (default: base64).
34
42
  `;
43
+ }
35
44
 
36
45
  function print(message) {
37
46
  console.log(message);
@@ -87,11 +96,20 @@ function parseArgs(argv) {
87
96
  const args = argv.slice(2);
88
97
  const result = {
89
98
  command: args[0],
90
- file: args[1],
99
+ file: undefined,
91
100
  options: {}
92
101
  };
93
102
 
94
- for (let i = 2; i < args.length; i += 1) {
103
+ let index = 1;
104
+ const potentialFile = args[1];
105
+ if (potentialFile && !potentialFile.startsWith('-')) {
106
+ result.file = potentialFile;
107
+ index = 2;
108
+ } else {
109
+ index = 1;
110
+ }
111
+
112
+ for (let i = index; i < args.length; i += 1) {
95
113
  const arg = args[i];
96
114
  const next = args[i + 1];
97
115
 
@@ -107,17 +125,32 @@ function parseArgs(argv) {
107
125
  } else if (arg === '-p' || arg === '--paths') {
108
126
  result.options.paths = parsePaths(next);
109
127
  i += 1;
128
+ } else if (arg === '--length') {
129
+ result.options.length = next;
130
+ i += 1;
131
+ } else if (arg === '--format') {
132
+ result.options.format = next;
133
+ i += 1;
110
134
  }
111
135
  }
112
136
 
113
137
  return result;
114
138
  }
115
139
 
140
+ function generateRandomKey(length, format) {
141
+ const size = Number.isFinite(length) && length > 0 ? Math.floor(length) : 32;
142
+ const buffer = randomBytes(size);
143
+ if (format === 'hex') {
144
+ return buffer.toString('hex');
145
+ }
146
+ return buffer.toString('base64');
147
+ }
148
+
116
149
  export async function runCli(argv = process.argv) {
117
150
  const { command, file, options } = parseArgs(argv);
118
151
 
119
152
  if (!command) {
120
- print(HELP_TEXT.trim());
153
+ print(getHelpText().trim());
121
154
  return exit(1);
122
155
  }
123
156
 
@@ -133,9 +166,32 @@ export async function runCli(argv = process.argv) {
133
166
  return exit(0);
134
167
  }
135
168
 
169
+ if (command === 'keygen') {
170
+ const desiredLength = options.length ? Number(options.length) : 32;
171
+ const normalizedFormat = (options.format ?? 'base64').toLowerCase();
172
+
173
+ if (!Number.isFinite(desiredLength) || desiredLength <= 0) {
174
+ printError('Key length must be a positive number.');
175
+ return exit(1);
176
+ }
177
+
178
+ if (!['base64', 'hex'].includes(normalizedFormat)) {
179
+ printError('Key format must be either "base64" or "hex".');
180
+ return exit(1);
181
+ }
182
+
183
+ const keyValue = generateRandomKey(desiredLength, normalizedFormat);
184
+ print(`Generated key (${normalizedFormat}, ${Math.floor(desiredLength)} bytes of entropy):`);
185
+ print(keyValue);
186
+ print('\nStore it securely, e.g.');
187
+ print(` export YAMLOCK_KEY="${keyValue}"`);
188
+ print(' # or place in an .env file as YAMLOCK_KEY=your-key');
189
+ return exit(0);
190
+ }
191
+
136
192
  if (!file) {
137
193
  printError('A file path is required for this command.');
138
- print(HELP_TEXT.trim());
194
+ print(getHelpText().trim());
139
195
  return exit(1);
140
196
  }
141
197
 
@@ -184,7 +240,7 @@ export async function runCli(argv = process.argv) {
184
240
  }
185
241
 
186
242
  printError(`Unknown command: ${command}`);
187
- print(HELP_TEXT.trim());
243
+ print(getHelpText().trim());
188
244
  return exit(1);
189
245
  } catch (error) {
190
246
  printError(`Operation failed: ${error.message}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yamlock",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "author": "PAVEL TKACHEV (phoenixweiss) <mail@phoenixweiss.me>",
5
5
  "description": "Value-level encryption for YAML/JSON configuration files with CLI + Node.js APIs.",
6
6
  "license": "MIT",