






















@@ -4,7 +4,7 @@ import path from "node:path";
44import { pathToFileURL } from "node:url";
55import { describe, expect, it, vi } from "vitest";
66import { resolvePreferredOpenClawTmpDir } from "../infra/tmp-openclaw-dir.js";
7-import { resolveSandboxedMediaSource } from "./sandbox-paths.js";
7+import { resolveAllowedManagedMediaPath, resolveSandboxedMediaSource } from "./sandbox-paths.js";
8899async function withSandboxRoot<T>(run: (sandboxDir: string) => Promise<T>) {
1010const sandboxDir = await fs.mkdtemp(path.join(os.tmpdir(), "sandbox-media-"));
@@ -137,6 +137,25 @@ describe("resolveSandboxedMediaSource", () => {
137137});
138138});
139139140+it("resolves checked managed media paths for non-sandbox callers", async () => {
141+await withManagedMediaRoot(async ({ stateDir }) => {
142+const media = path.join(stateDir, "media", "outbound", "reply.png");
143+await fs.writeFile(media, "image", "utf8");
144+145+await expect(resolveAllowedManagedMediaPath(media)).resolves.toBe(media);
146+});
147+});
148+149+it("does not allow unrelated state media directories as managed media", async () => {
150+await withManagedMediaRoot(async ({ stateDir }) => {
151+const media = path.join(stateDir, "media", "inbound", "reply.png");
152+await fs.mkdir(path.dirname(media), { recursive: true });
153+await fs.writeFile(media, "image", "utf8");
154+155+await expect(resolveAllowedManagedMediaPath(media)).resolves.toBeUndefined();
156+});
157+});
158+140159// Group 2: Sandbox-relative paths (existing behavior)
141160it("resolves sandbox-relative paths", async () => {
142161await withSandboxRoot(async (sandboxDir) => {
@@ -343,6 +362,28 @@ describe("resolveSandboxedMediaSource", () => {
343362});
344363});
345364365+it("rejects checked managed media symlinks escaping the managed media root", async () => {
366+if (process.platform === "win32") {
367+return;
368+}
369+await withManagedMediaRoot(async ({ stateDir }) => {
370+const outsideDir = await fs.mkdtemp(path.join(os.tmpdir(), "managed-media-outside-"));
371+const outsideFile = path.join(outsideDir, "secret.png");
372+const symlinkPath = path.join(stateDir, "media", "outbound", "linked-secret.png");
373+try {
374+await fs.writeFile(outsideFile, "secret", "utf8");
375+await fs.symlink(outsideFile, symlinkPath);
376+377+await expect(resolveAllowedManagedMediaPath(symlinkPath)).rejects.toThrow(
378+/managed media root|symlink/i,
379+);
380+} finally {
381+await fs.rm(symlinkPath, { force: true });
382+await fs.rm(outsideDir, { recursive: true, force: true });
383+}
384+});
385+});
386+346387// Group 4: Passthrough
347388it("passes HTTP URLs through unchanged", async () => {
348389const result = await resolveSandboxedMediaSource({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。