import { beforeEach, describe, expect, it, vi } from "vitest"; import { ObjectId } from "mongodb"; import type { Workspace } from "$lib/types/Workspace"; const mocks = vi.hoisted(() => ({ workspaceFindOne: vi.fn(), sessionFindOne: vi.fn(), findUser: vi.fn(), config: { USE_USER_TOKEN: "false", OPENAI_API_KEY: "hf_inference_service_must_not_clone", HF_TOKEN: "hf_jobs_service_must_not_clone", }, })); vi.mock("$lib/server/database", () => ({ collections: { workspaces: { findOne: mocks.workspaceFindOne }, sessions: { findOne: mocks.sessionFindOne }, }, })); vi.mock("$lib/server/config", () => ({ config: mocks.config })); vi.mock("$lib/server/auth", () => ({ findUser: mocks.findUser })); import { AGENT_PROXY_MAX_BODY_BYTES, issueAgentProxyCredential } from "./llmProxy"; import { GET, HEAD, POST } from "../../../routes/api/agent/git/[...path]/+server"; const NOW = new Date("2026-08-20T00:00:00.000Z"); const OWNER_HF_TOKEN = "hf_owner_oauth_server_side_only"; function workspace( tokenHash: string, repo: Workspace["repo"] = { kind: "hf", repoType: "space", ref: "owner/private-app" } ): Workspace { return { _id: new ObjectId(), conversationId: new ObjectId(), userId: new ObjectId(), sessionId: "owner-session", repo, branch: "main", flavor: "cpu-basic", model: "moonshotai/Kimi-K2.7-Code", thinkingLevel: "medium", policy: "auto", sandbox: { status: "provisioning", id: "job-agent-git-proxy", namespace: "service-owner", baseUrl: "https://job-agent-git-proxy--49983.hf.jobs", nonce: "nonce-git-proxy", image: "hf.co/spaces/example/runtime", flavor: "cpu-basic", }, llmProxy: { tokenHash, createdAt: NOW, expiresAt: new Date("2100-01-01T00:00:00Z"), allowedModels: ["moonshotai/Kimi-K2.7-Code"], }, spend: { accruedUsd: 0 }, createdAt: NOW, updatedAt: NOW, }; } async function arrange(repo?: Workspace["repo"]) { const credential = await issueAgentProxyCredential(["moonshotai/Kimi-K2.7-Code"]); const value = workspace(credential.tokenHash, repo); mocks.workspaceFindOne.mockResolvedValue(value); mocks.sessionFindOne.mockResolvedValue({ sessionId: "owner-session" }); mocks.findUser.mockResolvedValue({ user: null, invalidateSession: false, oauth: { token: { value: OWNER_HF_TOKEN, expiresAt: new Date(Date.now() + 60_000) } }, }); return { credential, workspace: value }; } function infoRequest(token: string, method = "GET", suffix = "") { return new Request( `https://chat.example/api/agent/git/repository.git/info/refs?service=git-upload-pack${suffix}`, { method, headers: { accept: "application/x-git-upload-pack-advertisement", authorization: "Bearer attacker-controlled", cookie: "browser=session", "git-protocol": "version=2", "x-agent-session": token, }, } ); } function uploadRequest(token: string, body: BodyInit, headers: Record = {}) { return new Request("https://chat.example/api/agent/git/repository.git/git-upload-pack", { method: "POST", headers: { "content-type": "application/x-git-upload-pack-request", "x-agent-session": token, ...headers, }, body, }); } beforeEach(() => { vi.restoreAllMocks(); mocks.workspaceFindOne.mockReset(); mocks.sessionFindOne.mockReset(); mocks.findUser.mockReset(); vi.stubGlobal("fetch", vi.fn()); }); describe("agent Hugging Face Git smart-HTTP proxy", () => { it("derives the exact Space repo and attaches owner OAuth server-side even when inference user tokens are disabled", async () => { const { credential } = await arrange(); vi.mocked(fetch).mockResolvedValue( new Response("001e# service=git-upload-pack\n0000", { status: 200, headers: { "content-type": "application/x-git-upload-pack-advertisement", "set-cookie": "hub=secret", location: "https://evil.example/", "x-reflected-auth": OWNER_HF_TOKEN, }, }) ); const response = await GET({ request: infoRequest(credential.token), params: { path: "repository.git/info/refs" }, } as never); expect(response.status).toBe(200); expect(fetch).toHaveBeenCalledOnce(); const [url, init] = vi.mocked(fetch).mock.calls[0]; expect(String(url)).toBe( "https://huggingface.co/spaces/owner/private-app.git/info/refs?service=git-upload-pack" ); const upstreamHeaders = new Headers(init?.headers); expect(upstreamHeaders.get("authorization")).toBe(`Bearer ${OWNER_HF_TOKEN}`); expect(upstreamHeaders.get("git-protocol")).toBe("version=2"); expect(upstreamHeaders.get("x-agent-session")).toBeNull(); expect(upstreamHeaders.get("cookie")).toBeNull(); expect(init?.redirect).toBe("manual"); expect(response.headers.get("set-cookie")).toBeNull(); expect(response.headers.get("location")).toBeNull(); expect(response.headers.get("x-reflected-auth")).toBeNull(); expect(response.headers.get("cache-control")).toBe("no-store"); expect(await response.text()).not.toContain(OWNER_HF_TOKEN); expect(mocks.config.USE_USER_TOKEN).toBe("false"); }); it.each([ [ "model", "owner/source", "https://huggingface.co/owner/source.git/info/refs?service=git-upload-pack", ], [ "dataset", "owner/source", "https://huggingface.co/datasets/owner/source.git/info/refs?service=git-upload-pack", ], ] as const)( "confines a %s checkout to its persisted repo type and ref", async (repoType, ref, expected) => { const { credential } = await arrange({ kind: "hf", repoType, ref }); vi.mocked(fetch).mockResolvedValue( new Response("advertisement", { headers: { "content-type": "application/x-git-upload-pack-advertisement" }, }) ); const response = await GET({ request: infoRequest(credential.token), params: { path: "repository.git/info/refs" }, } as never); expect(response.status).toBe(200); expect(String(vi.mocked(fetch).mock.calls[0][0])).toBe(expected); } ); it("uses anonymous Hub access for a public repo instead of falling back to service credentials", async () => { const { credential } = await arrange(); mocks.sessionFindOne.mockResolvedValue(null); vi.mocked(fetch).mockResolvedValue( new Response("advertisement", { headers: { "content-type": "application/x-git-upload-pack-advertisement" }, }) ); const response = await GET({ request: infoRequest(credential.token), params: { path: "repository.git/info/refs" }, } as never); expect(response.status).toBe(200); const headers = new Headers(vi.mocked(fetch).mock.calls[0][1]?.headers); expect(headers.get("authorization")).toBeNull(); expect(JSON.stringify([...headers])).not.toContain(mocks.config.OPENAI_API_KEY); expect(JSON.stringify([...headers])).not.toContain(mocks.config.HF_TOKEN); }); it("streams upload-pack bytes while forwarding only the allowlisted request headers", async () => { const { credential } = await arrange({ kind: "hf", repoType: "dataset", ref: "owner/data" }); const result = Uint8Array.from([0, 1, 2, 3, 255]); vi.mocked(fetch).mockResolvedValue( new Response(result, { headers: { "content-type": "application/x-git-upload-pack-result" }, }) ); const requestBody = Buffer.from("000eversion 2\n0000"); const response = await POST({ request: uploadRequest(credential.token, requestBody), params: { path: "repository.git/git-upload-pack" }, } as never); expect(response.status).toBe(200); expect(new Uint8Array(await response.arrayBuffer())).toEqual(result); const [url, init] = vi.mocked(fetch).mock.calls[0]; expect(String(url)).toBe("https://huggingface.co/datasets/owner/data.git/git-upload-pack"); expect(new Uint8Array(init?.body as ArrayBuffer)).toEqual(new Uint8Array(requestBody)); expect(new Headers(init?.headers).get("authorization")).toBe(`Bearer ${OWNER_HF_TOKEN}`); }); it("supports HEAD only for the exact upload-pack advertisement", async () => { const { credential } = await arrange(); vi.mocked(fetch).mockResolvedValue( new Response(null, { headers: { "content-type": "application/x-git-upload-pack-advertisement" }, }) ); const response = await HEAD({ request: infoRequest(credential.token, "HEAD"), params: { path: "repository.git/info/refs" }, } as never); expect(response.status).toBe(200); expect(vi.mocked(fetch).mock.calls[0][1]?.method).toBe("HEAD"); expect(await response.text()).toBe(""); }); it.each([ "other.git/info/refs", "repository.git/git-receive-pack", "repository.git/info/lfs/objects/batch", "repository.git/../../owner/other.git/info/refs", "models/expensive-model", ])( "rejects cross-repo or operation escalation path %s before any upstream request", async (path) => { const { credential } = await arrange(); const response = await GET({ request: infoRequest(credential.token), params: { path }, } as never); expect(response.status).toBe(404); expect(fetch).not.toHaveBeenCalled(); } ); it("rejects extra query controls, non-HF workspaces, and invalid persisted refs", async () => { let arranged = await arrange(); let response = await GET({ request: infoRequest(arranged.credential.token, "GET", "&repo=attacker/other"), params: { path: "repository.git/info/refs" }, } as never); expect(response.status).toBe(404); arranged = await arrange({ kind: "git", ref: "https://example.com/public.git" }); response = await GET({ request: infoRequest(arranged.credential.token), params: { path: "repository.git/info/refs" }, } as never); expect(response.status).toBe(403); arranged = await arrange({ kind: "hf", repoType: "space", ref: "owner/valid" as string, }); arranged.workspace.repo = { kind: "hf", repoType: "space", ref: "owner/../../other", }; response = await GET({ request: infoRequest(arranged.credential.token), params: { path: "repository.git/info/refs" }, } as never); expect(response.status).toBe(403); expect(fetch).not.toHaveBeenCalled(); }); it("rejects unsupported methods and upload-pack media types", async () => { const { credential } = await arrange(); let response = await GET({ request: infoRequest(credential.token, "DELETE"), params: { path: "repository.git/info/refs" }, } as never); expect(response.status).toBe(405); response = await POST({ request: uploadRequest(credential.token, "payload", { "content-type": "application/json" }), params: { path: "repository.git/git-upload-pack" }, } as never); expect(response.status).toBe(415); expect(fetch).not.toHaveBeenCalled(); }); it("enforces both declared and streamed 8 MiB upload-pack request limits", async () => { const { credential } = await arrange(); let response = await POST({ request: uploadRequest(credential.token, "x", { "content-length": String(AGENT_PROXY_MAX_BODY_BYTES + 1), }), params: { path: "repository.git/git-upload-pack" }, } as never); expect(response.status).toBe(413); response = await POST({ request: uploadRequest(credential.token, Buffer.alloc(AGENT_PROXY_MAX_BODY_BYTES + 1)), params: { path: "repository.git/git-upload-pack" }, } as never); expect(response.status).toBe(413); expect(fetch).not.toHaveBeenCalled(); }); it("never reflects upstream errors, redirects, cookies, or the raw owner credential", async () => { const { credential } = await arrange(); vi.mocked(fetch).mockResolvedValue( new Response(`denied: ${OWNER_HF_TOKEN}`, { status: 401, headers: { location: `https://example.com/?token=${OWNER_HF_TOKEN}`, "set-cookie": `token=${OWNER_HF_TOKEN}`, "www-authenticate": `Bearer token=${OWNER_HF_TOKEN}`, }, }) ); const response = await GET({ request: infoRequest(credential.token), params: { path: "repository.git/info/refs" }, } as never); const serialized = `${JSON.stringify([...response.headers])}\n${await response.text()}`; expect(response.status).toBe(403); expect(serialized).toContain("Reconnect Hugging Face"); expect(serialized).not.toContain(OWNER_HF_TOKEN); expect(response.headers.get("location")).toBeNull(); expect(response.headers.get("set-cookie")).toBeNull(); expect(response.headers.get("www-authenticate")).toBeNull(); }); it("returns an actionable non-challenge error when a private repo has no owner OAuth", async () => { const { credential } = await arrange(); mocks.sessionFindOne.mockResolvedValue(null); vi.mocked(fetch).mockResolvedValue( new Response("private upstream detail", { status: 401, headers: { "www-authenticate": "Basic realm=private" }, }) ); const response = await GET({ request: infoRequest(credential.token), params: { path: "repository.git/info/refs" }, } as never); expect(response.status).toBe(403); expect(await response.text()).toBe("Sign in with Hugging Face to access this repository\n"); expect(response.headers.get("www-authenticate")).toBeNull(); }); });