mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 18:24:57 +00:00
fix(tools): land #31015 from @haosenwang1018
Co-authored-by: haosenwang1018 <1293965075@qq.com>
This commit is contained in:
@@ -20,6 +20,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
|
- Tools/Edit workspace boundary errors: preserve the real `Path escapes workspace root` failure path instead of surfacing a misleading access/file-not-found error when editing outside workspace roots. Landed from contributor PR #31015 by @haosenwang1018. Thanks @haosenwang1018.
|
||||||
- Sandbox/mkdirp boundary checks: allow directory-safe boundary validation for existing in-boundary subdirectories, preventing false `cannot create directories` failures in sandbox write mode. (#30610) Thanks @glitch418x.
|
- Sandbox/mkdirp boundary checks: allow directory-safe boundary validation for existing in-boundary subdirectories, preventing false `cannot create directories` failures in sandbox write mode. (#30610) Thanks @glitch418x.
|
||||||
- Android/Voice screen TTS: stream assistant speech via ElevenLabs WebSocket in Talk Mode, stop cleanly on speaker mute/barge-in, and ignore stale out-of-order stream events. (#29521) Thanks @gregmousseau.
|
- Android/Voice screen TTS: stream assistant speech via ElevenLabs WebSocket in Talk Mode, stop cleanly on speaker mute/barge-in, and ignore stale out-of-order stream events. (#29521) Thanks @gregmousseau.
|
||||||
- Web UI/Cron: include configured agent model defaults/fallbacks in cron model suggestions so scheduled-job model autocomplete reflects configured models. (#29709) Thanks @Sid-Qin.
|
- Web UI/Cron: include configured agent model defaults/fallbacks in cron model suggestions so scheduled-job model autocomplete reflects configured models. (#29709) Thanks @Sid-Qin.
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ describe("createHostWorkspaceEditTool host access mapping", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it.runIf(process.platform !== "win32")(
|
it.runIf(process.platform !== "win32")(
|
||||||
"maps outside-workspace safe-open failures to EACCES",
|
"silently passes access for outside-workspace paths so readFile reports the real error",
|
||||||
async () => {
|
async () => {
|
||||||
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-edit-access-test-"));
|
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-edit-access-test-"));
|
||||||
const workspaceDir = path.join(tmpDir, "workspace");
|
const workspaceDir = path.join(tmpDir, "workspace");
|
||||||
@@ -58,9 +58,13 @@ describe("createHostWorkspaceEditTool host access mapping", () => {
|
|||||||
createHostWorkspaceEditTool(workspaceDir, { workspaceOnly: true });
|
createHostWorkspaceEditTool(workspaceDir, { workspaceOnly: true });
|
||||||
expect(mocks.operations).toBeDefined();
|
expect(mocks.operations).toBeDefined();
|
||||||
|
|
||||||
|
// access must NOT throw for outside-workspace paths; the upstream
|
||||||
|
// library replaces any access error with a misleading "File not found".
|
||||||
|
// By resolving silently the subsequent readFile call surfaces the real
|
||||||
|
// "Path escapes workspace root" / "outside-workspace" error instead.
|
||||||
await expect(
|
await expect(
|
||||||
mocks.operations!.access(path.join(workspaceDir, "escape", "secret.txt")),
|
mocks.operations!.access(path.join(workspaceDir, "escape", "secret.txt")),
|
||||||
).rejects.toMatchObject({ code: "EACCES" });
|
).resolves.toBeUndefined();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -843,7 +843,17 @@ function createHostEditOperations(root: string, options?: { workspaceOnly?: bool
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
access: async (absolutePath: string) => {
|
access: async (absolutePath: string) => {
|
||||||
const relative = toRelativePathInRoot(root, absolutePath);
|
let relative: string;
|
||||||
|
try {
|
||||||
|
relative = toRelativePathInRoot(root, absolutePath);
|
||||||
|
} catch {
|
||||||
|
// Path escapes workspace root. Don't throw here – the upstream
|
||||||
|
// library replaces any `access` error with a misleading "File not
|
||||||
|
// found" message. By returning silently the subsequent `readFile`
|
||||||
|
// call will throw the same "Path escapes workspace root" error
|
||||||
|
// through a code-path that propagates the original message.
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const opened = await openFileWithinRoot({
|
const opened = await openFileWithinRoot({
|
||||||
rootDir: root,
|
rootDir: root,
|
||||||
@@ -855,7 +865,9 @@ function createHostEditOperations(root: string, options?: { workspaceOnly?: bool
|
|||||||
throw createFsAccessError("ENOENT", absolutePath);
|
throw createFsAccessError("ENOENT", absolutePath);
|
||||||
}
|
}
|
||||||
if (error instanceof SafeOpenError && error.code === "outside-workspace") {
|
if (error instanceof SafeOpenError && error.code === "outside-workspace") {
|
||||||
throw createFsAccessError("EACCES", absolutePath);
|
// Don't throw here – see the comment above about the upstream
|
||||||
|
// library swallowing access errors as "File not found".
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user