dotsec 0.1.1 → 0.4.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/dist/esm/cli.js CHANGED
@@ -25,19 +25,15 @@ var __export = (target, all) => {
25
25
  import { hideBin } from "yargs/helpers";
26
26
  import yargs from "yargs/yargs";
27
27
 
28
- // src/commands/decryptSecCommand.ts
29
- var decryptSecCommand_exports = {};
30
- __export(decryptSecCommand_exports, {
28
+ // src/commands/debugCommand.ts
29
+ var debugCommand_exports = {};
30
+ __export(debugCommand_exports, {
31
31
  builder: () => builder,
32
32
  command: () => command,
33
33
  desc: () => desc,
34
34
  handler: () => handler
35
35
  });
36
- import { KMSClient, DecryptCommand } from "@aws-sdk/client-kms";
37
- import { redBright } from "chalk";
38
- import { parse } from "dotenv";
39
- import fs from "node:fs";
40
- import path from "node:path";
36
+ import { GetParametersByPathCommand } from "@aws-sdk/client-ssm";
41
37
 
42
38
  // src/commonCliOptions.ts
43
39
  var commonCliOptions = {
@@ -51,11 +47,30 @@ var commonCliOptions = {
51
47
  },
52
48
  awsKeyAlias: {
53
49
  string: true,
54
- describe: "AWS KMS asymmetric key alias"
50
+ default: "alias/top-secret",
51
+ describe: "AWS KMS key alias"
52
+ },
53
+ awsKeyArn: {
54
+ string: true,
55
+ describe: "AWS KMS key id"
55
56
  },
56
57
  awsKey: {
57
58
  string: true,
58
- describe: "AWS KMS asymmetric key arn"
59
+ describe: "AWS KMS key arn"
60
+ },
61
+ envFile: {
62
+ string: true,
63
+ describe: ".env file",
64
+ default: ".env"
65
+ },
66
+ secFile: {
67
+ string: true,
68
+ describe: ".sec file",
69
+ default: ".sec"
70
+ },
71
+ awsAssumeRoleArn: {
72
+ string: true,
73
+ describe: "arn or role to assume"
59
74
  },
60
75
  verbose: {
61
76
  boolean: true,
@@ -72,12 +87,23 @@ var commonCliOptions = {
72
87
  };
73
88
 
74
89
  // src/utils/getCredentialsProfileRegion.ts
75
- import { fromEnv, fromIni } from "@aws-sdk/credential-providers";
90
+ import {
91
+ fromEnv,
92
+ fromIni,
93
+ fromTemporaryCredentials
94
+ } from "@aws-sdk/credential-providers";
76
95
  import { loadSharedConfigFiles } from "@aws-sdk/shared-ini-file-loader";
77
96
 
78
97
  // src/utils/logger.ts
79
98
  import chalk from "chalk";
80
- var bold = (str) => chalk.yellowBright.bold(str);
99
+ var _logger;
100
+ var getLogger = () => {
101
+ if (!_logger) {
102
+ _logger = console;
103
+ }
104
+ return _logger;
105
+ };
106
+ var bold = (str) => chalk.greenBright.bold(str);
81
107
  var underline = (str) => chalk.cyanBright.bold(str);
82
108
 
83
109
  // src/utils/getCredentialsProfileRegion.ts
@@ -91,12 +117,25 @@ var getCredentialsProfileRegion = async ({
91
117
  let profileAndOrigin = void 0;
92
118
  let regionAndOrigin = void 0;
93
119
  if (argv.profile) {
94
- profileAndOrigin = { value: argv.profile, origin: `command line option: ${bold(argv.profile)}` };
95
- credentialsAndOrigin = { value: await fromIni({ profile: argv.profile })(), origin: `${bold(`[${argv.profile}]`)} in credentials file` };
120
+ profileAndOrigin = {
121
+ value: argv.profile,
122
+ origin: `command line option: ${bold(argv.profile)}`
123
+ };
124
+ credentialsAndOrigin = {
125
+ value: await fromIni({
126
+ profile: argv.profile
127
+ })(),
128
+ origin: `${bold(`[${argv.profile}]`)} in credentials file`
129
+ };
96
130
  } else if (env.AWS_PROFILE) {
97
- profileAndOrigin = { value: env.AWS_PROFILE, origin: `env variable ${bold("AWS_PROFILE")}: ${underline(env.AWS_PROFILE)}` };
131
+ profileAndOrigin = {
132
+ value: env.AWS_PROFILE,
133
+ origin: `env variable ${bold("AWS_PROFILE")}: ${underline(env.AWS_PROFILE)}`
134
+ };
98
135
  credentialsAndOrigin = {
99
- value: await fromIni({ profile: env.AWS_PROFILE })(),
136
+ value: await fromIni({
137
+ profile: env.AWS_PROFILE
138
+ })(),
100
139
  origin: `env variable ${underline("AWS_PROFILE")}: ${bold(env.AWS_PROFILE)}`
101
140
  };
102
141
  } else if (env.AWS_ACCESS_KEY_ID && env.AWS_SECRET_ACCESS_KEY) {
@@ -105,13 +144,27 @@ var getCredentialsProfileRegion = async ({
105
144
  origin: `env variables ${bold("AWS_ACCESS_KEY_ID")} and ${bold("AWS_SECRET_ACCESS_KEY")}`
106
145
  };
107
146
  } else if ((_a = sharedConfigFiles.credentialsFile) == null ? void 0 : _a.default) {
108
- profileAndOrigin = { value: "default", origin: `${bold("[default]")} in credentials file` };
109
- credentialsAndOrigin = { value: await fromIni({ profile: "default" })(), origin: `profile ${bold("[default]")}` };
147
+ profileAndOrigin = {
148
+ value: "default",
149
+ origin: `${bold("[default]")} in credentials file`
150
+ };
151
+ credentialsAndOrigin = {
152
+ value: await fromIni({
153
+ profile: "default"
154
+ })(),
155
+ origin: `profile ${bold("[default]")}`
156
+ };
110
157
  }
111
158
  if (argv.region) {
112
- regionAndOrigin = { value: argv.region, origin: `command line option: ${bold(argv.region)}` };
159
+ regionAndOrigin = {
160
+ value: argv.region,
161
+ origin: `command line option: ${bold(argv.region)}`
162
+ };
113
163
  } else if (env.AWS_REGION) {
114
- regionAndOrigin = { value: env.AWS_REGION, origin: `env variable ${bold("AWS_REGION")}: ${underline(env.AWS_REGION)}` };
164
+ regionAndOrigin = {
165
+ value: env.AWS_REGION,
166
+ origin: `env variable ${bold("AWS_REGION")}: ${underline(env.AWS_REGION)}`
167
+ };
115
168
  } else if (env.AWS_DEFAULT_REGION) {
116
169
  regionAndOrigin = {
117
170
  value: env.AWS_DEFAULT_REGION,
@@ -120,9 +173,27 @@ var getCredentialsProfileRegion = async ({
120
173
  } else if (profileAndOrigin) {
121
174
  const foundRegion = (_c = (_b = sharedConfigFiles == null ? void 0 : sharedConfigFiles.configFile) == null ? void 0 : _b[profileAndOrigin.value]) == null ? void 0 : _c.region;
122
175
  if (foundRegion) {
123
- regionAndOrigin = { value: foundRegion, origin: `${bold(`[profile ${profileAndOrigin.value}]`)} in config file` };
176
+ regionAndOrigin = {
177
+ value: foundRegion,
178
+ origin: `${bold(`[profile ${profileAndOrigin.value}]`)} in config file`
179
+ };
124
180
  }
125
181
  }
182
+ if (argv.assumeRoleArn) {
183
+ console.log("assume this yo");
184
+ credentialsAndOrigin = {
185
+ value: await fromTemporaryCredentials({
186
+ masterCredentials: credentialsAndOrigin == null ? void 0 : credentialsAndOrigin.value,
187
+ params: {
188
+ RoleArn: argv.assumeRoleArn
189
+ },
190
+ clientConfig: {
191
+ region: regionAndOrigin == null ? void 0 : regionAndOrigin.value
192
+ }
193
+ })(),
194
+ origin: `assume role ${bold(`[${argv.assumeRoleArn}]`)}`
195
+ };
196
+ }
126
197
  return { credentialsAndOrigin, regionAndOrigin, profileAndOrigin };
127
198
  };
128
199
  var printVerboseCredentialsProfileRegion = ({
@@ -148,12 +219,20 @@ var handleCredentialsAndRegion = async ({
148
219
  argv,
149
220
  env
150
221
  }) => {
151
- const { credentialsAndOrigin, regionAndOrigin } = await getCredentialsProfileRegion({
152
- argv: { region: argv.awsRegion, profile: argv.awsProfile },
222
+ const { credentialsAndOrigin, regionAndOrigin, profileAndOrigin } = await getCredentialsProfileRegion({
223
+ argv: {
224
+ region: argv.awsRegion,
225
+ profile: argv.awsProfile,
226
+ assumeRoleArn: argv.awsAssumeRoleArn
227
+ },
153
228
  env: __spreadValues({}, env)
154
229
  });
155
230
  if (argv.verbose === true) {
156
- console.log(printVerboseCredentialsProfileRegion({ credentialsAndOrigin, regionAndOrigin }));
231
+ console.log(printVerboseCredentialsProfileRegion({
232
+ credentialsAndOrigin,
233
+ regionAndOrigin,
234
+ profileAndOrigin
235
+ }));
157
236
  }
158
237
  if (!credentialsAndOrigin || !regionAndOrigin) {
159
238
  if (!credentialsAndOrigin) {
@@ -168,8 +247,67 @@ var handleCredentialsAndRegion = async ({
168
247
  return { credentialsAndOrigin, regionAndOrigin };
169
248
  };
170
249
 
250
+ // src/utils/ssm.ts
251
+ import { SSMClient } from "@aws-sdk/client-ssm";
252
+ var getSSMClient = ({
253
+ configuration
254
+ }) => {
255
+ const ssmClient = new SSMClient(configuration);
256
+ return ssmClient;
257
+ };
258
+
259
+ // src/commands/debugCommand.ts
260
+ var command = "debug";
261
+ var desc = "Debugs all the things";
262
+ var builder = {
263
+ "aws-profile": commonCliOptions.awsProfile,
264
+ "aws-region": commonCliOptions.awsRegion,
265
+ "aws-key-alias": commonCliOptions.awsKeyAlias,
266
+ "aws-assume-role-arn": commonCliOptions.awsAssumeRoleArn,
267
+ verbose: commonCliOptions.verbose,
268
+ yes: __spreadValues({}, commonCliOptions.yes)
269
+ };
270
+ var handler = async (argv) => {
271
+ try {
272
+ const { credentialsAndOrigin, regionAndOrigin } = await handleCredentialsAndRegion({
273
+ argv: __spreadValues({}, argv),
274
+ env: __spreadValues({}, process.env)
275
+ });
276
+ const ssmClient = getSSMClient({
277
+ configuration: {
278
+ credentials: credentialsAndOrigin.value,
279
+ region: regionAndOrigin.value
280
+ },
281
+ verbose: argv.verbose
282
+ });
283
+ const getParametersByPathCommand = new GetParametersByPathCommand({
284
+ Path: `arn:aws:ssm:eu-west-1:060014838622:parameter/dotsec/*`,
285
+ Recursive: true
286
+ });
287
+ const commandResult = await ssmClient.send(getParametersByPathCommand);
288
+ console.log(commandResult);
289
+ } catch (e) {
290
+ console.error(e);
291
+ }
292
+ };
293
+
294
+ // src/commands/decryptSecCommand.ts
295
+ var decryptSecCommand_exports = {};
296
+ __export(decryptSecCommand_exports, {
297
+ builder: () => builder2,
298
+ command: () => command2,
299
+ desc: () => desc2,
300
+ handler: () => handler2
301
+ });
302
+ import { KMSClient, DecryptCommand } from "@aws-sdk/client-kms";
303
+ import { redBright } from "chalk";
304
+ import { parse } from "dotenv";
305
+ import fs from "node:fs";
306
+ import path from "node:path";
307
+
171
308
  // src/utils/io.ts
172
309
  import { stat } from "fs/promises";
310
+ import prompts from "prompts";
173
311
  var fileExists = async (source) => {
174
312
  try {
175
313
  await stat(source);
@@ -178,30 +316,43 @@ var fileExists = async (source) => {
178
316
  return false;
179
317
  }
180
318
  };
319
+ var promptOverwriteIfFileExists = async ({
320
+ filePath,
321
+ skip
322
+ }) => {
323
+ let overwriteResponse;
324
+ if (await fileExists(filePath) && skip !== true) {
325
+ overwriteResponse = await prompts({
326
+ type: "confirm",
327
+ name: "overwrite",
328
+ message: () => {
329
+ return `Overwrite '${filePath}' ?`;
330
+ }
331
+ });
332
+ } else {
333
+ overwriteResponse = void 0;
334
+ }
335
+ return overwriteResponse;
336
+ };
181
337
 
182
338
  // src/commands/decryptSecCommand.ts
183
- var command = "decrypt-sec";
184
- var desc = "Decrypts a dotsec file";
185
- var builder = {
186
- "aws-profile": __spreadValues({}, commonCliOptions.awsProfile),
187
- "aws-region": __spreadValues({}, commonCliOptions.awsRegion),
188
- "aws-key-alias": { string: true, default: "alias/top-secret" },
189
- "env-file": {
190
- string: true,
191
- describe: ".env file",
192
- default: ".env"
193
- },
194
- "sec-file": {
195
- string: true,
196
- describe: ".sec file",
197
- default: ".sec"
198
- },
199
- verbose: __spreadValues({}, commonCliOptions.verbose),
200
- yes: __spreadValues({}, commonCliOptions.yes)
339
+ var command2 = "decrypt-sec";
340
+ var desc2 = "Decrypts a dotsec file";
341
+ var builder2 = {
342
+ "aws-profile": commonCliOptions.awsProfile,
343
+ "aws-region": commonCliOptions.awsRegion,
344
+ "aws-key-alias": commonCliOptions.awsKeyAlias,
345
+ "assume-role-arn": commonCliOptions.awsAssumeRoleArn,
346
+ "env-file": commonCliOptions.envFile,
347
+ "sec-file": commonCliOptions.secFile,
348
+ verbose: commonCliOptions.verbose
201
349
  };
202
- var handler = async (argv) => {
350
+ var handler2 = async (argv) => {
203
351
  try {
204
- const { credentialsAndOrigin, regionAndOrigin } = await handleCredentialsAndRegion({ argv: __spreadValues({}, argv), env: __spreadValues({}, process.env) });
352
+ const { credentialsAndOrigin, regionAndOrigin } = await handleCredentialsAndRegion({
353
+ argv: __spreadValues({}, argv),
354
+ env: __spreadValues({}, process.env)
355
+ });
205
356
  const secSource = path.resolve(process.cwd(), argv.secFile);
206
357
  if (!await fileExists(secSource)) {
207
358
  console.error(`Could not open ${redBright(secSource)}`);
@@ -220,7 +371,11 @@ var handler = async (argv) => {
220
371
  });
221
372
  const decryptionResult = await kmsClient.send(decryptCommand);
222
373
  if (!(decryptionResult == null ? void 0 : decryptionResult.Plaintext)) {
223
- throw new Error(`No: ${JSON.stringify({ key, cipherText, decryptCommand })}`);
374
+ throw new Error(`No: ${JSON.stringify({
375
+ key,
376
+ cipherText,
377
+ decryptCommand
378
+ })}`);
224
379
  }
225
380
  const value = Buffer.from(decryptionResult.Plaintext).toString();
226
381
  return [key, value];
@@ -231,62 +386,203 @@ var handler = async (argv) => {
231
386
  }
232
387
  };
233
388
 
234
- // src/commands/defaultCommand.ts
235
- var defaultCommand_exports = {};
236
- __export(defaultCommand_exports, {
237
- builder: () => builder2,
238
- command: () => command2,
239
- desc: () => desc2,
240
- handler: () => handler2
389
+ // src/commands/decryptSecretsJson.ts
390
+ var decryptSecretsJson_exports = {};
391
+ __export(decryptSecretsJson_exports, {
392
+ builder: () => builder3,
393
+ command: () => command3,
394
+ desc: () => desc3,
395
+ handler: () => handler3
241
396
  });
242
- import { KMSClient as KMSClient2, DecryptCommand as DecryptCommand2 } from "@aws-sdk/client-kms";
397
+ import { DecryptCommand as DecryptCommand2, DescribeKeyCommand } from "@aws-sdk/client-kms";
243
398
  import { redBright as redBright2 } from "chalk";
244
- import { spawn } from "cross-spawn";
245
- import { parse as parse2 } from "dotenv";
399
+ import flat from "flat";
246
400
  import fs2 from "node:fs";
247
401
  import path2 from "node:path";
248
- var command2 = "$0 <command>";
249
- var desc2 = "Decrypts a .sec file, injects the results into a separate process and runs a command";
250
- var builder2 = {
251
- "aws-profile": __spreadValues({}, commonCliOptions.awsProfile),
252
- "aws-region": __spreadValues({}, commonCliOptions.awsRegion),
253
- "aws-key-alias": { string: true, default: "alias/top-secret" },
254
- "sec-file": {
402
+
403
+ // src/utils/kms.ts
404
+ import { KMSClient as KMSClient2 } from "@aws-sdk/client-kms";
405
+ var getKMSClient = ({
406
+ configuration
407
+ }) => {
408
+ const kmsClient = new KMSClient2(configuration);
409
+ return kmsClient;
410
+ };
411
+
412
+ // src/commands/decryptSecretsJson.ts
413
+ var command3 = "decrypt-secrets-json";
414
+ var desc3 = "Derypts an encrypted file";
415
+ var builder3 = {
416
+ "aws-profile": commonCliOptions.awsProfile,
417
+ "aws-region": commonCliOptions.awsRegion,
418
+ "aws-key-alias": commonCliOptions.awsKeyAlias,
419
+ "secrets-file": {
255
420
  string: true,
256
- describe: ".sec file",
257
- default: ".sec"
421
+ describe: "filename of json file writing secrets",
422
+ default: "secrets.json"
258
423
  },
259
- verbose: __spreadValues({}, commonCliOptions.verbose),
260
- yes: __spreadValues({}, commonCliOptions.yes),
261
- command: { string: true, required: true }
424
+ "encrypted-secrets-file": {
425
+ string: true,
426
+ describe: "filename of json file for reading encrypted secrets",
427
+ default: "secrets.encrypted.json"
428
+ },
429
+ "assume-role-arn": commonCliOptions.awsAssumeRoleArn,
430
+ verbose: commonCliOptions.verbose,
431
+ yes: __spreadValues({}, commonCliOptions.yes)
262
432
  };
263
- var handler2 = async (argv) => {
433
+ var handler3 = async (argv) => {
434
+ const { info, error } = getLogger();
264
435
  try {
265
- const { credentialsAndOrigin, regionAndOrigin } = await handleCredentialsAndRegion({ argv: __spreadValues({}, argv), env: __spreadValues({}, process.env) });
266
- const secSource = path2.resolve(process.cwd(), argv.secFile);
267
- if (!await fileExists(secSource)) {
268
- console.error(`Could not open ${redBright2(secSource)}`);
436
+ const { credentialsAndOrigin, regionAndOrigin } = await handleCredentialsAndRegion({
437
+ argv: __spreadValues({}, argv),
438
+ env: __spreadValues({}, process.env)
439
+ });
440
+ const encryptedSecretsPath = path2.resolve(process.cwd(), argv.encryptedSecretsFile);
441
+ if (!await fileExists(encryptedSecretsPath)) {
442
+ error(`Could not open ${redBright2(encryptedSecretsPath)}`);
269
443
  return;
270
444
  }
271
- const parsedSec = parse2(fs2.readFileSync(secSource, { encoding: "utf8" }));
272
- const kmsClient = new KMSClient2({
273
- credentials: credentialsAndOrigin.value,
274
- region: regionAndOrigin.value
445
+ const encryptedSecrets = JSON.parse(fs2.readFileSync(encryptedSecretsPath, { encoding: "utf8" }));
446
+ if (!encryptedSecrets.encryptedParameters) {
447
+ throw new Error(`Expected 'encryptedParameters' property, but got none`);
448
+ }
449
+ const flatEncryptedParameters = flat(encryptedSecrets.encryptedParameters, { delimiter: "/" });
450
+ const kmsClient = getKMSClient({
451
+ configuration: {
452
+ credentials: credentialsAndOrigin.value,
453
+ region: regionAndOrigin.value
454
+ },
455
+ verbose: argv.verbose
275
456
  });
276
- const envEntries = await Promise.all(Object.entries(parsedSec).map(async ([key, cipherText]) => {
457
+ if (argv.verbose) {
458
+ info(`Encrypting using key alias ${bold(argv.awsKeyAlias)} in ${bold(await kmsClient.config.region())}`);
459
+ const describeKeyCommand = new DescribeKeyCommand({
460
+ KeyId: argv.awsKeyAlias
461
+ });
462
+ const describeKeyResult = await kmsClient.send(describeKeyCommand);
463
+ console.log("describeKeyResult", { describeKeyResult });
464
+ }
465
+ const flatParameters = Object.fromEntries(await Promise.all(Object.entries(flatEncryptedParameters).map(async ([parameterName, encryptedParameter]) => {
277
466
  const decryptCommand = new DecryptCommand2({
278
467
  KeyId: argv.awsKeyAlias,
279
- CiphertextBlob: Buffer.from(cipherText, "base64"),
468
+ CiphertextBlob: Buffer.from(encryptedParameter, "base64"),
280
469
  EncryptionAlgorithm: "RSAES_OAEP_SHA_256"
281
470
  });
282
471
  const decryptionResult = await kmsClient.send(decryptCommand);
283
- if (!(decryptionResult == null ? void 0 : decryptionResult.Plaintext)) {
284
- throw new Error(`No: ${JSON.stringify({ key, cipherText, decryptCommand })}`);
472
+ if (!decryptionResult.Plaintext) {
473
+ throw new Error(`Something bad happened: ${JSON.stringify({
474
+ key: parameterName,
475
+ cipherText: encryptedParameter,
476
+ decryptCommand
477
+ })}`);
478
+ }
479
+ if (argv.verbose) {
480
+ info(`Encrypting key ${bold(parameterName)} ${underline("ok")}`);
285
481
  }
286
482
  const value = Buffer.from(decryptionResult.Plaintext).toString();
287
- return [key, value];
288
- }));
289
- const env = Object.fromEntries(envEntries);
483
+ return [parameterName, value];
484
+ })));
485
+ const parameters = flat.unflatten(flatParameters, { delimiter: "/" });
486
+ const secrets = {
487
+ config: encryptedSecrets.config,
488
+ parameters
489
+ };
490
+ const secretsPath = path2.resolve(process.cwd(), argv.secretsFile);
491
+ const overwriteResponse = await promptOverwriteIfFileExists({
492
+ filePath: secretsPath,
493
+ skip: argv.yes
494
+ });
495
+ if (overwriteResponse === void 0 || overwriteResponse.overwrite === true) {
496
+ fs2.writeFileSync(secretsPath, JSON.stringify(secrets, null, 4));
497
+ }
498
+ } catch (e) {
499
+ error(e);
500
+ }
501
+ };
502
+
503
+ // src/commands/defaultCommand.ts
504
+ var defaultCommand_exports = {};
505
+ __export(defaultCommand_exports, {
506
+ builder: () => builder4,
507
+ command: () => command4,
508
+ desc: () => desc4,
509
+ handler: () => handler4
510
+ });
511
+ import fs3 from "node:fs";
512
+ import path3 from "node:path";
513
+ import { KMSClient as KMSClient3, DecryptCommand as DecryptCommand3 } from "@aws-sdk/client-kms";
514
+ import { redBright as redBright3 } from "chalk";
515
+ import { spawn } from "cross-spawn";
516
+ import { parse as parse2 } from "dotenv";
517
+ var command4 = "$0 <command>";
518
+ var desc4 = "Decrypts a .sec file, injects the results into a separate process and runs a command";
519
+ var builder4 = {
520
+ "aws-profile": commonCliOptions.awsProfile,
521
+ "aws-region": commonCliOptions.awsRegion,
522
+ "aws-key-alias": commonCliOptions.awsKeyAlias,
523
+ "sec-file": commonCliOptions.secFile,
524
+ "env-file": commonCliOptions.envFile,
525
+ "assume-role-arn": commonCliOptions.awsAssumeRoleArn,
526
+ verbose: commonCliOptions.verbose,
527
+ command: { string: true, required: true }
528
+ };
529
+ var handleSec = async ({
530
+ secFile,
531
+ credentialsAndOrigin,
532
+ regionAndOrigin,
533
+ awsKeyAlias
534
+ }) => {
535
+ const secSource = path3.resolve(process.cwd(), secFile);
536
+ if (!await fileExists(secSource)) {
537
+ console.error(`Could not open ${redBright3(secSource)}`);
538
+ return;
539
+ }
540
+ const parsedSec = parse2(fs3.readFileSync(secSource, { encoding: "utf8" }));
541
+ const kmsClient = new KMSClient3({
542
+ credentials: credentialsAndOrigin.value,
543
+ region: regionAndOrigin.value
544
+ });
545
+ const envEntries = await Promise.all(Object.entries(parsedSec).map(async ([key, cipherText]) => {
546
+ const decryptCommand = new DecryptCommand3({
547
+ KeyId: awsKeyAlias,
548
+ CiphertextBlob: Buffer.from(cipherText, "base64"),
549
+ EncryptionAlgorithm: "RSAES_OAEP_SHA_256"
550
+ });
551
+ const decryptionResult = await kmsClient.send(decryptCommand);
552
+ if (!(decryptionResult == null ? void 0 : decryptionResult.Plaintext)) {
553
+ throw new Error(`No: ${JSON.stringify({
554
+ key,
555
+ cipherText,
556
+ decryptCommand
557
+ })}`);
558
+ }
559
+ const value = Buffer.from(decryptionResult.Plaintext).toString();
560
+ return [key, value];
561
+ }));
562
+ const env = Object.fromEntries(envEntries);
563
+ return env;
564
+ };
565
+ var handler4 = async (argv) => {
566
+ try {
567
+ const { credentialsAndOrigin, regionAndOrigin } = await handleCredentialsAndRegion({
568
+ argv: __spreadValues({}, argv),
569
+ env: __spreadValues({}, process.env)
570
+ });
571
+ if (argv.verbose) {
572
+ console.log({ credentialsAndOrigin, regionAndOrigin });
573
+ }
574
+ let env;
575
+ if (argv.envFile) {
576
+ console.log("OK");
577
+ env = parse2(fs3.readFileSync(argv.envFile, { encoding: "utf8" }));
578
+ } else if (argv.secFile) {
579
+ env = await handleSec({
580
+ secFile: argv.secFile,
581
+ credentialsAndOrigin,
582
+ regionAndOrigin,
583
+ awsKeyAlias: argv.awsKeyAlias
584
+ });
585
+ }
290
586
  const userCommandArgs = process.argv.slice(process.argv.indexOf(argv.command) + 1);
291
587
  if (argv.command) {
292
588
  spawn(argv.command, [...userCommandArgs], {
@@ -303,48 +599,55 @@ var handler2 = async (argv) => {
303
599
  // src/commands/encryptEnvCommand.ts
304
600
  var encryptEnvCommand_exports = {};
305
601
  __export(encryptEnvCommand_exports, {
306
- builder: () => builder3,
307
- command: () => command3,
308
- desc: () => desc3,
309
- handler: () => handler3
602
+ builder: () => builder5,
603
+ command: () => command5,
604
+ desc: () => desc5,
605
+ handler: () => handler5
310
606
  });
311
- import { KMSClient as KMSClient3, EncryptCommand } from "@aws-sdk/client-kms";
312
- import { redBright as redBright3 } from "chalk";
607
+ import { DescribeKeyCommand as DescribeKeyCommand2, EncryptCommand } from "@aws-sdk/client-kms";
608
+ import { redBright as redBright4 } from "chalk";
313
609
  import { parse as parse3 } from "dotenv";
314
- import fs3 from "node:fs";
315
- import path3 from "node:path";
316
- var command3 = "encrypt-env";
317
- var desc3 = "Encrypts a dotenv file";
318
- var builder3 = {
319
- "aws-profile": __spreadValues({}, commonCliOptions.awsProfile),
320
- "aws-region": __spreadValues({}, commonCliOptions.awsRegion),
321
- "aws-key-alias": { string: true, default: "alias/top-secret" },
322
- "env-file": {
323
- string: true,
324
- describe: ".env file",
325
- default: ".env"
326
- },
327
- "sec-file": {
328
- string: true,
329
- describe: ".sec file",
330
- default: ".sec"
331
- },
332
- verbose: __spreadValues({}, commonCliOptions.verbose),
333
- yes: __spreadValues({}, commonCliOptions.yes)
610
+ import fs4 from "node:fs";
611
+ import path4 from "node:path";
612
+ var command5 = "encrypt-env";
613
+ var desc5 = "Encrypts a dotenv file";
614
+ var builder5 = {
615
+ "aws-profile": commonCliOptions.awsProfile,
616
+ "aws-region": commonCliOptions.awsRegion,
617
+ "aws-key-alias": commonCliOptions.awsKeyAlias,
618
+ "env-file": commonCliOptions.envFile,
619
+ "sec-file": commonCliOptions.secFile,
620
+ "assume-role-arn": commonCliOptions.awsAssumeRoleArn,
621
+ verbose: commonCliOptions.verbose
334
622
  };
335
- var handler3 = async (argv) => {
623
+ var handler5 = async (argv) => {
624
+ const { info, error } = getLogger();
336
625
  try {
337
- const { credentialsAndOrigin, regionAndOrigin } = await handleCredentialsAndRegion({ argv: __spreadValues({}, argv), env: __spreadValues({}, process.env) });
338
- const envSource = path3.resolve(process.cwd(), argv.envFile);
626
+ const { credentialsAndOrigin, regionAndOrigin } = await handleCredentialsAndRegion({
627
+ argv: __spreadValues({}, argv),
628
+ env: __spreadValues({}, process.env)
629
+ });
630
+ const envSource = path4.resolve(process.cwd(), argv.envFile);
339
631
  if (!await fileExists(envSource)) {
340
- console.error(`Could not open ${redBright3(envSource)}`);
632
+ error(`Could not open ${redBright4(envSource)}`);
341
633
  return;
342
634
  }
343
- const parsedEnv = parse3(fs3.readFileSync(envSource, { encoding: "utf8" }));
344
- const kmsClient = new KMSClient3({
345
- credentials: credentialsAndOrigin.value,
346
- region: regionAndOrigin.value
635
+ const parsedEnv = parse3(fs4.readFileSync(envSource, { encoding: "utf8" }));
636
+ const kmsClient = getKMSClient({
637
+ configuration: {
638
+ credentials: credentialsAndOrigin.value,
639
+ region: regionAndOrigin.value
640
+ },
641
+ verbose: argv.verbose
347
642
  });
643
+ if (argv.verbose) {
644
+ info(`Encrypting using key alias ${bold(argv.awsKeyAlias)} in ${bold(await kmsClient.config.region())}`);
645
+ const describeKeyCommand = new DescribeKeyCommand2({
646
+ KeyId: argv.awsKeyAlias
647
+ });
648
+ const describeKeyResult = await kmsClient.send(describeKeyCommand);
649
+ console.log("describeKeyResult", { describeKeyResult });
650
+ }
348
651
  const sec = (await Promise.all(Object.entries(parsedEnv).map(async ([key, value]) => {
349
652
  const encryptCommand = new EncryptCommand({
350
653
  KeyId: argv.awsKeyAlias,
@@ -353,17 +656,232 @@ var handler3 = async (argv) => {
353
656
  });
354
657
  const encryptionResult = await kmsClient.send(encryptCommand);
355
658
  if (!encryptionResult.CiphertextBlob) {
356
- throw new Error(`No: ${JSON.stringify({ key, value, encryptCommand })}`);
659
+ throw new Error(`Something bad happened: ${JSON.stringify({
660
+ key,
661
+ value,
662
+ encryptCommand
663
+ })}`);
664
+ }
665
+ if (argv.verbose) {
666
+ info(`Encrypting key ${bold(key)} ${underline("ok")}`);
357
667
  }
358
668
  const cipherText = Buffer.from(encryptionResult.CiphertextBlob).toString("base64");
359
669
  return `${key}="${cipherText}"`;
360
670
  }))).join("\n");
361
- fs3.writeFileSync(path3.resolve(process.cwd(), argv.secFile), sec);
671
+ fs4.writeFileSync(path4.resolve(process.cwd(), argv.secFile), sec);
362
672
  } catch (e) {
363
- console.error(e);
673
+ error(e);
674
+ }
675
+ };
676
+
677
+ // src/commands/encryptSecretsJson.ts
678
+ var encryptSecretsJson_exports = {};
679
+ __export(encryptSecretsJson_exports, {
680
+ builder: () => builder6,
681
+ command: () => command6,
682
+ desc: () => desc6,
683
+ handler: () => handler6
684
+ });
685
+ import fs5 from "node:fs";
686
+ import path5 from "node:path";
687
+ import { DescribeKeyCommand as DescribeKeyCommand3, EncryptCommand as EncryptCommand2 } from "@aws-sdk/client-kms";
688
+ import { redBright as redBright5 } from "chalk";
689
+ import flat2 from "flat";
690
+ var command6 = "encrypt-secrets-json";
691
+ var desc6 = "Encrypts an unencrypted file";
692
+ var builder6 = {
693
+ "aws-profile": commonCliOptions.awsProfile,
694
+ "aws-region": commonCliOptions.awsRegion,
695
+ "aws-key-alias": commonCliOptions.awsKeyAlias,
696
+ "secrets-file": {
697
+ string: true,
698
+ describe: "filename of json file reading secrets",
699
+ default: "secrets.json"
700
+ },
701
+ "encrypted-secrets-file": {
702
+ string: true,
703
+ describe: "filename of json file for writing encrypted secrets",
704
+ default: "secrets.encrypted.json"
705
+ },
706
+ "assume-role-arn": commonCliOptions.awsAssumeRoleArn,
707
+ verbose: commonCliOptions.verbose,
708
+ yes: __spreadValues({}, commonCliOptions.yes)
709
+ };
710
+ var handler6 = async (argv) => {
711
+ const { info, error } = getLogger();
712
+ try {
713
+ const { credentialsAndOrigin, regionAndOrigin } = await handleCredentialsAndRegion({
714
+ argv: __spreadValues({}, argv),
715
+ env: __spreadValues({}, process.env)
716
+ });
717
+ const secretsPath = path5.resolve(process.cwd(), argv.secretsFile);
718
+ if (!await fileExists(secretsPath)) {
719
+ error(`Could not open ${redBright5(secretsPath)}`);
720
+ return;
721
+ }
722
+ const secrets = JSON.parse(fs5.readFileSync(secretsPath, { encoding: "utf8" }));
723
+ if (!secrets.parameters) {
724
+ throw new Error(`Expected 'parameters' property, but got none`);
725
+ }
726
+ const flatParameters = flat2(secrets.parameters, { delimiter: "/" });
727
+ if (argv.verbose) {
728
+ console.log(flatParameters);
729
+ }
730
+ const kmsClient = getKMSClient({
731
+ configuration: {
732
+ credentials: credentialsAndOrigin.value,
733
+ region: regionAndOrigin.value
734
+ },
735
+ verbose: argv.verbose
736
+ });
737
+ if (argv.verbose) {
738
+ info(`Encrypting using key alias ${bold(argv.awsKeyAlias)} in ${bold(await kmsClient.config.region())}`);
739
+ const describeKeyCommand = new DescribeKeyCommand3({
740
+ KeyId: argv.awsKeyAlias
741
+ });
742
+ const describeKeyResult = await kmsClient.send(describeKeyCommand);
743
+ console.log("describeKeyResult", { describeKeyResult });
744
+ }
745
+ const encryptedFlatParameters = Object.fromEntries(await Promise.all(Object.entries(flatParameters).map(async ([parameterName, parameter]) => {
746
+ const encryptCommand = new EncryptCommand2({
747
+ KeyId: argv.awsKeyAlias,
748
+ Plaintext: Buffer.from(parameter),
749
+ EncryptionAlgorithm: "RSAES_OAEP_SHA_256"
750
+ });
751
+ const encryptionResult = await kmsClient.send(encryptCommand);
752
+ if (!encryptionResult.CiphertextBlob) {
753
+ throw new Error(`Something bad happened: ${JSON.stringify({
754
+ key: parameterName,
755
+ value: parameter,
756
+ encryptCommand
757
+ })}`);
758
+ }
759
+ if (argv.verbose) {
760
+ info(`Encrypting key ${bold(parameterName)} ${underline("ok")}`);
761
+ }
762
+ const cipherText = Buffer.from(encryptionResult.CiphertextBlob).toString("base64");
763
+ return [parameterName, cipherText];
764
+ })));
765
+ const encryptedParameters = flat2.unflatten(encryptedFlatParameters, { delimiter: "/" });
766
+ const encryptedSecrets = {
767
+ config: secrets.config,
768
+ encryptedParameters
769
+ };
770
+ const encryptedSecretsPath = path5.resolve(process.cwd(), argv.encryptedSecretsFile);
771
+ const overwriteResponse = await promptOverwriteIfFileExists({
772
+ filePath: encryptedSecretsPath,
773
+ skip: argv.yes
774
+ });
775
+ if (overwriteResponse === void 0 || overwriteResponse.overwrite === true) {
776
+ fs5.writeFileSync(encryptedSecretsPath, JSON.stringify(encryptedSecrets, null, 4));
777
+ }
778
+ } catch (e) {
779
+ error(e);
780
+ }
781
+ };
782
+
783
+ // src/commands/offloadToSSMCommand.ts
784
+ var offloadToSSMCommand_exports = {};
785
+ __export(offloadToSSMCommand_exports, {
786
+ builder: () => builder7,
787
+ command: () => command7,
788
+ desc: () => desc7,
789
+ handler: () => handler7
790
+ });
791
+ import { DecryptCommand as DecryptCommand4, DescribeKeyCommand as DescribeKeyCommand4 } from "@aws-sdk/client-kms";
792
+ import { PutParameterCommand } from "@aws-sdk/client-ssm";
793
+ import { redBright as redBright6 } from "chalk";
794
+ import flat3 from "flat";
795
+ import fs6 from "node:fs";
796
+ import path6 from "node:path";
797
+ var command7 = "offload-secrets-json-to-ssm";
798
+ var desc7 = "Sends decrypted values of secrets.encrypted.json file to SSM parameter store";
799
+ var builder7 = {
800
+ "aws-profile": commonCliOptions.awsProfile,
801
+ "aws-region": commonCliOptions.awsRegion,
802
+ "aws-key-alias": commonCliOptions.awsKeyAlias,
803
+ "encrypted-secrets-file": {
804
+ string: true,
805
+ describe: "filename of json file for reading encrypted secrets",
806
+ default: "secrets.encrypted.json"
807
+ },
808
+ "assume-role-arn": commonCliOptions.awsAssumeRoleArn,
809
+ verbose: commonCliOptions.verbose,
810
+ yes: __spreadValues({}, commonCliOptions.yes)
811
+ };
812
+ var handler7 = async (argv) => {
813
+ const { info, error } = getLogger();
814
+ try {
815
+ const { credentialsAndOrigin, regionAndOrigin } = await handleCredentialsAndRegion({
816
+ argv: __spreadValues({}, argv),
817
+ env: __spreadValues({}, process.env)
818
+ });
819
+ const encryptedSecretsPath = path6.resolve(process.cwd(), argv.encryptedSecretsFile);
820
+ if (!await fileExists(encryptedSecretsPath)) {
821
+ error(`Could not open ${redBright6(encryptedSecretsPath)}`);
822
+ return;
823
+ }
824
+ const encryptedSecrets = JSON.parse(fs6.readFileSync(encryptedSecretsPath, { encoding: "utf8" }));
825
+ if (!encryptedSecrets.encryptedParameters) {
826
+ throw new Error(`Expected 'encryptedParameters' property, but got none`);
827
+ }
828
+ const flatEncryptedParameters = flat3(encryptedSecrets.encryptedParameters, { delimiter: "/" });
829
+ const kmsClient = getKMSClient({
830
+ configuration: {
831
+ credentials: credentialsAndOrigin.value,
832
+ region: regionAndOrigin.value
833
+ },
834
+ verbose: argv.verbose
835
+ });
836
+ if (argv.verbose) {
837
+ info(`Encrypting using key alias ${bold(argv.awsKeyAlias)} in ${bold(await kmsClient.config.region())}`);
838
+ const describeKeyCommand = new DescribeKeyCommand4({
839
+ KeyId: argv.awsKeyAlias
840
+ });
841
+ const describeKeyResult = await kmsClient.send(describeKeyCommand);
842
+ console.log("describeKeyResult", { describeKeyResult });
843
+ }
844
+ const flatParameters = Object.fromEntries(await Promise.all(Object.entries(flatEncryptedParameters).map(async ([parameterName, encryptedParameter]) => {
845
+ const decryptCommand = new DecryptCommand4({
846
+ KeyId: argv.awsKeyAlias,
847
+ CiphertextBlob: Buffer.from(encryptedParameter, "base64"),
848
+ EncryptionAlgorithm: "RSAES_OAEP_SHA_256"
849
+ });
850
+ const decryptionResult = await kmsClient.send(decryptCommand);
851
+ if (!decryptionResult.Plaintext) {
852
+ throw new Error(`Something bad happened: ${JSON.stringify({
853
+ key: parameterName,
854
+ cipherText: encryptedParameter,
855
+ decryptCommand
856
+ })}`);
857
+ }
858
+ if (argv.verbose) {
859
+ info(`Encrypting key ${bold(parameterName)} ${underline("ok")}`);
860
+ }
861
+ const value = Buffer.from(decryptionResult.Plaintext).toString();
862
+ return [parameterName, value];
863
+ })));
864
+ const ssmClient = getSSMClient({
865
+ configuration: {
866
+ credentials: credentialsAndOrigin.value,
867
+ region: regionAndOrigin.value
868
+ },
869
+ verbose: argv.verbose
870
+ });
871
+ await Promise.all(Object.entries(flatParameters).map(([parameterName, value]) => {
872
+ const putParameterCommand = new PutParameterCommand({
873
+ Name: `/${parameterName}`,
874
+ Value: value,
875
+ Type: "String",
876
+ Overwrite: true
877
+ });
878
+ return ssmClient.send(putParameterCommand);
879
+ }));
880
+ } catch (e) {
881
+ error(e);
364
882
  }
365
883
  };
366
884
 
367
885
  // src/cli.ts
368
- void yargs(hideBin(process.argv)).command(defaultCommand_exports).command(encryptEnvCommand_exports).command(decryptSecCommand_exports).parse();
886
+ void yargs(hideBin(process.argv)).command(defaultCommand_exports).command(offloadToSSMCommand_exports).command(debugCommand_exports).command(encryptEnvCommand_exports).command(decryptSecCommand_exports).command(encryptSecretsJson_exports).command(decryptSecretsJson_exports).parse();
369
887
  //# sourceMappingURL=cli.js.map