GHSA-C4QG-J8JG-42Q5: Root-cause SSRF Guard for QQBot Direct Upload URLs
Summary
The patch adds pre-validation for direct-upload media URLs in the OpenClaw QQBot extension by requiring HTTPS and resolving the target hostname through the SSRF policy layer before the URL is forwarded upstream. This addresses the core issue described in the advisory: attacker-controlled URLs were previously relayed to the QQ Open Platform API without local trust-boundary enforcement.
Analysis
Vulnerability
GHSA-C4QG-J8JG-42Q5 describes a server-side request forgery condition in the OpenClaw QQBot extension. The vulnerable flow accepted attacker-controlled media URLs and assigned them directly into the outbound request body for QQ direct-upload operations. In the patched diff for extensions/qqbot/src/api.ts, the pre-fix behavior is shown as direct assignment:
body.url = url;
body.url = url;Because no local validation was performed before relaying the URL, the extension could be used as a broker for requests to internal, private, or special-use network targets via the upstream QQ Open Platform API. That is a classic SSRF trust-boundary failure: untrusted input crossed from application input into a network-capable downstream integration without scheme or destination policy enforcement. The advisory and commit both ground this issue in the QQBot direct-upload path rather than a generic fetch sink inside the application itself.
Source references: GitHub Security Advisory, patch commit, report summary.
Patch
The patch introduces a dedicated validation helper, assertDirectUploadUrlAllowed, and applies it to both uploadC2CMedia and uploadGroupMedia before setting body.url. The new logic parses the URL, rejects malformed input, enforces https:, and invokes the SSRF runtime policy via resolvePinnedHostnameWithPolicy(parsed.hostname).
import {
fetchWithSsrFGuard,
resolvePinnedHostnameWithPolicy,
} from "openclaw/plugin-sdk/ssrf-runtime";
async function assertDirectUploadUrlAllowed(url: string): Promise<string> {
let parsed: URL;
try {
parsed = new URL(url);
} catch (err) {
throw new Error(`Invalid media URL: ${formatErrorMessage(err)}`, { cause: err });
}
if (parsed.protocol !== "https:") {
throw new Error("Direct-upload media URL must use HTTPS");
}
await resolvePinnedHostnameWithPolicy(parsed.hostname);
return parsed.toString();
}
body.url = await assertDirectUploadUrlAllowed(url);The accompanying tests in extensions/qqbot/src/api.security.test.ts verify four important behaviors from the commit at 49db424c8001f2f419aad85f434894d8d85c1a09:
- private/internal targets are blocked when hostname policy resolution rejects them;
- non-HTTPS URLs are rejected before any upload request is made;
- public HTTPS URLs are allowed for both C2C and group uploads;
- cache-hit paths skip validation when previously computed
fileDatais reused.
This is consistent with the changelog entry stating that the release adds an SSRF guard to direct-upload URL paths.
Review
Pros
- The patch addresses the actual trust-boundary failure at the point where attacker-controlled URLs enter the direct-upload workflow, which is the right architectural location for the fix.
- HTTPS-only enforcement removes cleartext schemes and narrows the allowed protocol surface.
- Using
resolvePinnedHostnameWithPolicyties the validation to the existing SSRF policy runtime instead of introducing ad hoc hostname filtering. - The helper is reused in both affected upload paths, reducing drift between C2C and group behavior.
- The tests are meaningful and source-grounded: they assert blocking of internal destinations, rejection of non-HTTPS URLs, and successful handling of public HTTPS URLs.
Cons
- The validation helper only resolves and checks
parsed.hostname; from the visible diff it does not itself bind the later upstream consumer to the resolved address set. If the downstream platform re-resolves the hostname independently, this local check cannot fully prevent all DNS rebinding or time-of-check/time-of-use discrepancies. Whether that matters depends on the upstream QQ API semantics, which are not shown in the patch. - The cache-hit tests explicitly confirm that URL validation is skipped when
fileDatareuse hits the upload cache. That is probably acceptable because the cached path avoids a fresh direct-upload request, but it creates a behavioral exception that reviewers should keep in mind when reasoning about future changes to cache semantics. - The patch enforces scheme and hostname policy, but the visible diff does not show additional constraints such as allowlisting media content hosts, port restrictions, redirect handling, or response-size limits. Those may be handled elsewhere, but they are not demonstrated here.
Verdict
Root-cause.
This patch fixes the core issue identified in GHSA-C4QG-J8JG-42Q5: unvalidated direct-upload URLs were previously forwarded upstream, and now they are parsed, restricted to HTTPS, and checked against the SSRF hostname policy before use. The remediation is well-targeted and backed by regression tests in the commit at the patch source. Residual uncertainty remains around downstream re-resolution and broader egress controls, but based on the provided sources this is a substantive root-cause fix for the vulnerable entry point rather than a superficial filter.
Recommended Labs
Try this vulnerability pattern yourself with hands-on labs.
- SSRF.api
Best direct match for this advisory’s root cause: attacker-controlled upload/media URLs being forwarded to an upstream platform API. This lab focuses on SSRF in an API context, which aligns closely with validating outbound request targets, enforcing allowlists, and blocking access to internal services and metadata endpoints.
- SSRF.js
Good fit if you want hands-on practice in the JavaScript/TypeScript ecosystem similar to bot and integration code. Useful for learning defensive patterns such as strict URL parsing, scheme/host restrictions, redirect handling, and rejecting private or loopback destinations before making server-side fetches.
- SSRF.ts
Recommended when your remediation work involves typed service code or modern backend integrations. It reinforces the same SSRF guardrails relevant to this case: validating direct upload URLs, constraining outbound network reachability, and treating user-supplied URLs as untrusted input even when relayed through third-party APIs.