mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:30:42 +00:00
fix(agents): preserve sandbox write file modes
This commit is contained in:
@@ -24,6 +24,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Agents/sandbox: preserve existing workspace file modes when sandbox edits atomically replace files, so 0644 files do not collapse to 0600 after Write/Edit/apply_patch. Fixes #44077. Thanks @patosullivan.
|
||||
- Agents/models: keep legacy CLI runtime model refs such as `claude-cli/*` in the configured allowlist after canonical runtime migration, so cron `payload.model` overrides keep working. Fixes #75753. Thanks @RyanSandoval.
|
||||
- Gateway/watch: keep colored subsystem log prefixes in the managed tmux pane even when the parent shell exports `NO_COLOR`, while preserving explicit `FORCE_COLOR=0` opt-out. Thanks @vincentkoc.
|
||||
- Plugin SDK: re-export `isPrivateIpAddress` from `plugin-sdk/ssrf-runtime`, restoring source-checkout builds for SearXNG and Firecrawl private-network guards. Thanks @vincentkoc.
|
||||
|
||||
@@ -80,6 +80,46 @@ describe("sandbox pinned mutation helper", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it.runIf(process.platform !== "win32")(
|
||||
"preserves existing target file mode while writing",
|
||||
async () => {
|
||||
await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => {
|
||||
const workspace = path.join(root, "workspace");
|
||||
const filePath = path.join(workspace, "note.txt");
|
||||
await fs.mkdir(workspace, { recursive: true });
|
||||
await fs.writeFile(filePath, "before", "utf8");
|
||||
await fs.chmod(filePath, 0o644);
|
||||
|
||||
const result = runMutation(["write", workspace, "", "note.txt", "0"], "after");
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
await expect(fs.readFile(filePath, "utf8")).resolves.toBe("after");
|
||||
const fileStat = await fs.stat(filePath);
|
||||
expect(fileStat.mode & 0o777).toBe(0o644);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it.runIf(process.platform !== "win32")(
|
||||
"keeps restrictive existing target file mode while writing",
|
||||
async () => {
|
||||
await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => {
|
||||
const workspace = path.join(root, "workspace");
|
||||
const filePath = path.join(workspace, "secret.txt");
|
||||
await fs.mkdir(workspace, { recursive: true });
|
||||
await fs.writeFile(filePath, "before", "utf8");
|
||||
await fs.chmod(filePath, 0o600);
|
||||
|
||||
const result = runMutation(["write", workspace, "", "secret.txt", "0"], "after");
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
await expect(fs.readFile(filePath, "utf8")).resolves.toBe("after");
|
||||
const fileStat = await fs.stat(filePath);
|
||||
expect(fileStat.mode & 0o777).toBe(0o600);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it.runIf(process.platform !== "win32")(
|
||||
"reads through a pinned directory fd and rejects hardlinked files",
|
||||
async () => {
|
||||
|
||||
@@ -89,7 +89,17 @@ export const SANDBOX_PINNED_MUTATION_PYTHON = [
|
||||
" continue",
|
||||
" raise RuntimeError('failed to allocate sandbox temp directory')",
|
||||
"",
|
||||
"def existing_regular_file_mode(parent_fd, basename):",
|
||||
" try:",
|
||||
" target_stat = os.lstat(basename, dir_fd=parent_fd)",
|
||||
" except FileNotFoundError:",
|
||||
" return None",
|
||||
" if stat.S_ISREG(target_stat.st_mode):",
|
||||
" return stat.S_IMODE(target_stat.st_mode)",
|
||||
" return None",
|
||||
"",
|
||||
"def write_atomic(parent_fd, basename, stdin_buffer):",
|
||||
" target_mode = existing_regular_file_mode(parent_fd, basename)",
|
||||
" temp_fd = None",
|
||||
" temp_name = None",
|
||||
" try:",
|
||||
@@ -99,6 +109,11 @@ export const SANDBOX_PINNED_MUTATION_PYTHON = [
|
||||
" if not chunk:",
|
||||
" break",
|
||||
" os.write(temp_fd, chunk)",
|
||||
" if target_mode is not None:",
|
||||
" try:",
|
||||
" os.fchmod(temp_fd, target_mode)",
|
||||
" except AttributeError:",
|
||||
" pass",
|
||||
" os.fsync(temp_fd)",
|
||||
" os.close(temp_fd)",
|
||||
" temp_fd = None",
|
||||
|
||||
Reference in New Issue
Block a user