Skip to content

Commit 3b02eb4

Browse files
committed
improve safeworkspacereolve in workspace
1 parent 3e0c8b1 commit 3b02eb4

File tree

1 file changed

+52
-60
lines changed
  • remote-workspace/src/servers/api-server/platform-api

1 file changed

+52
-60
lines changed

remote-workspace/src/servers/api-server/platform-api/handler.ts

Lines changed: 52 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,19 @@ const workspaceRoot = "/workspace";
1212
const settingsPath = path.join(appRoot, "settings.json");
1313

1414
function safeWorkspaceResolve(uri: string): string {
15-
if (!uri || typeof uri !== "string") {
16-
throw new Error("Invalid path");
17-
}
15+
const absPath = path.isAbsolute(uri)
16+
? path.resolve(uri)
17+
: path.resolve(workspaceRoot, uri);
1818

19-
// uri should be an absolute path
20-
if (!path.isAbsolute(uri)) {
21-
throw new Error("Path must be absolute");
22-
}
19+
// Resolve symlinks to their actual paths
20+
const realPath = fs.existsSync(absPath) ? fs.realpathSync(absPath) : absPath;
2321

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;
22+
// Ensure it’s inside the workspace root
23+
if (!realPath.startsWith(workspaceRoot + path.sep)) {
24+
throw new Error("Cannot access path outside of workspace path.");
2925
}
3026

31-
throw new Error("Can only access paths within the project home directory.");
27+
return realPath;
3228
}
3329

3430
export async function handlePlatformAPIRequest(
@@ -50,20 +46,20 @@ export async function handlePlatformAPIRequest(
5046
throw new Error("Method not implemented.");
5147
case "list-projects": {
5248
const { uri }: { uri: string } = args;
53-
return await handleListProjects(uri);
49+
return await handleListProjects(safeWorkspaceResolve(uri));
5450
}
5551
case "list-path-content": {
5652
const { uri, options }: { uri: string; options?: any } = args;
57-
return await handleListPathContent(uri, options);
53+
return await handleListPathContent(safeWorkspaceResolve(uri), options);
5854
}
5955
case "create-project": {
6056
const { uri }: { uri: string } = args;
61-
await handleCreateProject(uri);
57+
await handleCreateProject(safeWorkspaceResolve(uri));
6258
return;
6359
}
6460
case "delete-project": {
6561
const { uri }: { uri: string } = args;
66-
await handleDeleteProject(uri);
62+
await handleDeleteProject(safeWorkspaceResolve(uri));
6763
return;
6864
}
6965
case "update-project": {
@@ -77,45 +73,51 @@ export async function handlePlatformAPIRequest(
7773
ctime?: Date;
7874
};
7975
} = args;
80-
await handleUpdateProject(uri, updatedInfo);
76+
await handleUpdateProject(safeWorkspaceResolve(uri), updatedInfo);
8177
return;
8278
}
8379
case "create-folder": {
8480
const { uri }: { uri: string } = args;
85-
await handleCreateFolder(uri);
81+
await handleCreateFolder(safeWorkspaceResolve(uri));
8682
return;
8783
}
8884
case "create-file": {
8985
const { uri }: { uri: string } = args;
90-
await handleCreateFile(uri);
86+
await handleCreateFile(safeWorkspaceResolve(uri));
9187
return;
9288
}
9389
case "rename": {
9490
const { oldUri, newUri }: { oldUri: string; newUri: string } = args;
95-
await handleRename(oldUri, newUri);
91+
await handleRename(
92+
safeWorkspaceResolve(oldUri),
93+
safeWorkspaceResolve(newUri),
94+
);
9695
return;
9796
}
9897
case "delete": {
9998
const { uri }: { uri: string } = args;
100-
await handleDelete(uri);
99+
await handleDelete(safeWorkspaceResolve(uri));
101100
return;
102101
}
103102
case "has-path": {
104103
const { uri }: { uri: string } = args;
105-
return await handleHasPath(uri);
104+
return await handleHasPath(safeWorkspaceResolve(uri));
106105
}
107106
case "read-file": {
108107
const { uri }: { uri: string } = args;
109-
return await handleReadFile(uri);
108+
return await handleReadFile(safeWorkspaceResolve(uri));
110109
}
111110
case "write-file": {
112111
const { data, uri }: { data: any; uri: string } = args;
113-
await handleWriteFile(data, uri);
112+
await handleWriteFile(data, safeWorkspaceResolve(uri));
114113
return;
115114
}
116115
case "copy-files": {
117116
const { from, to }: { from: string; to: string } = args;
118-
await handleCopyFiles(from, to);
117+
await handleCopyFiles(
118+
safeWorkspaceResolve(from),
119+
safeWorkspaceResolve(to),
120+
);
119121
return;
120122
}
121123
case "get-persistent-settings":
@@ -137,14 +139,13 @@ export async function handlePlatformAPIRequest(
137139

138140
// List all folders in a path
139141
async function handleListProjects(uri: string) {
140-
const rootPath = safeWorkspaceResolve(uri);
141-
const files = await fs.promises.readdir(rootPath, { withFileTypes: true });
142+
const files = await fs.promises.readdir(uri, { withFileTypes: true });
142143
const folders = files
143144
.filter((file) => file.isDirectory())
144145
.map((file) => file.name)
145146
.map((projectName) => ({
146147
name: projectName,
147-
ctime: fs.statSync(path.join(rootPath, projectName)).ctime,
148+
ctime: fs.statSync(path.join(uri, projectName)).ctime,
148149
}));
149150

150151
return folders;
@@ -155,8 +156,7 @@ async function listPathContent(
155156
options: any,
156157
baseUri: string | undefined = undefined,
157158
) {
158-
const rootPath = safeWorkspaceResolve(uri);
159-
const files = await fs.promises.readdir(rootPath, { withFileTypes: true });
159+
const files = await fs.promises.readdir(uri, { withFileTypes: true });
160160

161161
const promise: Promise<any>[] = files
162162
// Filter by file type
@@ -183,7 +183,7 @@ async function listPathContent(
183183
})
184184
.map(async (file) => {
185185
const name = file.name;
186-
const absoluteUri = path.join(rootPath, name);
186+
const absoluteUri = path.join(uri, name);
187187
if (file.isDirectory()) {
188188
return {
189189
name: name,
@@ -212,14 +212,12 @@ async function handleListPathContent(uri: string, options: any) {
212212

213213
async function handleCreateProject(uri: string) {
214214
// Create a folder at the validated path
215-
const safe = safeWorkspaceResolve(uri);
216-
await fs.promises.mkdir(safe, { recursive: true });
215+
await fs.promises.mkdir(uri, { recursive: true });
217216
}
218217

219218
async function handleDeleteProject(uri: string) {
220219
// Delete the folder at the validated path
221-
const safe = safeWorkspaceResolve(uri);
222-
await fs.promises.rm(safe, { recursive: true, force: true });
220+
await fs.promises.rm(uri, { recursive: true, force: true });
223221
}
224222

225223
async function handleUpdateProject(
@@ -229,73 +227,67 @@ async function handleUpdateProject(
229227
ctime?: Date;
230228
},
231229
) {
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);
230+
// Make sure updatedInfo.name is valid name, not a path
231+
if (path.basename(updatedInfo.name) !== updatedInfo.name) {
232+
throw new Error("Invalid project name");
233+
}
234+
235+
// Rename the project folder
236+
const newUri = path.join(path.dirname(uri), updatedInfo.name);
237+
await fs.promises.rename(uri, newUri);
236238
}
237239

238240
async function handleCreateFolder(uri: string) {
239241
// Create a folder at the validated path
240-
const safe = safeWorkspaceResolve(uri);
241-
await fs.promises.mkdir(safe, { recursive: true });
242+
await fs.promises.mkdir(uri, { recursive: true });
242243
}
243244

244245
async function handleCreateFile(uri: string) {
245246
// Create a file at the validated path
246-
const safe = safeWorkspaceResolve(uri);
247247
// ensure parent exists
248-
await fs.promises.mkdir(path.dirname(safe), { recursive: true });
249-
await fs.promises.writeFile(safe, "");
248+
await fs.promises.mkdir(path.dirname(uri), { recursive: true });
249+
await fs.promises.writeFile(uri, "");
250250
}
251251

252252
async function handleRename(oldUri: string, newUri: string) {
253-
const safeOld = safeWorkspaceResolve(oldUri);
254-
const safeNew = safeWorkspaceResolve(newUri);
255-
await fs.promises.rename(safeOld, safeNew);
253+
await fs.promises.rename(oldUri, newUri);
256254
}
257255

258256
async function handleDelete(uri: string) {
259-
const safe = safeWorkspaceResolve(uri);
260-
await fs.promises.rm(safe, {
257+
await fs.promises.rm(uri, {
261258
recursive: true,
262259
force: true,
263260
});
264261
}
265262

266263
async function handleHasPath(uri: string) {
267264
try {
268-
const safe = safeWorkspaceResolve(uri);
269-
return fs.existsSync(safe);
265+
return fs.existsSync(uri);
270266
} catch (err) {
271267
return false;
272268
}
273269
}
274270

275271
async function handleReadFile(uri: string) {
276272
// Read the file at validated path
277-
const safe = safeWorkspaceResolve(uri);
278-
const data = await fs.promises.readFile(safe, "utf-8");
273+
const data = await fs.promises.readFile(uri, "utf-8");
279274
return data;
280275
}
281276

282277
async function handleWriteFile(data: any, uri: string) {
283278
// Write the data at validated path
284-
const safePath = safeWorkspaceResolve(uri);
285279
// create parent directory if it doesn't exist
286-
const dir = path.dirname(safePath);
280+
const dir = path.dirname(uri);
287281
if (!fs.existsSync(dir)) {
288282
fs.mkdirSync(dir, { recursive: true });
289283
}
290284

291-
fs.writeFileSync(safePath, data);
285+
fs.writeFileSync(uri, data);
292286
}
293287

294288
async function handleCopyFiles(from: string, to: string) {
295289
// 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 });
290+
await fs.promises.cp(from, to, { recursive: true });
299291
}
300292

301293
async function handleLoadSettings() {

0 commit comments

Comments
 (0)