@catladder/cli 5.8.1 → 5.9.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.
Files changed (83) hide show
  1. package/dist/apps/cli/src/apps/catenv/catenv.js +3 -0
  2. package/dist/apps/cli/src/apps/catenv/catenv.js.map +1 -1
  3. package/dist/apps/cli/src/apps/cli/commands/project/doctor/GcloudProjectInspector.d.ts +4 -0
  4. package/dist/apps/cli/src/apps/cli/commands/project/doctor/GcloudProjectInspector.js +12 -0
  5. package/dist/apps/cli/src/apps/cli/commands/project/doctor/GcloudProjectInspector.js.map +1 -1
  6. package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkCloudRun.d.ts +2 -2
  7. package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkCloudRun.js +3 -1
  8. package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkCloudRun.js.map +1 -1
  9. package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkLocalAccess.d.ts +10 -0
  10. package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkLocalAccess.js +97 -0
  11. package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkLocalAccess.js.map +1 -0
  12. package/dist/apps/cli/src/apps/cli/commands/project/doctor/index.js +1 -1
  13. package/dist/apps/cli/src/apps/cli/commands/project/doctor/index.js.map +1 -1
  14. package/dist/apps/cli/src/apps/cli/commands/project/setup/setupContext.js +2 -0
  15. package/dist/apps/cli/src/apps/cli/commands/project/setup/setupContext.js.map +1 -1
  16. package/dist/apps/cli/src/apps/cli/commands/project/setup/setupLocalAccess.d.ts +9 -0
  17. package/dist/apps/cli/src/apps/cli/commands/project/setup/setupLocalAccess.js +29 -0
  18. package/dist/apps/cli/src/apps/cli/commands/project/setup/setupLocalAccess.js.map +1 -0
  19. package/dist/apps/cli/src/config/getProjectConfig.js +9 -1
  20. package/dist/apps/cli/src/config/getProjectConfig.js.map +1 -1
  21. package/dist/apps/cli/src/gcloud/localAccess.d.ts +53 -0
  22. package/dist/apps/cli/src/gcloud/localAccess.js +176 -0
  23. package/dist/apps/cli/src/gcloud/localAccess.js.map +1 -0
  24. package/dist/apps/cli/src/gcloud/serviceAccounts.js +9 -2
  25. package/dist/apps/cli/src/gcloud/serviceAccounts.js.map +1 -1
  26. package/dist/apps/cli/src/localAccess/declared.d.ts +8 -0
  27. package/dist/apps/cli/src/localAccess/declared.js +20 -0
  28. package/dist/apps/cli/src/localAccess/declared.js.map +1 -0
  29. package/dist/apps/cli/src/localAccess/keyFiles.d.ts +26 -0
  30. package/dist/apps/cli/src/localAccess/keyFiles.js +87 -0
  31. package/dist/apps/cli/src/localAccess/keyFiles.js.map +1 -0
  32. package/dist/apps/cli/src/utils/writePrivateFile.d.ts +7 -0
  33. package/dist/apps/cli/src/utils/writePrivateFile.js +25 -0
  34. package/dist/apps/cli/src/utils/writePrivateFile.js.map +1 -0
  35. package/dist/apps/cli/src/vault/BitwardenVault.js +13 -22
  36. package/dist/apps/cli/src/vault/BitwardenVault.js.map +1 -1
  37. package/dist/apps/cli/src/vault/VaultManager.d.ts +6 -0
  38. package/dist/apps/cli/src/vault/VaultManager.js +18 -0
  39. package/dist/apps/cli/src/vault/VaultManager.js.map +1 -1
  40. package/dist/apps/cli/src/vault/bw.d.ts +6 -2
  41. package/dist/apps/cli/src/vault/bw.js +32 -6
  42. package/dist/apps/cli/src/vault/bw.js.map +1 -1
  43. package/dist/apps/cli/src/vault/cache.js +5 -2
  44. package/dist/apps/cli/src/vault/cache.js.map +1 -1
  45. package/dist/bundles/catenv/index.js +390 -31
  46. package/dist/bundles/cli/index.js +737 -35
  47. package/dist/bundles/skills/catladder-secrets/SKILL.md +24 -3
  48. package/dist/packages/pipeline/src/index.d.ts +1 -0
  49. package/dist/packages/pipeline/src/index.js +1 -0
  50. package/dist/packages/pipeline/src/index.js.map +1 -1
  51. package/dist/packages/pipeline/src/localAccess/index.d.ts +46 -0
  52. package/dist/packages/pipeline/src/localAccess/index.js +149 -0
  53. package/dist/packages/pipeline/src/localAccess/index.js.map +1 -0
  54. package/dist/packages/pipeline/src/types/config.d.ts +57 -0
  55. package/dist/packages/pipeline/src/types/config.js.map +1 -1
  56. package/dist/skills/catladder-secrets/SKILL.md +24 -3
  57. package/dist/tsconfig.tsbuildinfo +1 -1
  58. package/package.json +1 -1
  59. package/src/apps/catenv/catenv.ts +4 -0
  60. package/src/apps/cli/commands/project/doctor/GcloudProjectInspector.ts +25 -1
  61. package/src/apps/cli/commands/project/doctor/checkCloudRun.ts +5 -1
  62. package/src/apps/cli/commands/project/doctor/checkLocalAccess.test.ts +112 -0
  63. package/src/apps/cli/commands/project/doctor/checkLocalAccess.ts +144 -0
  64. package/src/apps/cli/commands/project/doctor/index.ts +1 -1
  65. package/src/apps/cli/commands/project/setup/setupContext.ts +2 -0
  66. package/src/apps/cli/commands/project/setup/setupLocalAccess.ts +41 -0
  67. package/src/config/__tests__/getEnvVarsResolved.test.ts +45 -0
  68. package/src/config/getProjectConfig.ts +11 -1
  69. package/src/gcloud/__tests__/localAccess.test.ts +82 -0
  70. package/src/gcloud/__tests__/syncLocalAccess.test.ts +196 -0
  71. package/src/gcloud/localAccess.ts +257 -0
  72. package/src/gcloud/serviceAccounts.ts +8 -2
  73. package/src/localAccess/__tests__/keyFiles.test.ts +107 -0
  74. package/src/localAccess/declared.ts +19 -0
  75. package/src/localAccess/keyFiles.ts +114 -0
  76. package/src/utils/__tests__/writePrivateFile.test.ts +40 -0
  77. package/src/utils/writePrivateFile.ts +20 -0
  78. package/src/vault/BitwardenVault.ts +8 -9
  79. package/src/vault/VaultManager.ts +28 -0
  80. package/src/vault/__tests__/BitwardenVault.test.ts +4 -4
  81. package/src/vault/__tests__/cache.test.ts +37 -0
  82. package/src/vault/bw.ts +34 -9
  83. package/src/vault/cache.ts +5 -2
@@ -67,14 +67,40 @@ const getBwSession = async (io, { allowPrompt }) => {
67
67
  };
68
68
  exports.getBwSession = getBwSession;
69
69
  /**
70
- * runs a bw command with the given session and returns stdout
70
+ * runs a bw command with the given session and returns stdout.
71
+ *
72
+ * `input` is written to stdin: use it for payloads (e.g. the secrets of
73
+ * `bw create item`), which would otherwise be visible in `ps` and be
74
+ * echoed by node's "Command failed" error.
71
75
  */
72
- const runBw = async (session, args) => {
73
- const { stdout } = await execFile("bw", [...args, "--raw", "--nointeraction"], {
74
- env: { ...process.env, BW_SESSION: session },
75
- maxBuffer: 50 * 1024 * 1024,
76
+ const runBw = async (session, args, input) => {
77
+ const fullArgs = [...args, "--raw", "--nointeraction"];
78
+ const env = { ...process.env, BW_SESSION: session };
79
+ if (input === undefined) {
80
+ const { stdout } = await execFile("bw", fullArgs, {
81
+ env,
82
+ maxBuffer: 50 * 1024 * 1024,
83
+ });
84
+ return stdout;
85
+ }
86
+ return new Promise((resolve, reject) => {
87
+ const child = (0, child_process_1.spawn)("bw", fullArgs, {
88
+ env,
89
+ stdio: ["pipe", "pipe", "pipe"],
90
+ });
91
+ let stdout = "";
92
+ let stderr = "";
93
+ child.stdout.on("data", (data) => (stdout += data.toString("utf-8")));
94
+ child.stderr.on("data", (data) => (stderr += data.toString("utf-8")));
95
+ child.on("error", reject);
96
+ child.on("close", (code) => code === 0
97
+ ? resolve(stdout)
98
+ : reject(new Error(`bw ${args[0]} failed: ${stderr.trim()}`)));
99
+ child.stdin.on("error", () => {
100
+ // bw exiting early closes the pipe, the close handler reports why
101
+ });
102
+ child.stdin.end(input);
76
103
  });
77
- return stdout;
78
104
  };
79
105
  exports.runBw = runBw;
80
106
  const runBwJson = async (session, args) => JSON.parse(await (0, exports.runBw)(session, args));
@@ -1 +1 @@
1
- {"version":3,"file":"bw.js","sourceRoot":"","sources":["../../../../../src/vault/bw.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bw.js","sourceRoot":"","sources":["../../../../../src/vault/bw.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.updateSecretsCache = exports.writeSecretsCache = exports.readSecretsCache = void 0;
4
4
  const promises_1 = require("fs/promises");
5
5
  const path_1 = require("path");
6
+ const writePrivateFile_1 = require("../utils/writePrivateFile");
6
7
  const gitProjectInformation_1 = require("../git/gitProjectInformation");
7
8
  const getCacheFolder = async () => (0, path_1.join)(await (0, gitProjectInformation_1.getProjectRootPath)(), ".catladder");
8
9
  const getCachePath = async () => (0, path_1.join)(await getCacheFolder(), "secrets.json");
@@ -17,7 +18,7 @@ const readSecretsCache = async () => {
17
18
  exports.readSecretsCache = readSecretsCache;
18
19
  const writeSecretsCache = async (vaultId, secrets) => {
19
20
  const folder = await getCacheFolder();
20
- await (0, promises_1.mkdir)(folder, { recursive: true });
21
+ await (0, promises_1.mkdir)(folder, { recursive: true, mode: 0o700 });
21
22
  // the folder ignores itself, so the cache can never be committed
22
23
  await (0, promises_1.writeFile)((0, path_1.join)(folder, ".gitignore"), "*\n");
23
24
  const cache = {
@@ -25,7 +26,9 @@ const writeSecretsCache = async (vaultId, secrets) => {
25
26
  updatedAt: new Date().toISOString(),
26
27
  secrets,
27
28
  };
28
- await (0, promises_1.writeFile)(await getCachePath(), JSON.stringify(cache, null, 2));
29
+ const path = await getCachePath();
30
+ // may hold key material (local access keys): owner only
31
+ await (0, writePrivateFile_1.writePrivateFile)(path, JSON.stringify(cache, null, 2));
29
32
  };
30
33
  exports.writeSecretsCache = writeSecretsCache;
31
34
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../../../src/vault/cache.ts"],"names":[],"mappings":";;;AAAA,0CAAyD;AACzD,+BAA4B;AAC5B,wEAAkE;AA6BlE,MAAM,cAAc,GAAG,KAAK,IAAI,EAAE,CAChC,IAAA,WAAI,EAAC,MAAM,IAAA,0CAAkB,GAAE,EAAE,YAAY,CAAC,CAAC;AAEjD,MAAM,YAAY,GAAG,KAAK,IAAI,EAAE,CAAC,IAAA,WAAI,EAAC,MAAM,cAAc,EAAE,EAAE,cAAc,CAAC,CAAC;AAEvE,MAAM,gBAAgB,GAAG,KAAK,IAAkC,EAAE;IACvE,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,IAAA,mBAAQ,EAAC,MAAM,YAAY,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC;IACnE,CAAC;IAAC,WAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AANW,QAAA,gBAAgB,oBAM3B;AAEK,MAAM,iBAAiB,GAAG,KAAK,EACpC,OAAe,EACf,OAAsB,EACP,EAAE;IACjB,MAAM,MAAM,GAAG,MAAM,cAAc,EAAE,CAAC;IACtC,MAAM,IAAA,gBAAK,EAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,iEAAiE;IACjE,MAAM,IAAA,oBAAS,EAAC,IAAA,WAAI,EAAC,MAAM,EAAE,YAAY,CAAC,EAAE,KAAK,CAAC,CAAC;IACnD,MAAM,KAAK,GAAiB;QAC1B,OAAO;QACP,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,OAAO;KACR,CAAC;IACF,MAAM,IAAA,oBAAS,EAAC,MAAM,YAAY,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACxE,CAAC,CAAC;AAdW,QAAA,iBAAiB,qBAc5B;AAEF;;;;GAIG;AACI,MAAM,kBAAkB,GAAG,KAAK,EACrC,OAAe,EACf,GAAW,EACX,aAAqB,EACrB,MAA8B,EACf,EAAE;;IACjB,MAAM,QAAQ,GAAG,MAAM,IAAA,wBAAgB,GAAE,CAAC;IAC1C,MAAM,OAAO,GACX,CAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,OAAO,MAAK,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IACxD,OAAO,CAAC,GAAG,CAAC,GAAG;QACb,GAAG,OAAO,CAAC,GAAG,CAAC;QACf,CAAC,aAAa,CAAC,EAAE,EAAE,GAAG,MAAA,OAAO,CAAC,GAAG,CAAC,0CAAG,aAAa,CAAC,EAAE,GAAG,MAAM,EAAE;KACjE,CAAC;IACF,MAAM,IAAA,yBAAiB,EAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC5C,CAAC,CAAC;AAdW,QAAA,kBAAkB,sBAc7B"}
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../../../src/vault/cache.ts"],"names":[],"mappings":";;;AAAA,0CAAyD;AACzD,+BAA4B;AAC5B,gEAA6D;AAC7D,wEAAkE;AA6BlE,MAAM,cAAc,GAAG,KAAK,IAAI,EAAE,CAChC,IAAA,WAAI,EAAC,MAAM,IAAA,0CAAkB,GAAE,EAAE,YAAY,CAAC,CAAC;AAEjD,MAAM,YAAY,GAAG,KAAK,IAAI,EAAE,CAAC,IAAA,WAAI,EAAC,MAAM,cAAc,EAAE,EAAE,cAAc,CAAC,CAAC;AAEvE,MAAM,gBAAgB,GAAG,KAAK,IAAkC,EAAE;IACvE,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,IAAA,mBAAQ,EAAC,MAAM,YAAY,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC;IACnE,CAAC;IAAC,WAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AANW,QAAA,gBAAgB,oBAM3B;AAEK,MAAM,iBAAiB,GAAG,KAAK,EACpC,OAAe,EACf,OAAsB,EACP,EAAE;IACjB,MAAM,MAAM,GAAG,MAAM,cAAc,EAAE,CAAC;IACtC,MAAM,IAAA,gBAAK,EAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACtD,iEAAiE;IACjE,MAAM,IAAA,oBAAS,EAAC,IAAA,WAAI,EAAC,MAAM,EAAE,YAAY,CAAC,EAAE,KAAK,CAAC,CAAC;IACnD,MAAM,KAAK,GAAiB;QAC1B,OAAO;QACP,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,OAAO;KACR,CAAC;IACF,MAAM,IAAI,GAAG,MAAM,YAAY,EAAE,CAAC;IAClC,wDAAwD;IACxD,MAAM,IAAA,mCAAgB,EAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/D,CAAC,CAAC;AAhBW,QAAA,iBAAiB,qBAgB5B;AAEF;;;;GAIG;AACI,MAAM,kBAAkB,GAAG,KAAK,EACrC,OAAe,EACf,GAAW,EACX,aAAqB,EACrB,MAA8B,EACf,EAAE;;IACjB,MAAM,QAAQ,GAAG,MAAM,IAAA,wBAAgB,GAAE,CAAC;IAC1C,MAAM,OAAO,GACX,CAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,OAAO,MAAK,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IACxD,OAAO,CAAC,GAAG,CAAC,GAAG;QACb,GAAG,OAAO,CAAC,GAAG,CAAC;QACf,CAAC,aAAa,CAAC,EAAE,EAAE,GAAG,MAAA,OAAO,CAAC,GAAG,CAAC,0CAAG,aAAa,CAAC,EAAE,GAAG,MAAM,EAAE;KACjE,CAAC;IACF,MAAM,IAAA,yBAAiB,EAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC5C,CAAC,CAAC;AAdW,QAAA,kBAAkB,sBAc7B"}
@@ -331,6 +331,7 @@ exports.createVaultManagerGetter = createVaultManagerGetter;
331
331
 
332
332
  Object.defineProperty(exports, "__esModule", ({ value: true }));
333
333
  const getProjectConfig_1 = __nccwpck_require__(70478);
334
+ const keyFiles_1 = __nccwpck_require__(70180);
334
335
  const printVariables_1 = __nccwpck_require__(32507);
335
336
  const verboseBanner_1 = __nccwpck_require__(95423);
336
337
  const writeDotEnvFiles_1 = __nccwpck_require__(16699);
@@ -356,6 +357,8 @@ exports["default"] = async (choice, options) => {
356
357
  if (options === null || options === void 0 ? void 0 : options.verbose) {
357
358
  (0, verboseBanner_1.printVerboseBanner)();
358
359
  }
360
+ // before the env vars are resolved: they point at the key files
361
+ await (0, keyFiles_1.writeLocalAccessKeyFiles)(io, config);
359
362
  await Promise.all([
360
363
  (0, pipeline_1.generatePipelineFiles)(context),
361
364
  (0, writeDotEnvFiles_1.writeDotEnvFiles)(context, choice),
@@ -671,6 +674,7 @@ program.parse();
671
674
  Object.defineProperty(exports, "__esModule", ({ value: true }));
672
675
  exports.getJobOnlyEnvVarsResolved = exports.getEnvVarsResolved = exports.getGitlabVar = exports.getEnvironment = exports.getAllPipelineContexts = exports.getAllComponentsWithAllEnvsHierarchical = exports.getAllComponentsWithAllEnvsFlat = exports.getPipelineContextByChoice = exports.getProjectComponents = exports.invalidateProjectConfig = exports.getProjectConfig = exports.parseChoice = void 0;
673
676
  const pipeline_1 = __nccwpck_require__(53695);
677
+ const keyFiles_1 = __nccwpck_require__(70180);
674
678
  const gitlab_1 = __nccwpck_require__(17697);
675
679
  const projects_1 = __nccwpck_require__(20575);
676
680
  var parseChoice_1 = __nccwpck_require__(70769);
@@ -791,12 +795,19 @@ const getEnvVarsResolved = async (io, env, componentName) => {
791
795
  const envionment = await (0, exports.getEnvironment)(env, componentName);
792
796
  // in the pipeline the secrets alreadyy exists and bash will expand them
793
797
  // but here we need to manually load them
794
- return resolveSecrets(io, [
798
+ const resolved = await resolveSecrets(io, [
795
799
  {
796
800
  envVars: envionment.envVars,
797
801
  secretEnvVarKeys: envionment.secretEnvVarKeys,
798
802
  },
799
803
  ]);
804
+ if (env !== "local")
805
+ return resolved;
806
+ // paths of the local access key files (written by catenv)
807
+ return {
808
+ ...resolved,
809
+ ...(await (0, keyFiles_1.getLocalAccessKeyFileVars)(await (0, exports.getProjectConfig)(), componentName)),
810
+ };
800
811
  };
801
812
  exports.getEnvVarsResolved = getEnvVarsResolved;
802
813
  /**
@@ -931,6 +942,127 @@ exports.gitProjectInformation = gitProjectInformation;
931
942
 
932
943
  /***/ }),
933
944
 
945
+ /***/ 83228:
946
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
947
+
948
+ "use strict";
949
+
950
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
951
+ exports.getDeclaredLocalAccessEnvs = void 0;
952
+ const pipeline_1 = __nccwpck_require__(53695);
953
+ /**
954
+ * the envs of a component declaring gcp local access. An invalid
955
+ * declaration yields none instead of throwing: catenv and the vault
956
+ * cache must keep working (`project doctor` and `project setup` report
957
+ * the invalid declaration loudly).
958
+ */
959
+ const getDeclaredLocalAccessEnvs = (config, componentName) => {
960
+ try {
961
+ return (0, pipeline_1.getLocalAccessGcpEnvs)(config, componentName);
962
+ }
963
+ catch (_a) {
964
+ return [];
965
+ }
966
+ };
967
+ exports.getDeclaredLocalAccessEnvs = getDeclaredLocalAccessEnvs;
968
+ //# sourceMappingURL=declared.js.map
969
+
970
+ /***/ }),
971
+
972
+ /***/ 70180:
973
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
974
+
975
+ "use strict";
976
+
977
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
978
+ exports.writeLocalAccessKeyFiles = exports.getLocalAccessKeyFileVars = exports.getLocalAccessKeyFileVarName = exports.getLocalAccessKeyFileName = void 0;
979
+ const pipeline_1 = __nccwpck_require__(53695);
980
+ const promises_1 = __nccwpck_require__(91943);
981
+ const fs_1 = __nccwpck_require__(79896);
982
+ const path_1 = __nccwpck_require__(16928);
983
+ const writePrivateFile_1 = __nccwpck_require__(14550);
984
+ const declared_1 = __nccwpck_require__(83228);
985
+ const gitProjectInformation_1 = __nccwpck_require__(86884);
986
+ const sanitize = (s) => s.replace(/[^A-Za-z0-9]+/g, "_");
987
+ /** file name of a component/env key, relative to the local access folder */
988
+ const getLocalAccessKeyFileName = (env, componentName) => `gcp-${sanitize(env)}-${sanitize(componentName)}.json`;
989
+ exports.getLocalAccessKeyFileName = getLocalAccessKeyFileName;
990
+ /**
991
+ * env var exposing the key file path. Deliberately NOT
992
+ * GOOGLE_APPLICATION_CREDENTIALS: the key must only be used where a
993
+ * command opts in (isolated gcloud config), never by ADC.
994
+ */
995
+ const getLocalAccessKeyFileVarName = (env) => `CATLADDER_LOCAL_ACCESS_GCP_${sanitize(env).toUpperCase()}_KEY_FILE`;
996
+ exports.getLocalAccessKeyFileVarName = getLocalAccessKeyFileVarName;
997
+ // inside .catladder/, which ignores itself (see vault/cache.ts)
998
+ const getFolder = async () => (0, path_1.join)(await (0, gitProjectInformation_1.getProjectRootPath)(), ".catladder", "local-access");
999
+ /**
1000
+ * env vars pointing at the key files that exist for the component
1001
+ * (only paths, no secrets)
1002
+ */
1003
+ const getLocalAccessKeyFileVars = async (config, componentName) => {
1004
+ const folder = await getFolder();
1005
+ return Object.fromEntries((0, declared_1.getDeclaredLocalAccessEnvs)(config, componentName)
1006
+ .map((env) => [
1007
+ (0, exports.getLocalAccessKeyFileVarName)(env),
1008
+ (0, path_1.join)(folder, (0, exports.getLocalAccessKeyFileName)(env, componentName)),
1009
+ ])
1010
+ .filter(([, path]) => (0, fs_1.existsSync)(path)));
1011
+ };
1012
+ exports.getLocalAccessKeyFileVars = getLocalAccessKeyFileVars;
1013
+ /**
1014
+ * writes the stored local access service account keys to gitignored
1015
+ * files (mode 0600) and removes files of declarations that are gone.
1016
+ * A no-op without any declaration (nothing is created). Never throws:
1017
+ * a vault that cannot be reached or an invalid declaration (reported by
1018
+ * `project doctor`) must not break catenv.
1019
+ *
1020
+ * The key is only read through the cache; if setup rotated it on another
1021
+ * machine, refresh with `catenv --vault-mode refresh`.
1022
+ */
1023
+ const writeLocalAccessKeyFiles = async (io, config) => {
1024
+ const wanted = Object.keys(config.components).flatMap((componentName) => (0, declared_1.getDeclaredLocalAccessEnvs)(config, componentName).map((env) => ({
1025
+ env,
1026
+ componentName,
1027
+ secretName: (0, pipeline_1.getSecretVarName)("local", componentName, (0, pipeline_1.getLocalAccessGcpSecretName)(env)),
1028
+ fileName: (0, exports.getLocalAccessKeyFileName)(env, componentName),
1029
+ })));
1030
+ const folder = await getFolder();
1031
+ if (wanted.length === 0 && !(0, fs_1.existsSync)(folder))
1032
+ return;
1033
+ try {
1034
+ const secrets = wanted.length > 0
1035
+ ? await (await io.getVaultManager()).readSecrets(wanted.map((w) => w.secretName), io)
1036
+ : {};
1037
+ await (0, promises_1.mkdir)(folder, { recursive: true, mode: 0o700 });
1038
+ // the parent folder ignores itself
1039
+ await (0, promises_1.writeFile)((0, path_1.join)(folder, "..", ".gitignore"), "*\n");
1040
+ const written = new Set();
1041
+ for (const { secretName, fileName, env, componentName } of wanted) {
1042
+ const key = secrets[secretName];
1043
+ if (!key) {
1044
+ io.log(`⚠️ no local access key for ${env}:${componentName} in the secret store — run \`catladder project setup\` (or \`catenv --vault-mode refresh\` if it was set up on another machine)`);
1045
+ continue;
1046
+ }
1047
+ const path = (0, path_1.join)(folder, fileName);
1048
+ await (0, writePrivateFile_1.writePrivateFile)(path, key);
1049
+ written.add(fileName);
1050
+ }
1051
+ for (const file of await (0, promises_1.readdir)(folder)) {
1052
+ if (!written.has(file)) {
1053
+ await (0, promises_1.rm)((0, path_1.join)(folder, file), { force: true });
1054
+ }
1055
+ }
1056
+ }
1057
+ catch (e) {
1058
+ io.log(`⚠️ could not write local access keys: ${e.message}`);
1059
+ }
1060
+ };
1061
+ exports.writeLocalAccessKeyFiles = writeLocalAccessKeyFiles;
1062
+ //# sourceMappingURL=keyFiles.js.map
1063
+
1064
+ /***/ }),
1065
+
934
1066
  /***/ 55689:
935
1067
  /***/ ((__unused_webpack_module, exports) => {
936
1068
 
@@ -1347,6 +1479,38 @@ exports.getProjectNamespace = getProjectNamespace;
1347
1479
 
1348
1480
  /***/ }),
1349
1481
 
1482
+ /***/ 14550:
1483
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
1484
+
1485
+ "use strict";
1486
+
1487
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
1488
+ exports.writePrivateFile = void 0;
1489
+ const promises_1 = __nccwpck_require__(91943);
1490
+ /**
1491
+ * writes a file readable by the owner only. Goes through a 0600 temp
1492
+ * file and a rename, so the content is never visible with looser
1493
+ * permissions (writing first and chmod-ing afterwards would leave an
1494
+ * existing 0644 file readable until the chmod).
1495
+ */
1496
+ const writePrivateFile = async (path, content) => {
1497
+ const tmp = `${path}.${process.pid}.tmp`;
1498
+ try {
1499
+ await (0, promises_1.writeFile)(tmp, content, { mode: 0o600 });
1500
+ // the mode is only applied when the file is created
1501
+ await (0, promises_1.chmod)(tmp, 0o600);
1502
+ await (0, promises_1.rename)(tmp, path);
1503
+ }
1504
+ catch (e) {
1505
+ await (0, promises_1.rm)(tmp, { force: true });
1506
+ throw e;
1507
+ }
1508
+ };
1509
+ exports.writePrivateFile = writePrivateFile;
1510
+ //# sourceMappingURL=writePrivateFile.js.map
1511
+
1512
+ /***/ }),
1513
+
1350
1514
  /***/ 42827:
1351
1515
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
1352
1516
 
@@ -1453,30 +1617,21 @@ class BitwardenVault {
1453
1617
  : {};
1454
1618
  const notes = (0, yaml_1.stringify)({ ...previous, ...secrets });
1455
1619
  if (existing) {
1456
- await (0, bw_1.runBw)(bwSession, [
1457
- "edit",
1458
- "item",
1459
- existing.id,
1460
- encodePayload({ ...existing, notes }),
1461
- ]);
1620
+ await (0, bw_1.runBw)(bwSession, ["edit", "item", existing.id], encodePayload({ ...existing, notes }));
1462
1621
  }
1463
1622
  else {
1464
- await (0, bw_1.runBw)(bwSession, [
1465
- "create",
1466
- "item",
1467
- encodePayload({
1468
- type: SECURE_NOTE_TYPE,
1469
- secureNote: { type: 0 },
1470
- name,
1471
- notes,
1472
- ...(collection.organizationId
1473
- ? {
1474
- organizationId: collection.organizationId,
1475
- collectionIds: [collection.id],
1476
- }
1477
- : {}),
1478
- }),
1479
- ]);
1623
+ await (0, bw_1.runBw)(bwSession, ["create", "item"], encodePayload({
1624
+ type: SECURE_NOTE_TYPE,
1625
+ secureNote: { type: 0 },
1626
+ name,
1627
+ notes,
1628
+ ...(collection.organizationId
1629
+ ? {
1630
+ organizationId: collection.organizationId,
1631
+ collectionIds: [collection.id],
1632
+ }
1633
+ : {}),
1634
+ }));
1480
1635
  }
1481
1636
  }
1482
1637
  }
@@ -1523,6 +1678,7 @@ exports.GitlabVault = GitlabVault;
1523
1678
  Object.defineProperty(exports, "__esModule", ({ value: true }));
1524
1679
  exports.VaultManager = void 0;
1525
1680
  const pipeline_1 = __nccwpck_require__(53695);
1681
+ const declared_1 = __nccwpck_require__(83228);
1526
1682
  const BitwardenVault_1 = __nccwpck_require__(42827);
1527
1683
  const cache_1 = __nccwpck_require__(57011);
1528
1684
  const GitlabVault_1 = __nccwpck_require__(6020);
@@ -1605,6 +1761,15 @@ class VaultManager {
1605
1761
  ])));
1606
1762
  }
1607
1763
  }
1764
+ /**
1765
+ * updates the local cache only (the vault already holds the values),
1766
+ * "local" env only
1767
+ */
1768
+ async cacheSecrets(env, componentName, secrets) {
1769
+ if (env !== CACHED_ENV)
1770
+ return;
1771
+ await (0, cache_1.updateSecretsCache)(this.vault.id, env, componentName, secrets);
1772
+ }
1608
1773
  /**
1609
1774
  * the subset of the vault secrets that may be cached: the declared
1610
1775
  * secrets of the "local" env, per component, as a hierarchy
@@ -1628,6 +1793,14 @@ class VaultManager {
1628
1793
  return Object.keys(local).length > 0 ? { [CACHED_ENV]: local } : {};
1629
1794
  }
1630
1795
  async declaredSecretKeys(componentName) {
1796
+ return [
1797
+ ...(await this.declaredEnvSecretKeys(componentName)),
1798
+ // the local access keys `project setup` provisions are not part
1799
+ // of any env's vars, but belong to the cached local secrets
1800
+ ...(0, declared_1.getDeclaredLocalAccessEnvs)(this.config, componentName).map(pipeline_1.getLocalAccessGcpSecretName),
1801
+ ];
1802
+ }
1803
+ async declaredEnvSecretKeys(componentName) {
1631
1804
  try {
1632
1805
  const { secretEnvVarKeys, jobOnlyVars } = await (0, pipeline_1.getEnvironment)({
1633
1806
  config: this.config,
@@ -1737,14 +1910,40 @@ const getBwSession = async (io, { allowPrompt }) => {
1737
1910
  };
1738
1911
  exports.getBwSession = getBwSession;
1739
1912
  /**
1740
- * runs a bw command with the given session and returns stdout
1913
+ * runs a bw command with the given session and returns stdout.
1914
+ *
1915
+ * `input` is written to stdin: use it for payloads (e.g. the secrets of
1916
+ * `bw create item`), which would otherwise be visible in `ps` and be
1917
+ * echoed by node's "Command failed" error.
1741
1918
  */
1742
- const runBw = async (session, args) => {
1743
- const { stdout } = await execFile("bw", [...args, "--raw", "--nointeraction"], {
1744
- env: { ...process.env, BW_SESSION: session },
1745
- maxBuffer: 50 * 1024 * 1024,
1919
+ const runBw = async (session, args, input) => {
1920
+ const fullArgs = [...args, "--raw", "--nointeraction"];
1921
+ const env = { ...process.env, BW_SESSION: session };
1922
+ if (input === undefined) {
1923
+ const { stdout } = await execFile("bw", fullArgs, {
1924
+ env,
1925
+ maxBuffer: 50 * 1024 * 1024,
1926
+ });
1927
+ return stdout;
1928
+ }
1929
+ return new Promise((resolve, reject) => {
1930
+ const child = (0, child_process_1.spawn)("bw", fullArgs, {
1931
+ env,
1932
+ stdio: ["pipe", "pipe", "pipe"],
1933
+ });
1934
+ let stdout = "";
1935
+ let stderr = "";
1936
+ child.stdout.on("data", (data) => (stdout += data.toString("utf-8")));
1937
+ child.stderr.on("data", (data) => (stderr += data.toString("utf-8")));
1938
+ child.on("error", reject);
1939
+ child.on("close", (code) => code === 0
1940
+ ? resolve(stdout)
1941
+ : reject(new Error(`bw ${args[0]} failed: ${stderr.trim()}`)));
1942
+ child.stdin.on("error", () => {
1943
+ // bw exiting early closes the pipe, the close handler reports why
1944
+ });
1945
+ child.stdin.end(input);
1746
1946
  });
1747
- return stdout;
1748
1947
  };
1749
1948
  exports.runBw = runBw;
1750
1949
  const runBwJson = async (session, args) => JSON.parse(await (0, exports.runBw)(session, args));
@@ -1762,6 +1961,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
1762
1961
  exports.updateSecretsCache = exports.writeSecretsCache = exports.readSecretsCache = void 0;
1763
1962
  const promises_1 = __nccwpck_require__(91943);
1764
1963
  const path_1 = __nccwpck_require__(16928);
1964
+ const writePrivateFile_1 = __nccwpck_require__(14550);
1765
1965
  const gitProjectInformation_1 = __nccwpck_require__(86884);
1766
1966
  const getCacheFolder = async () => (0, path_1.join)(await (0, gitProjectInformation_1.getProjectRootPath)(), ".catladder");
1767
1967
  const getCachePath = async () => (0, path_1.join)(await getCacheFolder(), "secrets.json");
@@ -1776,7 +1976,7 @@ const readSecretsCache = async () => {
1776
1976
  exports.readSecretsCache = readSecretsCache;
1777
1977
  const writeSecretsCache = async (vaultId, secrets) => {
1778
1978
  const folder = await getCacheFolder();
1779
- await (0, promises_1.mkdir)(folder, { recursive: true });
1979
+ await (0, promises_1.mkdir)(folder, { recursive: true, mode: 0o700 });
1780
1980
  // the folder ignores itself, so the cache can never be committed
1781
1981
  await (0, promises_1.writeFile)((0, path_1.join)(folder, ".gitignore"), "*\n");
1782
1982
  const cache = {
@@ -1784,7 +1984,9 @@ const writeSecretsCache = async (vaultId, secrets) => {
1784
1984
  updatedAt: new Date().toISOString(),
1785
1985
  secrets,
1786
1986
  };
1787
- await (0, promises_1.writeFile)(await getCachePath(), JSON.stringify(cache, null, 2));
1987
+ const path = await getCachePath();
1988
+ // may hold key material (local access keys): owner only
1989
+ await (0, writePrivateFile_1.writePrivateFile)(path, JSON.stringify(cache, null, 2));
1788
1990
  };
1789
1991
  exports.writeSecretsCache = writeSecretsCache;
1790
1992
  /**
@@ -12677,6 +12879,163 @@ __exportStar(__nccwpck_require__(91804), exports);
12677
12879
  __exportStar(__nccwpck_require__(94143), exports);
12678
12880
  __exportStar(__nccwpck_require__(98011), exports);
12679
12881
  __exportStar(__nccwpck_require__(61459), exports);
12882
+ __exportStar(__nccwpck_require__(54165), exports);
12883
+ //# sourceMappingURL=index.js.map
12884
+
12885
+ /***/ }),
12886
+
12887
+ /***/ 54165:
12888
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
12889
+
12890
+ "use strict";
12891
+
12892
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
12893
+ exports.diffRoleBindings = exports.getLocalAccessGcpSecretName = exports.getLocalAccessGcpEnvs = exports.getLocalAccessGcpRoles = exports.getLocalAccessConfig = exports.getDeniedLocalAccessRoleReason = exports.LOCAL_ACCESS_GCP_SA_NAME = exports.LOCAL_ACCESS_GCP_PRESETS = void 0;
12894
+ const configruedEnvs_1 = __nccwpck_require__(38866);
12895
+ const getEnvConfig_1 = __nccwpck_require__(69578);
12896
+ const types_1 = __nccwpck_require__(79689);
12897
+ exports.LOCAL_ACCESS_GCP_PRESETS = {
12898
+ // no run.viewer on purpose, see LocalAccessGcpPreset
12899
+ logs: ["roles/logging.viewer"],
12900
+ };
12901
+ /** name prefix of the service account `project setup` creates (see getGcloudServiceAccountNames) */
12902
+ exports.LOCAL_ACCESS_GCP_SA_NAME = "local-diag";
12903
+ const ROLE_FORMAT = /^(roles\/[A-Za-z0-9_.]+|projects\/[a-z0-9-]+\/roles\/[A-Za-z0-9_.]+|organizations\/\d+\/roles\/[A-Za-z0-9_.]+)$/;
12904
+ /**
12905
+ * roles that are read-only on paper but expose secrets: cloud run
12906
+ * services/jobs carry their secrets as plain env vars, so anything that
12907
+ * may read them reads every deployed secret. Denied even with
12908
+ * `allowAnyRole`.
12909
+ */
12910
+ const SECRET_EXPOSING_ROLES = {
12911
+ "roles/viewer": "roles/viewer can read cloud run env vars (secrets)",
12912
+ "roles/run.viewer": "roles/run.viewer can read cloud run env vars (secrets)",
12913
+ "roles/secretmanager.secretAccessor": "this role reads secret values",
12914
+ "roles/secretmanager.admin": "this role reads secret values",
12915
+ };
12916
+ /**
12917
+ * roles granted without `allowAnyRole`. An explicit list instead of a
12918
+ * name pattern: a role called `*.viewer` / `*.reader` says nothing about
12919
+ * its permissions (custom roles can hold anything, and predefined ones
12920
+ * like storage.objectViewer, cloudfunctions.viewer, compute.viewer or
12921
+ * logging.privateLogViewer expose data or secrets).
12922
+ */
12923
+ const SAFE_ROLES = [
12924
+ "roles/logging.viewer",
12925
+ "roles/monitoring.viewer",
12926
+ "roles/errorreporting.viewer",
12927
+ ];
12928
+ /** `roles/foo.bar.v1` -> `foo.bar` (version suffixes must not defeat the checks) */
12929
+ const getRoleName = (role) => { var _a; return ((_a = role.split("/").pop()) !== null && _a !== void 0 ? _a : role).replace(/(\.v\d+)+$/i, ""); };
12930
+ /**
12931
+ * why a role may not be granted for local access, or undefined if it
12932
+ * is acceptable.
12933
+ *
12934
+ * By default only the roles in {@link SAFE_ROLES} pass; custom roles
12935
+ * and everything else need `allowAnyRole`. `allowAnyRole` lifts the
12936
+ * allow-list, but the obvious write/admin/impersonation roles and the
12937
+ * secret exposing roles stay denied.
12938
+ */
12939
+ const getDeniedLocalAccessRoleReason = (role, options = {}) => {
12940
+ var _a;
12941
+ const secretReason = SECRET_EXPOSING_ROLES[role];
12942
+ if (secretReason)
12943
+ return secretReason;
12944
+ const name = getRoleName(role);
12945
+ if (name === "owner" || name === "editor") {
12946
+ return "owner/editor are full write roles";
12947
+ }
12948
+ const last = (_a = name.split(".").pop()) !== null && _a !== void 0 ? _a : name;
12949
+ if (/admin$/i.test(last)) {
12950
+ return "admin roles can change resources";
12951
+ }
12952
+ if (/(editor|developer|writer|creator|deployer|invoker|builder|publisher|client|user|objectUser)$/i.test(last) &&
12953
+ !/^iam\./.test(name)) {
12954
+ return "this role can change resources";
12955
+ }
12956
+ if (/^iam\./.test(name) &&
12957
+ /(serviceAccountUser|serviceAccountTokenCreator|serviceAccountKeyAdmin|workloadIdentityUser)$/.test(name)) {
12958
+ return "this role allows impersonating service accounts";
12959
+ }
12960
+ if (!options.allowAnyRole && !SAFE_ROLES.includes(role)) {
12961
+ return `only these roles are allowed: ${SAFE_ROLES.join(", ")} (set allowAnyRole: true to grant others at your own risk)`;
12962
+ }
12963
+ return undefined;
12964
+ };
12965
+ exports.getDeniedLocalAccessRoleReason = getDeniedLocalAccessRoleReason;
12966
+ /**
12967
+ * the effective local access declaration of an env: the env's own
12968
+ * declaration replaces the project default entirely
12969
+ */
12970
+ const getLocalAccessConfig = (config, env) => {
12971
+ var _a;
12972
+ if (env === "local")
12973
+ return undefined;
12974
+ const envEntry = (_a = config.environments) === null || _a === void 0 ? void 0 : _a[env];
12975
+ const declared = envEntry && envEntry.localAccess !== undefined
12976
+ ? envEntry.localAccess
12977
+ : config.localAccess;
12978
+ return declared || undefined;
12979
+ };
12980
+ exports.getLocalAccessConfig = getLocalAccessConfig;
12981
+ /**
12982
+ * the gcp roles local access grants in `env`: preset roles plus the
12983
+ * explicit ones, deduplicated and sorted. Throws on an unknown preset,
12984
+ * a malformed role or a denied (write/admin) role.
12985
+ */
12986
+ const getLocalAccessGcpRoles = (config, env) => {
12987
+ var _a, _b, _c;
12988
+ const gcp = (_a = (0, exports.getLocalAccessConfig)(config, env)) === null || _a === void 0 ? void 0 : _a.gcp;
12989
+ if (!gcp)
12990
+ return [];
12991
+ const roles = new Set();
12992
+ for (const preset of (_b = gcp.presets) !== null && _b !== void 0 ? _b : []) {
12993
+ const presetRoles = exports.LOCAL_ACCESS_GCP_PRESETS[preset];
12994
+ if (!presetRoles) {
12995
+ throw new Error(`localAccess (${env}): unknown gcp preset '${preset}', known: ${Object.keys(exports.LOCAL_ACCESS_GCP_PRESETS).join(", ")}`);
12996
+ }
12997
+ presetRoles.forEach((role) => roles.add(role));
12998
+ }
12999
+ for (const role of (_c = gcp.roles) !== null && _c !== void 0 ? _c : []) {
13000
+ if (!ROLE_FORMAT.test(role)) {
13001
+ throw new Error(`localAccess (${env}): '${role}' is not a valid role (expected e.g. roles/logging.viewer)`);
13002
+ }
13003
+ const reason = (0, exports.getDeniedLocalAccessRoleReason)(role, {
13004
+ allowAnyRole: gcp.allowAnyRole,
13005
+ });
13006
+ if (reason) {
13007
+ throw new Error(`localAccess (${env}): role '${role}' is not allowed (${reason})`);
13008
+ }
13009
+ roles.add(role);
13010
+ }
13011
+ return [...roles].sort();
13012
+ };
13013
+ exports.getLocalAccessGcpRoles = getLocalAccessGcpRoles;
13014
+ /**
13015
+ * the envs of a component that declare gcp local access. Empty for
13016
+ * components that do not deploy to google cloud run.
13017
+ */
13018
+ const getLocalAccessGcpEnvs = (config, componentName) => (0, configruedEnvs_1.getAllEnvs)(config, componentName).filter((env) =>
13019
+ // only google cloud run envs have a project to grant roles in; the
13020
+ // env's own `deploy` overrides the component's
13021
+ (0, types_1.isOfDeployType)((0, getEnvConfig_1.getEnvConfig)(config, componentName, env).deploy, "google-cloudrun") && (0, exports.getLocalAccessGcpRoles)(config, env).length > 0);
13022
+ exports.getLocalAccessGcpEnvs = getLocalAccessGcpEnvs;
13023
+ /**
13024
+ * name of the secret (stored under the `local` env of the component)
13025
+ * holding the service account key of `env`'s local access
13026
+ */
13027
+ const getLocalAccessGcpSecretName = (env) => `LOCAL_ACCESS_GCP_${env.replace(/[^A-Za-z0-9]+/g, "_").toUpperCase()}_CREDENTIALS`;
13028
+ exports.getLocalAccessGcpSecretName = getLocalAccessGcpSecretName;
13029
+ /**
13030
+ * the bindings to add and remove so that `current` matches `desired`.
13031
+ * The local access account is dedicated, so every binding it carries
13032
+ * beyond the declaration is extra.
13033
+ */
13034
+ const diffRoleBindings = (desired, current) => ({
13035
+ missing: desired.filter((role) => !current.includes(role)),
13036
+ extra: current.filter((role) => !desired.includes(role)),
13037
+ });
13038
+ exports.diffRoleBindings = diffRoleBindings;
12680
13039
  //# sourceMappingURL=index.js.map
12681
13040
 
12682
13041
  /***/ }),