@lwrjs/shared-utils 0.19.8 → 0.19.9
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/build/cjs/env.cjs +1 -10
- package/build/cjs/identity.cjs +25 -0
- package/build/es/env.js +3 -27
- package/build/es/identity.d.ts +17 -0
- package/build/es/identity.js +43 -0
- package/package.json +4 -4
package/build/cjs/env.cjs
CHANGED
|
@@ -39,7 +39,6 @@ function getFeatureFlags() {
|
|
|
39
39
|
LEGACY_LOADER: parseBooleanFlag("LEGACY_LOADER"),
|
|
40
40
|
LWR_TRACING: parseTracingFlag(),
|
|
41
41
|
DISABLE_B3_TRACING: parseBooleanFlag("DISABLE_B3_TRACING"),
|
|
42
|
-
MRT_B3_TRACING: parseBooleanFlag("MRT_B3_TRACING"),
|
|
43
42
|
MAX_VIEW_CACHE_TTL: parseStringFlag("MAX_VIEW_CACHE_TTL"),
|
|
44
43
|
REEVALUATE_MODULES: parseBooleanFlag("REEVALUATE_MODULES"),
|
|
45
44
|
SSR_COMPILER_ENABLED: parseBooleanFlag("SSR_COMPILER_ENABLED"),
|
|
@@ -63,14 +62,6 @@ function parseTracingFlag() {
|
|
|
63
62
|
const tracingValue = process.env.LWR_TRACING?.toLowerCase();
|
|
64
63
|
return tracingValue && tracingValue !== "off" ? process.env.LWR_TRACING : void 0;
|
|
65
64
|
}
|
|
66
|
-
function shouldEnableB3Headers() {
|
|
67
|
-
const flags = getFeatureFlags();
|
|
68
|
-
const isMRT = isLambdaEnv() || process.env.MRT_REQUEST_CLASS || process.env.MOBIFY_PROPERTY_ID;
|
|
69
|
-
if (isMRT) {
|
|
70
|
-
return flags.MRT_B3_TRACING !== false;
|
|
71
|
-
}
|
|
72
|
-
return true;
|
|
73
|
-
}
|
|
74
65
|
function isLambdaEnv() {
|
|
75
66
|
return process.env.AWS_LAMBDA_FUNCTION_NAME !== void 0;
|
|
76
67
|
}
|
|
@@ -141,7 +132,7 @@ function getTraceHeaders(runtimeParams, span) {
|
|
|
141
132
|
headers[TRUE_CLIENT_IP] = runtimeParams.trueClientIP;
|
|
142
133
|
if (runtimeParams.correlationID)
|
|
143
134
|
headers[CORRELATION_ID] = runtimeParams.correlationID;
|
|
144
|
-
if (
|
|
135
|
+
if (!getFeatureFlags().DISABLE_B3_TRACING) {
|
|
145
136
|
if (span?.traceId) {
|
|
146
137
|
headers[B3_TRACE_ID] = span.traceId;
|
|
147
138
|
headers[B3_SAMPLED] = parseTracingFlag() ? "1" : "0";
|
package/build/cjs/identity.cjs
CHANGED
|
@@ -37,8 +37,10 @@ __export(exports, {
|
|
|
37
37
|
VERSION_NOT_PROVIDED: () => VERSION_NOT_PROVIDED,
|
|
38
38
|
VERSION_PREFIX: () => VERSION_PREFIX,
|
|
39
39
|
VERSION_SIGIL: () => VERSION_SIGIL,
|
|
40
|
+
containsPathTraversal: () => containsPathTraversal,
|
|
40
41
|
explodeSpecifier: () => explodeSpecifier,
|
|
41
42
|
explodeSpecifiers: () => explodeSpecifiers,
|
|
43
|
+
fullyDecodeSpecifier: () => fullyDecodeSpecifier,
|
|
42
44
|
getCacheKeyFromJson: () => import_fast_json_stable_stringify.default,
|
|
43
45
|
getMappingUriPrefix: () => getMappingUriPrefix,
|
|
44
46
|
getModuleUriPrefix: () => getModuleUriPrefix,
|
|
@@ -85,7 +87,30 @@ function normalizeVersionFromUri(version) {
|
|
|
85
87
|
function prettyModuleUriSuffix(specifier) {
|
|
86
88
|
return `${specifier.replace(/[\/\.\?\#]/g, "_")}`;
|
|
87
89
|
}
|
|
90
|
+
var MAX_DECODE_ITERATIONS = 5;
|
|
91
|
+
function fullyDecodeSpecifier(rawSpecifier) {
|
|
92
|
+
let decoded = rawSpecifier;
|
|
93
|
+
for (let i = 0; i < MAX_DECODE_ITERATIONS; i++) {
|
|
94
|
+
let next;
|
|
95
|
+
try {
|
|
96
|
+
next = decodeURIComponent(decoded);
|
|
97
|
+
} catch {
|
|
98
|
+
return decoded;
|
|
99
|
+
}
|
|
100
|
+
if (next === decoded) {
|
|
101
|
+
return decoded;
|
|
102
|
+
}
|
|
103
|
+
decoded = next;
|
|
104
|
+
}
|
|
105
|
+
throw new Error(`Specifier "${rawSpecifier}" could not be fully decoded within ${MAX_DECODE_ITERATIONS} passes`);
|
|
106
|
+
}
|
|
107
|
+
function containsPathTraversal(decodedSpecifier) {
|
|
108
|
+
return decodedSpecifier.split(/[\\/#?]/).some((segment) => segment === "..");
|
|
109
|
+
}
|
|
88
110
|
function explodeSpecifier(rawSpecifier) {
|
|
111
|
+
if (containsPathTraversal(fullyDecodeSpecifier(rawSpecifier))) {
|
|
112
|
+
throw new Error(`Invalid module specifier "${rawSpecifier}": path traversal is not allowed`);
|
|
113
|
+
}
|
|
89
114
|
const decodedSpecifier = decodeURIComponent(rawSpecifier);
|
|
90
115
|
const versionMatch = decodedSpecifier.match(/(.+)\/v\/([a-zA-Z0-9-_.]+)$/);
|
|
91
116
|
const version = versionMatch ? versionMatch[2] : void 0;
|
package/build/es/env.js
CHANGED
|
@@ -15,10 +15,7 @@ export function getFeatureFlags() {
|
|
|
15
15
|
// Enable metrics log level 'off', 'default' or 'verbose'
|
|
16
16
|
LWR_TRACING: parseTracingFlag(),
|
|
17
17
|
// Turn off distributed tracing (B3 headers, traceparent meta tag, MRT span exporting)
|
|
18
|
-
// Legacy flag - kept for backward compatibility but no longer controls B3 headers
|
|
19
18
|
DISABLE_B3_TRACING: parseBooleanFlag('DISABLE_B3_TRACING'),
|
|
20
|
-
// Control x-b3-* distributed tracing headers specifically for MRT sites
|
|
21
|
-
MRT_B3_TRACING: parseBooleanFlag('MRT_B3_TRACING'),
|
|
22
19
|
// Max size of ViewDefinition time to live
|
|
23
20
|
MAX_VIEW_CACHE_TTL: parseStringFlag('MAX_VIEW_CACHE_TTL'),
|
|
24
21
|
// Forces SSR to re-evaluate modules for every page render. By default, modules are evaluated only once.
|
|
@@ -59,32 +56,13 @@ function parseStringFlag(flag) {
|
|
|
59
56
|
return value || undefined;
|
|
60
57
|
}
|
|
61
58
|
/**
|
|
62
|
-
* Helper function to parse the LWR_TRACING flag
|
|
63
|
-
* Returns the value if it's not 'off' and is defined, otherwise returns
|
|
64
|
-
* This controls OpenTelemetry spans, NOT B3 distributed tracing headers.
|
|
59
|
+
* Helper function to parse the LWR_TRACING flag.
|
|
60
|
+
* Returns the value if it's not 'off' and is defined, otherwise returns false.
|
|
65
61
|
*/
|
|
66
62
|
function parseTracingFlag() {
|
|
67
63
|
const tracingValue = process.env.LWR_TRACING?.toLowerCase();
|
|
68
64
|
return tracingValue && tracingValue !== 'off' ? process.env.LWR_TRACING : undefined;
|
|
69
65
|
}
|
|
70
|
-
/**
|
|
71
|
-
* Helper function to determine if B3 distributed tracing headers should be enabled.
|
|
72
|
-
* Uses MRT_B3_TRACING for MRT sites, always enabled for non-MRT sites.
|
|
73
|
-
* Separates OpenTelemetry spans (LWR_TRACING) from B3 header propagation.
|
|
74
|
-
*
|
|
75
|
-
* @returns true if B3 distributed tracing headers should be set
|
|
76
|
-
*/
|
|
77
|
-
function shouldEnableB3Headers() {
|
|
78
|
-
const flags = getFeatureFlags();
|
|
79
|
-
// Check if this is an MRT environment
|
|
80
|
-
const isMRT = isLambdaEnv() || process.env.MRT_REQUEST_CLASS || process.env.MOBIFY_PROPERTY_ID;
|
|
81
|
-
if (isMRT) {
|
|
82
|
-
// For MRT sites, use the MRT-specific distributed tracing flag
|
|
83
|
-
return flags.MRT_B3_TRACING !== false;
|
|
84
|
-
}
|
|
85
|
-
// For non-MRT sites, always enable B3 headers
|
|
86
|
-
return true;
|
|
87
|
-
}
|
|
88
66
|
/**
|
|
89
67
|
* This function is used to determine if the current environment is a lambda.
|
|
90
68
|
*
|
|
@@ -191,9 +169,7 @@ export function getTraceHeaders(runtimeParams, span) {
|
|
|
191
169
|
headers[TRUE_CLIENT_IP] = runtimeParams.trueClientIP;
|
|
192
170
|
if (runtimeParams.correlationID)
|
|
193
171
|
headers[CORRELATION_ID] = runtimeParams.correlationID;
|
|
194
|
-
|
|
195
|
-
// These headers are controlled independently from OpenTelemetry spans (LWR_TRACING)
|
|
196
|
-
if (shouldEnableB3Headers()) {
|
|
172
|
+
if (!getFeatureFlags().DISABLE_B3_TRACING) {
|
|
197
173
|
if (span?.traceId) {
|
|
198
174
|
headers[B3_TRACE_ID] = span.traceId;
|
|
199
175
|
headers[B3_SAMPLED] = parseTracingFlag() ? '1' : '0';
|
package/build/es/identity.d.ts
CHANGED
|
@@ -35,6 +35,23 @@ export declare function normalizeVersionFromUri(version: string): string;
|
|
|
35
35
|
* examples/app/scoped/css?scoped=true => examples_app_scoped_css_scoped=true
|
|
36
36
|
*/
|
|
37
37
|
export declare function prettyModuleUriSuffix(specifier: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* Repeatedly percent-decodes a string until it stabilizes (or the iteration bound is hit).
|
|
40
|
+
* A single decode pass can be defeated by nesting encodings (e.g. '%252e%252e%252f'
|
|
41
|
+
* decodes once to '%2e%2e%2f', which looks harmless until it's decoded again). Fully
|
|
42
|
+
* resolving the encoding first means a later traversal check can't be smuggled past.
|
|
43
|
+
*/
|
|
44
|
+
export declare function fullyDecodeSpecifier(rawSpecifier: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* True if any '/'-, '\'-, '#'-, or '?'-delimited segment of an already-decoded specifier
|
|
47
|
+
* is exactly '..' (a directory traversal segment). Segment based so legitimate names
|
|
48
|
+
* containing dots (e.g. 'my.label') are never mistaken for traversal. '#' and '?' are
|
|
49
|
+
* included because callers (e.g. lwc-module-provider) split the fragment after '#' off
|
|
50
|
+
* of a specifier and path.join it against a resolved directory - a '..' glued directly
|
|
51
|
+
* after '#' (e.g. 'c/app#../secret') would otherwise never appear as its own '/'-delimited
|
|
52
|
+
* segment and slip past this check (W-24189528).
|
|
53
|
+
*/
|
|
54
|
+
export declare function containsPathTraversal(decodedSpecifier: string): boolean;
|
|
38
55
|
/**
|
|
39
56
|
* Create a partial Module Identifier from a specifier
|
|
40
57
|
* The Module Identifier is partial because the specifier may not contain a version
|
package/build/es/identity.js
CHANGED
|
@@ -42,7 +42,50 @@ export function prettyModuleUriSuffix(specifier) {
|
|
|
42
42
|
// eslint-disable-next-line no-useless-escape
|
|
43
43
|
return `${specifier.replace(/[\/\.\?\#]/g, '_')}`;
|
|
44
44
|
}
|
|
45
|
+
const MAX_DECODE_ITERATIONS = 5;
|
|
46
|
+
/**
|
|
47
|
+
* Repeatedly percent-decodes a string until it stabilizes (or the iteration bound is hit).
|
|
48
|
+
* A single decode pass can be defeated by nesting encodings (e.g. '%252e%252e%252f'
|
|
49
|
+
* decodes once to '%2e%2e%2f', which looks harmless until it's decoded again). Fully
|
|
50
|
+
* resolving the encoding first means a later traversal check can't be smuggled past.
|
|
51
|
+
*/
|
|
52
|
+
export function fullyDecodeSpecifier(rawSpecifier) {
|
|
53
|
+
let decoded = rawSpecifier;
|
|
54
|
+
for (let i = 0; i < MAX_DECODE_ITERATIONS; i++) {
|
|
55
|
+
let next;
|
|
56
|
+
try {
|
|
57
|
+
next = decodeURIComponent(decoded);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return decoded; // malformed escape sequence, nothing further to decode
|
|
61
|
+
}
|
|
62
|
+
if (next === decoded) {
|
|
63
|
+
return decoded; // fully stable within budget
|
|
64
|
+
}
|
|
65
|
+
decoded = next;
|
|
66
|
+
}
|
|
67
|
+
// Every pass up to the budget still changed the string, so the encoding
|
|
68
|
+
// depth is unknown and possibly unbounded. Fail closed instead of checking
|
|
69
|
+
// a value that might still have traversal segments hidden behind more
|
|
70
|
+
// encoding than we were willing to unwrap.
|
|
71
|
+
throw new Error(`Specifier "${rawSpecifier}" could not be fully decoded within ${MAX_DECODE_ITERATIONS} passes`);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* True if any '/'-, '\'-, '#'-, or '?'-delimited segment of an already-decoded specifier
|
|
75
|
+
* is exactly '..' (a directory traversal segment). Segment based so legitimate names
|
|
76
|
+
* containing dots (e.g. 'my.label') are never mistaken for traversal. '#' and '?' are
|
|
77
|
+
* included because callers (e.g. lwc-module-provider) split the fragment after '#' off
|
|
78
|
+
* of a specifier and path.join it against a resolved directory - a '..' glued directly
|
|
79
|
+
* after '#' (e.g. 'c/app#../secret') would otherwise never appear as its own '/'-delimited
|
|
80
|
+
* segment and slip past this check (W-24189528).
|
|
81
|
+
*/
|
|
82
|
+
export function containsPathTraversal(decodedSpecifier) {
|
|
83
|
+
return decodedSpecifier.split(/[\\/#?]/).some((segment) => segment === '..');
|
|
84
|
+
}
|
|
45
85
|
export function explodeSpecifier(rawSpecifier) {
|
|
86
|
+
if (containsPathTraversal(fullyDecodeSpecifier(rawSpecifier))) {
|
|
87
|
+
throw new Error(`Invalid module specifier "${rawSpecifier}": path traversal is not allowed`);
|
|
88
|
+
}
|
|
46
89
|
const decodedSpecifier = decodeURIComponent(rawSpecifier);
|
|
47
90
|
// Split up the version and bare specifier
|
|
48
91
|
const versionMatch = decodedSpecifier.match(/(.+)\/v\/([a-zA-Z0-9-_.]+)$/);
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"version": "0.19.
|
|
7
|
+
"version": "0.19.9",
|
|
8
8
|
"homepage": "https://developer.salesforce.com/docs/platform/lwr/overview",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"build/**/*.d.ts"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@lwrjs/diagnostics": "0.19.
|
|
40
|
+
"@lwrjs/diagnostics": "0.19.9",
|
|
41
41
|
"es-module-lexer": "^1.5.4",
|
|
42
42
|
"fast-json-stable-stringify": "^2.1.0",
|
|
43
43
|
"magic-string": "^0.30.9",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"slugify": "^1.4.5"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@lwrjs/types": "0.19.
|
|
53
|
+
"@lwrjs/types": "0.19.9",
|
|
54
54
|
"@types/mime-types": "2.1.4",
|
|
55
55
|
"@types/path-to-regexp": "^1.7.0",
|
|
56
56
|
"memfs": "^4.13.0"
|
|
@@ -58,5 +58,5 @@
|
|
|
58
58
|
"engines": {
|
|
59
59
|
"node": ">=20.0.0"
|
|
60
60
|
},
|
|
61
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "721f776d04844cbc99531d449c8200fac74c5fa2"
|
|
62
62
|
}
|