fix(trajectory): preserve safe path checks for window writes

This commit is contained in:
Ayaan Zaidi
2026-05-29 17:40:35 +05:30
parent 538b405fdd
commit 39e3daa168
2 changed files with 34 additions and 2 deletions
+24
View File
@@ -228,6 +228,30 @@ describe("trajectory runtime", () => {
expect(raw).toContain("new-recorder");
});
it.runIf(process.platform !== "win32")(
"refuses runtime capture through symlinked parent directories",
async () => {
const tmpDir = makeTempDir();
const targetDir = path.join(tmpDir, "target");
const linkDir = path.join(tmpDir, "link");
fs.mkdirSync(targetDir);
fs.symlinkSync(targetDir, linkDir);
const recorder = createTrajectoryRuntimeRecorder({
sessionId: "session-1",
sessionFile: path.join(linkDir, "session.jsonl"),
maxRuntimeFileBytes: 2_400,
});
const runtimeRecorder = expectTrajectoryRuntimeRecorder(recorder);
runtimeRecorder.recordEvent("prompt.submitted", {
prompt: "hello",
});
await runtimeRecorder.flush();
expect(fs.existsSync(path.join(targetDir, "session.trajectory.jsonl"))).toBe(false);
},
);
it("describes queued writer state for cleanup timeout logs", () => {
const recorder = createTrajectoryRuntimeRecorder({
sessionId: "session-1",
+10 -2
View File
@@ -6,7 +6,7 @@ import type {
QueuedFileWriterDiagnostics,
} from "../agents/queued-file-writer.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { writeSiblingTempFile } from "../infra/fs-safe-advanced.js";
import { assertNoSymlinkParents, writeSiblingTempFile } from "../infra/fs-safe-advanced.js";
import { readRegularFileSync } from "../infra/fs-safe.js";
import { redactSecrets } from "../logging/redact.js";
import { parseBooleanValue } from "../utils/boolean.js";
@@ -283,10 +283,18 @@ async function replaceTrajectoryWindow(params: {
appendedLines: string[];
}): Promise<void> {
const dir = path.dirname(params.filePath);
await fs.promises.mkdir(dir, { recursive: true, mode: 0o700 });
await assertNoSymlinkParents({
rootDir: path.parse(path.resolve(dir)).root,
targetPath: path.resolve(dir),
allowMissing: false,
allowRootChildSymlink: true,
requireDirectories: true,
messagePrefix: "Refusing to write trajectory under",
});
const lines = readTrajectoryWindowLines(params.filePath, params.maxFileBytes);
lines.push(...params.appendedLines);
trimJsonlWindow(lines, params.maxFileBytes);
await fs.promises.mkdir(dir, { recursive: true, mode: 0o700 });
await writeSiblingTempFile({
dir,
chmodDir: false,