@salesforce/core 3.30.14 → 3.31.7

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 (153) hide show
  1. package/LICENSE.txt +11 -11
  2. package/README.md +222 -227
  3. package/lib/config/aliasesConfig.d.ts +12 -12
  4. package/lib/config/aliasesConfig.js +27 -27
  5. package/lib/config/authInfoConfig.d.ts +19 -19
  6. package/lib/config/authInfoConfig.js +34 -34
  7. package/lib/config/config.d.ts +311 -311
  8. package/lib/config/config.js +574 -574
  9. package/lib/config/configAggregator.d.ts +232 -232
  10. package/lib/config/configAggregator.js +379 -379
  11. package/lib/config/configFile.d.ts +199 -199
  12. package/lib/config/configFile.js +340 -340
  13. package/lib/config/configGroup.d.ts +141 -141
  14. package/lib/config/configGroup.js +224 -224
  15. package/lib/config/configStore.d.ts +241 -241
  16. package/lib/config/configStore.js +352 -352
  17. package/lib/config/envVars.d.ts +101 -101
  18. package/lib/config/envVars.js +456 -456
  19. package/lib/config/orgUsersConfig.d.ts +31 -31
  20. package/lib/config/orgUsersConfig.js +41 -41
  21. package/lib/config/sandboxOrgConfig.d.ts +37 -37
  22. package/lib/config/sandboxOrgConfig.js +50 -50
  23. package/lib/config/sandboxProcessCache.d.ts +16 -16
  24. package/lib/config/sandboxProcessCache.js +37 -37
  25. package/lib/config/tokensConfig.d.ts +10 -10
  26. package/lib/config/tokensConfig.js +28 -28
  27. package/lib/config/ttlConfig.d.ts +34 -34
  28. package/lib/config/ttlConfig.js +54 -54
  29. package/lib/crypto/crypto.d.ts +54 -54
  30. package/lib/crypto/crypto.js +220 -220
  31. package/lib/crypto/keyChain.d.ts +8 -8
  32. package/lib/crypto/keyChain.js +61 -61
  33. package/lib/crypto/keyChainImpl.d.ts +116 -116
  34. package/lib/crypto/keyChainImpl.js +486 -486
  35. package/lib/crypto/secureBuffer.d.ts +46 -46
  36. package/lib/crypto/secureBuffer.js +82 -82
  37. package/lib/deviceOauthService.d.ts +71 -71
  38. package/lib/deviceOauthService.js +191 -191
  39. package/lib/exported.d.ts +38 -38
  40. package/lib/exported.js +118 -118
  41. package/lib/global.d.ts +70 -70
  42. package/lib/global.js +109 -109
  43. package/lib/lifecycleEvents.d.ts +93 -93
  44. package/lib/lifecycleEvents.js +188 -188
  45. package/lib/logger.d.ts +381 -381
  46. package/lib/logger.js +734 -734
  47. package/lib/messages.d.ts +291 -291
  48. package/lib/messages.js +543 -543
  49. package/lib/org/authInfo.d.ts +344 -344
  50. package/lib/org/authInfo.js +892 -892
  51. package/lib/org/authRemover.d.ts +88 -88
  52. package/lib/org/authRemover.js +182 -182
  53. package/lib/org/connection.d.ts +197 -197
  54. package/lib/org/connection.js +395 -395
  55. package/lib/org/index.d.ts +6 -6
  56. package/lib/org/index.js +28 -28
  57. package/lib/org/org.d.ts +558 -558
  58. package/lib/org/org.js +1267 -1267
  59. package/lib/org/orgConfigProperties.d.ts +69 -69
  60. package/lib/org/orgConfigProperties.js +136 -136
  61. package/lib/org/permissionSetAssignment.d.ts +35 -35
  62. package/lib/org/permissionSetAssignment.js +125 -125
  63. package/lib/org/scratchOrgCache.d.ts +20 -20
  64. package/lib/org/scratchOrgCache.js +32 -32
  65. package/lib/org/scratchOrgCreate.d.ts +54 -54
  66. package/lib/org/scratchOrgCreate.js +216 -216
  67. package/lib/org/scratchOrgErrorCodes.d.ts +10 -10
  68. package/lib/org/scratchOrgErrorCodes.js +88 -88
  69. package/lib/org/scratchOrgFeatureDeprecation.d.ts +26 -26
  70. package/lib/org/scratchOrgFeatureDeprecation.js +109 -109
  71. package/lib/org/scratchOrgInfoApi.d.ts +68 -68
  72. package/lib/org/scratchOrgInfoApi.js +413 -413
  73. package/lib/org/scratchOrgInfoGenerator.d.ts +64 -64
  74. package/lib/org/scratchOrgInfoGenerator.js +241 -241
  75. package/lib/org/scratchOrgLifecycleEvents.d.ts +10 -10
  76. package/lib/org/scratchOrgLifecycleEvents.js +40 -40
  77. package/lib/org/scratchOrgSettingsGenerator.d.ts +78 -78
  78. package/lib/org/scratchOrgSettingsGenerator.js +276 -276
  79. package/lib/org/scratchOrgTypes.d.ts +43 -43
  80. package/lib/org/scratchOrgTypes.js +8 -8
  81. package/lib/org/user.d.ts +187 -187
  82. package/lib/org/user.js +448 -448
  83. package/lib/schema/printer.d.ts +79 -79
  84. package/lib/schema/printer.js +260 -260
  85. package/lib/schema/validator.d.ts +70 -70
  86. package/lib/schema/validator.js +169 -169
  87. package/lib/sfError.d.ts +73 -73
  88. package/lib/sfError.js +136 -136
  89. package/lib/sfProject.d.ts +357 -357
  90. package/lib/sfProject.js +671 -671
  91. package/lib/stateAggregator/accessors/aliasAccessor.d.ts +98 -98
  92. package/lib/stateAggregator/accessors/aliasAccessor.js +145 -145
  93. package/lib/stateAggregator/accessors/orgAccessor.d.ts +101 -101
  94. package/lib/stateAggregator/accessors/orgAccessor.js +240 -240
  95. package/lib/stateAggregator/accessors/sandboxAccessor.d.ts +8 -8
  96. package/lib/stateAggregator/accessors/sandboxAccessor.js +27 -27
  97. package/lib/stateAggregator/accessors/tokenAccessor.d.ts +63 -63
  98. package/lib/stateAggregator/accessors/tokenAccessor.js +79 -79
  99. package/lib/stateAggregator/index.d.ts +4 -4
  100. package/lib/stateAggregator/index.js +26 -26
  101. package/lib/stateAggregator/stateAggregator.d.ts +25 -25
  102. package/lib/stateAggregator/stateAggregator.js +45 -45
  103. package/lib/status/myDomainResolver.d.ts +66 -66
  104. package/lib/status/myDomainResolver.js +124 -124
  105. package/lib/status/pollingClient.d.ts +85 -85
  106. package/lib/status/pollingClient.js +115 -115
  107. package/lib/status/streamingClient.d.ts +244 -244
  108. package/lib/status/streamingClient.js +436 -436
  109. package/lib/status/types.d.ts +89 -89
  110. package/lib/status/types.js +17 -17
  111. package/lib/testSetup.d.ts +553 -530
  112. package/lib/testSetup.js +871 -727
  113. package/lib/util/cache.d.ts +11 -11
  114. package/lib/util/cache.js +69 -69
  115. package/lib/util/checkLightningDomain.d.ts +1 -1
  116. package/lib/util/checkLightningDomain.js +28 -28
  117. package/lib/util/directoryWriter.d.ts +12 -12
  118. package/lib/util/directoryWriter.js +53 -53
  119. package/lib/util/getJwtAudienceUrl.d.ts +4 -4
  120. package/lib/util/getJwtAudienceUrl.js +18 -18
  121. package/lib/util/internal.d.ts +58 -58
  122. package/lib/util/internal.js +118 -118
  123. package/lib/util/jsonXmlTools.d.ts +14 -14
  124. package/lib/util/jsonXmlTools.js +38 -38
  125. package/lib/util/mapKeys.d.ts +14 -14
  126. package/lib/util/mapKeys.js +51 -51
  127. package/lib/util/sfdc.d.ts +52 -52
  128. package/lib/util/sfdc.js +85 -85
  129. package/lib/util/sfdcUrl.d.ts +72 -72
  130. package/lib/util/sfdcUrl.js +215 -215
  131. package/lib/util/structuredWriter.d.ts +9 -9
  132. package/lib/util/structuredWriter.js +2 -2
  133. package/lib/util/zipWriter.d.ts +16 -16
  134. package/lib/util/zipWriter.js +67 -67
  135. package/lib/webOAuthServer.d.ts +156 -156
  136. package/lib/webOAuthServer.js +388 -388
  137. package/messages/auth.md +37 -37
  138. package/messages/config.md +156 -156
  139. package/messages/connection.md +30 -30
  140. package/messages/core.json +20 -20
  141. package/messages/core.md +67 -67
  142. package/messages/encryption.md +85 -85
  143. package/messages/envVars.md +303 -303
  144. package/messages/org.md +63 -63
  145. package/messages/permissionSetAssignment.md +31 -31
  146. package/messages/scratchOrgCreate.md +23 -23
  147. package/messages/scratchOrgErrorCodes.md +115 -115
  148. package/messages/scratchOrgFeatureDeprecation.md +11 -11
  149. package/messages/scratchOrgInfoApi.md +15 -15
  150. package/messages/scratchOrgInfoGenerator.md +23 -23
  151. package/messages/streaming.md +23 -23
  152. package/messages/user.md +35 -35
  153. package/package.json +97 -97
@@ -1,487 +1,487 @@
1
- "use strict";
2
- /*
3
- * Copyright (c) 2020, salesforce.com, inc.
4
- * All rights reserved.
5
- * Licensed under the BSD 3-Clause license.
6
- * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
7
- */
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.keyChainImpl = exports.GenericWindowsKeychainAccess = exports.GenericUnixKeychainAccess = exports.GenericKeychainAccess = exports.KeychainAccess = void 0;
10
- const childProcess = require("child_process");
11
- const nodeFs = require("fs");
12
- const fs = require("fs");
13
- const os = require("os");
14
- const os_1 = require("os");
15
- const path = require("path");
16
- const ts_types_1 = require("@salesforce/ts-types");
17
- const kit_1 = require("@salesforce/kit");
18
- const global_1 = require("../global");
19
- const messages_1 = require("../messages");
20
- messages_1.Messages.importMessagesDirectory(__dirname);
21
- const messages = messages_1.Messages.load('@salesforce/core', 'encryption', [
22
- 'missingCredentialProgramError',
23
- 'credentialProgramAccessError',
24
- 'keyChainServiceRequiredError',
25
- 'keyChainAccountRequiredError',
26
- 'passwordRetryError',
27
- 'passwordRequiredError',
28
- 'passwordNotFoundError',
29
- 'setCredentialError',
30
- 'keyChainUserCanceledError',
31
- 'genericKeychainServiceError',
32
- 'genericKeychainInvalidPermsError',
33
- ]);
34
- const GET_PASSWORD_RETRY_COUNT = 3;
35
- /**
36
- * Helper to reduce an array of cli args down to a presentable string for logging.
37
- *
38
- * @param optionsArray CLI command args.
39
- */
40
- const optionsToString = (optionsArray) => optionsArray.join(' ');
41
- /**
42
- * Helper to determine if a program is executable. Returns `true` if the program is executable for the user. For
43
- * Windows true is always returned.
44
- *
45
- * @param mode Stats mode.
46
- * @param gid Unix group id.
47
- * @param uid Unix user id.
48
- */
49
- const isExe = (mode, gid, uid) => {
50
- if (process.platform === 'win32') {
51
- return true;
52
- }
53
- return Boolean(mode & parseInt('0001', 8) ||
54
- (mode & parseInt('0010', 8) && process.getgid && gid === process.getgid()) ||
55
- (mode & parseInt('0100', 8) && process.getuid && uid === process.getuid()));
56
- };
57
- /**
58
- * Private helper to validate that a program exists on the file system and is executable.
59
- *
60
- * **Throws** *{@link SfError}{ name: 'MissingCredentialProgramError' }* When the OS credential program isn't found.
61
- *
62
- * **Throws** *{@link SfError}{ name: 'CredentialProgramAccessError' }* When the OS credential program isn't accessible.
63
- *
64
- * @param programPath The absolute path of the program.
65
- * @param fsIfc The file system interface.
66
- * @param isExeIfc Executable validation function.
67
- */
68
- // eslint-disable-next-line no-underscore-dangle
69
- const _validateProgram = async (programPath, fsIfc, isExeIfc
70
- // eslint-disable-next-line @typescript-eslint/require-await
71
- ) => {
72
- let noPermission;
73
- try {
74
- const stats = fsIfc.statSync(programPath);
75
- noPermission = !isExeIfc(stats.mode, stats.gid, stats.uid);
76
- }
77
- catch (e) {
78
- throw messages.createError('missingCredentialProgramError', [programPath]);
79
- }
80
- if (noPermission) {
81
- throw messages.createError('credentialProgramAccessError', [programPath]);
82
- }
83
- };
84
- /**
85
- * @private
86
- */
87
- class KeychainAccess {
88
- /**
89
- * Abstract prototype for general cross platform keychain interaction.
90
- *
91
- * @param osImpl The platform impl for (linux, darwin, windows).
92
- * @param fsIfc The file system interface.
93
- */
94
- constructor(osImpl, fsIfc) {
95
- this.osImpl = osImpl;
96
- this.fsIfc = fsIfc;
97
- }
98
- /**
99
- * Validates the os level program is executable.
100
- */
101
- async validateProgram() {
102
- await _validateProgram(this.osImpl.getProgram(), this.fsIfc, isExe);
103
- }
104
- /**
105
- * Returns a password using the native program for credential management.
106
- *
107
- * @param opts Options for the credential lookup.
108
- * @param fn Callback function (err, password).
109
- * @param retryCount Used internally to track the number of retries for getting a password out of the keychain.
110
- */
111
- async getPassword(opts, fn, retryCount = 0) {
112
- if (opts.service == null) {
113
- fn(messages.createError('keyChainServiceRequiredError'));
114
- return;
115
- }
116
- if (opts.account == null) {
117
- fn(messages.createError('keyChainAccountRequiredError'));
118
- return;
119
- }
120
- await this.validateProgram();
121
- const credManager = this.osImpl.getCommandFunc(opts, childProcess.spawn);
122
- let stdout = '';
123
- let stderr = '';
124
- if (credManager.stdout) {
125
- credManager.stdout.on('data', (data) => {
126
- stdout += data;
127
- });
128
- }
129
- if (credManager.stderr) {
130
- credManager.stderr.on('data', (data) => {
131
- stderr += data;
132
- });
133
- }
134
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
135
- credManager.on('close', async (code) => {
136
- try {
137
- return await this.osImpl.onGetCommandClose(code, stdout, stderr, opts, fn);
138
- }
139
- catch (e) {
140
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
141
- // @ts-ignore
142
- if (e.retry) {
143
- if (retryCount >= GET_PASSWORD_RETRY_COUNT) {
144
- throw messages.createError('passwordRetryError', [GET_PASSWORD_RETRY_COUNT]);
145
- }
146
- return this.getPassword(opts, fn, retryCount + 1);
147
- }
148
- else {
149
- // if retry
150
- throw e;
151
- }
152
- }
153
- });
154
- if (credManager.stdin) {
155
- credManager.stdin.end();
156
- }
157
- }
158
- /**
159
- * Sets a password using the native program for credential management.
160
- *
161
- * @param opts Options for the credential lookup.
162
- * @param fn Callback function (err, ConfigContents).
163
- */
164
- async setPassword(opts, fn) {
165
- if (opts.service == null) {
166
- fn(messages.createError('keyChainServiceRequiredError'));
167
- return;
168
- }
169
- if (opts.account == null) {
170
- fn(messages.createError('keyChainAccountRequiredError'));
171
- return;
172
- }
173
- if (opts.password == null) {
174
- fn(messages.createError('passwordRequiredError'));
175
- return;
176
- }
177
- await _validateProgram(this.osImpl.getProgram(), this.fsIfc, isExe);
178
- const credManager = this.osImpl.setCommandFunc(opts, childProcess.spawn);
179
- let stdout = '';
180
- let stderr = '';
181
- if (credManager.stdout) {
182
- credManager.stdout.on('data', (data) => {
183
- stdout += data;
184
- });
185
- }
186
- if (credManager.stderr) {
187
- credManager.stderr.on('data', (data) => {
188
- stderr += data;
189
- });
190
- }
191
- credManager.on('close',
192
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
193
- async (code) => this.osImpl.onSetCommandClose(code, stdout, stderr, opts, fn));
194
- if (credManager.stdin) {
195
- credManager.stdin.end();
196
- }
197
- }
198
- }
199
- exports.KeychainAccess = KeychainAccess;
200
- /**
201
- * Linux implementation.
202
- *
203
- * Uses libsecret.
204
- */
205
- const linuxImpl = {
206
- getProgram() {
207
- return process.env.SFDX_SECRET_TOOL_PATH ?? path.join(path.sep, 'usr', 'bin', 'secret-tool');
208
- },
209
- getProgramOptions(opts) {
210
- return ['lookup', 'user', opts.account, 'domain', opts.service];
211
- },
212
- getCommandFunc(opts, fn) {
213
- return fn(linuxImpl.getProgram(), linuxImpl.getProgramOptions(opts));
214
- },
215
- // eslint-disable-next-line @typescript-eslint/require-await
216
- async onGetCommandClose(code, stdout, stderr, opts, fn) {
217
- if (code === 1) {
218
- const command = `${linuxImpl.getProgram()} ${optionsToString(linuxImpl.getProgramOptions(opts))}`;
219
- const error = messages.createError('passwordNotFoundError', [], [command]);
220
- // This is a workaround for linux.
221
- // Calling secret-tool too fast can cause it to return an unexpected error. (below)
222
- if (stderr?.includes('invalid or unencryptable secret')) {
223
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
224
- // @ts-ignore TODO: make an error subclass with this field
225
- error.retry = true;
226
- // Throwing here allows us to perform a retry in KeychainAccess
227
- throw error;
228
- }
229
- // All other issues we will report back to the handler.
230
- fn(error);
231
- }
232
- else {
233
- fn(null, stdout.trim());
234
- }
235
- },
236
- setProgramOptions(opts) {
237
- return ['store', "--label='salesforce.com'", 'user', opts.account, 'domain', opts.service];
238
- },
239
- setCommandFunc(opts, fn) {
240
- const secretTool = fn(linuxImpl.getProgram(), linuxImpl.setProgramOptions(opts));
241
- if (secretTool.stdin) {
242
- secretTool.stdin.write(`${opts.password}\n`);
243
- }
244
- return secretTool;
245
- },
246
- // eslint-disable-next-line @typescript-eslint/require-await
247
- async onSetCommandClose(code, stdout, stderr, opts, fn) {
248
- if (code !== 0) {
249
- const command = `${linuxImpl.getProgram()} ${optionsToString(linuxImpl.setProgramOptions(opts))}`;
250
- fn(messages.createError('setCredentialError', [`${stdout} - ${stderr}`], [os.userInfo().username, command]));
251
- }
252
- else {
253
- fn(null);
254
- }
255
- },
256
- };
257
- /**
258
- * OSX implementation.
259
- *
260
- * /usr/bin/security is a cli front end for OSX keychain.
261
- */
262
- const darwinImpl = {
263
- getProgram() {
264
- return path.join(path.sep, 'usr', 'bin', 'security');
265
- },
266
- getProgramOptions(opts) {
267
- return ['find-generic-password', '-a', opts.account, '-s', opts.service, '-g'];
268
- },
269
- getCommandFunc(opts, fn) {
270
- return fn(darwinImpl.getProgram(), darwinImpl.getProgramOptions(opts));
271
- },
272
- // eslint-disable-next-line @typescript-eslint/require-await
273
- async onGetCommandClose(code, stdout, stderr, opts, fn) {
274
- let err;
275
- if (code !== 0) {
276
- switch (code) {
277
- case 128: {
278
- err = messages.createError('keyChainUserCanceledError');
279
- break;
280
- }
281
- default: {
282
- const command = `${darwinImpl.getProgram()} ${optionsToString(darwinImpl.getProgramOptions(opts))}`;
283
- err = messages.createError('passwordNotFoundError', [`${stdout} - ${stderr}`], [command]);
284
- }
285
- }
286
- fn(err);
287
- return;
288
- }
289
- // For better or worse, the last line (containing the actual password) is actually written to stderr instead of
290
- // stdout. Reference: http://blog.macromates.com/2006/keychain-access-from-shell/
291
- if (stderr.includes('password')) {
292
- const match = RegExp(/"(.*)"/).exec(stderr);
293
- if (!match || !match[1]) {
294
- fn(messages.createError('passwordNotFoundError', [`${stdout} - ${stderr}`]));
295
- }
296
- else {
297
- fn(null, match[1]);
298
- }
299
- }
300
- else {
301
- const command = `${darwinImpl.getProgram()} ${optionsToString(darwinImpl.getProgramOptions(opts))}`;
302
- fn(messages.createError('passwordNotFoundError', [`${stdout} - ${stderr}`], [command]));
303
- }
304
- },
305
- setProgramOptions(opts) {
306
- const result = ['add-generic-password', '-a', opts.account, '-s', opts.service];
307
- if (opts.password) {
308
- result.push('-w', opts.password);
309
- }
310
- return result;
311
- },
312
- setCommandFunc(opts, fn) {
313
- return fn(darwinImpl.getProgram(), darwinImpl.setProgramOptions(opts));
314
- },
315
- // eslint-disable-next-line @typescript-eslint/require-await
316
- async onSetCommandClose(code, stdout, stderr, opts, fn) {
317
- if (code !== 0) {
318
- const command = `${darwinImpl.getProgram()} ${optionsToString(darwinImpl.setProgramOptions(opts))}`;
319
- fn(messages.createError('setCredentialError', [`${stdout} - ${stderr}`], [os.userInfo().username, command]));
320
- }
321
- else {
322
- fn(null);
323
- }
324
- },
325
- };
326
- const getSecretFile = () => path.join(global_1.Global.DIR, 'key.json');
327
- var SecretField;
328
- (function (SecretField) {
329
- SecretField["SERVICE"] = "service";
330
- SecretField["ACCOUNT"] = "account";
331
- SecretField["KEY"] = "key";
332
- })(SecretField || (SecretField = {}));
333
- async function writeFile(opts, fn) {
334
- try {
335
- const contents = {
336
- [SecretField.ACCOUNT]: opts.account,
337
- [SecretField.KEY]: opts.password,
338
- [SecretField.SERVICE]: opts.service,
339
- };
340
- const secretFile = getSecretFile();
341
- await fs.promises.mkdir(path.dirname(secretFile), { recursive: true });
342
- await fs.promises.writeFile(secretFile, JSON.stringify(contents, null, 4), { mode: '600' });
343
- fn(null, contents);
344
- }
345
- catch (err) {
346
- fn(err);
347
- }
348
- }
349
- async function readFile() {
350
- // The file and access is validated before this method is called
351
- const fileContents = (0, kit_1.parseJsonMap)(await fs.promises.readFile(getSecretFile(), 'utf8'));
352
- return {
353
- account: (0, ts_types_1.ensureString)(fileContents[SecretField.ACCOUNT]),
354
- password: (0, ts_types_1.asString)(fileContents[SecretField.KEY]),
355
- service: (0, ts_types_1.ensureString)(fileContents[SecretField.SERVICE]),
356
- };
357
- }
358
- // istanbul ignore next - getPassword/setPassword is always mocked out
359
- /**
360
- * @@ignore
361
- */
362
- class GenericKeychainAccess {
363
- async getPassword(opts, fn) {
364
- // validate the file in .sfdx
365
- await this.isValidFileAccess(async (fileAccessError) => {
366
- // the file checks out.
367
- if (fileAccessError == null) {
368
- // read it's contents
369
- try {
370
- const { service, account, password } = await readFile();
371
- // validate service name and account just because
372
- if (opts.service === service && opts.account === account) {
373
- fn(null, password);
374
- }
375
- else {
376
- // if the service and account names don't match then maybe someone or something is editing
377
- // that file. #donotallow
378
- fn(messages.createError('genericKeychainServiceError', [getSecretFile()]));
379
- }
380
- }
381
- catch (readJsonErr) {
382
- fn(readJsonErr);
383
- }
384
- }
385
- else if (fileAccessError.code === 'ENOENT') {
386
- fn(messages.createError('passwordNotFoundError'));
387
- }
388
- else {
389
- fn(fileAccessError);
390
- }
391
- });
392
- }
393
- async setPassword(opts, fn) {
394
- // validate the file in .sfdx
395
- await this.isValidFileAccess(async (fileAccessError) => {
396
- // if there is a validation error
397
- if (fileAccessError != null) {
398
- // file not found
399
- if (fileAccessError.code === 'ENOENT') {
400
- // create the file
401
- await writeFile.call(this, opts, fn);
402
- }
403
- else {
404
- fn(fileAccessError);
405
- }
406
- }
407
- else {
408
- // the existing file validated. we can write the updated key
409
- await writeFile.call(this, opts, fn);
410
- }
411
- });
412
- }
413
- // eslint-disable-next-line class-methods-use-this
414
- async isValidFileAccess(cb) {
415
- try {
416
- const root = (0, os_1.homedir)();
417
- await fs.promises.access(path.join(root, global_1.Global.SFDX_STATE_FOLDER), fs.constants.R_OK | fs.constants.X_OK | fs.constants.W_OK);
418
- await cb(null);
419
- }
420
- catch (err) {
421
- await cb(err);
422
- }
423
- }
424
- }
425
- exports.GenericKeychainAccess = GenericKeychainAccess;
426
- /**
427
- * @ignore
428
- */
429
- // istanbul ignore next - getPassword/setPassword is always mocked out
430
- class GenericUnixKeychainAccess extends GenericKeychainAccess {
431
- async isValidFileAccess(cb) {
432
- await super.isValidFileAccess(async (err) => {
433
- if (err != null) {
434
- await cb(err);
435
- }
436
- else {
437
- const secretFile = getSecretFile();
438
- const stats = await fs.promises.stat(secretFile);
439
- const octalModeStr = (stats.mode & 0o777).toString(8);
440
- const EXPECTED_OCTAL_PERM_VALUE = '600';
441
- if (octalModeStr === EXPECTED_OCTAL_PERM_VALUE) {
442
- await cb(null);
443
- }
444
- else {
445
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
446
- cb(messages.createError('genericKeychainInvalidPermsError', [secretFile], [secretFile, EXPECTED_OCTAL_PERM_VALUE]));
447
- }
448
- }
449
- });
450
- }
451
- }
452
- exports.GenericUnixKeychainAccess = GenericUnixKeychainAccess;
453
- /**
454
- * @ignore
455
- */
456
- class GenericWindowsKeychainAccess extends GenericKeychainAccess {
457
- async isValidFileAccess(cb) {
458
- await super.isValidFileAccess(async (err) => {
459
- if (err != null) {
460
- await cb(err);
461
- }
462
- else {
463
- try {
464
- await fs.promises.access(getSecretFile(), fs.constants.R_OK | fs.constants.W_OK);
465
- await cb(null);
466
- }
467
- catch (e) {
468
- await cb(e);
469
- }
470
- }
471
- });
472
- }
473
- }
474
- exports.GenericWindowsKeychainAccess = GenericWindowsKeychainAccess;
475
- /**
476
- * @ignore
477
- */
478
- exports.keyChainImpl = {
479
- // eslint-disable-next-line camelcase
480
- generic_unix: new GenericUnixKeychainAccess(),
481
- // eslint-disable-next-line camelcase
482
- generic_windows: new GenericWindowsKeychainAccess(),
483
- darwin: new KeychainAccess(darwinImpl, nodeFs),
484
- linux: new KeychainAccess(linuxImpl, nodeFs),
485
- validateProgram: _validateProgram,
486
- };
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) 2020, salesforce.com, inc.
4
+ * All rights reserved.
5
+ * Licensed under the BSD 3-Clause license.
6
+ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.keyChainImpl = exports.GenericWindowsKeychainAccess = exports.GenericUnixKeychainAccess = exports.GenericKeychainAccess = exports.KeychainAccess = void 0;
10
+ const childProcess = require("child_process");
11
+ const nodeFs = require("fs");
12
+ const fs = require("fs");
13
+ const os = require("os");
14
+ const os_1 = require("os");
15
+ const path = require("path");
16
+ const ts_types_1 = require("@salesforce/ts-types");
17
+ const kit_1 = require("@salesforce/kit");
18
+ const global_1 = require("../global");
19
+ const messages_1 = require("../messages");
20
+ messages_1.Messages.importMessagesDirectory(__dirname);
21
+ const messages = messages_1.Messages.load('@salesforce/core', 'encryption', [
22
+ 'missingCredentialProgramError',
23
+ 'credentialProgramAccessError',
24
+ 'keyChainServiceRequiredError',
25
+ 'keyChainAccountRequiredError',
26
+ 'passwordRetryError',
27
+ 'passwordRequiredError',
28
+ 'passwordNotFoundError',
29
+ 'setCredentialError',
30
+ 'keyChainUserCanceledError',
31
+ 'genericKeychainServiceError',
32
+ 'genericKeychainInvalidPermsError',
33
+ ]);
34
+ const GET_PASSWORD_RETRY_COUNT = 3;
35
+ /**
36
+ * Helper to reduce an array of cli args down to a presentable string for logging.
37
+ *
38
+ * @param optionsArray CLI command args.
39
+ */
40
+ const optionsToString = (optionsArray) => optionsArray.join(' ');
41
+ /**
42
+ * Helper to determine if a program is executable. Returns `true` if the program is executable for the user. For
43
+ * Windows true is always returned.
44
+ *
45
+ * @param mode Stats mode.
46
+ * @param gid Unix group id.
47
+ * @param uid Unix user id.
48
+ */
49
+ const isExe = (mode, gid, uid) => {
50
+ if (process.platform === 'win32') {
51
+ return true;
52
+ }
53
+ return Boolean(mode & parseInt('0001', 8) ||
54
+ (mode & parseInt('0010', 8) && process.getgid && gid === process.getgid()) ||
55
+ (mode & parseInt('0100', 8) && process.getuid && uid === process.getuid()));
56
+ };
57
+ /**
58
+ * Private helper to validate that a program exists on the file system and is executable.
59
+ *
60
+ * **Throws** *{@link SfError}{ name: 'MissingCredentialProgramError' }* When the OS credential program isn't found.
61
+ *
62
+ * **Throws** *{@link SfError}{ name: 'CredentialProgramAccessError' }* When the OS credential program isn't accessible.
63
+ *
64
+ * @param programPath The absolute path of the program.
65
+ * @param fsIfc The file system interface.
66
+ * @param isExeIfc Executable validation function.
67
+ */
68
+ // eslint-disable-next-line no-underscore-dangle
69
+ const _validateProgram = async (programPath, fsIfc, isExeIfc
70
+ // eslint-disable-next-line @typescript-eslint/require-await
71
+ ) => {
72
+ let noPermission;
73
+ try {
74
+ const stats = fsIfc.statSync(programPath);
75
+ noPermission = !isExeIfc(stats.mode, stats.gid, stats.uid);
76
+ }
77
+ catch (e) {
78
+ throw messages.createError('missingCredentialProgramError', [programPath]);
79
+ }
80
+ if (noPermission) {
81
+ throw messages.createError('credentialProgramAccessError', [programPath]);
82
+ }
83
+ };
84
+ /**
85
+ * @private
86
+ */
87
+ class KeychainAccess {
88
+ /**
89
+ * Abstract prototype for general cross platform keychain interaction.
90
+ *
91
+ * @param osImpl The platform impl for (linux, darwin, windows).
92
+ * @param fsIfc The file system interface.
93
+ */
94
+ constructor(osImpl, fsIfc) {
95
+ this.osImpl = osImpl;
96
+ this.fsIfc = fsIfc;
97
+ }
98
+ /**
99
+ * Validates the os level program is executable.
100
+ */
101
+ async validateProgram() {
102
+ await _validateProgram(this.osImpl.getProgram(), this.fsIfc, isExe);
103
+ }
104
+ /**
105
+ * Returns a password using the native program for credential management.
106
+ *
107
+ * @param opts Options for the credential lookup.
108
+ * @param fn Callback function (err, password).
109
+ * @param retryCount Used internally to track the number of retries for getting a password out of the keychain.
110
+ */
111
+ async getPassword(opts, fn, retryCount = 0) {
112
+ if (opts.service == null) {
113
+ fn(messages.createError('keyChainServiceRequiredError'));
114
+ return;
115
+ }
116
+ if (opts.account == null) {
117
+ fn(messages.createError('keyChainAccountRequiredError'));
118
+ return;
119
+ }
120
+ await this.validateProgram();
121
+ const credManager = this.osImpl.getCommandFunc(opts, childProcess.spawn);
122
+ let stdout = '';
123
+ let stderr = '';
124
+ if (credManager.stdout) {
125
+ credManager.stdout.on('data', (data) => {
126
+ stdout += data;
127
+ });
128
+ }
129
+ if (credManager.stderr) {
130
+ credManager.stderr.on('data', (data) => {
131
+ stderr += data;
132
+ });
133
+ }
134
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
135
+ credManager.on('close', async (code) => {
136
+ try {
137
+ return await this.osImpl.onGetCommandClose(code, stdout, stderr, opts, fn);
138
+ }
139
+ catch (e) {
140
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
141
+ // @ts-ignore
142
+ if (e.retry) {
143
+ if (retryCount >= GET_PASSWORD_RETRY_COUNT) {
144
+ throw messages.createError('passwordRetryError', [GET_PASSWORD_RETRY_COUNT]);
145
+ }
146
+ return this.getPassword(opts, fn, retryCount + 1);
147
+ }
148
+ else {
149
+ // if retry
150
+ throw e;
151
+ }
152
+ }
153
+ });
154
+ if (credManager.stdin) {
155
+ credManager.stdin.end();
156
+ }
157
+ }
158
+ /**
159
+ * Sets a password using the native program for credential management.
160
+ *
161
+ * @param opts Options for the credential lookup.
162
+ * @param fn Callback function (err, ConfigContents).
163
+ */
164
+ async setPassword(opts, fn) {
165
+ if (opts.service == null) {
166
+ fn(messages.createError('keyChainServiceRequiredError'));
167
+ return;
168
+ }
169
+ if (opts.account == null) {
170
+ fn(messages.createError('keyChainAccountRequiredError'));
171
+ return;
172
+ }
173
+ if (opts.password == null) {
174
+ fn(messages.createError('passwordRequiredError'));
175
+ return;
176
+ }
177
+ await _validateProgram(this.osImpl.getProgram(), this.fsIfc, isExe);
178
+ const credManager = this.osImpl.setCommandFunc(opts, childProcess.spawn);
179
+ let stdout = '';
180
+ let stderr = '';
181
+ if (credManager.stdout) {
182
+ credManager.stdout.on('data', (data) => {
183
+ stdout += data;
184
+ });
185
+ }
186
+ if (credManager.stderr) {
187
+ credManager.stderr.on('data', (data) => {
188
+ stderr += data;
189
+ });
190
+ }
191
+ credManager.on('close',
192
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
193
+ async (code) => this.osImpl.onSetCommandClose(code, stdout, stderr, opts, fn));
194
+ if (credManager.stdin) {
195
+ credManager.stdin.end();
196
+ }
197
+ }
198
+ }
199
+ exports.KeychainAccess = KeychainAccess;
200
+ /**
201
+ * Linux implementation.
202
+ *
203
+ * Uses libsecret.
204
+ */
205
+ const linuxImpl = {
206
+ getProgram() {
207
+ return process.env.SFDX_SECRET_TOOL_PATH ?? path.join(path.sep, 'usr', 'bin', 'secret-tool');
208
+ },
209
+ getProgramOptions(opts) {
210
+ return ['lookup', 'user', opts.account, 'domain', opts.service];
211
+ },
212
+ getCommandFunc(opts, fn) {
213
+ return fn(linuxImpl.getProgram(), linuxImpl.getProgramOptions(opts));
214
+ },
215
+ // eslint-disable-next-line @typescript-eslint/require-await
216
+ async onGetCommandClose(code, stdout, stderr, opts, fn) {
217
+ if (code === 1) {
218
+ const command = `${linuxImpl.getProgram()} ${optionsToString(linuxImpl.getProgramOptions(opts))}`;
219
+ const error = messages.createError('passwordNotFoundError', [], [command]);
220
+ // This is a workaround for linux.
221
+ // Calling secret-tool too fast can cause it to return an unexpected error. (below)
222
+ if (stderr?.includes('invalid or unencryptable secret')) {
223
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
224
+ // @ts-ignore TODO: make an error subclass with this field
225
+ error.retry = true;
226
+ // Throwing here allows us to perform a retry in KeychainAccess
227
+ throw error;
228
+ }
229
+ // All other issues we will report back to the handler.
230
+ fn(error);
231
+ }
232
+ else {
233
+ fn(null, stdout.trim());
234
+ }
235
+ },
236
+ setProgramOptions(opts) {
237
+ return ['store', "--label='salesforce.com'", 'user', opts.account, 'domain', opts.service];
238
+ },
239
+ setCommandFunc(opts, fn) {
240
+ const secretTool = fn(linuxImpl.getProgram(), linuxImpl.setProgramOptions(opts));
241
+ if (secretTool.stdin) {
242
+ secretTool.stdin.write(`${opts.password}\n`);
243
+ }
244
+ return secretTool;
245
+ },
246
+ // eslint-disable-next-line @typescript-eslint/require-await
247
+ async onSetCommandClose(code, stdout, stderr, opts, fn) {
248
+ if (code !== 0) {
249
+ const command = `${linuxImpl.getProgram()} ${optionsToString(linuxImpl.setProgramOptions(opts))}`;
250
+ fn(messages.createError('setCredentialError', [`${stdout} - ${stderr}`], [os.userInfo().username, command]));
251
+ }
252
+ else {
253
+ fn(null);
254
+ }
255
+ },
256
+ };
257
+ /**
258
+ * OSX implementation.
259
+ *
260
+ * /usr/bin/security is a cli front end for OSX keychain.
261
+ */
262
+ const darwinImpl = {
263
+ getProgram() {
264
+ return path.join(path.sep, 'usr', 'bin', 'security');
265
+ },
266
+ getProgramOptions(opts) {
267
+ return ['find-generic-password', '-a', opts.account, '-s', opts.service, '-g'];
268
+ },
269
+ getCommandFunc(opts, fn) {
270
+ return fn(darwinImpl.getProgram(), darwinImpl.getProgramOptions(opts));
271
+ },
272
+ // eslint-disable-next-line @typescript-eslint/require-await
273
+ async onGetCommandClose(code, stdout, stderr, opts, fn) {
274
+ let err;
275
+ if (code !== 0) {
276
+ switch (code) {
277
+ case 128: {
278
+ err = messages.createError('keyChainUserCanceledError');
279
+ break;
280
+ }
281
+ default: {
282
+ const command = `${darwinImpl.getProgram()} ${optionsToString(darwinImpl.getProgramOptions(opts))}`;
283
+ err = messages.createError('passwordNotFoundError', [`${stdout} - ${stderr}`], [command]);
284
+ }
285
+ }
286
+ fn(err);
287
+ return;
288
+ }
289
+ // For better or worse, the last line (containing the actual password) is actually written to stderr instead of
290
+ // stdout. Reference: http://blog.macromates.com/2006/keychain-access-from-shell/
291
+ if (stderr.includes('password')) {
292
+ const match = RegExp(/"(.*)"/).exec(stderr);
293
+ if (!match || !match[1]) {
294
+ fn(messages.createError('passwordNotFoundError', [`${stdout} - ${stderr}`]));
295
+ }
296
+ else {
297
+ fn(null, match[1]);
298
+ }
299
+ }
300
+ else {
301
+ const command = `${darwinImpl.getProgram()} ${optionsToString(darwinImpl.getProgramOptions(opts))}`;
302
+ fn(messages.createError('passwordNotFoundError', [`${stdout} - ${stderr}`], [command]));
303
+ }
304
+ },
305
+ setProgramOptions(opts) {
306
+ const result = ['add-generic-password', '-a', opts.account, '-s', opts.service];
307
+ if (opts.password) {
308
+ result.push('-w', opts.password);
309
+ }
310
+ return result;
311
+ },
312
+ setCommandFunc(opts, fn) {
313
+ return fn(darwinImpl.getProgram(), darwinImpl.setProgramOptions(opts));
314
+ },
315
+ // eslint-disable-next-line @typescript-eslint/require-await
316
+ async onSetCommandClose(code, stdout, stderr, opts, fn) {
317
+ if (code !== 0) {
318
+ const command = `${darwinImpl.getProgram()} ${optionsToString(darwinImpl.setProgramOptions(opts))}`;
319
+ fn(messages.createError('setCredentialError', [`${stdout} - ${stderr}`], [os.userInfo().username, command]));
320
+ }
321
+ else {
322
+ fn(null);
323
+ }
324
+ },
325
+ };
326
+ const getSecretFile = () => path.join(global_1.Global.DIR, 'key.json');
327
+ var SecretField;
328
+ (function (SecretField) {
329
+ SecretField["SERVICE"] = "service";
330
+ SecretField["ACCOUNT"] = "account";
331
+ SecretField["KEY"] = "key";
332
+ })(SecretField || (SecretField = {}));
333
+ async function writeFile(opts, fn) {
334
+ try {
335
+ const contents = {
336
+ [SecretField.ACCOUNT]: opts.account,
337
+ [SecretField.KEY]: opts.password,
338
+ [SecretField.SERVICE]: opts.service,
339
+ };
340
+ const secretFile = getSecretFile();
341
+ await fs.promises.mkdir(path.dirname(secretFile), { recursive: true });
342
+ await fs.promises.writeFile(secretFile, JSON.stringify(contents, null, 4), { mode: '600' });
343
+ fn(null, contents);
344
+ }
345
+ catch (err) {
346
+ fn(err);
347
+ }
348
+ }
349
+ async function readFile() {
350
+ // The file and access is validated before this method is called
351
+ const fileContents = (0, kit_1.parseJsonMap)(await fs.promises.readFile(getSecretFile(), 'utf8'));
352
+ return {
353
+ account: (0, ts_types_1.ensureString)(fileContents[SecretField.ACCOUNT]),
354
+ password: (0, ts_types_1.asString)(fileContents[SecretField.KEY]),
355
+ service: (0, ts_types_1.ensureString)(fileContents[SecretField.SERVICE]),
356
+ };
357
+ }
358
+ // istanbul ignore next - getPassword/setPassword is always mocked out
359
+ /**
360
+ * @@ignore
361
+ */
362
+ class GenericKeychainAccess {
363
+ async getPassword(opts, fn) {
364
+ // validate the file in .sfdx
365
+ await this.isValidFileAccess(async (fileAccessError) => {
366
+ // the file checks out.
367
+ if (fileAccessError == null) {
368
+ // read it's contents
369
+ try {
370
+ const { service, account, password } = await readFile();
371
+ // validate service name and account just because
372
+ if (opts.service === service && opts.account === account) {
373
+ fn(null, password);
374
+ }
375
+ else {
376
+ // if the service and account names don't match then maybe someone or something is editing
377
+ // that file. #donotallow
378
+ fn(messages.createError('genericKeychainServiceError', [getSecretFile()]));
379
+ }
380
+ }
381
+ catch (readJsonErr) {
382
+ fn(readJsonErr);
383
+ }
384
+ }
385
+ else if (fileAccessError.code === 'ENOENT') {
386
+ fn(messages.createError('passwordNotFoundError'));
387
+ }
388
+ else {
389
+ fn(fileAccessError);
390
+ }
391
+ });
392
+ }
393
+ async setPassword(opts, fn) {
394
+ // validate the file in .sfdx
395
+ await this.isValidFileAccess(async (fileAccessError) => {
396
+ // if there is a validation error
397
+ if (fileAccessError != null) {
398
+ // file not found
399
+ if (fileAccessError.code === 'ENOENT') {
400
+ // create the file
401
+ await writeFile.call(this, opts, fn);
402
+ }
403
+ else {
404
+ fn(fileAccessError);
405
+ }
406
+ }
407
+ else {
408
+ // the existing file validated. we can write the updated key
409
+ await writeFile.call(this, opts, fn);
410
+ }
411
+ });
412
+ }
413
+ // eslint-disable-next-line class-methods-use-this
414
+ async isValidFileAccess(cb) {
415
+ try {
416
+ const root = (0, os_1.homedir)();
417
+ await fs.promises.access(path.join(root, global_1.Global.SFDX_STATE_FOLDER), fs.constants.R_OK | fs.constants.X_OK | fs.constants.W_OK);
418
+ await cb(null);
419
+ }
420
+ catch (err) {
421
+ await cb(err);
422
+ }
423
+ }
424
+ }
425
+ exports.GenericKeychainAccess = GenericKeychainAccess;
426
+ /**
427
+ * @ignore
428
+ */
429
+ // istanbul ignore next - getPassword/setPassword is always mocked out
430
+ class GenericUnixKeychainAccess extends GenericKeychainAccess {
431
+ async isValidFileAccess(cb) {
432
+ await super.isValidFileAccess(async (err) => {
433
+ if (err != null) {
434
+ await cb(err);
435
+ }
436
+ else {
437
+ const secretFile = getSecretFile();
438
+ const stats = await fs.promises.stat(secretFile);
439
+ const octalModeStr = (stats.mode & 0o777).toString(8);
440
+ const EXPECTED_OCTAL_PERM_VALUE = '600';
441
+ if (octalModeStr === EXPECTED_OCTAL_PERM_VALUE) {
442
+ await cb(null);
443
+ }
444
+ else {
445
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
446
+ cb(messages.createError('genericKeychainInvalidPermsError', [secretFile], [secretFile, EXPECTED_OCTAL_PERM_VALUE]));
447
+ }
448
+ }
449
+ });
450
+ }
451
+ }
452
+ exports.GenericUnixKeychainAccess = GenericUnixKeychainAccess;
453
+ /**
454
+ * @ignore
455
+ */
456
+ class GenericWindowsKeychainAccess extends GenericKeychainAccess {
457
+ async isValidFileAccess(cb) {
458
+ await super.isValidFileAccess(async (err) => {
459
+ if (err != null) {
460
+ await cb(err);
461
+ }
462
+ else {
463
+ try {
464
+ await fs.promises.access(getSecretFile(), fs.constants.R_OK | fs.constants.W_OK);
465
+ await cb(null);
466
+ }
467
+ catch (e) {
468
+ await cb(e);
469
+ }
470
+ }
471
+ });
472
+ }
473
+ }
474
+ exports.GenericWindowsKeychainAccess = GenericWindowsKeychainAccess;
475
+ /**
476
+ * @ignore
477
+ */
478
+ exports.keyChainImpl = {
479
+ // eslint-disable-next-line camelcase
480
+ generic_unix: new GenericUnixKeychainAccess(),
481
+ // eslint-disable-next-line camelcase
482
+ generic_windows: new GenericWindowsKeychainAccess(),
483
+ darwin: new KeychainAccess(darwinImpl, nodeFs),
484
+ linux: new KeychainAccess(linuxImpl, nodeFs),
485
+ validateProgram: _validateProgram,
486
+ };
487
487
  //# sourceMappingURL=keyChainImpl.js.map