CVE-2024-32964: Insufficient Information (NVD-CWE-noinfo)
value: Lobe Chat is a chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Prior to 0.150.6, lobe-chat had an unauthorized Server-Side Request Forgery vulnerability in the /api/proxy endpoint. An attacker can construct malicious requests to cause Server-Side Request Forgery without logging in, attack intranet services, and leak sensitive information.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
Published: 2024-05-14T15:37Z
Repository: https://github.com/lobehub/lobe-chat
Language: JSON
Files changed: package.json, src/app/api/proxy/route.ts
Commit date: 2024-04-28 17:03:58 +0800
Commit message: 🐛 fix: fix `/api/proxy` internal proxy attack (#2255)


Diff: diff --git a/package.json b/package.json
index 5f501205..738a460a 100644
--- a/package.json
+++ b/package.json
@@ -116,10 +116,11 @@
     "i18next": "^23.11.2",
     "i18next-browser-languagedetector": "^7.2.1",
     "i18next-resources-to-backend": "^1.2.1",
     "idb-keyval": "^6.2.1",
     "immer": "^10.0.4",
+    "ip": "^2.0.1",
     "jose": "^5.2.4",
     "langfuse": "^3.7.0",
     "langfuse-core": "^3.7.0",
     "lodash-es": "^4.17.21",
     "lucide-react": "latest",
@@ -181,10 +182,11 @@
     "@testing-library/jest-dom": "^6.4.2",
     "@testing-library/react": "^15.0.4",
     "@types/chroma-js": "^2.4.4",
     "@types/debug": "^4.1.12",
     "@types/diff": "^5.2.0",
+    "@types/ip": "^1.1.3",
     "@types/json-schema": "^7.0.15",
     "@types/lodash": "^4.17.0",
     "@types/lodash-es": "^4.17.12",
     "@types/node": "^20.12.7",
     "@types/numeral": "^2.0.5",
diff --git a/src/app/api/proxy/route.ts b/src/app/api/proxy/route.ts
index c17dde10..67277ac7 100644
--- a/src/app/api/proxy/route.ts
+++ b/src/app/api/proxy/route.ts
@@ -1,12 +1,34 @@
-export const runtime = 'edge';
+import { isPrivate } from 'ip';
+import { NextResponse } from 'next/server';
+import dns from 'node:dns';
+import { promisify } from 'node:util';
+
+const lookupAsync = promisify(dns.lookup);
+
+export const runtime = 'nodejs';
 
 /**
  * just for a proxy
  */
 export const POST = async (req: Request) => {
-  const url = await req.text();
+  const url = new URL(await req.text());
+  let address;
+
+  try {
+    const lookupResult = await lookupAsync(url.hostname);
+    address = lookupResult.address;
+  } catch (err) {
+    console.error(`${url.hostname} DNS parser error:`, err);
+
+    return NextResponse.json({ error: 'DNS parser error' }, { status: 504 });
+  }
+
+  const isInternalHost = isPrivate(address);
+
+  if (isInternalHost)
+    return NextResponse.json({ error: 'Not support internal host proxy' }, { status: 400 });
 
-  const res = await fetch(url);
+  const res = await fetch(url.toString());
 
   return new Response(res.body, { headers: res.headers });
 };

Vulnerable code: export const runtime = 'edge';
  const url = await req.text();
  const res = await fetch(url);
Fixed code:     "ip": "^2.0.1",
    "@types/ip": "^1.1.3",
import { isPrivate } from 'ip';
import { NextResponse } from 'next/server';
import dns from 'node:dns';
import { promisify } from 'node:util';

const lookupAsync = promisify(dns.lookup);

export const runtime = 'nodejs';
  const url = new URL(await req.text());
  let address;

  try {
    const lookupResult = await lookupAsync(url.hostname);
    address = lookupResult.address;
  } catch (err) {
    console.error(`${url.hostname} DNS parser error:`, err);

    return NextResponse.json({ error: 'DNS parser error' }, { status: 504 });
  }

  const isInternalHost = isPrivate(address);

  if (isInternalHost)
    return NextResponse.json({ error: 'Not support internal host proxy' }, { status: 400 });
  const res = await fetch(url.toString());