mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:00:43 +00:00
Harden config backup restore permissions (#77488)
* Harden config backup restore permissions * docs(changelog): credit config restore mode hardening Adds the user-facing Unreleased Fixes entry for the suspicious-read backup restore chmod hardening shipped in this PR.
This commit is contained in:
@@ -325,6 +325,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Codex/app-server: stabilize transcript mirror dedupe across re-mirrored turns so reordered snapshots no longer drop reasoning entries or duplicate the assistant reply. Refs #77012. (#77046) Thanks @openperf.
|
||||
- Agents/auth-profiles: do not record request-shape (`format`) rejections as auth-profile health failures, so a single per-session transcript-shape error (such as a prefill-strict 400 "conversation must end with a user message") no longer triggers a profile-wide cooldown that blocks every other healthy session sharing the same auth profile. Refs #77228. (#77280) Thanks @openperf.
|
||||
- CLI/update: stop dev-channel source updates immediately when `git fetch` fails, so tag conflicts cannot keep preflight, rebase, or build steps running against stale refs while the Gateway is still on the old runtime. (#77845) Thanks @obviyus.
|
||||
- Config/recovery: chmod restored `openclaw.json` back to owner-only (`0600`) after suspicious-read backup recovery on POSIX hosts, so a previously world-readable config mode cannot persist into a freshly restored credential-bearing config. (#77488) Thanks @drobison00.
|
||||
|
||||
## 2026.5.3-1
|
||||
|
||||
|
||||
@@ -275,6 +275,22 @@ describe("config observe recovery", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("hardens async backup restores to owner-only config permissions", async () => {
|
||||
if (process.platform === "win32") {
|
||||
return;
|
||||
}
|
||||
await withSuiteHome(async (home) => {
|
||||
const { deps, configPath } = makeDeps(home);
|
||||
await seedConfigBackup(configPath, recoverableTelegramConfig);
|
||||
await writeClobberedUpdateChannel(configPath);
|
||||
await fsp.chmod(configPath, 0o644);
|
||||
|
||||
await recoverClobberedUpdateChannel({ deps, configPath });
|
||||
|
||||
expect((await fsp.stat(configPath)).mode & 0o777).toBe(0o600);
|
||||
});
|
||||
});
|
||||
|
||||
it("auto-restores after a large size drop against last-good config", async () => {
|
||||
await withSuiteHome(async (home) => {
|
||||
const { deps, configPath, auditPath } = makeDeps(home);
|
||||
@@ -456,6 +472,22 @@ describe("config observe recovery", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("hardens sync backup restores to owner-only config permissions", async () => {
|
||||
if (process.platform === "win32") {
|
||||
return;
|
||||
}
|
||||
await withSuiteHome(async (home) => {
|
||||
const { deps, configPath } = makeDeps(home);
|
||||
await seedConfigBackup(configPath, recoverableTelegramConfig);
|
||||
await writeClobberedUpdateChannel(configPath);
|
||||
await fsp.chmod(configPath, 0o644);
|
||||
|
||||
recoverClobberedUpdateChannelSync({ deps, configPath });
|
||||
|
||||
expect((await fsp.stat(configPath)).mode & 0o777).toBe(0o600);
|
||||
});
|
||||
});
|
||||
|
||||
it("logs async health-state write failures", async () => {
|
||||
await withSuiteHome(async (home) => {
|
||||
const { deps, configPath, warn } = makeDeps(home);
|
||||
|
||||
@@ -640,6 +640,7 @@ export async function maybeRecoverSuspiciousConfigRead(params: {
|
||||
let restoreError: unknown;
|
||||
try {
|
||||
await params.deps.fs.promises.copyFile(backupPath, params.configPath);
|
||||
await params.deps.fs.promises.chmod?.(params.configPath, 0o600).catch(() => {});
|
||||
restoredFromBackup = true;
|
||||
} catch (error) {
|
||||
restoreError = error;
|
||||
@@ -747,6 +748,9 @@ export function maybeRecoverSuspiciousConfigReadSync(params: {
|
||||
let restoreError: unknown;
|
||||
try {
|
||||
params.deps.fs.copyFileSync(backupPath, params.configPath);
|
||||
try {
|
||||
params.deps.fs.chmodSync?.(params.configPath, 0o600);
|
||||
} catch {}
|
||||
restoredFromBackup = true;
|
||||
} catch (error) {
|
||||
restoreError = error;
|
||||
|
||||
Reference in New Issue
Block a user