fix(web): reject path-traversal segments in the API proxy

This commit is contained in:
Munkherdene 2026-08-22 20:19:31 +08:00
parent 25bdfda5a3
commit 3fd45fe54d
2 changed files with 25 additions and 0 deletions

View file

@ -4,6 +4,9 @@ import { readToken } from "../../../../server/session";
async function proxy(req: NextRequest, ctx: { params: Promise<{ path: string[] }> }) { async function proxy(req: NextRequest, ctx: { params: Promise<{ path: string[] }> }) {
const { path } = await ctx.params; const { path } = await ctx.params;
if (path.some((seg) => seg === ".." || seg === "." || seg === "" || seg.includes("/") || seg.includes("\\"))) {
return new NextResponse("bad path", { status: 400 });
}
const token = readToken(req); const token = readToken(req);
const search = req.nextUrl.search; const search = req.nextUrl.search;
const body = req.method === "GET" || req.method === "HEAD" ? undefined : await req.text(); const body = req.method === "GET" || req.method === "HEAD" ? undefined : await req.text();

View file

@ -0,0 +1,22 @@
// @vitest-environment node
import { describe, it, expect, vi } from "vitest";
vi.mock("../../../server/backend", () => ({ backendFetch: vi.fn(async () => new Response("{}", { status: 200 })) }));
import { NextRequest } from "next/server";
import { backendFetch } from "../../../server/backend";
import { GET } from "./[...path]/route";
describe("v1 proxy route", () => {
it("rejects a path-traversal segment with 400 and never calls backendFetch", async () => {
const req = new NextRequest("http://x/api/v1/../auth/login");
const res = await GET(req, { params: Promise.resolve({ path: ["..", "auth", "login"] }) });
expect(res.status).toBe(400);
expect(backendFetch).not.toHaveBeenCalled();
});
it("passes through a normal path", async () => {
const req = new NextRequest("http://x/api/v1/accounts");
const res = await GET(req, { params: Promise.resolve({ path: ["accounts"] }) });
expect(res.status).toBe(200);
expect(backendFetch).toHaveBeenCalled();
});
});