CodeVetter · Repo Unpack · Risk map
trpc/trpc
The framework does not authenticate requests; authorization is a user middleware. The risky defaults are unlimited Node request bodies, batching on with no max batch, stack traces in any non-production NODE_ENV, and transformer.deserialize running on attacker-controlled JSON. Several high-churn areas (Next app dir, the upgrade CLI, the SSE chat example) are outside the coverage gate or marked continue-on-error. WebSocket and HTTP stacks duplicate procedure invocation, so adapter bugs do not all flow through resolveResponse.
- getErrorShape copies error.stack into the client-visible error when config.isDev is true. isDev defaults to process.env.NODE_ENV !== production, so any deployment that forgets NODE_ENV=production leaks stacks. The Prisma and SSE examples' errorFormatters return the shape unchanged.
- nodeHTTPRequestHandler passes maxBodySize: opts.maxBodySize ?? null. incomingMessageToRequest only rejects PAYLOAD_TOO_LARGE when maxBodySize is truthy and size exceeds it, so the default null means no body cap. The check also treats 0 as unlimited because it uses !opts.maxBodySize.
- Batching is on unless the adapter sets allowBatching or batching.enabled to false. maxBatchSize is optional; jsonContentTypeHandler checks it only when it is a number. httpBatchLink defaults maxURLLength and maxItems to Infinity, so a client can build arbitrarily large batched URLs or bodies unless the app opts in.
- GET query inputs are a raw JSON string in the input search param, then JSON.parse, then transformer.input.deserialize. Mutations cannot use GET: TYPE_ACCEPTED_METHOD_MAP and the method-override map both list mutation as POST only, with an explicit comment that GET must never perform a mutation.
- resolveResponse returns 204 for every HEAD with no router or auth check, documented as a Lambda warmup. Subscriptions cannot be batched. A single-call JSON response that is an async iterable or contains promises/async iterables throws UNSUPPORTED_MEDIA_TYPE and tells the client to use httpBatchStreamLink.
- There is no auth inside @trpc/server. The SSE example's authedProcedure only checks that session.user.name is non-empty. post.onAdd and post.infinite are publicProcedure, so channel messages are readable without that check. When GitHub credentials are absent outside production, auth.tsx installs a Credentials provider that authorizes any name.
- CI e2e sets NEXTAUTH_SECRET to the literal supersecret and starts Postgres with trust auth. That is test-only, but the workflow file is the template people copy. SECURITY.md supports versions greater than 9.x and says to report vulnerabilities by Twitter DM to alexdotjs or email alex@kattcorp.com.
- eval() appears only in tests that round-trip a devalue/uneval transformer (packages/tests/server/transformer.test.ts, httpSubscriptionLink.test.ts, and packages/next/test/issue-4130-ssr-different-transformers.test.tsx). It is not on the request path. The production risk is user-supplied transformers: deserialize runs on request JSON, and next-sse-chat uses superjson.
- @trpc/upgrade shells out: src/lib/execa.ts imports node:child_process exec, the CLI calls assertCleanGitTree unless --force, and it can installPackage/uninstallPackage. OpenAPI tests call execFileSync. Standalone HTTP/2 tests import child_process. These are maintainer and test tools, not the RPC runtime.
- The e2e job is continue-on-error: true, and next-sse-chat (the Drizzle plus auth plus SSE example) is commented out of the matrix. App-dir sources are excluded from coverage and from codecov ignore paths. A green unit job does not prove the example apps or the experimental Next app-dir caller.
- Client and server both depend on the unstable-core-do-not-import export for proxies, callProcedure, and transformResult. A breaking change there breaks @trpc/client even if the public index stays stable. Several v12 removals are already marked: inferRouterClient, header objects that are not Headers, experimental_lazy, and the sse() alias.
- Adapter duplication is a blast-radius hotspot. Express and Next pages share nodeHTTPRequestHandler, but WebSockets call callTRPCProcedure directly and Fastify has its own fastifyRequestHandler. A procedure-lookup bug fixed only in resolveResponse will not automatically apply to applyWSSHandler.