Skip to content

Commit ef086f5

Browse files
authored
Merge pull request #93 from ClayPulse/hotfix
Hotfix capacitor versions and workspace server uncontrolled data used in path expression
2 parents 333b46d + 43e8885 commit ef086f5

File tree

7 files changed

+99
-66
lines changed

7 files changed

+99
-66
lines changed

.github/workflows/build-mobile.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: Build Mobile
22

33
env:
4-
ANDROID_SDK_VERSION: "35.0.0"
4+
ANDROID_SDK_VERSION: "36.1.0"
55

66
on:
77
pull_request:

.github/workflows/release-mobile.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: Build Capacitor Release
22

33
env:
4-
ANDROID_SDK_VERSION: "35.0.0"
4+
ANDROID_SDK_VERSION: "36.1.0"
55

66
on:
77
push:

mobile/package.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@
99
},
1010
"dependencies": {
1111
"@capacitor-community/safe-area": "^7.0.0-alpha.1",
12-
"@capacitor/android": "^7.2.0",
12+
"@capacitor/android": "^7.4.4",
1313
"@capacitor/app": "^7.1.0",
1414
"@capacitor/browser": "^7.0.2",
15-
"@capacitor/cli": "^7.2.0",
16-
"@capacitor/core": "^7.2.0",
17-
"@capacitor/filesystem": "^7.0.1",
18-
"@capacitor/keyboard": "^7.0.1",
15+
"@capacitor/cli": "^7.4.4",
16+
"@capacitor/core": "^7.4.4",
17+
"@capacitor/filesystem": "^7.1.4",
18+
"@capacitor/keyboard": "^7.0.3",
1919
"@capacitor/preferences": "^7.0.2",
20-
"@capacitor/screen-orientation": "^7.0.1",
21-
"@capacitor/status-bar": "^7.0.1",
22-
"@capawesome/capacitor-file-picker": "^7.0.1"
20+
"@capacitor/screen-orientation": "^7.0.2",
21+
"@capacitor/status-bar": "^7.0.3",
22+
"@capawesome/capacitor-file-picker": "^7.2.0"
2323
}
2424
}

package-lock.json

Lines changed: 21 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 56 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,30 @@ import ignore from "ignore";
33
import path from "path";
44

55
// Define a safe root directory for projects. Can be overridden by env or configured as needed.
6+
// All incoming URIs will be resolved and validated to ensure they don't escape this root.
7+
const SAFE_ROOT = path.resolve(
8+
process.env.PLATFORM_API_ROOT ?? "/pulse-editor",
9+
);
610

7-
const settingsPath = path.join("/pulse-editor", "settings.json");
11+
const settingsPath = path.join(SAFE_ROOT, "settings.json");
12+
13+
function safeResolve(uri: string): string {
14+
if (!uri || typeof uri !== "string") {
15+
throw new Error("Invalid path");
16+
}
17+
18+
// Canonicalize the SAFE_ROOT once for this function
19+
const rootPath = path.resolve(SAFE_ROOT);
20+
// Combine and normalize the user input relative to the safe root
21+
const candidate = path.resolve(SAFE_ROOT, uri);
22+
23+
// Check that candidate is strictly under rootPath (or equal to rootPath)
24+
if (candidate === rootPath || candidate.startsWith(rootPath + path.sep)) {
25+
return candidate;
26+
}
27+
28+
throw new Error("Can only access paths within the project home directory.");
29+
}
830

931
export async function handlePlatformAPIRequest(
1032
data: {
@@ -110,10 +132,9 @@ export async function handlePlatformAPIRequest(
110132
}
111133
}
112134

113-
114135
// List all folders in a path
115136
async function handleListProjects(uri: string) {
116-
const rootPath = uri;
137+
const rootPath = safeResolve(uri);
117138
const files = await fs.promises.readdir(rootPath, { withFileTypes: true });
118139
const folders = files
119140
.filter((file) => file.isDirectory())
@@ -131,7 +152,7 @@ async function listPathContent(
131152
options: any,
132153
baseUri: string | undefined = undefined,
133154
) {
134-
const rootPath = uri;
155+
const rootPath = safeResolve(uri);
135156
const files = await fs.promises.readdir(rootPath, { withFileTypes: true });
136157

137158
const promise: Promise<any>[] = files
@@ -188,12 +209,14 @@ async function handleListPathContent(uri: string, options: any) {
188209

189210
async function handleCreateProject(uri: string) {
190211
// Create a folder at the validated path
191-
await fs.promises.mkdir(uri);
212+
const safe = safeResolve(uri);
213+
await fs.promises.mkdir(safe, { recursive: true });
192214
}
193215

194216
async function handleDeleteProject(uri: string) {
195217
// Delete the folder at the validated path
196-
await fs.promises.rm(uri, { recursive: true, force: true });
218+
const safe = safeResolve(uri);
219+
await fs.promises.rm(safe, { recursive: true, force: true });
197220
}
198221

199222
async function handleUpdateProject(
@@ -203,51 +226,59 @@ async function handleUpdateProject(
203226
ctime?: Date;
204227
},
205228
) {
206-
const newUri = path.join(path.dirname(uri), updatedInfo.name);
207-
await fs.promises.rename(uri, newUri);
229+
const safeOld = safeResolve(uri);
230+
const newPathCandidate = path.join(path.dirname(safeOld), updatedInfo.name);
231+
const safeNew = safeResolve(newPathCandidate);
232+
await fs.promises.rename(safeOld, safeNew);
208233
}
209234

210235
async function handleCreateFolder(uri: string) {
211236
// Create a folder at the validated path
212-
await fs.promises.mkdir(uri);
237+
const safe = safeResolve(uri);
238+
await fs.promises.mkdir(safe, { recursive: true });
213239
}
214240

215241
async function handleCreateFile(uri: string) {
216242
// Create a file at the validated path
217-
await fs.promises.writeFile(uri, "");
243+
const safe = safeResolve(uri);
244+
// ensure parent exists
245+
await fs.promises.mkdir(path.dirname(safe), { recursive: true });
246+
await fs.promises.writeFile(safe, "");
218247
}
219248

220249
async function handleRename(oldUri: string, newUri: string) {
221-
await fs.promises.rename(
222-
oldUri,
223-
newUri,
224-
);
250+
const safeOld = safeResolve(oldUri);
251+
const safeNew = safeResolve(newUri);
252+
await fs.promises.rename(safeOld, safeNew);
225253
}
226254

227255
async function handleDelete(uri: string) {
228-
await fs.promises.rm(uri, {
256+
const safe = safeResolve(uri);
257+
await fs.promises.rm(safe, {
229258
recursive: true,
230259
force: true,
231260
});
232261
}
233262

234263
async function handleHasPath(uri: string) {
235-
return fs.existsSync(uri);
264+
try {
265+
const safe = safeResolve(uri);
266+
return fs.existsSync(safe);
267+
} catch (err) {
268+
return false;
269+
}
236270
}
237271

238272
async function handleReadFile(uri: string) {
239273
// Read the file at validated path
240-
const data = await fs.promises.readFile(
241-
uri,
242-
"utf-8",
243-
);
244-
274+
const safe = safeResolve(uri);
275+
const data = await fs.promises.readFile(safe, "utf-8");
245276
return data;
246277
}
247278

248279
async function handleWriteFile(data: any, uri: string) {
249280
// Write the data at validated path
250-
const safePath = uri;
281+
const safePath = safeResolve(uri);
251282
// create parent directory if it doesn't exist
252283
const dir = path.dirname(safePath);
253284
if (!fs.existsSync(dir)) {
@@ -259,13 +290,9 @@ async function handleWriteFile(data: any, uri: string) {
259290

260291
async function handleCopyFiles(from: string, to: string) {
261292
// Copy the files from the validated from path to the validated to path
262-
await fs.promises.cp(
263-
from,
264-
to,
265-
{
266-
recursive: true,
267-
},
268-
);
293+
const safeFrom = safeResolve(from);
294+
const safeTo = safeResolve(to);
295+
await fs.promises.cp(safeFrom, safeTo, { recursive: true });
269296
}
270297

271298
async function handleLoadSettings() {

web/components/modals/login-modal.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,13 @@ export default function LoginModal({ signIn }: { signIn: () => void }) {
2828
return (
2929
<ModalWrapper
3030
isOpen={isModelOpen}
31-
setIsOpen={setIsModelOpen}
31+
setIsOpen={(open: boolean) => {
32+
editorContext?.setEditorStates((prev) => ({
33+
...prev,
34+
isSigningIn: false,
35+
}));
36+
setIsModelOpen(open);
37+
}}
3238
title={"Access Pulse Editor Workspace"}
3339
placement={"center"}
3440
>

web/package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@
1212
},
1313
"dependencies": {
1414
"@capacitor-community/safe-area": "^7.0.0-alpha.1",
15-
"@capacitor/android": "^7.4.3",
15+
"@capacitor/android": "^7.4.4",
1616
"@capacitor/app": "^7.1.0",
1717
"@capacitor/browser": "^7.0.2",
18-
"@capacitor/cli": "^7.4.3",
19-
"@capacitor/core": "^7.4.3",
20-
"@capacitor/filesystem": "7.1.4",
18+
"@capacitor/cli": "^7.4.4",
19+
"@capacitor/core": "^7.4.4",
20+
"@capacitor/filesystem": "^7.1.4",
2121
"@capacitor/keyboard": "^7.0.3",
2222
"@capacitor/preferences": "^7.0.2",
2323
"@capacitor/screen-orientation": "^7.0.2",
@@ -73,4 +73,4 @@
7373
"typescript": "^5",
7474
"workbox-webpack-plugin": "^7.3.0"
7575
}
76-
}
76+
}

0 commit comments

Comments
 (0)