Просмотр исходного кода

fix: address Gemini Code Assist review feedback

- Enhanced SSRF protection with IPv6 ULA address checking (fc00::/7, fe80::/10)
- Fixed leaderboard-view.tsx useEffect logic to prevent state reset issues
- Replaced manual Cookie parsing with Hono's getCookie helper for better robustness
ding113 3 месяцев назад
Родитель
Сommit
e394b9d5dd

+ 12 - 0
src/actions/notifications.ts

@@ -34,6 +34,18 @@ function isInternalUrl(urlString: string): boolean {
       if (a === 0) return true; // 0.0.0.0/8
       if (a === 0) return true; // 0.0.0.0/8
     }
     }
 
 
+    // 检查 IPv6 私有地址范围
+    // 移除方括号(如果存在)用于 IPv6 地址检查
+    const ipv6Hostname = hostname.replace(/^\[|\]$/g, "");
+    // ULA (Unique Local Address): fc00::/7
+    if (ipv6Hostname.startsWith("fc") || ipv6Hostname.startsWith("fd")) {
+      return true;
+    }
+    // Link-local: fe80::/10
+    if (ipv6Hostname.startsWith("fe80:")) {
+      return true;
+    }
+
     // 危险端口
     // 危险端口
     const dangerousPorts = [22, 23, 3306, 5432, 27017, 6379, 11211];
     const dangerousPorts = [22, 23, 3306, 5432, 27017, 6379, 11211];
     if (url.port && dangerousPorts.includes(parseInt(url.port, 10))) {
     if (url.port && dangerousPorts.includes(parseInt(url.port, 10))) {

+ 1 - 1
src/app/[locale]/dashboard/leaderboard/_components/leaderboard-view.tsx

@@ -45,7 +45,7 @@ export function LeaderboardView({ isAdmin }: LeaderboardViewProps) {
     if (normalizedPeriod !== period) {
     if (normalizedPeriod !== period) {
       setPeriod(normalizedPeriod);
       setPeriod(normalizedPeriod);
     }
     }
-  }, [isAdmin, period, scope, searchParams]);
+  }, [isAdmin, searchParams]);
 
 
   useEffect(() => {
   useEffect(() => {
     let cancelled = false;
     let cancelled = false;

+ 2 - 1
src/lib/api/action-adapter-openapi.ts

@@ -10,6 +10,7 @@
 
 
 import { createRoute, z } from "@hono/zod-openapi";
 import { createRoute, z } from "@hono/zod-openapi";
 import type { Context } from "hono";
 import type { Context } from "hono";
+import { getCookie } from "hono/cookie";
 import type { ActionResult } from "@/actions/types";
 import type { ActionResult } from "@/actions/types";
 import { logger } from "@/lib/logger";
 import { logger } from "@/lib/logger";
 import { validateKey } from "@/lib/auth";
 import { validateKey } from "@/lib/auth";
@@ -197,7 +198,7 @@ export function createActionRoute(
     try {
     try {
       // 0. 认证检查 (如果需要)
       // 0. 认证检查 (如果需要)
       if (requiresAuth) {
       if (requiresAuth) {
-        const authToken = c.req.header("Cookie")?.match(/auth-token=([^;]+)/)?.[1];
+        const authToken = getCookie(c, "auth-token");
         if (!authToken) {
         if (!authToken) {
           logger.warn(`[ActionAPI] ${fullPath} 认证失败: 缺少 auth-token`);
           logger.warn(`[ActionAPI] ${fullPath} 认证失败: 缺少 auth-token`);
           return c.json({ ok: false, error: "未认证" }, 401);
           return c.json({ ok: false, error: "未认证" }, 401);