follow-up: align ingress, atomic paths, and channel tests with credential semantics (#33733)

Merged via squash.

Prepared head SHA: c290c2ab6a
Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
Reviewed-by: @joshavant
This commit is contained in:
Josh Avant
2026-03-03 20:29:46 -06:00
committed by GitHub
parent 6842877b2e
commit 1c200ca7ae
36 changed files with 1130 additions and 219 deletions

View File

@@ -15,8 +15,14 @@ export async function writeViaSiblingTempPath(params: {
targetPath: string;
writeTemp: (tempPath: string) => Promise<void>;
}): Promise<void> {
const rootDir = path.resolve(params.rootDir);
const targetPath = path.resolve(params.targetPath);
const rootDir = await fs
.realpath(path.resolve(params.rootDir))
.catch(() => path.resolve(params.rootDir));
const requestedTargetPath = path.resolve(params.targetPath);
const targetPath = await fs
.realpath(path.dirname(requestedTargetPath))
.then((realDir) => path.join(realDir, path.basename(requestedTargetPath)))
.catch(() => requestedTargetPath);
const relativeTargetPath = path.relative(rootDir, targetPath);
if (
!relativeTargetPath ||

View File

@@ -4,11 +4,7 @@ import path from "node:path";
import type { Page } from "playwright-core";
import { resolvePreferredOpenClawTmpDir } from "../infra/tmp-openclaw-dir.js";
import { writeViaSiblingTempPath } from "./output-atomic.js";
import {
DEFAULT_DOWNLOAD_DIR,
DEFAULT_UPLOAD_DIR,
resolveStrictExistingPathsWithinRoot,
} from "./paths.js";
import { DEFAULT_UPLOAD_DIR, resolveStrictExistingPathsWithinRoot } from "./paths.js";
import {
ensurePageState,
getPageForTargetId,
@@ -96,7 +92,7 @@ async function saveDownloadPayload(download: DownloadPayload, outPath: string) {
await download.saveAs?.(resolvedOutPath);
} else {
await writeViaSiblingTempPath({
rootDir: DEFAULT_DOWNLOAD_DIR,
rootDir: path.dirname(resolvedOutPath),
targetPath: resolvedOutPath,
writeTemp: async (tempPath) => {
await download.saveAs?.(tempPath);

View File

@@ -87,7 +87,11 @@ describe("pw-tools-core", () => {
const savedPath = params.saveAs.mock.calls[0]?.[0];
expect(typeof savedPath).toBe("string");
expect(savedPath).not.toBe(params.targetPath);
expect(path.dirname(String(savedPath))).toBe(params.tempDir);
const [savedDirReal, tempDirReal] = await Promise.all([
fs.realpath(path.dirname(String(savedPath))).catch(() => path.dirname(String(savedPath))),
fs.realpath(params.tempDir).catch(() => params.tempDir),
]);
expect(savedDirReal).toBe(tempDirReal);
expect(path.basename(String(savedPath))).toContain(".openclaw-output-");
expect(path.basename(String(savedPath))).toContain(".part");
expect(await fs.readFile(params.targetPath, "utf8")).toBe(params.content);
@@ -120,7 +124,7 @@ describe("pw-tools-core", () => {
const res = await p;
await expectAtomicDownloadSave({ saveAs, targetPath, tempDir, content: "file-content" });
expect(res.path).toBe(targetPath);
await expect(fs.realpath(res.path)).resolves.toBe(await fs.realpath(targetPath));
});
});
it("clicks a ref and atomically finalizes explicit download paths", async () => {
@@ -156,7 +160,7 @@ describe("pw-tools-core", () => {
const res = await p;
await expectAtomicDownloadSave({ saveAs, targetPath, tempDir, content: "report-content" });
expect(res.path).toBe(targetPath);
await expect(fs.realpath(res.path)).resolves.toBe(await fs.realpath(targetPath));
});
});
@@ -188,9 +192,8 @@ describe("pw-tools-core", () => {
saveAs,
});
const res = await p;
expect(res.path).toBe(linkedPath);
expect(await fs.readFile(linkedPath, "utf8")).toBe("download-content");
await expect(p).rejects.toThrow(/alias escape blocked|Hardlinked path is not allowed/i);
expect(await fs.readFile(linkedPath, "utf8")).toBe("outside-before");
expect(await fs.readFile(outsidePath, "utf8")).toBe("outside-before");
});
},