fix: slug remote media cache session keys

This commit is contained in:
Frank Yang
2026-04-24 16:01:48 +08:00
parent a0f4fb2c15
commit 59b25bae9d
2 changed files with 34 additions and 2 deletions

View File

@@ -1,6 +1,8 @@
import fs from "node:fs/promises";
import { basename, join } from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import { slugifySessionKey } from "../agents/sandbox/shared.js";
import { CONFIG_DIR } from "../utils.js";
import {
createSandboxMediaContexts,
createSandboxMediaStageConfig,
@@ -50,7 +52,7 @@ function createRemoteStageParams(home: string): {
cfg: createSandboxMediaStageConfig(home),
workspaceDir: join(home, "openclaw"),
sessionKey,
remoteCacheDir: join(home, ".openclaw", "media", "remote-cache", sessionKey),
remoteCacheDir: join(home, ".openclaw", "media", "remote-cache", slugifySessionKey(sessionKey)),
};
}
@@ -86,4 +88,33 @@ describe("stageSandboxMedia scp remote paths", () => {
expect(sessionCtx.MediaUrl).toBe(remotePath);
});
});
it("uses a slugged remote cache directory for session keys with path separators", async () => {
await withSandboxMediaTempHome("openclaw-triggers-", async (home) => {
const { cfg, workspaceDir } = createRemoteStageParams(home);
const sessionKey = "agent:main:explicit:../../escape";
const remotePath = "/Users/demo/Library/Messages/Attachments/ab/cd/photo.jpg";
const { ctx, sessionCtx } = createRemoteContexts(remotePath);
childProcessMocks.spawn.mockImplementation(() => {
throw new Error("stop before scp");
});
await stageSandboxMedia({
ctx,
sessionCtx,
cfg,
sessionKey,
workspaceDir,
});
const remoteCacheRoot = join(CONFIG_DIR, "media", "remote-cache");
const expectedSafeDir = join(remoteCacheRoot, slugifySessionKey(sessionKey));
try {
await expect(fs.stat(expectedSafeDir)).resolves.toBeTruthy();
await expect(fs.stat(join(CONFIG_DIR, "escape"))).rejects.toThrow();
} finally {
await fs.rm(expectedSafeDir, { recursive: true, force: true });
}
});
});
});

View File

@@ -4,6 +4,7 @@ import path from "node:path";
import { fileURLToPath } from "node:url";
import { assertSandboxPath } from "../../agents/sandbox-paths.js";
import { ensureSandboxWorkspaceForSession } from "../../agents/sandbox.js";
import { slugifySessionKey } from "../../agents/sandbox/shared.js";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import { logVerbose } from "../../globals.js";
import { copyFileWithinRoot, SafeOpenError } from "../../infra/fs-safe.js";
@@ -40,7 +41,7 @@ export async function stageSandboxMedia(params: {
// For remote attachments without sandbox, use ~/.openclaw/media (not agent workspace for privacy)
const remoteMediaCacheDir = ctx.MediaRemoteHost
? path.join(CONFIG_DIR, "media", "remote-cache", sessionKey)
? path.join(CONFIG_DIR, "media", "remote-cache", slugifySessionKey(sessionKey))
: null;
const effectiveWorkspaceDir = sandbox?.workspaceDir ?? remoteMediaCacheDir;
if (!effectiveWorkspaceDir) {