mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 09:50:44 +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
@@ -9,12 +9,13 @@ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
||||
import { withFileLock } from "openclaw/plugin-sdk/file-lock";
|
||||
import {
|
||||
createSubsystemLogger,
|
||||
isPathInside,
|
||||
root,
|
||||
resolveAgentContextLimits,
|
||||
resolveMemorySearchSyncConfig,
|
||||
resolveAgentWorkspaceDir,
|
||||
resolveGlobalSingleton,
|
||||
resolveStateDir,
|
||||
writeFileWithinRoot,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/memory-core-host-engine-foundation";
|
||||
import {
|
||||
@@ -1302,7 +1303,15 @@ export class QmdMemoryManager implements MemorySearchManager {
|
||||
if (!absPath.endsWith(".md")) {
|
||||
throw new Error("path required");
|
||||
}
|
||||
const statResult = await statRegularFile(absPath);
|
||||
let statResult: Awaited<ReturnType<typeof statRegularFile>>;
|
||||
try {
|
||||
statResult = await statRegularFile(absPath);
|
||||
} catch (err) {
|
||||
if (err instanceof Error && err.message === "path must be a regular file") {
|
||||
throw new Error("path required", { cause: err });
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
if (statResult.missing) {
|
||||
return { text: "", path: relPath };
|
||||
}
|
||||
@@ -2203,6 +2212,7 @@ export class QmdMemoryManager implements MemorySearchManager {
|
||||
}
|
||||
const exportDir = this.sessionExporter.dir;
|
||||
await fs.mkdir(exportDir, { recursive: true });
|
||||
const exportRoot = await root(exportDir);
|
||||
const files = await listSessionFilesForAgent(this.agentId);
|
||||
const keep = new Set<string>();
|
||||
const tracked = new Set<string>();
|
||||
@@ -2222,10 +2232,7 @@ export class QmdMemoryManager implements MemorySearchManager {
|
||||
tracked.add(sessionFile);
|
||||
const state = this.exportedSessionState.get(sessionFile);
|
||||
if (!state || state.hash !== entry.hash || state.mtimeMs !== entry.mtimeMs) {
|
||||
await writeFileWithinRoot({
|
||||
rootDir: exportDir,
|
||||
relativePath: targetName,
|
||||
data: this.renderSessionMarkdown(entry),
|
||||
await exportRoot.write(targetName, this.renderSessionMarkdown(entry), {
|
||||
encoding: "utf-8",
|
||||
});
|
||||
}
|
||||
@@ -2236,18 +2243,18 @@ export class QmdMemoryManager implements MemorySearchManager {
|
||||
});
|
||||
keep.add(target);
|
||||
}
|
||||
const exported = await fs.readdir(exportDir).catch(() => []);
|
||||
const exported = await exportRoot.list(".").catch(() => []);
|
||||
for (const name of exported) {
|
||||
if (!name.endsWith(".md")) {
|
||||
continue;
|
||||
}
|
||||
const full = path.join(exportDir, name);
|
||||
if (!keep.has(full)) {
|
||||
await fs.rm(full, { force: true });
|
||||
await exportRoot.remove(name).catch(() => undefined);
|
||||
}
|
||||
}
|
||||
for (const [sessionFile, state] of this.exportedSessionState) {
|
||||
if (!tracked.has(sessionFile) || !state.target.startsWith(exportDir + path.sep)) {
|
||||
if (!tracked.has(sessionFile) || !isPathInside(exportDir, state.target)) {
|
||||
this.exportedSessionState.delete(sessionFile);
|
||||
}
|
||||
}
|
||||
@@ -2788,23 +2795,11 @@ export class QmdMemoryManager implements MemorySearchManager {
|
||||
}
|
||||
|
||||
private isWithinWorkspace(absPath: string): boolean {
|
||||
const normalizedWorkspace = this.workspaceDir.endsWith(path.sep)
|
||||
? this.workspaceDir
|
||||
: `${this.workspaceDir}${path.sep}`;
|
||||
if (absPath === this.workspaceDir) {
|
||||
return true;
|
||||
}
|
||||
const candidate = absPath.endsWith(path.sep) ? absPath : `${absPath}${path.sep}`;
|
||||
return candidate.startsWith(normalizedWorkspace);
|
||||
return isPathInside(this.workspaceDir, absPath);
|
||||
}
|
||||
|
||||
private isWithinRoot(root: string, candidate: string): boolean {
|
||||
const normalizedRoot = root.endsWith(path.sep) ? root : `${root}${path.sep}`;
|
||||
if (candidate === root) {
|
||||
return true;
|
||||
}
|
||||
const next = candidate.endsWith(path.sep) ? candidate : `${candidate}${path.sep}`;
|
||||
return next.startsWith(normalizedRoot);
|
||||
return isPathInside(root, candidate);
|
||||
}
|
||||
|
||||
private clampResultsByInjectedChars(results: MemorySearchResult[]): MemorySearchResult[] {
|
||||
|
||||
Reference in New Issue
Block a user