@lwrjs/shared-utils 0.23.19 → 0.23.21
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/identity.cjs +25 -0
- package/build/es/identity.d.ts +17 -0
- package/build/es/identity.js +43 -0
- package/package.json +4 -4
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/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.23.
|
|
7
|
+
"version": "0.23.21",
|
|
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.23.
|
|
40
|
+
"@lwrjs/diagnostics": "0.23.21",
|
|
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.23.
|
|
53
|
+
"@lwrjs/types": "0.23.21",
|
|
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": ">=22.0.0"
|
|
60
60
|
},
|
|
61
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "c20f56af604d81026133ef9e9a575275056e7a91"
|
|
62
62
|
}
|