mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 04:50:43 +00:00
[codex] Extract filesystem safety primitives (#77918)
* refactor: extract filesystem safety primitives * refactor: use fs-safe for file access helpers * refactor: reuse fs-safe for media reads * refactor: use fs-safe for image reads * refactor: reuse fs-safe in qqbot media opener * refactor: reuse fs-safe for local media checks * refactor: consume cleaner fs-safe api * refactor: align fs-safe json option names * fix: preserve fs-safe migration contracts * refactor: use fs-safe primitive subpaths * refactor: use grouped fs-safe subpaths * refactor: align fs-safe api usage * refactor: adapt private state store api * chore: refresh proof gate * refactor: follow fs-safe json api split * refactor: follow reduced fs-safe surface * build: default fs-safe python helper off * fix: preserve fs-safe plugin sdk aliases * refactor: consolidate fs-safe usage * refactor: unify fs-safe store usage * refactor: trim fs-safe temp workspace usage * refactor: hide low-level fs-safe primitives * build: use published fs-safe package * fix: preserve outbound recovery durability after rebase * chore: refresh pr checks
This commit is contained in:
committed by
GitHub
parent
61481eb34f
commit
538605ff44
@@ -10,6 +10,8 @@ import type { SourceReplyDeliveryMode } from "../../auto-reply/get-reply-options
|
||||
import type { ThinkLevel } from "../../auto-reply/thinking.js";
|
||||
import type { CliBackendConfig } from "../../config/types.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { privateFileStore } from "../../infra/private-file-store.js";
|
||||
import { tempWorkspace } from "../../infra/private-temp-workspace.js";
|
||||
import { resolvePreferredOpenClawTmpDir } from "../../infra/tmp-openclaw-dir.js";
|
||||
import { MAX_IMAGE_BYTES } from "../../media/constants.js";
|
||||
import { extensionForMime } from "../../media/mime.js";
|
||||
@@ -283,14 +285,14 @@ export async function writeCliImages(params: {
|
||||
workspaceDir: params.workspaceDir,
|
||||
});
|
||||
await fs.mkdir(imageRoot, { recursive: true, mode: 0o700 });
|
||||
const store = privateFileStore(imageRoot);
|
||||
const paths: string[] = [];
|
||||
for (let i = 0; i < params.images.length; i += 1) {
|
||||
const image = params.images[i];
|
||||
const fileName = path.basename(resolveCliImagePath(image));
|
||||
const filePath = path.join(imageRoot, fileName);
|
||||
const buffer = Buffer.from(image.data, "base64");
|
||||
await fs.writeFile(filePath, buffer, { mode: 0o600 });
|
||||
paths.push(filePath);
|
||||
await store.writeText(fileName, buffer);
|
||||
paths.push(store.path(fileName));
|
||||
}
|
||||
// Keep content-addressed image paths stable across Claude CLI runs so prompt
|
||||
// text and argv don't churn on every turn with fresh temp-dir suffixes.
|
||||
@@ -308,19 +310,17 @@ export async function writeCliSystemPromptFile(params: {
|
||||
) {
|
||||
return { cleanup: async () => {} };
|
||||
}
|
||||
const tempDir = await fs.mkdtemp(
|
||||
path.join(resolvePreferredOpenClawTmpDir(), "openclaw-cli-system-prompt-"),
|
||||
);
|
||||
const filePath = path.join(tempDir, "system-prompt.md");
|
||||
await fs.writeFile(filePath, stripSystemPromptCacheBoundary(params.systemPrompt), {
|
||||
encoding: "utf-8",
|
||||
mode: 0o600,
|
||||
const workspace = await tempWorkspace({
|
||||
rootDir: resolvePreferredOpenClawTmpDir(),
|
||||
prefix: "openclaw-cli-system-prompt-",
|
||||
});
|
||||
const filePath = await workspace.write(
|
||||
"system-prompt.md",
|
||||
stripSystemPromptCacheBoundary(params.systemPrompt),
|
||||
);
|
||||
return {
|
||||
filePath,
|
||||
cleanup: async () => {
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
},
|
||||
cleanup: async () => await workspace.cleanup(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
resolveSessionFilePathOptions,
|
||||
} from "../../config/sessions/paths.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { isPathInside } from "../../infra/path-guards.js";
|
||||
import { resolveSessionAgentIds } from "../agent-scope.js";
|
||||
import {
|
||||
limitAgentHookHistoryMessages,
|
||||
@@ -108,11 +109,6 @@ async function safeRealpath(filePath: string): Promise<string | undefined> {
|
||||
}
|
||||
}
|
||||
|
||||
function isPathWithinBase(basePath: string, targetPath: string): boolean {
|
||||
const relative = path.relative(basePath, targetPath);
|
||||
return Boolean(relative) && !relative.startsWith("..") && !path.isAbsolute(relative);
|
||||
}
|
||||
|
||||
function resolveSafeCliSessionFile(params: {
|
||||
sessionId: string;
|
||||
sessionFile: string;
|
||||
@@ -155,7 +151,11 @@ async function loadCliSessionEntries(params: {
|
||||
}
|
||||
const realSessionsDir = (await safeRealpath(sessionsDir)) ?? path.resolve(sessionsDir);
|
||||
const realSessionFile = await safeRealpath(sessionFile);
|
||||
if (!realSessionFile || !isPathWithinBase(realSessionsDir, realSessionFile)) {
|
||||
if (
|
||||
!realSessionFile ||
|
||||
realSessionFile === realSessionsDir ||
|
||||
!isPathInside(realSessionsDir, realSessionFile)
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
const stat = await fsp.stat(realSessionFile);
|
||||
|
||||
Reference in New Issue
Block a user