























@@ -368,7 +368,14 @@ describe("handleControlUiHttpRequest", () => {
368368});
369369expect(handled).toBe(true);
370370expect(res.statusCode).toBe(200);
371-expect(JSON.parse(String(end.mock.calls[0]?.[0] ?? ""))).toEqual({ available: true });
371+const payload = JSON.parse(String(end.mock.calls[0]?.[0] ?? "")) as {
372+available?: boolean;
373+mediaTicket?: string;
374+mediaTicketExpiresAt?: string;
375+};
376+expect(payload).toMatchObject({ available: true });
377+expect(payload.mediaTicket).toMatch(/^v1\./);
378+expect(Date.parse(payload.mediaTicketExpiresAt ?? "")).not.toBeNaN();
372379} finally {
373380await fs.rm(filePath, { force: true });
374381}
@@ -403,7 +410,94 @@ describe("handleControlUiHttpRequest", () => {
403410});
404411expect(handled).toBe(true);
405412expect(res.statusCode).toBe(200);
406-expect(JSON.parse(String(end.mock.calls[0]?.[0] ?? ""))).toEqual({ available: true });
413+const payload = JSON.parse(String(end.mock.calls[0]?.[0] ?? "")) as {
414+available?: boolean;
415+mediaTicket?: string;
416+mediaTicketExpiresAt?: string;
417+};
418+expect(payload).toMatchObject({ available: true });
419+expect(payload.mediaTicket).toMatch(/^v1\./);
420+expect(Date.parse(payload.mediaTicketExpiresAt ?? "")).not.toBeNaN();
421+},
422+});
423+});
424+425+it("serves assistant local media with a scoped media ticket after metadata auth", async () => {
426+await withAllowedAssistantMediaRoot({
427+prefix: "ui-media-ticket-",
428+fn: async (tmpRoot) => {
429+const filePath = path.join(tmpRoot, "photo.png");
430+await fs.writeFile(filePath, Buffer.from("not-a-real-png"));
431+const meta = await runAssistantMediaRequest({
432+url: `/__openclaw__/assistant-media?meta=1&source=${encodeURIComponent(filePath)}`,
433+method: "GET",
434+auth: { mode: "token", token: "test-token", allowTailscale: false },
435+headers: {
436+authorization: "Bearer test-token",
437+},
438+});
439+const payload = JSON.parse(String(meta.end.mock.calls[0]?.[0] ?? "")) as {
440+mediaTicket?: string;
441+};
442+expect(meta.handled).toBe(true);
443+expect(meta.res.statusCode).toBe(200);
444+expect(payload.mediaTicket).toMatch(/^v1\./);
445+446+const media = await runAssistantMediaRequest({
447+url: `/__openclaw__/assistant-media?source=${encodeURIComponent(filePath)}&mediaTicket=${encodeURIComponent(payload.mediaTicket ?? "")}`,
448+method: "GET",
449+auth: { mode: "token", token: "test-token", allowTailscale: false },
450+});
451+expect(media.handled).toBe(true);
452+expect(media.res.statusCode).toBe(200);
453+},
454+});
455+});
456+457+it("does not refresh assistant media tickets without operator auth", async () => {
458+await withAllowedAssistantMediaRoot({
459+prefix: "ui-media-ticket-refresh-",
460+fn: async (tmpRoot) => {
461+const filePath = path.join(tmpRoot, "photo.png");
462+await fs.writeFile(filePath, Buffer.from("not-a-real-png"));
463+const meta = await runAssistantMediaRequest({
464+url: `/__openclaw__/assistant-media?meta=1&source=${encodeURIComponent(filePath)}`,
465+method: "GET",
466+auth: { mode: "token", token: "test-token", allowTailscale: false },
467+headers: {
468+authorization: "Bearer test-token",
469+},
470+});
471+const payload = JSON.parse(String(meta.end.mock.calls[0]?.[0] ?? "")) as {
472+mediaTicket?: string;
473+};
474+475+const refresh = await runAssistantMediaRequest({
476+url: `/__openclaw__/assistant-media?meta=1&source=${encodeURIComponent(filePath)}&mediaTicket=${encodeURIComponent(payload.mediaTicket ?? "")}`,
477+method: "GET",
478+auth: { mode: "token", token: "test-token", allowTailscale: false },
479+});
480+expect(refresh.handled).toBe(true);
481+expect(refresh.res.statusCode).toBe(401);
482+expect(String(refresh.end.mock.calls[0]?.[0] ?? "")).toContain("Unauthorized");
483+},
484+});
485+});
486+487+it("rejects assistant local media with an invalid scoped media ticket", async () => {
488+await withAllowedAssistantMediaRoot({
489+prefix: "ui-media-ticket-invalid-",
490+fn: async (tmpRoot) => {
491+const filePath = path.join(tmpRoot, "photo.png");
492+await fs.writeFile(filePath, Buffer.from("not-a-real-png"));
493+const { res, handled, end } = await runAssistantMediaRequest({
494+url: `/__openclaw__/assistant-media?source=${encodeURIComponent(filePath)}&mediaTicket=v1.invalid.invalid`,
495+method: "GET",
496+auth: { mode: "token", token: "test-token", allowTailscale: false },
497+});
498+expect(handled).toBe(true);
499+expect(res.statusCode).toBe(401);
500+expect(String(end.mock.calls[0]?.[0] ?? "")).toContain("Unauthorized");
407501},
408502});
409503});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。