fix(10.1-04): refuse percent-encoded dot segments in plugin asset URLs
The URL parser resolves %2e%2e like .., so /{base}/assets/%2e%2e/api/...
passed assetAllowed and would load from outside the asset prefix. A
segment is now a dot segment after decoding %2e, in any case.
- tests/app/pluginAssets.test.ts covers the URL check, loadScript,
loadStyles, activateStyles and loadControllerAssets
- modules/boardwalk/dist rebuilt
This commit is contained in:
@@ -25,7 +25,9 @@ export function assetPrefix(): string {
|
||||
/**
|
||||
* Whether a URL may load as a plugin asset: a same-origin path under
|
||||
* {base}/assets/ with no dot segment, backslash, whitespace or control
|
||||
* character (browsers strip or rewrite those before resolving).
|
||||
* character (browsers strip or rewrite those before resolving). A
|
||||
* percent-encoded dot counts as a dot: the URL parser resolves %2e%2e like
|
||||
* .., which would climb out of the asset prefix.
|
||||
*/
|
||||
export function assetAllowed(url: string): boolean {
|
||||
if (!url.startsWith(assetPrefix()) || url.includes('\\')) {
|
||||
@@ -38,7 +40,10 @@ export function assetAllowed(url: string): boolean {
|
||||
}
|
||||
}
|
||||
const path = url.split(/[?#]/, 1)[0] ?? ''
|
||||
return !path.split('/').some((segment) => segment === '.' || segment === '..')
|
||||
return !path.split('/').some((segment) => {
|
||||
const dots = segment.toLowerCase().replaceAll('%2e', '.')
|
||||
return dots === '.' || dots === '..'
|
||||
})
|
||||
}
|
||||
|
||||
/** Loads one module script; the same URL always yields the same promise. */
|
||||
|
||||
Reference in New Issue
Block a user