fix(qa-channel): reject non-http attachment urls · openclaw/openclaw@15ff861
vincentkoc
·
2026-04-24
·
via Recent Commits to openclaw:main
File tree
extensions/qa-channel/src
| Original file line number | Diff line number | Diff line change |
|---|
@@ -8,6 +8,7 @@ Docs: https://docs.openclaw.ai
|
8 | 8 | |
9 | 9 | ### Fixes |
10 | 10 | |
| 11 | +- QA channel/security: reject non-HTTP(S) inbound attachment URLs before media fetch, and log rejected schemes so suspicious or misconfigured payloads are visible during debugging. (#70708) Thanks @vincentkoc. |
11 | 12 | - Teams/security: require shared Bot Framework audience tokens to name the configured Teams app via verified `appid` or `azp`, blocking cross-bot token replay on the global audience. (#70724) Thanks @vincentkoc. |
12 | 13 | - Plugins/startup: resolve bundled plugin Jiti loads relative to the target plugin module instead of the central loader, so Bun global installs no longer hang while discovering bundled image providers. (#70073) Thanks @yidianyiko. |
13 | 14 | - Anthropic/CLI security: stop Claude CLI backend defaults from forcing `bypassPermissions`, and strip malformed permission-mode overrides instead of silently falling back to a bypass. (#70723) Thanks @vincentkoc. |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +import { describe, expect, it } from "vitest"; |
| 2 | +import { isHttpMediaUrl } from "./inbound.js"; |
| 3 | + |
| 4 | +describe("isHttpMediaUrl", () => { |
| 5 | +it("accepts only http and https urls", () => { |
| 6 | +expect(isHttpMediaUrl("https://example.com/image.png")).toBe(true); |
| 7 | +expect(isHttpMediaUrl("http://example.com/image.png")).toBe(true); |
| 8 | +expect(isHttpMediaUrl("file:///etc/passwd")).toBe(false); |
| 9 | +expect(isHttpMediaUrl("/etc/passwd")).toBe(false); |
| 10 | +expect(isHttpMediaUrl("data:text/plain;base64,SGVsbG8=")).toBe(false); |
| 11 | +}); |
| 12 | +}); |
| Original file line number | Diff line number | Diff line change |
|---|
@@ -9,6 +9,15 @@ import { buildQaTarget, sendQaBusMessage, type QaBusMessage } from "./bus-client
|
9 | 9 | import { getQaChannelRuntime } from "./runtime.js"; |
10 | 10 | import type { CoreConfig, ResolvedQaChannelAccount } from "./types.js"; |
11 | 11 | |
| 12 | +export function isHttpMediaUrl(value: string): boolean { |
| 13 | +try { |
| 14 | +const parsed = new URL(value); |
| 15 | +return parsed.protocol === "http:" || parsed.protocol === "https:"; |
| 16 | +} catch { |
| 17 | +return false; |
| 18 | +} |
| 19 | +} |
| 20 | + |
12 | 21 | async function resolveQaInboundMediaPayload(attachments: QaBusMessage["attachments"]) { |
13 | 22 | if (!Array.isArray(attachments) || attachments.length === 0) { |
14 | 23 | return {}; |
@@ -33,6 +42,12 @@ async function resolveQaInboundMediaPayload(attachments: QaBusMessage["attachmen
|
33 | 42 | continue; |
34 | 43 | } |
35 | 44 | if (typeof attachment.url === "string" && attachment.url.trim()) { |
| 45 | +if (!isHttpMediaUrl(attachment.url)) { |
| 46 | +console.warn( |
| 47 | +`[qa-channel] inbound attachment URL rejected (non-http scheme): ${attachment.url}`, |
| 48 | +); |
| 49 | +continue; |
| 50 | +} |
36 | 51 | const saved = await saveMediaSource(attachment.url, undefined, "inbound"); |
37 | 52 | mediaList.push({ |
38 | 53 | path: saved.path, |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。