Vulnerability Summary

Field Value
Product ApiFlow
Vendor trueleaf.cn
Vulnerability Type Server-Side Request Forgery (CWE-918)
Affected Versions <= 0.9.81 (all versions)
Severity High (CVSS 8.6)
Attack Vector Network
Attack Complexity Low
Privileges Required None
User Interaction None
Scope Unchanged
Confidentiality Impact High
Integrity Impact None
Availability Impact None

1. Description

ApiFlow is an API documentation management platform written in TypeScript/Node.js. The HTTP proxy functionality in the server component contains a Server-Side Request Forgery (SSRF) vulnerability that allows unauthenticated attackers to bypass URL validation and make arbitrary requests to internal network resources.

The vulnerability exists in the HttpProxyService class where URL validation is performed only on the initial request URL, but not on redirect targets. By using an external redirect service, attackers can bypass the IP address allowlist and access internal services such as databases, cloud metadata endpoints, and other sensitive internal resources.


2. Affected Components

Component File Path
HTTP Proxy Service packages/server/src/service/proxy/http_proxy.service.ts
HTTP Proxy Controller packages/server/src/controller/proxy/http_proxy.controller.ts
API Endpoint POST /api/proxy/http

3. Technical Analysis

3.1 Vulnerable Code

File: packages/server/src/service/proxy/http_proxy.service.ts

URL Validation Function (Lines 17-63):

private validateUrlSecurity(url: string): { valid: boolean; error?: string } {
  try {
    const parsedUrl = new URL(url);

    // Only allow http and https protocols
    if (!['http:', 'https:'].includes(parsedUrl.protocol)) {
      return {
        valid: false,
        error: `Unsupported protocol:${parsedUrl.protocol}`
      };
    }

    const hostname = parsedUrl.hostname.toLowerCase();

    // Forbidden internal IP patterns
    const forbiddenPatterns = [
      /^127\\./,              // 127.0.0.0/8
      /^10\\./,               // 10.0.0.0/8
      /^172\\.(1[6-9]|2\\d|3[01])\\./, // 172.16.0.0/12
      /^192\\.168\\./,         // 192.168.0.0/16
      /^169\\.254\\./,         // Link-local
      /^0\\./,                // 0.0.0.0/8
      /^localhost$/,         // localhost
      /^::1$/,               // IPv6 loopback
      /^fe80:/,              // IPv6 link-local
      /^fc00:/,              // IPv6 unique local
      /^fd00:/,              // IPv6 unique local
    ];

    for (const pattern of forbiddenPatterns) {
      if (pattern.test(hostname)) {
        return {
          valid: false,
          error: `Access to internal address is forbidden:${hostname}`
        };
      }
    }

    return { valid: true };
  } catch (error) {
    return {
      valid: false,
      error: `Invalid URL format:${(error as Error).message}`
    };
  }
}

Request Execution with Redirect Following (Lines 140-146):

const gotOptions: Omit<OptionsInit, 'isStream'> = {
  url: params.url,
  method: params.method,
  body: willSendBody,
  headers,
  followRedirect: params.followRedirect ?? true,  // VULNERABILITY: Redirects followed by default
  maxRedirects: params.maxRedirects ?? 10,        // Up to 10 redirects allowed
  // ...
};

3.2 Root Cause Analysis

The vulnerability stems from a fundamental design flaw in the URL validation logic:

  1. Single-point validation: URL security validation (validateUrlSecurity) is performed only once before the request is initiated.