@salesforce/core 8.25.1 → 8.26.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/logger/filters.js +2 -0
- package/lib/org/authInfo.d.ts +0 -1
- package/lib/org/authInfo.js +2 -2
- package/lib/org/scratchOrgInfoGenerator.js +18 -3
- package/lib/org/user.js +1 -1
- package/lib/schema/project-scratch-def/scratchOrgDef.d.ts +8 -8
- package/lib/schema/project-scratch-def/scratchOrgDef.js +8 -8
- package/lib/schema/project-scratch-def/simpleFeaturesList.js +1 -0
- package/lib/util/sfdc.d.ts +15 -2
- package/lib/util/sfdc.js +45 -4
- package/package.json +1 -1
- package/schemas/project-scratch-def.schema.json +9 -8
package/lib/logger/filters.js
CHANGED
|
@@ -17,6 +17,7 @@ const buildKeyRegex = (expElement) => RegExp(`(['"]\\s*key\\s*['"]\\s*:)\\s*['"]
|
|
|
17
17
|
// This will redact values when the keys match certain patterns
|
|
18
18
|
const FILTERED_KEYS = [
|
|
19
19
|
{ name: 'sid' },
|
|
20
|
+
{ name: 'jwt' },
|
|
20
21
|
{ name: 'Authorization' },
|
|
21
22
|
// Any json attribute that contains the words "refresh" and "token" will have the attribute/value hidden
|
|
22
23
|
{ name: 'refresh_token', regex: 'refresh[^\'"]*token' },
|
|
@@ -39,6 +40,7 @@ const replacementFunctions = FILTERED_KEYS_FOR_PROCESSING.flatMap((key) => [
|
|
|
39
40
|
// use these for secrets with known patterns
|
|
40
41
|
(input) => input
|
|
41
42
|
.replace(new RegExp(sfdc_1.accessTokenRegex, 'g'), '<REDACTED ACCESS TOKEN>')
|
|
43
|
+
.replace(new RegExp(sfdc_1.jwtTokenRegex, 'g'), '<REDACTED JWT TOKEN>')
|
|
42
44
|
.replace(new RegExp(sfdc_1.sfdxAuthUrlRegex, 'g'), '<REDACTED AUTH URL TOKEN>'),
|
|
43
45
|
// conditional replacement for clientId: leave the value if it's the PlatformCLI, otherwise redact it
|
|
44
46
|
(input) => input.replace(/(['"]client.*Id['"])\s*:\s*(['"][^'"]*['"])/gi, (all, key, value) => value.includes('PlatformCLI') ? `${key}:${value}` : `${key}:"<REDACTED CLIENT ID>"`),
|
package/lib/org/authInfo.d.ts
CHANGED
package/lib/org/authInfo.js
CHANGED
|
@@ -425,7 +425,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
425
425
|
async save(authData) {
|
|
426
426
|
this.update(authData);
|
|
427
427
|
const username = (0, ts_types_1.ensure)(this.getUsername());
|
|
428
|
-
if ((0, sfdc_1.
|
|
428
|
+
if ((0, sfdc_1.matchesOpaqueAccessToken)(username)) {
|
|
429
429
|
this.logger.debug('Username is an accesstoken. Skip saving authinfo to disk.');
|
|
430
430
|
return this;
|
|
431
431
|
}
|
|
@@ -662,7 +662,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
662
662
|
await this.stateAggregator.orgs.read(oauthUsername, false, false);
|
|
663
663
|
} // Else it will be set in initAuthOptions below.
|
|
664
664
|
// If the username is an access token, use that for auth and don't persist
|
|
665
|
-
if ((0, ts_types_1.isString)(oauthUsername) && (0, sfdc_1.
|
|
665
|
+
if ((0, ts_types_1.isString)(oauthUsername) && (0, sfdc_1.matchesOpaqueAccessToken)(oauthUsername)) {
|
|
666
666
|
// Need to initAuthOptions the logger and authInfoCrypto since we don't call init()
|
|
667
667
|
this.logger = await logger_1.Logger.child('AuthInfo');
|
|
668
668
|
const aggregator = await configAggregator_1.ConfigAggregator.create();
|
|
@@ -181,12 +181,14 @@ exports.generateScratchOrgInfo = generateScratchOrgInfo;
|
|
|
181
181
|
*/
|
|
182
182
|
const getScratchOrgInfoPayload = async (options) => {
|
|
183
183
|
let warnings = [];
|
|
184
|
-
//
|
|
184
|
+
// Merge after all validations complete
|
|
185
185
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
186
186
|
const scratchOrgInfoPayload = {
|
|
187
187
|
...(options.definitionfile ? await parseDefinitionFile(options.definitionfile) : {}),
|
|
188
|
-
...(options.definitionjson
|
|
189
|
-
|
|
188
|
+
...(options.definitionjson
|
|
189
|
+
? await validateInputDef(JSON.parse(options.definitionjson), 'definitionjson')
|
|
190
|
+
: {}),
|
|
191
|
+
...(options.orgConfig ? await validateInputDef(options.orgConfig, 'orgConfig') : {}),
|
|
190
192
|
};
|
|
191
193
|
// scratchOrgInfoPayload must be heads down camelcase.
|
|
192
194
|
Object.keys(scratchOrgInfoPayload).forEach((key) => {
|
|
@@ -223,6 +225,19 @@ const getScratchOrgInfoPayload = async (options) => {
|
|
|
223
225
|
};
|
|
224
226
|
};
|
|
225
227
|
exports.getScratchOrgInfoPayload = getScratchOrgInfoPayload;
|
|
228
|
+
/**
|
|
229
|
+
* Validates input definition objects (definitionjson or orgConfig) against schema
|
|
230
|
+
* Emits warnings if validation issues are found
|
|
231
|
+
*/
|
|
232
|
+
const validateInputDef = async (input, source) => {
|
|
233
|
+
const result = scratchOrgDef_1.ScratchOrgDefSchema.safeParse(input);
|
|
234
|
+
if (!result.success) {
|
|
235
|
+
const errorMessages = result.error.issues.map((err) => `${err.path.join('.')}: ${err.message}`).join('\n');
|
|
236
|
+
await lifecycleEvents_1.Lifecycle.getInstance().emitWarning(`Scratch org definition validation issues in ${source}:\n${errorMessages}`);
|
|
237
|
+
return input;
|
|
238
|
+
}
|
|
239
|
+
return result.data;
|
|
240
|
+
};
|
|
226
241
|
const parseDefinitionFile = async (definitionFile) => {
|
|
227
242
|
try {
|
|
228
243
|
const fileData = await fs_1.fs.promises.readFile(definitionFile, 'utf8');
|
package/lib/org/user.js
CHANGED
|
@@ -437,7 +437,7 @@ class User extends kit_1.AsyncCreatable {
|
|
|
437
437
|
}
|
|
438
438
|
exports.User = User;
|
|
439
439
|
const resolveUsernameFromAccessToken = (logger) => (conn) => async (usernameOrAccessToken) => {
|
|
440
|
-
if ((0, sfdc_1.
|
|
440
|
+
if ((0, sfdc_1.matchesOpaqueAccessToken)(usernameOrAccessToken)) {
|
|
441
441
|
logger.debug('received an accessToken for the username. Converting...');
|
|
442
442
|
const username = (await conn.identity()).username;
|
|
443
443
|
logger.debug(`accessToken converted to ${username}`);
|
|
@@ -6,14 +6,14 @@ import { z } from 'zod';
|
|
|
6
6
|
export declare const ScratchOrgDefSchema: z.ZodObject<{
|
|
7
7
|
orgName: z.ZodOptional<z.ZodString>;
|
|
8
8
|
edition: z.ZodEnum<{
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
|
|
9
|
+
group: "group";
|
|
10
|
+
developer: "developer";
|
|
11
|
+
enterprise: "enterprise";
|
|
12
|
+
"partner-developer": "partner-developer";
|
|
13
|
+
"partner-enterprise": "partner-enterprise";
|
|
14
|
+
"partner-group": "partner-group";
|
|
15
|
+
"partner-professional": "partner-professional";
|
|
16
|
+
professional: "professional";
|
|
17
17
|
}>;
|
|
18
18
|
country: z.ZodOptional<z.ZodString>;
|
|
19
19
|
username: z.ZodOptional<z.ZodString>;
|
|
@@ -19,14 +19,14 @@ exports.ScratchOrgDefSchema = zod_1.z
|
|
|
19
19
|
orgName: zod_1.z.string().optional().describe('The name of the scratch org.').meta({ title: 'Organization Name' }),
|
|
20
20
|
edition: zod_1.z
|
|
21
21
|
.enum([
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
'
|
|
26
|
-
'
|
|
27
|
-
'
|
|
28
|
-
'
|
|
29
|
-
'
|
|
22
|
+
'developer',
|
|
23
|
+
'enterprise',
|
|
24
|
+
'group',
|
|
25
|
+
'partner-developer',
|
|
26
|
+
'partner-enterprise',
|
|
27
|
+
'partner-group',
|
|
28
|
+
'partner-professional',
|
|
29
|
+
'professional',
|
|
30
30
|
])
|
|
31
31
|
.describe('The Salesforce edition of the scratch org.'),
|
|
32
32
|
country: zod_1.z
|
package/lib/util/sfdc.d.ts
CHANGED
|
@@ -31,10 +31,23 @@ export declare const validateSalesforceId: (value: string) => boolean;
|
|
|
31
31
|
*/
|
|
32
32
|
export declare const validatePathDoesNotContainInvalidChars: (value: string) => boolean;
|
|
33
33
|
export declare const accessTokenRegex: RegExp;
|
|
34
|
+
export declare const jwtTokenRegex: RegExp;
|
|
34
35
|
export declare const sfdxAuthUrlRegex: RegExp;
|
|
35
36
|
/**
|
|
36
|
-
* Tests whether a given string is an access token
|
|
37
|
+
* Tests whether a given string is an opaque access token, a JWT token, or neither.
|
|
37
38
|
*
|
|
38
39
|
* @param value
|
|
39
40
|
*/
|
|
40
|
-
export declare
|
|
41
|
+
export declare function matchesAccessToken(value: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Tests whether a given string is an opaque access token.
|
|
44
|
+
*
|
|
45
|
+
* @param value
|
|
46
|
+
*/
|
|
47
|
+
export declare const matchesOpaqueAccessToken: (value: string) => boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Tests whether a given string is a JWT-formatted access token.
|
|
50
|
+
*
|
|
51
|
+
* @param value
|
|
52
|
+
*/
|
|
53
|
+
export declare const matchesJwtAccessToken: (value: string) => boolean;
|
package/lib/util/sfdc.js
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.
|
|
9
|
+
exports.matchesJwtAccessToken = exports.matchesOpaqueAccessToken = exports.sfdxAuthUrlRegex = exports.jwtTokenRegex = exports.accessTokenRegex = exports.validatePathDoesNotContainInvalidChars = exports.validateSalesforceId = exports.validateEmail = exports.validateApiVersion = void 0;
|
|
10
10
|
exports.trimTo15 = trimTo15;
|
|
11
|
+
exports.matchesAccessToken = matchesAccessToken;
|
|
11
12
|
function trimTo15(id) {
|
|
12
13
|
if (!id) {
|
|
13
14
|
return undefined;
|
|
@@ -48,12 +49,52 @@ const validatePathDoesNotContainInvalidChars = (value) =>
|
|
|
48
49
|
!/[\["\?<>\|\]]+/.test(value);
|
|
49
50
|
exports.validatePathDoesNotContainInvalidChars = validatePathDoesNotContainInvalidChars;
|
|
50
51
|
exports.accessTokenRegex = /(00D\w{12,15})![.\w]*/;
|
|
52
|
+
// 'eyJ' strongly suggests that this is a base64 JSON, and so the general shape of the rest of it is enough to presume it's a JWT.
|
|
53
|
+
exports.jwtTokenRegex = /eyJ[A-Za-z0-9+=_-]+\.[A-Za-z0-9+=_-]+\.[A-Za-z0-9+=_-]+/;
|
|
51
54
|
exports.sfdxAuthUrlRegex = /force:\/\/([a-zA-Z0-9._-]+):([a-zA-Z0-9._-]*):([a-zA-Z0-9._-]+={0,2})@([a-zA-Z0-9._-]+)/;
|
|
52
55
|
/**
|
|
53
|
-
* Tests whether a given string is an access token
|
|
56
|
+
* Tests whether a given string is an opaque access token, a JWT token, or neither.
|
|
54
57
|
*
|
|
55
58
|
* @param value
|
|
56
59
|
*/
|
|
57
|
-
|
|
58
|
-
exports.
|
|
60
|
+
function matchesAccessToken(value) {
|
|
61
|
+
return (0, exports.matchesOpaqueAccessToken)(value) || (0, exports.matchesJwtAccessToken)(value);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Tests whether a given string is an opaque access token.
|
|
65
|
+
*
|
|
66
|
+
* @param value
|
|
67
|
+
*/
|
|
68
|
+
const matchesOpaqueAccessToken = (value) => exports.accessTokenRegex.test(value);
|
|
69
|
+
exports.matchesOpaqueAccessToken = matchesOpaqueAccessToken;
|
|
70
|
+
/**
|
|
71
|
+
* Tests whether a given string is a JWT-formatted access token.
|
|
72
|
+
*
|
|
73
|
+
* @param value
|
|
74
|
+
*/
|
|
75
|
+
const matchesJwtAccessToken = (value) => {
|
|
76
|
+
const segments = value.split('.');
|
|
77
|
+
if (segments.length !== 3) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
if (!isValidJson(segments[0], true)) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
return isValidJson(segments[1], false);
|
|
84
|
+
};
|
|
85
|
+
exports.matchesJwtAccessToken = matchesJwtAccessToken;
|
|
86
|
+
const isValidJson = (str, checkForTyp) => {
|
|
87
|
+
try {
|
|
88
|
+
const parsedJson = JSON.parse(Buffer.from(str, 'base64').toString('utf-8'));
|
|
89
|
+
if (checkForTyp) {
|
|
90
|
+
return 'typ' in parsedJson && parsedJson.typ === 'JWT';
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (e) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
59
100
|
//# sourceMappingURL=sfdc.js.map
|
package/package.json
CHANGED
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
"description": "The Salesforce edition of the scratch org.",
|
|
12
12
|
"type": "string",
|
|
13
13
|
"enum": [
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
14
|
+
"developer",
|
|
15
|
+
"enterprise",
|
|
16
|
+
"group",
|
|
17
|
+
"partner-developer",
|
|
18
|
+
"partner-enterprise",
|
|
19
|
+
"partner-group",
|
|
20
|
+
"partner-professional",
|
|
21
|
+
"professional"
|
|
22
22
|
]
|
|
23
23
|
},
|
|
24
24
|
"country": {
|
|
@@ -452,6 +452,7 @@
|
|
|
452
452
|
"EinsteinGPTForDevelopers",
|
|
453
453
|
"EinsteinRecommendationBuilder",
|
|
454
454
|
"EinsteinRecommendationBuilderMetadata",
|
|
455
|
+
"EinsteinSalesRepFdbk",
|
|
455
456
|
"EinsteinSearch",
|
|
456
457
|
"EinsteinVisits",
|
|
457
458
|
"EinsteinVisitsED",
|