Skip to content

fix(nest): run method-level guards on routes synthesized by router-contract @Implement - #1775

Merged
dinwwwh merged 1 commit into
1.xfrom
claude/implement-guards-bypass-3fc3ef
Aug 4, 2026
Merged

fix(nest): run method-level guards on routes synthesized by router-contract @Implement#1775
dinwwwh merged 1 commit into
1.xfrom
claude/implement-guards-bypass-3fc3ef

Conversation

@dinwwwh

@dinwwwh dinwwwh commented Aug 4, 2026

Copy link
Copy Markdown
Member

Method-level @UseGuards (and @UsePipes, @UseFilters, @SetMetadata) on a handler decorated with a router-contract @Implement were silently ignored: Nest stores that metadata on the decorated function object, but @Implement registers freshly synthesized functions as the actual routes, so the guards never ran. Routes that looked protected were publicly reachable. Each synthesized route function now inherits from the original method via its prototype chain, so Nest resolves the metadata from the registered callback in either decorator order.

Fixes

  • Method-level guards, pipes, filters, and @SetMetadata now execute on every route synthesized from a router contract, whether the decorator is placed above or below @Implement. An eager metadata copy could not achieve this: decorators evaluate bottom-up, so anything written above @Implement attaches its metadata after @Implement has already run.
  • User-supplied method-level @UseInterceptors is merged ahead of oRPC's own interceptor when placed below @Implement. Placing it above still loses it — an inherent limit of Nest's array-metadata scheme, since the synthesized route must define its own interceptor entry.
  • Single-procedure @Implement, class-level enhancers, and global providers were unaffected and remain so.

Testing

  • New end-to-end tests boot a Nest app with a CanActivate guard on the router method in both decorator orderings: denied requests get 403 with the procedure handler never invoked, allowed requests succeed. All 4 new tests fail against the previous code (unauthenticated requests returned 200).
  • Full @orpc/nest suite passes (29 tests).

@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
orpc Skipped Skipped Aug 4, 2026 2:15am

@pkg-pr-new

pkg-pr-new Bot commented Aug 4, 2026

Copy link
Copy Markdown
More templates

@orpc/ai-sdk

npm i https://pkg.pr.new/@orpc/ai-sdk@1775

@orpc/arktype

npm i https://pkg.pr.new/@orpc/arktype@1775

@orpc/client

npm i https://pkg.pr.new/@orpc/client@1775

@orpc/contract

npm i https://pkg.pr.new/@orpc/contract@1775

@orpc/experimental-durable-iterator

npm i https://pkg.pr.new/@orpc/experimental-durable-iterator@1775

@orpc/hey-api

npm i https://pkg.pr.new/@orpc/hey-api@1775

@orpc/interop

npm i https://pkg.pr.new/@orpc/interop@1775

@orpc/json-schema

npm i https://pkg.pr.new/@orpc/json-schema@1775

@orpc/nest

npm i https://pkg.pr.new/@orpc/nest@1775

@orpc/openapi

npm i https://pkg.pr.new/@orpc/openapi@1775

@orpc/openapi-client

npm i https://pkg.pr.new/@orpc/openapi-client@1775

@orpc/otel

npm i https://pkg.pr.new/@orpc/otel@1775

@orpc/experimental-pino

npm i https://pkg.pr.new/@orpc/experimental-pino@1775

@orpc/experimental-publisher

npm i https://pkg.pr.new/@orpc/experimental-publisher@1775

@orpc/experimental-publisher-durable-object

npm i https://pkg.pr.new/@orpc/experimental-publisher-durable-object@1775

@orpc/experimental-ratelimit

npm i https://pkg.pr.new/@orpc/experimental-ratelimit@1775

@orpc/react

npm i https://pkg.pr.new/@orpc/react@1775

@orpc/react-query

npm i https://pkg.pr.new/@orpc/react-query@1775

@orpc/experimental-react-swr

npm i https://pkg.pr.new/@orpc/experimental-react-swr@1775

@orpc/server

npm i https://pkg.pr.new/@orpc/server@1775

@orpc/shared

npm i https://pkg.pr.new/@orpc/shared@1775

@orpc/solid-query

npm i https://pkg.pr.new/@orpc/solid-query@1775

@orpc/standard-server

npm i https://pkg.pr.new/@orpc/standard-server@1775

@orpc/standard-server-aws-lambda

npm i https://pkg.pr.new/@orpc/standard-server-aws-lambda@1775

@orpc/standard-server-fastify

npm i https://pkg.pr.new/@orpc/standard-server-fastify@1775

@orpc/standard-server-fetch

npm i https://pkg.pr.new/@orpc/standard-server-fetch@1775

@orpc/standard-server-node

npm i https://pkg.pr.new/@orpc/standard-server-node@1775

@orpc/standard-server-peer

npm i https://pkg.pr.new/@orpc/standard-server-peer@1775

@orpc/svelte-query

npm i https://pkg.pr.new/@orpc/svelte-query@1775

@orpc/tanstack-query

npm i https://pkg.pr.new/@orpc/tanstack-query@1775

@orpc/trpc

npm i https://pkg.pr.new/@orpc/trpc@1775

@orpc/valibot

npm i https://pkg.pr.new/@orpc/valibot@1775

@orpc/vue-colada

npm i https://pkg.pr.new/@orpc/vue-colada@1775

@orpc/vue-query

npm i https://pkg.pr.new/@orpc/vue-query@1775

@orpc/zod

npm i https://pkg.pr.new/@orpc/zod@1775

commit: a90bf7f

@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

  • packages/nest/src/implement.tsObject.setPrototypeOf(target[methodName], descriptor.value!) makes each synthesized router-route function inherit from the original handler, so Nest's prototype-walking Reflector resolves method-level metadata (@UseGuards, @UsePipes, @UseFilters, @SetMetadata) in either decorator order.
  • packages/nest/src/implement.test.ts — 4 new e2e tests booting a Nest app with a CanActivate guard, covering @UseGuards above and below @Implement, asserting deny→403 (handler never invoked) and allow→200.

The fix is well-targeted: decorators evaluate bottom-up, so metadata written above @Implement lands on descriptor.value only after the existing eager own-metadata copy loop has run — meaning a copied approach literally cannot work for the above case, and prototype inheritance is the right mechanism. I verified the new tests genuinely fail against the previous code (unauthenticated requests returned 200 and the guard never ran) and pass with the fix, across both decorator orderings.

The documented @UseInterceptors limitation (a user interceptor placed above @Implement is shadowed by oRPC's own ImplementInterceptor own-metadata) is inherent to Nest's array-metadata merge and accurately described.

Pullfrog  | View workflow run | Using openrouter/deepseek/deepseek-v4-flash-0731 (free via Pullfrog for OSS) | 𝕏

@pullfrog

pullfrog Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Run failed. View the logs →

Pullfrog  | Rerun failed job ➔View workflow run | via Pullfrog | Using openrouter/deepseek/deepseek-v4-flash-0731 (free via Pullfrog for OSS) | 𝕏

@dinwwwh
dinwwwh force-pushed the claude/implement-guards-bypass-3fc3ef branch from a50be84 to d728c37 Compare August 4, 2026 02:36
@dinwwwh
dinwwwh merged commit 2b81d89 into 1.x Aug 4, 2026
9 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant