Skip to content

Commit c72b962

Browse files
authored
feat(server): renamed to StrictGetMethodPlugin and enabled it by default in RPCHandler (#348)
GetMethodGuardPlugin -> StrictGetMethodPlugin <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Documentation** - Updated sidebar navigation and guides to reflect the shift to a stricter GET method enforcement in RPC documentation. - Added clarifications regarding the default behavior of the RPCHandler and the necessity for explicit permissions for GET requests. - **Enhancements** - Improved RPC security defaults by enforcing strict GET request handling, with a new option to disable this behavior if needed. - **Tests** - Added test cases to ensure that the new default configuration is correctly initialized and applied. - Modified existing tests to incorporate the new configuration option for the RPCHandler. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 3ee2e95 commit c72b962

File tree

17 files changed

+141
-45
lines changed

17 files changed

+141
-45
lines changed

apps/content/.vitepress/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,10 @@ export default defineConfig({
110110
{ text: 'CORS', link: '/docs/plugins/cors' },
111111
{ text: 'Response Headers', link: '/docs/plugins/response-headers' },
112112
{ text: 'Batch Request/Response', link: '/docs/plugins/batch-request-response' },
113-
{ text: 'GET method guard', link: '/docs/plugins/get-method-guard' },
114113
{ text: 'Client Retry', link: '/docs/plugins/client-retry' },
115114
{ text: 'Body Limit', link: '/docs/plugins/body-limit' },
116115
{ text: 'Simple CSRF Protection', link: '/docs/plugins/simple-csrf-protection' },
116+
{ text: 'Strict GET method', link: '/docs/plugins/strict-get-method' },
117117
],
118118
},
119119
{

apps/content/docs/advanced/rpc-protocol.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const router = {
3030
Any HTTP method can be used. Input can be provided via URL query parameters or the request body.
3131

3232
:::info
33-
To help prevent [Cross-Site Request Forgery (CSRF)](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/CSRF_prevention) attacks, you can use the [GET Method Guard Plugin](/docs/plugins/get-method-guard) to restrict the use of the `GET` method.
33+
You can use any method, but by default, [RPCHandler](/docs/rpc-handler) enabled [StrictGetMethodPlugin](/docs/rpc-handler#default-plugins) which blocks GET requests except for procedures explicitly allowed.
3434
:::
3535

3636
### Input in URL Query

apps/content/docs/client/rpc-link.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ If a property in `ClientContext` is required, oRPC enforces its inclusion when c
6262

6363
By default, RPCLink sends requests via `POST`. You can override this to use methods like `GET` (for browser or CDN caching) based on your requirements.
6464

65+
::: warning
66+
By default, [RPCHandler](/docs/rpc-handler) enabled [StrictGetMethodPlugin](/docs/rpc-handler#default-plugins) which blocks GET requests except for procedures explicitly allowed. please refer to [StrictGetMethodPlugin](/docs/plugins/strict-get-method) for more details.
67+
:::
68+
6569
```ts twoslash
6670
import { RPCLink } from '@orpc/client/fetch'
6771

apps/content/docs/plugins/get-method-guard.md renamed to apps/content/docs/plugins/strict-get-method.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
---
2-
title: GET Method Guard Plugin
3-
description: Enhance security by restricting GET requests to explicitly allowed procedures, mitigating Cross-Site Request Forgery (CSRF) risks.
2+
title: Strict GET Method Plugin
3+
description: Enhance security by ensuring only procedures explicitly marked to accept `GET` requests can be called using the HTTP `GET` method for RPC Protocol. This helps prevent certain types of Cross-Site Request Forgery (CSRF) attacks.
44
---
55

6-
# GET Method Guard Plugin
6+
# Strict GET Method Plugin
77

88
This plugin enhances security by ensuring only procedures explicitly marked to accept `GET` requests can be called using the HTTP `GET` method for [RPC Protocol](/docs/advanced/rpc-protocol). This helps prevent certain types of [Cross-Site Request Forgery (CSRF)](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/CSRF_prevention) attacks.
99

10+
::: info
11+
[RPCHandler](/docs/rpc-handler) enabled this plugin by default.
12+
:::
13+
1014
## When to Use
1115

1216
This plugin is beneficial if your application stores sensitive data (like session or auth tokens) in Cookie storage using `SameSite=Lax` (the default) or `SameSite=None`.
@@ -29,11 +33,11 @@ const ping = os
2933
import { RPCHandler } from '@orpc/server/fetch'
3034
import { router } from './shared/planet'
3135
// ---cut---
32-
import { GetMethodGuardPlugin } from '@orpc/server/plugins'
36+
import { StrictGetMethodPlugin } from '@orpc/server/plugins'
3337

3438
const handler = new RPCHandler(router, {
3539
plugins: [
36-
new GetMethodGuardPlugin()
40+
new StrictGetMethodPlugin()
3741
],
3842
})
3943
```

apps/content/docs/rpc-handler.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,9 @@ const handler = new RPCHandler(router, {
8383
eventIteratorKeepAliveComment: '',
8484
})
8585
```
86+
87+
## Default Plugins
88+
89+
RPCHandler is pre-configured with plugins that help enforce best practices and enhance security out of the box. By default, the following plugin is enabled:
90+
91+
- [StrictGetMethodPlugin](/docs/plugins/strict-get-method) - Disable by setting `strictGetMethodPluginEnabled` to `false`.

packages/client/src/adapters/fetch/rpc-link.test.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ describe.each(supportedDataTypes)('rpcLink: $name', ({ value, expected }) => {
1313
async function assertSuccessCase(value: unknown, expected: unknown): Promise<true> {
1414
const handler = vi.fn(({ input }) => input)
1515

16-
const rpcHandler = new RPCHandler(os.handler(handler))
16+
const rpcHandler = new RPCHandler(os.handler(handler), {
17+
strictGetMethodPluginEnabled: false,
18+
})
1719

1820
const rpcLink = new RPCLink({
1921
url: 'http://api.example.com',
@@ -45,7 +47,9 @@ describe.each(supportedDataTypes)('rpcLink: $name', ({ value, expected }) => {
4547
})
4648
})
4749

48-
const rpcHandler = new RPCHandler(os.handler(handler))
50+
const rpcHandler = new RPCHandler(os.handler(handler), {
51+
strictGetMethodPluginEnabled: false,
52+
})
4953

5054
const rpcLink = new RPCLink({
5155
url: 'http://api.example.com',

packages/server/src/adapters/fetch/body-limit-plugin.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ describe('bodyLimitPlugin', () => {
77

88
it('should ignore for non-body request', async () => {
99
const handler = new RPCHandler(os.handler(() => 'ping'), {
10+
strictGetMethodPluginEnabled: false,
1011
plugins: [
1112
new BodyLimitPlugin({ maxBodySize: 22 }),
1213
],

packages/server/src/adapters/fetch/rpc-handler.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { RPCHandler } from './rpc-handler'
33

44
describe('rpcHandler', () => {
55
it('works', async () => {
6-
const handler = new RPCHandler(os.handler(() => 'pong'))
6+
const handler = new RPCHandler(os.handler(() => 'pong'), {
7+
strictGetMethodPluginEnabled: false,
8+
})
79

810
const { response } = await handler.handle(new Request('https://example.com/api/v1/?data=%7B%7D'), {
911
prefix: '/api/v1',
Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
11
import type { Context } from '../../context'
22
import type { Router } from '../../router'
3-
import type { StandardRPCHandlerOptions } from '../standard'
43
import type { FetchHandlerOptions } from './handler'
5-
import { StandardRPCJsonSerializer, StandardRPCSerializer } from '@orpc/client/standard'
6-
import { StandardHandler, StandardRPCCodec, StandardRPCMatcher } from '../standard'
4+
import { StandardRPCHandler, type StandardRPCHandlerOptions } from '../standard'
75
import { FetchHandler } from './handler'
86

97
export class RPCHandler<T extends Context> extends FetchHandler<T> {
108
constructor(router: Router<any, T>, options: NoInfer<FetchHandlerOptions<T> & StandardRPCHandlerOptions<T>> = {}) {
11-
const jsonSerializer = new StandardRPCJsonSerializer(options)
12-
const serializer = new StandardRPCSerializer(jsonSerializer)
13-
const matcher = new StandardRPCMatcher()
14-
const codec = new StandardRPCCodec(serializer)
15-
16-
super(new StandardHandler(router, matcher, codec, options), options)
9+
super(new StandardRPCHandler(router, options), options)
1710
}
1811
}

packages/server/src/adapters/node/rpc-handler.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import { RPCHandler } from './rpc-handler'
55

66
describe('rpcHandler', () => {
77
it('works', async () => {
8-
const handler = new RPCHandler(os.handler(() => 'pong'))
8+
const handler = new RPCHandler(os.handler(() => 'pong'), {
9+
strictGetMethodPluginEnabled: false,
10+
})
911

1012
const res = await request(async (req: IncomingMessage, res: ServerResponse) => {
1113
await handler.handle(req, res)

0 commit comments

Comments
 (0)