Skip to content

Bug: QQBot plugin token/API requests blocked by SSRF guard in fake-IP proxy environments (Surge/Clash) #70310

Description

@colin-chang

Bug Description

QQBot plugin fails to obtain access tokens and make API requests when running behind a fake-IP proxy (e.g., Surge, Clash). The SSRF guard blocks requests because DNS resolves to the RFC 2544 benchmark range (198.18.0.0/15) used by fake-IP proxies.

Error Log

[security] blocked URL fetch (qqbot.token) target=https://bots.qq.com/app/getAppAccessToken reason=Blocked: resolves to private/internal/special-use IP address
[qqbot] [qqbot:default] Connection failed: Network error getting access_token: Blocked: resolves to private/internal/special-use IP address

This repeats every ~30s for all QQBot instances.

Root Cause

In extensions/qqbot/api-*.js, the fetchWithSsrFGuard calls for token fetching and API requests are missing the ssrfPolicy parameter:

Token request (doFetchToken):

const guarded = await fetchWithSsrFGuard({
    url: TOKEN_URL,
    init: { method: "POST", headers, body },
    auditContext: "qqbot.token"
    // ❌ Missing: ssrfPolicy
});

API request (apiRequest):

const guarded = await fetchWithSsrFGuard({
    url,
    init: options,
    auditContext: `qqbot.api${path}`
    // ❌ Missing: ssrfPolicy
});

Meanwhile, the media download path correctly includes SSRF policy with allowRfc2544BenchmarkRange: true:

const QQBOT_MEDIA_SSRF_POLICY = {
    hostnameAllowlist: [
        "*.qpic.cn", "*.qq.com", "*.weiyun.com", "*.qq.com.cn",
        "*.ugcimg.cn", "*.myqcloud.com", "*.tencentcos.cn", "*.tencentcos.com"
    ],
    allowRfc2544BenchmarkRange: true  // ✅ Correctly handles fake-IP proxies
};

Environment

  • macOS with Surge proxy (fake-ip mode)
  • dns.lookup("bots.qq.com") returns 198.18.35.155 (fake-IP)
  • dns.resolve4("bots.qq.com") returns 183.47.104.95 (real IP)
  • OpenClaw v2026.4.21

Workaround

Patching api-*.js to add ssrfPolicy to the two fetchWithSsrFGuard calls:

const guarded = await fetchWithSsrFGuard({
    url: TOKEN_URL,
    init: { ... },
    auditContext: "qqbot.token",
    policy: {
        hostnameAllowlist: ["*.qq.com"],
        allowRfc2544BenchmarkRange: true
    }
});

This works but will be overwritten on upgrade.

Expected Fix

Apply the same QQBOT_MEDIA_SSRF_POLICY (or a similar policy) to token and API requests in the QQBot plugin, since all QQBot API endpoints are under *.qq.com and need the same fake-IP proxy compatibility that media downloads already have.

Related

The Telegram plugin already handles this scenario via dangerouslyAllowPrivateNetwork. The QQBot plugin should have equivalent support for fake-IP proxy environments.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions