@red-hat-developer-hub/e2e-test-utils 2.1.14 → 2.1.16

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 (79) hide show
  1. package/dist/deployment/rhdh/constants.d.ts +0 -1
  2. package/dist/deployment/rhdh/constants.d.ts.map +1 -1
  3. package/dist/deployment/rhdh/constants.js +0 -1
  4. package/dist/deployment/rhdh/deployment.d.ts.map +1 -1
  5. package/dist/deployment/rhdh/deployment.js +0 -3
  6. package/dist/secrets/bitwarden.d.ts +43 -0
  7. package/dist/secrets/bitwarden.d.ts.map +1 -1
  8. package/dist/secrets/bitwarden.js +465 -53
  9. package/dist/secrets/cli.d.ts +48 -1
  10. package/dist/secrets/cli.d.ts.map +1 -1
  11. package/dist/secrets/cli.js +455 -28
  12. package/dist/secrets/command.d.ts +7 -0
  13. package/dist/secrets/command.d.ts.map +1 -1
  14. package/dist/secrets/command.js +103 -26
  15. package/dist/secrets/config.d.ts +11 -1
  16. package/dist/secrets/config.d.ts.map +1 -1
  17. package/dist/secrets/config.js +32 -1
  18. package/dist/secrets/environment-name.d.ts +2 -0
  19. package/dist/secrets/environment-name.d.ts.map +1 -0
  20. package/dist/secrets/environment-name.js +4 -0
  21. package/dist/secrets/environment.d.ts +10 -0
  22. package/dist/secrets/environment.d.ts.map +1 -1
  23. package/dist/secrets/environment.js +35 -4
  24. package/dist/secrets/exec.d.ts +9 -1
  25. package/dist/secrets/exec.d.ts.map +1 -1
  26. package/dist/secrets/exec.js +318 -38
  27. package/dist/secrets/gsm-wrapper.d.ts +42 -0
  28. package/dist/secrets/gsm-wrapper.d.ts.map +1 -0
  29. package/dist/secrets/gsm-wrapper.js +238 -0
  30. package/dist/secrets/gsm.d.ts +33 -0
  31. package/dist/secrets/gsm.d.ts.map +1 -0
  32. package/dist/secrets/gsm.js +172 -0
  33. package/dist/secrets/index.d.ts +9 -4
  34. package/dist/secrets/index.d.ts.map +1 -1
  35. package/dist/secrets/index.js +7 -2
  36. package/dist/secrets/lock.d.ts +3 -0
  37. package/dist/secrets/lock.d.ts.map +1 -0
  38. package/dist/secrets/lock.js +28 -0
  39. package/dist/secrets/mutation.d.ts +71 -0
  40. package/dist/secrets/mutation.d.ts.map +1 -0
  41. package/dist/secrets/mutation.js +210 -0
  42. package/dist/secrets/secret-input.d.ts +17 -0
  43. package/dist/secrets/secret-input.d.ts.map +1 -0
  44. package/dist/secrets/secret-input.js +40 -0
  45. package/dist/secrets/stream.d.ts +9 -0
  46. package/dist/secrets/stream.d.ts.map +1 -0
  47. package/dist/secrets/stream.js +211 -0
  48. package/dist/secrets/temporary-secret.d.ts +13 -0
  49. package/dist/secrets/temporary-secret.d.ts.map +1 -0
  50. package/dist/secrets/temporary-secret.js +87 -0
  51. package/dist/secrets/tests/bitwarden.test.js +640 -0
  52. package/dist/secrets/tests/cli.integration.test.js +131 -3
  53. package/dist/secrets/tests/cli.test.js +281 -6
  54. package/dist/secrets/tests/command.test.d.ts +2 -0
  55. package/dist/secrets/tests/command.test.d.ts.map +1 -0
  56. package/dist/secrets/tests/command.test.js +39 -0
  57. package/dist/secrets/tests/config.test.js +20 -1
  58. package/dist/secrets/tests/environment.test.js +145 -3
  59. package/dist/secrets/tests/exec.test.js +469 -2
  60. package/dist/secrets/tests/gsm-wrapper.test.d.ts +2 -0
  61. package/dist/secrets/tests/gsm-wrapper.test.d.ts.map +1 -0
  62. package/dist/secrets/tests/gsm-wrapper.test.js +166 -0
  63. package/dist/secrets/tests/gsm.test.d.ts +2 -0
  64. package/dist/secrets/tests/gsm.test.d.ts.map +1 -0
  65. package/dist/secrets/tests/gsm.test.js +181 -0
  66. package/dist/secrets/tests/mutation.test.d.ts +2 -0
  67. package/dist/secrets/tests/mutation.test.d.ts.map +1 -0
  68. package/dist/secrets/tests/mutation.test.js +390 -0
  69. package/dist/secrets/tests/secret-input.test.d.ts +2 -0
  70. package/dist/secrets/tests/secret-input.test.d.ts.map +1 -0
  71. package/dist/secrets/tests/secret-input.test.js +71 -0
  72. package/dist/secrets/tests/stream.test.d.ts +2 -0
  73. package/dist/secrets/tests/stream.test.d.ts.map +1 -0
  74. package/dist/secrets/tests/stream.test.js +88 -0
  75. package/dist/secrets/tests/temporary-secret.test.d.ts +2 -0
  76. package/dist/secrets/tests/temporary-secret.test.d.ts.map +1 -0
  77. package/dist/secrets/tests/temporary-secret.test.js +76 -0
  78. package/package.json +1 -1
  79. package/dist/deployment/rhdh/config/new-frontend-system/secrets.yaml +0 -8
@@ -1,30 +1,107 @@
1
1
  import { spawn } from "node:child_process";
2
- export const runCommand = (command, args, options = {}) => new Promise((resolve) => {
3
- const child = spawn(command, args, {
4
- cwd: options.cwd,
5
- env: options.env,
6
- stdio: options.stdio === "inherit" ? "inherit" : "pipe",
7
- });
8
- let stdout = "";
9
- let stderr = "";
10
- if (options.stdio !== "inherit") {
11
- child.stdout?.setEncoding("utf8");
12
- child.stderr?.setEncoding("utf8");
13
- child.stdout?.on("data", (chunk) => {
14
- stdout += chunk;
2
+ import { access, constants, open } from "node:fs/promises";
3
+ export const MAX_TIMEOUT_MS = 2_147_483_647;
4
+ export function shouldDetachCommand(options = {}) {
5
+ return process.platform !== "win32" && options.tty !== true;
6
+ }
7
+ export const runCommand = async (command, args, options = {}) => {
8
+ if (options.timeoutMs !== undefined &&
9
+ (!Number.isSafeInteger(options.timeoutMs) ||
10
+ options.timeoutMs <= 0 ||
11
+ options.timeoutMs > MAX_TIMEOUT_MS)) {
12
+ throw new Error(`Command timeout must be a positive integer no greater than ${MAX_TIMEOUT_MS}ms`);
13
+ }
14
+ const detached = shouldDetachCommand(options);
15
+ let tty;
16
+ if (options.tty) {
17
+ tty = await open("/dev/tty", "r+");
18
+ }
19
+ return new Promise((resolve) => {
20
+ const child = spawn(command, args, {
21
+ cwd: options.cwd,
22
+ env: options.env,
23
+ stdio: options.tty
24
+ ? [tty.fd, "inherit", "inherit"]
25
+ : options.stdio === "inherit"
26
+ ? "inherit"
27
+ : "pipe",
28
+ detached,
15
29
  });
16
- child.stderr?.on("data", (chunk) => {
17
- stderr += chunk;
30
+ let stdout = "";
31
+ let stderr = "";
32
+ let timedOut = false;
33
+ let settled = false;
34
+ let forceTimeout;
35
+ const terminate = (signal) => {
36
+ if (child.pid !== undefined && detached) {
37
+ try {
38
+ process.kill(-child.pid, signal);
39
+ return;
40
+ }
41
+ catch {
42
+ // Fall through to the direct child signal.
43
+ }
44
+ }
45
+ child.kill(signal);
46
+ };
47
+ const signals = ["SIGINT", "SIGTERM", "SIGHUP"];
48
+ const forwarders = new Map();
49
+ for (const signal of signals) {
50
+ const forward = () => terminate(signal);
51
+ forwarders.set(signal, forward);
52
+ process.on(signal, forward);
53
+ }
54
+ const timeout = options.timeoutMs === undefined
55
+ ? undefined
56
+ : setTimeout(() => {
57
+ timedOut = true;
58
+ terminate("SIGTERM");
59
+ forceTimeout = setTimeout(() => {
60
+ forceTimeout = undefined;
61
+ terminate("SIGKILL");
62
+ }, 1_000);
63
+ }, options.timeoutMs);
64
+ const finish = (result) => {
65
+ if (settled)
66
+ return;
67
+ settled = true;
68
+ if (timeout !== undefined)
69
+ clearTimeout(timeout);
70
+ if (forceTimeout !== undefined)
71
+ clearTimeout(forceTimeout);
72
+ for (const [signal, forward] of forwarders) {
73
+ process.removeListener(signal, forward);
74
+ }
75
+ void tty?.close();
76
+ resolve({ ...result, ...(timedOut ? { timedOut: true } : {}) });
77
+ };
78
+ if (options.stdio !== "inherit") {
79
+ child.stdout?.setEncoding("utf8");
80
+ child.stderr?.setEncoding("utf8");
81
+ child.stdout?.on("data", (chunk) => {
82
+ stdout += chunk;
83
+ });
84
+ child.stderr?.on("data", (chunk) => {
85
+ stderr += chunk;
86
+ });
87
+ if (options.input !== undefined)
88
+ child.stdin?.end(options.input);
89
+ else
90
+ child.stdin?.end();
91
+ }
92
+ child.once("error", (error) => {
93
+ finish({ status: null, stdout, stderr, error });
94
+ });
95
+ child.once("close", (status, signal) => {
96
+ finish({
97
+ status,
98
+ stdout,
99
+ stderr,
100
+ ...(signal === null ? {} : { signal }),
101
+ });
18
102
  });
19
- if (options.input !== undefined)
20
- child.stdin?.end(options.input);
21
- else
22
- child.stdin?.end();
23
- }
24
- child.once("error", (error) => {
25
- resolve({ status: null, stdout, stderr, error });
26
- });
27
- child.once("close", (status) => {
28
- resolve({ status, stdout, stderr });
29
103
  });
30
- });
104
+ };
105
+ export async function assertInteractiveTerminal() {
106
+ await access("/dev/tty", constants.R_OK | constants.W_OK);
107
+ }
@@ -2,6 +2,10 @@ export type ReadableCollectionId = "rhdh-qe" | "rhdh-test-instance" | "rhdh-plug
2
2
  export interface CollectionMapping {
3
3
  id: ReadableCollectionId;
4
4
  bitwardenCollection: string;
5
+ gsmCollection?: string;
6
+ }
7
+ interface GsmCollectionMapping extends CollectionMapping {
8
+ gsmCollection: string;
5
9
  }
6
10
  export interface SecretProfile {
7
11
  schemaVersion: 1;
@@ -27,7 +31,13 @@ export interface ExpandedSecretProfile {
27
31
  collection: ReadableCollectionId;
28
32
  selectors: readonly ExpandedSecretSelector[];
29
33
  }
30
- export declare function getCollectionMapping(collection: string): CollectionMapping;
34
+ export declare const COLLECTIONS: readonly GsmCollectionMapping[];
35
+ export declare const READABLE_COLLECTIONS: readonly ReadableCollectionId[];
36
+ export declare function getCollectionMapping(collection: string): GsmCollectionMapping;
37
+ export declare function gsmPathFromBitwardenPath(path: string): string;
38
+ export declare function bitwardenPathFromGsmPath(path: string): string;
39
+ export declare function validateSecretPath(path: string): void;
31
40
  export declare function parseProfile(value: unknown): SecretProfile;
32
41
  export declare function expandProfile(profile: SecretProfile, workspaces?: readonly string[]): ExpandedSecretProfile;
42
+ export {};
33
43
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/secrets/config.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,oBAAoB,GAC5B,SAAS,GACT,oBAAoB,GACpB,6BAA6B,CAAC;AAElC,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,oBAAoB,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,CAAC,CAAC;IACjB,UAAU,EAAE,oBAAoB,CAAC;IACjC,SAAS,EAAE,SAAS,cAAc,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,sBAAsB,CAAC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,aAAa,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,UAAU,GAAG,YAAY,CAAC;CACzC;AAED,MAAM,WAAW,sBAAuB,SAAQ,cAAc;IAC5D,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,CAAC,CAAC;IACjB,UAAU,EAAE,oBAAoB,CAAC;IACjC,SAAS,EAAE,SAAS,sBAAsB,EAAE,CAAC;CAC9C;AAqBD,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,CAY1E;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAuB1D;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,aAAa,EACtB,UAAU,GAAE,SAAS,MAAM,EAAO,GACjC,qBAAqB,CA4CvB"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/secrets/config.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,oBAAoB,GAC5B,SAAS,GACT,oBAAoB,GACpB,6BAA6B,CAAC;AAElC,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,oBAAoB,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,oBAAqB,SAAQ,iBAAiB;IACtD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,CAAC,CAAC;IACjB,UAAU,EAAE,oBAAoB,CAAC;IACjC,SAAS,EAAE,SAAS,cAAc,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,sBAAsB,CAAC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,aAAa,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,UAAU,GAAG,YAAY,CAAC;CACzC;AAED,MAAM,WAAW,sBAAuB,SAAQ,cAAc;IAC5D,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,CAAC,CAAC;IACjB,UAAU,EAAE,oBAAoB,CAAC;IACjC,SAAS,EAAE,SAAS,sBAAsB,EAAE,CAAC;CAC9C;AAED,eAAO,MAAM,WAAW,EAAE,SAAS,oBAAoB,EAgBtD,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAE5B,SAAS,oBAAoB,EAAE,CAAC;AAMrC,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,oBAAoB,CAY7E;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK7D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAsBrD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAuB1D;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,aAAa,EACtB,UAAU,GAAE,SAAS,MAAM,EAAO,GACjC,qBAAqB,CA4CvB"}
@@ -1,17 +1,21 @@
1
- const COLLECTIONS = [
1
+ export const COLLECTIONS = [
2
2
  {
3
3
  id: "rhdh-qe",
4
4
  bitwardenCollection: "Rhdh Qe Ci Secrets",
5
+ gsmCollection: "rhdh-qe",
5
6
  },
6
7
  {
7
8
  id: "rhdh-test-instance",
8
9
  bitwardenCollection: "Rhdh Test Instance Ci Secrets",
10
+ gsmCollection: "rhdh-test-instance",
9
11
  },
10
12
  {
11
13
  id: "rhdh-plugin-export-overlays",
12
14
  bitwardenCollection: "Rhdh Plugin Export Overlays Ci Secrets",
15
+ gsmCollection: "rhdh-plugin-export-overlays",
13
16
  },
14
17
  ];
18
+ export const READABLE_COLLECTIONS = COLLECTIONS.map(({ id }) => id);
15
19
  const DENIED_COLLECTION = "rhdh-aws-credentials";
16
20
  const WORKSPACE_TOKEN = "${workspace}";
17
21
  const WORKSPACE_NAME = /^[a-z0-9][a-z0-9-]*$/;
@@ -24,6 +28,33 @@ export function getCollectionMapping(collection) {
24
28
  }
25
29
  return COLLECTIONS.find((mapping) => mapping.id === collection);
26
30
  }
31
+ export function gsmPathFromBitwardenPath(path) {
32
+ return bitwardenPathFromGsmPath(path).replaceAll(".", "--dot--");
33
+ }
34
+ export function bitwardenPathFromGsmPath(path) {
35
+ validateSecretPath(path);
36
+ const bitwardenPath = path.replaceAll("--dot--", ".");
37
+ validateSecretPath(bitwardenPath);
38
+ return bitwardenPath;
39
+ }
40
+ export function validateSecretPath(path) {
41
+ if (path.length === 0 || path.startsWith("/")) {
42
+ throw new Error(`Invalid secret path: ${path}`);
43
+ }
44
+ const segments = path.split("/");
45
+ if (segments.length < 2 || segments.some((segment) => segment.length === 0)) {
46
+ throw new Error(`Secret path must use group/field form: ${path}`);
47
+ }
48
+ if (segments.some((segment) => segment === "." ||
49
+ segment === ".." ||
50
+ segment.length > 255 ||
51
+ !/^[A-Za-z0-9_.-]+$/.test(segment))) {
52
+ if (segments.some((segment) => segment.length > 255)) {
53
+ throw new Error(`Secret path segment is too long: ${path}`);
54
+ }
55
+ throw new Error(`Invalid secret path segment: ${path}`);
56
+ }
57
+ }
27
58
  export function parseProfile(value) {
28
59
  if (!isRecord(value) || value.schemaVersion !== 1) {
29
60
  throw new Error("Invalid secret profile: schemaVersion must be 1");
@@ -0,0 +1,2 @@
1
+ export declare function isValidEnvironmentName(value: string): boolean;
2
+ //# sourceMappingURL=environment-name.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment-name.d.ts","sourceRoot":"","sources":["../../src/secrets/environment-name.ts"],"names":[],"mappings":"AAEA,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE7D"}
@@ -0,0 +1,4 @@
1
+ const ENVIRONMENT_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
2
+ export function isValidEnvironmentName(value) {
3
+ return ENVIRONMENT_NAME.test(value);
4
+ }
@@ -5,6 +5,16 @@ export interface EnvironmentSecret {
5
5
  value: string;
6
6
  selector: ExpandedSecretSelector;
7
7
  }
8
+ export interface MaterializedEnvironment {
9
+ environment: NodeJS.ProcessEnv;
10
+ secrets: readonly MaterializedSecret[];
11
+ }
12
+ export interface MaterializedSecret {
13
+ name: string;
14
+ value: string;
15
+ }
8
16
  export declare function removeProviderEnvironmentVariables(environment: NodeJS.ProcessEnv): void;
9
17
  export declare function materializeEnvironment(secrets: readonly EnvironmentSecret[], selectors: readonly ExpandedSecretSelector[], parent?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
18
+ export declare function materializeEnvironmentWithSecrets(secrets: readonly EnvironmentSecret[], selectors: readonly ExpandedSecretSelector[], parent?: NodeJS.ProcessEnv): MaterializedEnvironment;
19
+ export declare function materializeStreamEnvironment(materialized: MaterializedEnvironment): NodeJS.ProcessEnv;
10
20
  //# sourceMappingURL=environment.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../../src/secrets/environment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAE1D,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,sBAAsB,CAAC;CAClC;AAUD,wBAAgB,kCAAkC,CAChD,WAAW,EAAE,MAAM,CAAC,UAAU,GAC7B,IAAI,CAMN;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,SAAS,iBAAiB,EAAE,EACrC,SAAS,EAAE,SAAS,sBAAsB,EAAE,EAC5C,MAAM,GAAE,MAAM,CAAC,UAAwB,GACtC,MAAM,CAAC,UAAU,CAwCnB"}
1
+ {"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../../src/secrets/environment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAO1D,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,sBAAsB,CAAC;CAClC;AAED,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC;IAC/B,OAAO,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACxC;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AASD,wBAAgB,kCAAkC,CAChD,WAAW,EAAE,MAAM,CAAC,UAAU,GAC7B,IAAI,CAMN;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,SAAS,iBAAiB,EAAE,EACrC,SAAS,EAAE,SAAS,sBAAsB,EAAE,EAC5C,MAAM,GAAE,MAAM,CAAC,UAAwB,GACtC,MAAM,CAAC,UAAU,CAGnB;AAED,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,SAAS,iBAAiB,EAAE,EACrC,SAAS,EAAE,SAAS,sBAAsB,EAAE,EAC5C,MAAM,GAAE,MAAM,CAAC,UAAwB,GACtC,uBAAuB,CAgEzB;AAED,wBAAgB,4BAA4B,CAC1C,YAAY,EAAE,uBAAuB,GACpC,MAAM,CAAC,UAAU,CAKnB"}
@@ -1,4 +1,5 @@
1
- const ENVIRONMENT_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
1
+ import { SECRET_STREAM_ENVIRONMENT_VARIABLE, SECRET_STREAM_FD, } from "./stream.js";
2
+ import { isValidEnvironmentName } from "./environment-name.js";
2
3
  const PROVIDER_ENVIRONMENT_KEYS = new Set([
3
4
  "VAULT",
4
5
  "VAULT_TOKEN",
@@ -13,8 +14,13 @@ export function removeProviderEnvironmentVariables(environment) {
13
14
  }
14
15
  }
15
16
  export function materializeEnvironment(secrets, selectors, parent = process.env) {
17
+ return materializeEnvironmentWithSecrets(secrets, selectors, parent)
18
+ .environment;
19
+ }
20
+ export function materializeEnvironmentWithSecrets(secrets, selectors, parent = process.env) {
16
21
  const child = { ...parent };
17
22
  removeProviderEnvironmentVariables(child);
23
+ delete child[SECRET_STREAM_ENVIRONMENT_VARIABLE];
18
24
  const mapped = new Map();
19
25
  for (const secret of secrets) {
20
26
  const selector = selectors.find((candidate) => sameSelector(candidate, secret.selector));
@@ -30,16 +36,41 @@ export function materializeEnvironment(secrets, selectors, parent = process.env)
30
36
  continue;
31
37
  }
32
38
  const key = transformEnvironmentName(relativeName, selector.destination.keyTransform);
33
- if (!ENVIRONMENT_NAME.test(key)) {
39
+ if (!isValidEnvironmentName(key)) {
34
40
  throw new Error(`Invalid environment variable name: ${key}`);
35
41
  }
42
+ if (key === SECRET_STREAM_ENVIRONMENT_VARIABLE) {
43
+ throw new Error(`Reserved environment variable name: ${key}`);
44
+ }
45
+ if (key.startsWith("BW_")) {
46
+ throw new Error(`Bitwarden provider environment variable is not allowed: ${key}`);
47
+ }
36
48
  if (mapped.has(key)) {
37
49
  throw new Error(`Environment variable collision: ${key}`);
38
50
  }
39
51
  mapped.set(key, secret.value);
40
52
  }
41
- for (const [key, value] of mapped)
42
- child[key] = value;
53
+ const mappedSecrets = [...mapped.entries()]
54
+ .map(([name, value]) => ({ name, value }))
55
+ .sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
56
+ for (const [key, value] of mapped) {
57
+ Object.defineProperty(child, key, {
58
+ configurable: true,
59
+ enumerable: true,
60
+ value,
61
+ writable: true,
62
+ });
63
+ }
64
+ return {
65
+ environment: child,
66
+ secrets: mappedSecrets,
67
+ };
68
+ }
69
+ export function materializeStreamEnvironment(materialized) {
70
+ const child = { ...materialized.environment };
71
+ for (const secret of materialized.secrets)
72
+ delete child[secret.name];
73
+ child[SECRET_STREAM_ENVIRONMENT_VARIABLE] = String(SECRET_STREAM_FD);
43
74
  return child;
44
75
  }
45
76
  function transformEnvironmentName(value, transform) {
@@ -1,18 +1,26 @@
1
+ import { spawn } from "node:child_process";
1
2
  import { type BitwardenSecret } from "./bitwarden.js";
2
3
  import { type ExpandedSecretSelector, type SecretProfile } from "./config.js";
4
+ import { type SecretStreamEntry } from "./stream.js";
3
5
  export interface SecretReader {
4
6
  read(collection: SecretProfile["collection"], selectors: readonly ExpandedSecretSelector[]): Promise<BitwardenSecret[]>;
5
7
  }
6
- export type ChildRunner = (command: string, args: readonly string[], env: NodeJS.ProcessEnv) => Promise<number>;
8
+ export interface ChildRunnerOptions {
9
+ secretStream?: readonly SecretStreamEntry[];
10
+ }
11
+ export type ChildRunner = (command: string, args: readonly string[], env: NodeJS.ProcessEnv, options?: ChildRunnerOptions) => Promise<number>;
7
12
  export interface ExecuteCommandOptions {
8
13
  profile: SecretProfile;
9
14
  workspaces: readonly string[];
10
15
  command: string;
11
16
  args: readonly string[];
17
+ streamSecrets?: boolean;
12
18
  env?: NodeJS.ProcessEnv;
13
19
  client?: SecretReader;
14
20
  childRunner?: ChildRunner;
15
21
  }
16
22
  export declare function executeCommand(options: ExecuteCommandOptions): Promise<number>;
17
23
  export declare const runChild: ChildRunner;
24
+ /** Internal spawn seam used by lifecycle tests; omitted from the package barrel. */
25
+ export declare function createChildRunner(spawnCommand: typeof spawn): ChildRunner;
18
26
  //# sourceMappingURL=exec.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/secrets/exec.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,EAEL,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EACnB,MAAM,aAAa,CAAC;AAGrB,MAAM,WAAW,YAAY;IAC3B,IAAI,CACF,UAAU,EAAE,aAAa,CAAC,YAAY,CAAC,EACvC,SAAS,EAAE,SAAS,sBAAsB,EAAE,GAC3C,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;CAC/B;AAED,MAAM,MAAM,WAAW,GAAG,CACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;AAErB,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,aAAa,CAAC;IACvB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,MAAM,CAAC,CAejB;AAED,eAAO,MAAM,QAAQ,EAAE,WAqCnB,CAAC"}
1
+ {"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/secrets/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,EAEL,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EACnB,MAAM,aAAa,CAAC;AAKrB,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAExE,MAAM,WAAW,YAAY;IAC3B,IAAI,CACF,UAAU,EAAE,aAAa,CAAC,YAAY,CAAC,EACvC,SAAS,EAAE,SAAS,sBAAsB,EAAE,GAC3C,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAC7C;AAED,MAAM,MAAM,WAAW,GAAG,CACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,OAAO,CAAC,EAAE,kBAAkB,KACzB,OAAO,CAAC,MAAM,CAAC,CAAC;AAErB,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,aAAa,CAAC;IACvB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,MAAM,CAAC,CAuBjB;AAED,eAAO,MAAM,QAAQ,EAAE,WAAsC,CAAC;AAE9D,oFAAoF;AACpF,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,OAAO,KAAK,GAAG,WAAW,CAGzE"}