Skip to content

Commit 2ad0b41

Browse files
authored
Merge pull request #99 from ClayPulse/hotfix
Fix bugs
2 parents 3c9741e + ce99341 commit 2ad0b41

File tree

1 file changed

+58
-62
lines changed
  • remote-workspace/src/servers/api-server/platform-api

1 file changed

+58
-62
lines changed

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

Lines changed: 58 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,23 @@ 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");
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

3434
export 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
139145
async 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

213217
async 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

219222
async 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

225227
async 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

238244
async 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

244249
async 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

252256
async 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

258260
async 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

266267
async 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

275275
async 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

282281
async 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

294292
async 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

301297
async function handleLoadSettings() {

0 commit comments

Comments
 (0)