Problem
The tRPC adapter classifies procedures purely by the root builder token (publicProcedure, authedProcedure, custom *Procedure). This misses the standard tRPC pattern of attaching auth via middleware: publicProcedure.use(isAuthed).mutation(...) is currently classified public — a direct misclassification, not just missing evidence. Additionally, mergeRouters() composition is unresolved, ctx-based guards inside handlers are undetected, and tRPC procedures are not linked to reachable data mutations at all (docs/CAPABILITIES.md: "tRPC and GraphQL routes are inventoried with their declared protection but are not yet linked to reachable data mutations").
Current behavior
TrpcAdapter (crates/authmap-adapters/src/lib.rs) extracts the procedure root name via trpc_custom_procedure_token and the terminal operation (query/mutation/subscription); the .use(...) links in the chain are ignored.
- No handling for
mergeRouters(a, b) / t.mergeRouters(...); no handler-body ctx.user / TRPCError guard detection; mutation linking skips tRPC handlers entirely.
Proposed implementation
.use() middleware chains (highest impact — fixes an active misclassification)
- Walk the full member-call chain between the root token and the terminal operation; collect
.use(<symbol>) arguments and classify each symbol against evidence rules (an isAuthed/enforceUserIsAdmin middleware defined with t.middleware(...) in the scanned tree can be classified by inspecting its body for ctx.session/ctx.user checks + TRPCError({ code: 'UNAUTHORIZED' })).
- Also resolve derived builders:
const protectedProcedure = t.procedure.use(isAuthed) should make every protectedProcedure.* guarded — resolve the root token to its declaration and inherit its chain evidence.
mergeRouters()
- Treat
mergeRouters(r1, r2) / router.merge(...) as flattening: procedures keep their own paths/evidence but appear under the merged router's mount path when composed into the app router. Unresolvable arguments → diagnostic (trpc_unresolved_merge).
ctx-based guards in handler bodies
- Detect
if (!ctx.session /* ctx.user, ctx.auth */) throw new TRPCError(...) shapes inside query/mutation callbacks; emit lower-confidence authn_only (or role/permission per matched property) evidence with the check site cited.
- Route→mutation linking
- tRPC handler callbacks are ordinary functions; feed them through the existing
link_route_handler_mutations machinery (crates/authmap-analysis/src/lib.rs) so Prisma/TypeORM/etc. mutations inside procedures produce links, unlocking the sensitive-operation and tenant analyses for tRPC apps.
Acceptance criteria
References
docs/PARSERS_AND_ADAPTERS.md tRPC limitations paragraph; docs/CAPABILITIES.md linking gap
crates/authmap-adapters/src/lib.rs (TrpcAdapter, trpc_custom_procedure_token)
crates/authmap-analysis/src/lib.rs (link_route_handler_mutations)
Problem
The tRPC adapter classifies procedures purely by the root builder token (
publicProcedure,authedProcedure, custom*Procedure). This misses the standard tRPC pattern of attaching auth via middleware:publicProcedure.use(isAuthed).mutation(...)is currently classified public — a direct misclassification, not just missing evidence. Additionally,mergeRouters()composition is unresolved,ctx-based guards inside handlers are undetected, and tRPC procedures are not linked to reachable data mutations at all (docs/CAPABILITIES.md: "tRPC and GraphQL routes are inventoried with their declared protection but are not yet linked to reachable data mutations").Current behavior
TrpcAdapter(crates/authmap-adapters/src/lib.rs) extracts the procedure root name viatrpc_custom_procedure_tokenand the terminal operation (query/mutation/subscription); the.use(...)links in the chain are ignored.mergeRouters(a, b)/t.mergeRouters(...); no handler-bodyctx.user/TRPCErrorguard detection; mutation linking skips tRPC handlers entirely.Proposed implementation
.use()middleware chains (highest impact — fixes an active misclassification).use(<symbol>)arguments and classify each symbol against evidence rules (anisAuthed/enforceUserIsAdminmiddleware defined witht.middleware(...)in the scanned tree can be classified by inspecting its body forctx.session/ctx.userchecks +TRPCError({ code: 'UNAUTHORIZED' })).const protectedProcedure = t.procedure.use(isAuthed)should make everyprotectedProcedure.*guarded — resolve the root token to its declaration and inherit its chain evidence.mergeRouters()mergeRouters(r1, r2)/router.merge(...)as flattening: procedures keep their own paths/evidence but appear under the merged router's mount path when composed into the app router. Unresolvable arguments → diagnostic (trpc_unresolved_merge).ctx-based guards in handler bodiesif (!ctx.session /* ctx.user, ctx.auth */) throw new TRPCError(...)shapes insidequery/mutationcallbacks; emit lower-confidenceauthn_only(or role/permission per matched property) evidence with the check site cited.link_route_handler_mutationsmachinery (crates/authmap-analysis/src/lib.rs) so Prisma/TypeORM/etc. mutations inside procedures produce links, unlocking the sensitive-operation and tenant analyses for tRPC apps.Acceptance criteria
publicProcedure.use(isAuthed).mutation(...)classifies as guarded, not public (regression-style fixture).const protectedProcedure = t.procedure.use(isAuthed)) propagates evidence to all its procedures.ctx.sessionguard in a handler body yields lower-confidence evidence with cited span.docs/CAPABILITIES.mdlinking caveat updated.docs/PARSERS_AND_ADAPTERS.mdtRPC Not yet list updated; CHANGELOG entry.References
docs/PARSERS_AND_ADAPTERS.mdtRPC limitations paragraph;docs/CAPABILITIES.mdlinking gapcrates/authmap-adapters/src/lib.rs(TrpcAdapter,trpc_custom_procedure_token)crates/authmap-analysis/src/lib.rs(link_route_handler_mutations)