@@ -4,7 +4,9 @@ import path from "path";
44
55// Define a safe root directory for projects. Can be overridden by env or configured as needed.
66// All incoming URIs will be resolved and validated to ensure they don't escape this root.
7- const SAFE_ROOT = path . resolve ( process . env . PLATFORM_API_ROOT ?? "/pulse-editor" ) ;
7+ const SAFE_ROOT = path . resolve (
8+ process . env . PLATFORM_API_ROOT ?? "/pulse-editor" ,
9+ ) ;
810
911const settingsPath = path . join ( SAFE_ROOT , "settings.json" ) ;
1012
@@ -14,14 +16,22 @@ function safeResolve(uri: string): string {
1416 }
1517
1618 // Resolve the input and the safe root to absolute, normalized paths.
17- const resolved = path . resolve ( uri ) ;
18- const root = SAFE_ROOT ;
19-
20- const relative = path . relative ( root , resolved ) ;
19+ // Always resolve relative to SAFE_ROOT (do not trust 'uri' alone)
20+ const candidate = path . resolve ( SAFE_ROOT , uri ) ;
21+ let rootReal , candidateReal ;
22+ try {
23+ rootReal = fs . realpathSync ( SAFE_ROOT ) ;
24+ candidateReal = fs . realpathSync ( candidate ) ;
25+ } catch ( e ) {
26+ throw new Error ( "Path does not exist or cannot be accessed" ) ;
27+ }
2128
22- // If the relative path starts with '..' or is absolute, it escapes the SAFE_ROOT.
23- if ( relative === "" || ( ! relative . startsWith ( ".." ) && ! path . isAbsolute ( relative ) ) ) {
24- return resolved ;
29+ // Check that candidateReal is strictly under rootReal (or equal to rootReal)
30+ if (
31+ candidateReal === rootReal ||
32+ candidateReal . startsWith ( rootReal + path . sep )
33+ ) {
34+ return candidateReal ;
2535 }
2636
2737 throw new Error ( "Can only access paths within the project home directory." ) ;
@@ -131,7 +141,6 @@ export async function handlePlatformAPIRequest(
131141 }
132142}
133143
134-
135144// List all folders in a path
136145async function handleListProjects ( uri : string ) {
137146 const rootPath = safeResolve ( uri ) ;
0 commit comments