diff --git a/web/__tests__/api/connected-repos-skip-review.test.ts b/web/__tests__/api/connected-repos-skip-review.test.ts index d23ac364..a7f29e63 100644 --- a/web/__tests__/api/connected-repos-skip-review.test.ts +++ b/web/__tests__/api/connected-repos-skip-review.test.ts @@ -47,7 +47,10 @@ vi.mock("@/lib/mirror/connectedRepos", () => ({ import { POST as syncPost } from "@/app/api/agents/[pubkey]/repos/[id]/sync/route"; import { DELETE as disconnectDelete } from "@/app/api/agents/[pubkey]/repos/[id]/route"; -import { POST as connectPost } from "@/app/api/agents/[pubkey]/repos/route"; +import { + GET as listGet, + POST as connectPost, +} from "@/app/api/agents/[pubkey]/repos/route"; const PUBKEY = "WalletPubkey1111111111111111111111111111111"; const REPO_ID = "00000000-0000-4000-8000-000000000001"; @@ -88,6 +91,26 @@ function makeConnectRequest(body: Record) { }); } +describe("GET /api/agents/[pubkey]/repos", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("rejects malformed public keys before database or repository work", async () => { + const response = await listGet( + new NextRequest("http://localhost/api/agents/not-a-solana-address/repos"), + { params: Promise.resolve({ pubkey: "not-a-solana-address" }) } + ); + + expect(response.status).toBe(400); + await expect(response.json()).resolves.toEqual({ + error: "Agent routes require a valid Solana address", + }); + expect(mockInitializeDatabase).not.toHaveBeenCalled(); + expect(mockListConnectedRepos).not.toHaveBeenCalled(); + }); +}); + describe("POST /api/agents/[pubkey]/repos/[id]/sync — skip_review bypass", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/web/app/api/agents/[pubkey]/repos/route.ts b/web/app/api/agents/[pubkey]/repos/route.ts index 4e20d552..ab6523d8 100644 --- a/web/app/api/agents/[pubkey]/repos/route.ts +++ b/web/app/api/agents/[pubkey]/repos/route.ts @@ -2,6 +2,8 @@ import { NextRequest, NextResponse } from "next/server"; import { initializeDatabase } from "@/lib/db"; import { getErrorMessage } from "@/lib/errors"; import { PRIVATE_NO_STORE_CACHE_CONTROL } from "@/lib/cachePolicy"; +import { isValidChainAddress } from "@/lib/chainAddress"; +import { getConfiguredSolanaChainContext } from "@/lib/chains"; import type { AuthPayload } from "@/lib/authPayload"; import { createConnectedRepo, @@ -30,6 +32,17 @@ export async function GET( ) { try { const { pubkey } = await params; + if ( + !isValidChainAddress({ + chainContext: getConfiguredSolanaChainContext(), + value: pubkey, + }) + ) { + return NextResponse.json( + { error: "Agent routes require a valid Solana address" }, + { status: 400 } + ); + } await initializeDatabase(); const repos = await listConnectedRepos(pubkey); return NextResponse.json(