@@ -12,23 +12,23 @@ const workspaceRoot = "/workspace";
1212const settingsPath = path . join ( appRoot , "settings.json" ) ;
1313
1414function safeWorkspaceResolve ( uri : string ) : string {
15- if ( ! uri || typeof uri !== "string" ) {
16- throw new Error ( "Invalid path" ) ;
15+ const absPath = path . isAbsolute ( uri )
16+ ? path . resolve ( uri )
17+ : path . resolve ( workspaceRoot , uri ) ;
18+
19+ // Resolve symlinks to their actual paths
20+ const realPath = fs . existsSync ( absPath ) ? fs . realpathSync ( absPath ) : absPath ;
21+
22+ // Ensure it's inside the workspace root (strict, cross-platform)
23+ const rel = path . relative ( workspaceRoot , realPath ) ;
24+ if (
25+ rel . startsWith ( '..' ) ||
26+ path . isAbsolute ( rel )
27+ ) {
28+ throw new Error ( "Cannot access path outside of workspace path." ) ;
1729 }
1830
19- // uri should be an absolute path
20- if ( ! path . isAbsolute ( uri ) ) {
21- throw new Error ( "Path must be absolute" ) ;
22- }
23-
24- // Check that candidate is strictly under rootPath (or equal to rootPath)
25- const rel = path . relative ( workspaceRoot , uri ) ;
26- // Allow if candidate is rootPath itself, or a subpath (not escaping via '..', not absolute)
27- if ( rel === "" || ( ! rel . startsWith ( ".." ) && ! path . isAbsolute ( rel ) ) ) {
28- return uri ;
29- }
30-
31- throw new Error ( "Can only access paths within the project home directory." ) ;
31+ return realPath ;
3232}
3333
3434export async function handlePlatformAPIRequest (
@@ -50,20 +50,20 @@ export async function handlePlatformAPIRequest(
5050 throw new Error ( "Method not implemented." ) ;
5151 case "list-projects" : {
5252 const { uri } : { uri : string } = args ;
53- return await handleListProjects ( uri ) ;
53+ return await handleListProjects ( safeWorkspaceResolve ( uri ) ) ;
5454 }
5555 case "list-path-content" : {
5656 const { uri, options } : { uri : string ; options ?: any } = args ;
57- return await handleListPathContent ( uri , options ) ;
57+ return await handleListPathContent ( safeWorkspaceResolve ( uri ) , options ) ;
5858 }
5959 case "create-project" : {
6060 const { uri } : { uri : string } = args ;
61- await handleCreateProject ( uri ) ;
61+ await handleCreateProject ( safeWorkspaceResolve ( uri ) ) ;
6262 return ;
6363 }
6464 case "delete-project" : {
6565 const { uri } : { uri : string } = args ;
66- await handleDeleteProject ( uri ) ;
66+ await handleDeleteProject ( safeWorkspaceResolve ( uri ) ) ;
6767 return ;
6868 }
6969 case "update-project" : {
@@ -77,45 +77,51 @@ export async function handlePlatformAPIRequest(
7777 ctime ?: Date ;
7878 } ;
7979 } = args ;
80- await handleUpdateProject ( uri , updatedInfo ) ;
80+ await handleUpdateProject ( safeWorkspaceResolve ( uri ) , updatedInfo ) ;
8181 return ;
8282 }
8383 case "create-folder" : {
8484 const { uri } : { uri : string } = args ;
85- await handleCreateFolder ( uri ) ;
85+ await handleCreateFolder ( safeWorkspaceResolve ( uri ) ) ;
8686 return ;
8787 }
8888 case "create-file" : {
8989 const { uri } : { uri : string } = args ;
90- await handleCreateFile ( uri ) ;
90+ await handleCreateFile ( safeWorkspaceResolve ( uri ) ) ;
9191 return ;
9292 }
9393 case "rename" : {
9494 const { oldUri, newUri } : { oldUri : string ; newUri : string } = args ;
95- await handleRename ( oldUri , newUri ) ;
95+ await handleRename (
96+ safeWorkspaceResolve ( oldUri ) ,
97+ safeWorkspaceResolve ( newUri ) ,
98+ ) ;
9699 return ;
97100 }
98101 case "delete" : {
99102 const { uri } : { uri : string } = args ;
100- await handleDelete ( uri ) ;
103+ await handleDelete ( safeWorkspaceResolve ( uri ) ) ;
101104 return ;
102105 }
103106 case "has-path" : {
104107 const { uri } : { uri : string } = args ;
105- return await handleHasPath ( uri ) ;
108+ return await handleHasPath ( safeWorkspaceResolve ( uri ) ) ;
106109 }
107110 case "read-file" : {
108111 const { uri } : { uri : string } = args ;
109- return await handleReadFile ( uri ) ;
112+ return await handleReadFile ( safeWorkspaceResolve ( uri ) ) ;
110113 }
111114 case "write-file" : {
112115 const { data, uri } : { data : any ; uri : string } = args ;
113- await handleWriteFile ( data , uri ) ;
116+ await handleWriteFile ( data , safeWorkspaceResolve ( uri ) ) ;
114117 return ;
115118 }
116119 case "copy-files" : {
117120 const { from, to } : { from : string ; to : string } = args ;
118- await handleCopyFiles ( from , to ) ;
121+ await handleCopyFiles (
122+ safeWorkspaceResolve ( from ) ,
123+ safeWorkspaceResolve ( to ) ,
124+ ) ;
119125 return ;
120126 }
121127 case "get-persistent-settings" :
@@ -137,14 +143,13 @@ export async function handlePlatformAPIRequest(
137143
138144// List all folders in a path
139145async function handleListProjects ( uri : string ) {
140- const rootPath = safeWorkspaceResolve ( uri ) ;
141- const files = await fs . promises . readdir ( rootPath , { withFileTypes : true } ) ;
146+ const files = await fs . promises . readdir ( uri , { withFileTypes : true } ) ;
142147 const folders = files
143148 . filter ( ( file ) => file . isDirectory ( ) )
144149 . map ( ( file ) => file . name )
145150 . map ( ( projectName ) => ( {
146151 name : projectName ,
147- ctime : fs . statSync ( path . join ( rootPath , projectName ) ) . ctime ,
152+ ctime : fs . statSync ( path . join ( uri , projectName ) ) . ctime ,
148153 } ) ) ;
149154
150155 return folders ;
@@ -155,8 +160,7 @@ async function listPathContent(
155160 options : any ,
156161 baseUri : string | undefined = undefined ,
157162) {
158- const rootPath = safeWorkspaceResolve ( uri ) ;
159- const files = await fs . promises . readdir ( rootPath , { withFileTypes : true } ) ;
163+ const files = await fs . promises . readdir ( uri , { withFileTypes : true } ) ;
160164
161165 const promise : Promise < any > [ ] = files
162166 // Filter by file type
@@ -183,7 +187,7 @@ async function listPathContent(
183187 } )
184188 . map ( async ( file ) => {
185189 const name = file . name ;
186- const absoluteUri = path . join ( rootPath , name ) ;
190+ const absoluteUri = path . join ( uri , name ) ;
187191 if ( file . isDirectory ( ) ) {
188192 return {
189193 name : name ,
@@ -212,14 +216,12 @@ async function handleListPathContent(uri: string, options: any) {
212216
213217async function handleCreateProject ( uri : string ) {
214218 // Create a folder at the validated path
215- const safe = safeWorkspaceResolve ( uri ) ;
216- await fs . promises . mkdir ( safe , { recursive : true } ) ;
219+ await fs . promises . mkdir ( uri , { recursive : true } ) ;
217220}
218221
219222async function handleDeleteProject ( uri : string ) {
220223 // Delete the folder at the validated path
221- const safe = safeWorkspaceResolve ( uri ) ;
222- await fs . promises . rm ( safe , { recursive : true , force : true } ) ;
224+ await fs . promises . rm ( uri , { recursive : true , force : true } ) ;
223225}
224226
225227async function handleUpdateProject (
@@ -229,73 +231,67 @@ async function handleUpdateProject(
229231 ctime ?: Date ;
230232 } ,
231233) {
232- const safeOld = safeWorkspaceResolve ( uri ) ;
233- const newPathCandidate = path . join ( path . dirname ( safeOld ) , updatedInfo . name ) ;
234- const safeNew = safeWorkspaceResolve ( newPathCandidate ) ;
235- await fs . promises . rename ( safeOld , safeNew ) ;
234+ // Make sure updatedInfo.name is valid name, not a path
235+ if ( path . basename ( updatedInfo . name ) !== updatedInfo . name ) {
236+ throw new Error ( "Invalid project name" ) ;
237+ }
238+
239+ // Rename the project folder
240+ const newUri = path . join ( path . dirname ( uri ) , updatedInfo . name ) ;
241+ await fs . promises . rename ( uri , newUri ) ;
236242}
237243
238244async function handleCreateFolder ( uri : string ) {
239245 // Create a folder at the validated path
240- const safe = safeWorkspaceResolve ( uri ) ;
241- await fs . promises . mkdir ( safe , { recursive : true } ) ;
246+ await fs . promises . mkdir ( uri , { recursive : true } ) ;
242247}
243248
244249async function handleCreateFile ( uri : string ) {
245250 // Create a file at the validated path
246- const safe = safeWorkspaceResolve ( uri ) ;
247251 // ensure parent exists
248- await fs . promises . mkdir ( path . dirname ( safe ) , { recursive : true } ) ;
249- await fs . promises . writeFile ( safe , "" ) ;
252+ await fs . promises . mkdir ( path . dirname ( uri ) , { recursive : true } ) ;
253+ await fs . promises . writeFile ( uri , "" ) ;
250254}
251255
252256async function handleRename ( oldUri : string , newUri : string ) {
253- const safeOld = safeWorkspaceResolve ( oldUri ) ;
254- const safeNew = safeWorkspaceResolve ( newUri ) ;
255- await fs . promises . rename ( safeOld , safeNew ) ;
257+ await fs . promises . rename ( oldUri , newUri ) ;
256258}
257259
258260async function handleDelete ( uri : string ) {
259- const safe = safeWorkspaceResolve ( uri ) ;
260- await fs . promises . rm ( safe , {
261+ await fs . promises . rm ( uri , {
261262 recursive : true ,
262263 force : true ,
263264 } ) ;
264265}
265266
266267async function handleHasPath ( uri : string ) {
267268 try {
268- const safe = safeWorkspaceResolve ( uri ) ;
269- return fs . existsSync ( safe ) ;
269+ return fs . existsSync ( uri ) ;
270270 } catch ( err ) {
271271 return false ;
272272 }
273273}
274274
275275async function handleReadFile ( uri : string ) {
276276 // Read the file at validated path
277- const safe = safeWorkspaceResolve ( uri ) ;
278- const data = await fs . promises . readFile ( safe , "utf-8" ) ;
277+ const data = await fs . promises . readFile ( uri , "utf-8" ) ;
279278 return data ;
280279}
281280
282281async function handleWriteFile ( data : any , uri : string ) {
283282 // Write the data at validated path
284- const safePath = safeWorkspaceResolve ( uri ) ;
285283 // create parent directory if it doesn't exist
286- const dir = path . dirname ( safePath ) ;
284+ const dir = path . dirname ( uri ) ;
287285 if ( ! fs . existsSync ( dir ) ) {
288286 fs . mkdirSync ( dir , { recursive : true } ) ;
289287 }
290288
291- fs . writeFileSync ( safePath , data ) ;
289+ fs . writeFileSync ( uri , data ) ;
292290}
293291
294292async function handleCopyFiles ( from : string , to : string ) {
295293 // Copy the files from the validated from path to the validated to path
296- const safeFrom = safeWorkspaceResolve ( from ) ;
297- const safeTo = safeWorkspaceResolve ( to ) ;
298- await fs . promises . cp ( safeFrom , safeTo , { recursive : true } ) ;
294+ await fs . promises . cp ( from , to , { recursive : true } ) ;
299295}
300296
301297async function handleLoadSettings ( ) {
0 commit comments