Skip to content

tRPC: analyze .use() middleware chains, merged routers, ctx guards, and link procedures to mutations #111

Description

@bjcorder

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

  1. .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.
  2. 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).
  3. 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.
  4. 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

  • publicProcedure.use(isAuthed).mutation(...) classifies as guarded, not public (regression-style fixture).
  • Derived builder (const protectedProcedure = t.procedure.use(isAuthed)) propagates evidence to all its procedures.
  • Merged-router fixture inventories all procedures with correct composed paths.
  • ctx.session guard in a handler body yields lower-confidence evidence with cited span.
  • tRPC procedures with Prisma mutations produce route→mutation links; docs/CAPABILITIES.md linking caveat updated.
  • Unit tests + golden fixtures; docs/PARSERS_AND_ADAPTERS.md tRPC Not yet list updated; CHANGELOG entry.

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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:adapterFramework adapter workarea:linkingReachability and route-to-operation linkingdetectionAnalyzer detection behavior or pattern coverageenhancementNew feature or requestframework:trpctRPC adapterrustRust implementation work

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions