mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-15 14:50:48 +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
@@ -28,6 +28,7 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { resolveStateDir } from "../../../config/paths.js";
|
||||
import { formatErrorMessage } from "../../../infra/errors.js";
|
||||
import { appendRegularFile } from "../../../infra/fs-safe.js";
|
||||
import { createSubsystemLogger } from "../../../logging/subsystem.js";
|
||||
import type { HookHandler } from "../../hooks.js";
|
||||
|
||||
@@ -59,7 +60,11 @@ const logCommand: HookHandler = async (event) => {
|
||||
source: event.context.commandSource ?? "unknown",
|
||||
}) + "\n";
|
||||
|
||||
await fs.appendFile(logFile, logLine, "utf-8");
|
||||
await appendRegularFile({
|
||||
filePath: logFile,
|
||||
content: logLine,
|
||||
rejectSymlinkParents: true,
|
||||
});
|
||||
} catch (err) {
|
||||
const message = formatErrorMessage(err);
|
||||
log.error(`Failed to log command: ${message}`);
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
} from "../../../agents/agent-scope.js";
|
||||
import { resolveStateDir } from "../../../config/paths.js";
|
||||
import type { OpenClawConfig } from "../../../config/types.openclaw.js";
|
||||
import { writeFileWithinRoot } from "../../../infra/fs-safe.js";
|
||||
import { root } from "../../../infra/fs-safe.js";
|
||||
import { createSubsystemLogger } from "../../../logging/subsystem.js";
|
||||
import {
|
||||
parseAgentSessionKey,
|
||||
@@ -277,12 +277,8 @@ async function saveSessionMemoryNow(event: Parameters<HookHandler>[0]): Promise<
|
||||
const entry = entryParts.join("\n");
|
||||
|
||||
// Write under memory root with alias-safe file validation.
|
||||
await writeFileWithinRoot({
|
||||
rootDir: memoryDir,
|
||||
relativePath: filename,
|
||||
data: entry,
|
||||
encoding: "utf-8",
|
||||
});
|
||||
const memoryRoot = await root(memoryDir);
|
||||
await memoryRoot.write(filename, entry, { encoding: "utf-8" });
|
||||
log.debug("Memory file written successfully");
|
||||
|
||||
// Log completion (but don't send user-visible confirmation - it's internal housekeeping)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { fileExists, readJsonFile, resolveArchiveKind } from "../infra/archive.js";
|
||||
import { resolveArchiveKind } from "../infra/archive.js";
|
||||
import { pathExists } from "../infra/fs-safe.js";
|
||||
import { resolveExistingInstallPath, withExtractedArchiveRoot } from "../infra/install-flow.js";
|
||||
import { installFromValidatedNpmSpecArchive } from "../infra/install-from-npm-spec.js";
|
||||
import {
|
||||
@@ -18,19 +19,20 @@ import {
|
||||
ensureInstallTargetAvailable,
|
||||
resolveCanonicalInstallTarget,
|
||||
} from "../infra/install-target.js";
|
||||
import { readJson } from "../infra/json-files.js";
|
||||
import { isPathInside, isPathInsideWithRealpath } from "../security/scan-paths.js";
|
||||
|
||||
export type { NpmIntegrityDrift, NpmSpecResolution };
|
||||
|
||||
export {
|
||||
ensureInstallTargetAvailable,
|
||||
fileExists,
|
||||
pathExists as fileExists,
|
||||
installFromValidatedNpmSpecArchive,
|
||||
installPackageDir,
|
||||
installPackageDirWithManifestDeps,
|
||||
isPathInside,
|
||||
isPathInsideWithRealpath,
|
||||
readJsonFile,
|
||||
readJson as readJsonFile,
|
||||
resolveArchiveKind,
|
||||
resolveArchiveSourcePath,
|
||||
resolveCanonicalInstallTarget,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { openBoundaryFile } from "../infra/boundary-file-read.js";
|
||||
import { openRootFile } from "../infra/boundary-file-read.js";
|
||||
import { formatErrorMessage } from "../infra/errors.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import { resolveGlobalSingleton } from "../shared/global-singleton.js";
|
||||
@@ -119,7 +119,7 @@ export async function loadInternalHooks(
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const opened = await openBoundaryFile({
|
||||
const opened = await openRootFile({
|
||||
absolutePath: entry.hook.handlerPath,
|
||||
rootPath: hookBaseDir,
|
||||
boundaryLabel: "hook directory",
|
||||
@@ -215,7 +215,7 @@ export async function loadInternalHooks(
|
||||
log.error(`Handler module path must stay within workspaceDir: ${safeLogValue(rawModule)}`);
|
||||
continue;
|
||||
}
|
||||
const opened = await openBoundaryFile({
|
||||
const opened = await openRootFile({
|
||||
absolutePath: modulePathSafe,
|
||||
rootPath: baseDirReal,
|
||||
boundaryLabel: "workspace directory",
|
||||
|
||||
@@ -2,7 +2,7 @@ import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { MANIFEST_KEY } from "../compat/legacy-names.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
|
||||
import { openRootFileSync } from "../infra/boundary-file-read.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import { isPathInsideWithRealpath } from "../security/scan-paths.js";
|
||||
import { CONFIG_DIR, resolveUserPath } from "../utils.js";
|
||||
@@ -28,7 +28,7 @@ type LoadedHook = {
|
||||
|
||||
function readHookPackageManifest(dir: string): HookPackageManifest | null {
|
||||
const manifestPath = path.join(dir, "package.json");
|
||||
const raw = readBoundaryFileUtf8({
|
||||
const raw = readRootFileUtf8({
|
||||
absolutePath: manifestPath,
|
||||
rootPath: dir,
|
||||
boundaryLabel: "hook package directory",
|
||||
@@ -71,7 +71,7 @@ function loadHookFromDir(params: {
|
||||
nameHint?: string;
|
||||
}): LoadedHook | null {
|
||||
const hookMdPath = path.join(params.hookDir, "HOOK.md");
|
||||
const content = readBoundaryFileUtf8({
|
||||
const content = readRootFileUtf8({
|
||||
absolutePath: hookMdPath,
|
||||
rootPath: params.hookDir,
|
||||
boundaryLabel: "hook directory",
|
||||
@@ -89,7 +89,7 @@ function loadHookFromDir(params: {
|
||||
let handlerPath: string | undefined;
|
||||
for (const candidate of handlerCandidates) {
|
||||
const candidatePath = path.join(params.hookDir, candidate);
|
||||
const safeCandidatePath = resolveBoundaryFilePath({
|
||||
const safeCandidatePath = resolveRootFilePath({
|
||||
absolutePath: candidatePath,
|
||||
rootPath: params.hookDir,
|
||||
boundaryLabel: "hook directory",
|
||||
@@ -293,12 +293,12 @@ export function loadWorkspaceHookEntries(
|
||||
});
|
||||
}
|
||||
|
||||
function readBoundaryFileUtf8(params: {
|
||||
function readRootFileUtf8(params: {
|
||||
absolutePath: string;
|
||||
rootPath: string;
|
||||
boundaryLabel: string;
|
||||
}): string | null {
|
||||
return withOpenedBoundaryFileSync(params, (opened) => {
|
||||
return withOpenedRootFileSync(params, (opened) => {
|
||||
try {
|
||||
return fs.readFileSync(opened.fd, "utf-8");
|
||||
} catch {
|
||||
@@ -307,7 +307,7 @@ function readBoundaryFileUtf8(params: {
|
||||
});
|
||||
}
|
||||
|
||||
function withOpenedBoundaryFileSync<T>(
|
||||
function withOpenedRootFileSync<T>(
|
||||
params: {
|
||||
absolutePath: string;
|
||||
rootPath: string;
|
||||
@@ -315,7 +315,7 @@ function withOpenedBoundaryFileSync<T>(
|
||||
},
|
||||
read: (opened: { fd: number; path: string }) => T,
|
||||
): T | null {
|
||||
const opened = openBoundaryFileSync({
|
||||
const opened = openRootFileSync({
|
||||
absolutePath: params.absolutePath,
|
||||
rootPath: params.rootPath,
|
||||
boundaryLabel: params.boundaryLabel,
|
||||
@@ -330,10 +330,10 @@ function withOpenedBoundaryFileSync<T>(
|
||||
}
|
||||
}
|
||||
|
||||
function resolveBoundaryFilePath(params: {
|
||||
function resolveRootFilePath(params: {
|
||||
absolutePath: string;
|
||||
rootPath: string;
|
||||
boundaryLabel: string;
|
||||
}): string | null {
|
||||
return withOpenedBoundaryFileSync(params, (opened) => opened.path);
|
||||
return withOpenedRootFileSync(params, (opened) => opened.path);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user