fastify 6.0.0-alpha.4 → 6.0.0-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/SECURITY.md CHANGED
@@ -30,6 +30,12 @@ The following are **not** considered vulnerabilities in Fastify:
30
30
 
31
31
  - **Application code vulnerabilities**: XSS, SQL injection, or other flaws in
32
32
  user-written route handlers, hooks, or plugins
33
+ - **Unvalidated template selection**: Passing untrusted input directly as a
34
+ template name or path (for example, `reply.view(request.query.page)`) without
35
+ validating or allowlisting it is an application-level vulnerability. A template
36
+ root used to resolve filenames is not a security sandbox unless the API
37
+ explicitly guarantees path containment. Bypasses of documented containment
38
+ or built-in path validation remain in scope.
33
39
  - **Malicious application code**: Issues caused by intentionally malicious
34
40
  plugins or handlers (application code is trusted)
35
41
  - **Validation schema issues**: Weak or incorrect schemas provided by developers
@@ -172,8 +172,6 @@ section.
172
172
  A plugin to help with caching API responses using AWS DynamoDB.
173
173
  - [`@clerk/fastify`](https://github.com/clerk/javascript/tree/main/packages/fastify)
174
174
  Add authentication and user management to your Fastify application with Clerk.
175
- - [`@coobaha/typed-fastify`](https://github.com/Coobaha/typed-fastify) Strongly
176
- typed routes with a runtime validation using JSON schema generated from types.
177
175
  - [`@dnlup/fastify-doc`](https://github.com/dnlup/fastify-doc) A plugin for
178
176
  sampling process metrics.
179
177
  - [`@dnlup/fastify-traps`](https://github.com/dnlup/fastify-traps) A plugin to
@@ -211,6 +209,9 @@ section.
211
209
  - [`@matths/fastify-svelte-view`](https://github.com/matths/fastify-svelte-view)
212
210
  A Fastify plugin for rendering Svelte components with support for SSR
213
211
  (Server-Side Rendering), CSR (Client-Side Rendering), and SSR with hydration.
212
+ - [`@melroy89/fastify-mariadb`](https://github.com/melroy89/fastify-mariadb)
213
+ Fastify MariaDB connection plugin that shares a connection pool across the
214
+ server.
214
215
  - [`@mgcrea/fastify-graceful-exit`](https://github.com/mgcrea/fastify-graceful-exit)
215
216
  A plugin to close the server gracefully
216
217
  - [`@mgcrea/fastify-request-logger`](https://github.com/mgcrea/fastify-request-logger)
@@ -240,6 +241,9 @@ section.
240
241
  method with a body).
241
242
  - [`@trubavuong/fastify-seaweedfs`](https://github.com/trubavuong/fastify-seaweedfs)
242
243
  SeaweedFS for Fastify
244
+ - [`@validex/fastify`](https://github.com/chiptoma/validex/tree/main/packages/fastify)
245
+ Zod 4 validation plugin with 25 typed rules, structured error codes, and
246
+ `request.validate()` decorator.
243
247
  - [`@yeliex/fastify-problem-details`](https://github.com/yeliex/fastify-problem-details)
244
248
  RFC 9457 Problem Details implementation for Fastify, with typed HTTP errors.
245
249
  - [`@zrosenbauer/fastify-prisma`](https://github.com/zrosenbauer/fastify-prisma)
@@ -249,8 +253,6 @@ section.
249
253
  logging and monitoring tool.
250
254
  - [`arecibo`](https://github.com/ducktors/arecibo) Fastify ping responder for
251
255
  Kubernetes Liveness and Readiness Probes.
252
- - [`aws-xray-sdk-fastify`](https://github.com/aws/aws-xray-sdk-node/tree/master/sdk_contrib/fastify)
253
- A Fastify plugin to log requests and subsegments through AWSXray.
254
256
  - [`cls-rtracer`](https://github.com/puzpuzpuz/cls-rtracer) Fastify middleware
255
257
  for CLS-based request ID generation. An out-of-the-box solution for adding
256
258
  request IDs into your logs.
@@ -274,14 +276,8 @@ section.
274
276
  - [`fastify-amqp-async`](https://github.com/kffl/fastify-amqp-async) Fastify
275
277
  AMQP plugin with a Promise-based API provided by
276
278
  [`amqplib-as-promised`](https://github.com/twawszczak/amqplib-as-promised).
277
- - [`fastify-angular-universal`](https://github.com/exequiel09/fastify-angular-universal)
278
- Angular server-side rendering support using
279
- [`@angular/platform-server`](https://github.com/angular/angular/tree/master/packages/platform-server)
280
- for Fastify
281
279
  - [`fastify-api-key`](https://github.com/arkerone/fastify-api-key) Fastify
282
280
  plugin to authenticate HTTP requests based on API key and signature
283
- - [`fastify-appwrite`](https://github.com/maniecodes/fastify-appwrite) Fastify
284
- Plugin for interacting with Appwrite server.
285
281
  - [`fastify-arktype`](https://github.com/BlairCurrey/fastify-arktype) ArkType
286
282
  type provider with validation and @fastify/swagger OpenAPI generation.
287
283
  - [`fastify-asyncforge`](https://github.com/mcollina/fastify-asyncforge) Plugin
@@ -298,15 +294,9 @@ section.
298
294
  Auth0 verification plugin for Fastify, internally uses
299
295
  [fastify-jwt](https://www.npmjs.com/package/fastify-jwt) and
300
296
  [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken).
301
- - [`fastify-autoroutes`](https://github.com/GiovanniCardamone/fastify-autoroutes)
302
- Plugin to scan and load routes based on filesystem path from a custom
303
- directory.
304
297
  - [`fastify-aws-sns`](https://github.com/gzileni/fastify-aws-sns) Fastify plugin
305
298
  for AWS Simple Notification Service (AWS SNS) that coordinates and manages
306
299
  the delivery or sending of messages to subscribing endpoints or clients.
307
- - [`fastify-aws-timestream`](https://github.com/gzileni/fastify-aws-timestream)
308
- Fastify plugin for managing databases, tables, and querying and creating
309
- scheduled queries with AWS Timestream.
310
300
  - [`fastify-axios`](https://github.com/davidedantonio/fastify-axios) Plugin to
311
301
  send HTTP requests via [axios](https://github.com/axios/axios).
312
302
  - [`fastify-babel`](https://github.com/cfware/fastify-babel) Fastify plugin for
@@ -341,9 +331,6 @@ section.
341
331
  Fastify plugin for CloudFlare Turnstile.
342
332
  - [`fastify-cloudinary`](https://github.com/Vanilla-IceCream/fastify-cloudinary)
343
333
  Plugin to share a common Cloudinary connection across Fastify.
344
- - [`fastify-cockroachdb`](https://github.com/alex-ppg/fastify-cockroachdb)
345
- Fastify plugin to connect to a CockroachDB PostgreSQL instance via the
346
- Sequelize ORM.
347
334
  - [`fastify-constraints`](https://github.com/nearform/fastify-constraints)
348
335
  Fastify plugin to add constraints to multiple routes
349
336
  - [`fastify-couchdb`](https://github.com/nigelhanlon/fastify-couchdb) Fastify
@@ -361,10 +348,6 @@ section.
361
348
  - [`fastify-disablecache`](https://github.com/Fdawgs/fastify-disablecache)
362
349
  Fastify plugin to disable client-side caching, inspired by
363
350
  [nocache](https://github.com/helmetjs/nocache).
364
- - [`fastify-dynamodb`](https://github.com/matrus2/fastify-dynamodb) AWS DynamoDB
365
- plugin for Fastify. It exposes
366
- [AWS.DynamoDB.DocumentClient()](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html)
367
- object.
368
351
  - [`fastify-dynareg`](https://github.com/greguz/fastify-dynareg) Dynamic plugin
369
352
  register for Fastify.
370
353
  - [`fastify-envalid`](https://github.com/alemagio/fastify-envalid) Fastify
@@ -372,9 +355,6 @@ section.
372
355
  project.
373
356
  - [`fastify-error-page`](https://github.com/hemerajs/fastify-error-page) Fastify
374
357
  plugin to print errors in structured HTML to the browser.
375
- - [`fastify-esso`](https://github.com/patrickpissurno/fastify-esso) The easiest
376
- authentication plugin for Fastify, with built-in support for Single sign-on
377
- (and great documentation).
378
358
  - [`fastify-event-bus`](https://github.com/Shiva127/fastify-event-bus) Event bus
379
359
  support for Fastify. Built upon [js-event-bus](https://github.com/bcerati/js-event-bus).
380
360
  - [`fastify-evervault`](https://github.com/Briscoooe/fastify-evervault/) Fastify
@@ -457,14 +437,10 @@ section.
457
437
  plugin to serialize JSON responses into XML.
458
438
  - [`fastify-jwt-authz`](https://github.com/Ethan-Arrowood/fastify-jwt-authz) JWT
459
439
  user scope verifier.
460
- - [`fastify-jwt-webapp`](https://github.com/charlesread/fastify-jwt-webapp) JWT
461
- authentication for Fastify-based web apps.
462
440
  - [`fastify-kafkajs`](https://github.com/kffl/fastify-kafkajs) Fastify plugin
463
441
  that adds support for KafkaJS - a modern Apache Kafka client library.
464
442
  - [`fastify-keycloak-adapter`](https://github.com/yubinTW/fastify-keycloak-adapter)
465
443
  A keycloak adapter for a Fastify app.
466
- - [`fastify-koa`](https://github.com/rozzilla/fastify-koa) Convert Koa
467
- middlewares into Fastify plugins
468
444
  - [`fastify-kubernetes`](https://github.com/greguz/fastify-kubernetes) Fastify
469
445
  Kubernetes client plugin.
470
446
  - [`fastify-kysely`](https://github.com/alenap93/fastify-kysely) Fastify
@@ -504,6 +480,8 @@ middlewares into Fastify plugins
504
480
  - [`fastify-mongoose-driver`](https://github.com/alex-ppg/fastify-mongoose)
505
481
  Fastify Mongoose plugin that connects to a MongoDB via the Mongoose plugin
506
482
  with support for Models.
483
+ - [`fastify-mosquitto-dynsec`](https://github.com/LuizTakeda/fastify-mosquitto-dynsec)
484
+ Fastify plugin to manage Mosquitto Dynamic Security over MQTT.
507
485
  - [`fastify-mqtt`](https://github.com/love-lena/fastify-mqtt) Plugin to share
508
486
  [mqtt](https://www.npmjs.com/package/mqtt) client across Fastify.
509
487
  - [`fastify-msgpack`](https://github.com/kenriortega/fastify-msgpack) Fastify
@@ -546,8 +524,6 @@ middlewares into Fastify plugins
546
524
  [`oracledb`](https://github.com/oracle/node-oracledb) connection pool to a
547
525
  Fastify server instance.
548
526
  - [`fastify-orama`](https://github.com/mateonunez/fastify-orama)
549
- - [`fastify-osm`](https://github.com/gzileni/fastify-osm) Fastify
550
- OSM plugin to run overpass queries by OpenStreetMap.
551
527
  - [`fastify-override`](https://github.com/matthyk/fastify-override)
552
528
  Fastify plugin to override decorators, plugins and hooks for testing purposes
553
529
  - [`fastify-param-schema-validation`](https://github.com/Player1205/fastify-param-schema-validation)
@@ -568,8 +544,6 @@ middlewares into Fastify plugins
568
544
  - [`fastify-postgraphile`](https://github.com/alemagio/fastify-postgraphile)
569
545
  Plugin to integrate [PostGraphile](https://postgraphile.org/postgraphile/4/) in
570
546
  a Fastify project.
571
- - [`fastify-postgres-dot-js`](https://github.com/kylerush/fastify-postgresjs) Fastify
572
- PostgreSQL connection plugin that uses [Postgres.js](https://github.com/porsager/postgres).
573
547
  - [`fastify-prettier`](https://github.com/hsynlms/fastify-prettier) A Fastify
574
548
  plugin that uses [prettier](https://github.com/prettier/prettier) under the
575
549
  hood to beautify outgoing responses and/or other things in the Fastify server.
@@ -617,8 +591,6 @@ middlewares into Fastify plugins
617
591
  using name and parameters.
618
592
  - [`fastify-rob-config`](https://github.com/jeromemacias/fastify-rob-config)
619
593
  Fastify Rob-Config integration.
620
- - [`fastify-route-group`](https://github.com/TakNePoidet/fastify-route-group)
621
- Convenient grouping and inheritance of routes.
622
594
  - [`fastify-route-preset`](https://github.com/inyourtime/fastify-route-preset)
623
595
  A Fastify plugin that enables you to create route configurations that can be
624
596
  applied to multiple routes.
@@ -626,9 +598,6 @@ middlewares into Fastify plugins
626
598
  Ensure the existence of defined S3 buckets on the application startup.
627
599
  - [`fastify-schema-constraint`](https://github.com/Eomm/fastify-schema-constraint)
628
600
  Choose the JSON schema to use based on request parameters.
629
- - [`fastify-schema-to-typescript`](https://github.com/thomasthiebaud/fastify-schema-to-typescript)
630
- Generate typescript types based on your JSON/YAML validation schemas so they
631
- are always in sync.
632
601
  - [`fastify-sentry`](https://github.com/alex-ppg/fastify-sentry) Fastify plugin
633
602
  to add the Sentry SDK error handler to requests.
634
603
  - [`fastify-sequelize`](https://github.com/lyquocnam/fastify-sequelize) Fastify
@@ -639,8 +608,6 @@ middlewares into Fastify plugins
639
608
  `fastify-caching`.
640
609
  - [`fastify-ses-mailer`](https://github.com/KaranHotwani/fastify-ses-mailer) A
641
610
  Fastify plugin for sending emails via AWS SES using AWS SDK v3.
642
- - [`fastify-shared-schema`](https://github.com/Adibla/fastify-shared-schema) Plugin
643
- for sharing schemas between different routes.
644
611
  - [`fastify-slow-down`](https://github.com/nearform/fastify-slow-down) A plugin
645
612
  to delay the response from the server.
646
613
  - [`fastify-split-validator`](https://github.com/MetCoder95/fastify-split-validator)
@@ -665,10 +632,6 @@ middlewares into Fastify plugins
665
632
  localhost-only, TLS certificate.
666
633
  - [`fastify-totp`](https://github.com/beliven-it/fastify-totp) A plugin to handle
667
634
  TOTP (e.g. for 2FA).
668
- - [`fastify-type-provider-effect-schema`](https://github.com/daotl/fastify-type-provider-effect-schema)
669
- Fastify
670
- [type provider](https://fastify.dev/docs/latest/Reference/Type-Providers/)
671
- for [@effect/schema](https://github.com/Effect-TS/effect).
672
635
  - [`fastify-type-provider-zod`](https://github.com/turkerdev/fastify-type-provider-zod)
673
636
  Fastify
674
637
  [type provider](https://fastify.dev/docs/latest/Reference/Type-Providers/)
@@ -679,10 +642,6 @@ middlewares into Fastify plugins
679
642
  request's `user-agent` header.
680
643
  - [`fastify-uws`](https://github.com/geut/fastify-uws) A Fastify plugin to
681
644
  use the web server [uWebSockets.js](https://github.com/uNetworking/uWebSockets.js).
682
- - [`fastify-vhost`](https://github.com/patrickpissurno/fastify-vhost) Proxy
683
- subdomain HTTP requests to another server (useful if you want to point
684
- multiple subdomains to the same IP address, while running different servers on
685
- the same machine).
686
645
  - [`fastify-vue-plugin`](https://github.com/TheNoim/fastify-vue)
687
646
  [Nuxt.js](https://nuxt.com) plugin for Fastify. Control the routes nuxt
688
647
  should use.
@@ -725,10 +684,6 @@ middlewares into Fastify plugins
725
684
 
726
685
  #### [Community Tools](#community-tools)
727
686
 
728
- - [`fast-maker`](https://github.com/imjuni/fast-maker) route configuration
729
- generator by directory structure.
730
- - [`fastify-flux`](https://github.com/Jnig/fastify-flux) Tool for building
731
- Fastify APIs using decorators and convert Typescript interface to JSON Schema.
732
687
  - [`fastify-intlayer`](https://intlayer.org/doc/environment/fastify)
733
688
  i18n solution for error handling, email template
734
689
  - [`jeasx`](https://www.jeasx.dev)
@@ -94,7 +94,6 @@
94
94
  - [FST_ERR_ROUTE_HANDLER_TIMEOUT_OPTION_NOT_INT](#fst_err_route_handler_timeout_option_not_int)
95
95
  - [FST_ERR_ROUTE_REWRITE_NOT_STR](#fst_err_route_rewrite_not_str)
96
96
  - [FST_ERR_ROUTE_MISSING_CONTENT_TYPE](#fst_err_route_missing_content_type)
97
- - [FST_ERR_ROUTE_MISSING_CONTENT](#fst_err_route_missing_content)
98
97
  - [FST_ERR_REOPENED_CLOSE_SERVER](#fst_err_reopened_close_server)
99
98
  - [FST_ERR_REOPENED_SERVER](#fst_err_reopened_server)
100
99
  - [FST_ERR_PLUGIN_VERSION_MISMATCH](#fst_err_plugin_version_mismatch)
@@ -447,7 +446,6 @@ Below is a table with all the error codes used by Fastify.
447
446
  | <a id="fst_err_route_handler_timeout_option_not_int">FST_ERR_ROUTE_HANDLER_TIMEOUT_OPTION_NOT_INT</a> | `handlerTimeout` option must be a positive integer. | Use a positive integer for the `handlerTimeout` option. | - |
448
447
  | <a id="fst_err_route_rewrite_not_str">FST_ERR_ROUTE_REWRITE_NOT_STR</a> | `rewriteUrl` needs to be of type `string`. | Use a string for the `rewriteUrl`. | [#4554](https://github.com/fastify/fastify/pull/4554) |
449
448
  | <a id="fst_err_route_missing_content_type">FST_ERR_ROUTE_MISSING_CONTENT_TYPE</a> | `Content-Type` header is required for the request. | Send request with `Content-Type` header. | [#6832](https://github.com/fastify/fastify/pull/6832) |
450
- | <a id="fst_err_route_missing_content">FST_ERR_ROUTE_MISSING_CONTENT</a> | Body is required for the request. | Send request with payload. | [#6832](https://github.com/fastify/fastify/pull/6832) |
451
449
  | <a id="fst_err_reopened_close_server">FST_ERR_REOPENED_CLOSE_SERVER</a> | Fastify has already been closed and cannot be reopened. | - | [#2415](https://github.com/fastify/fastify/pull/2415) |
452
450
  | <a id="fst_err_reopened_server">FST_ERR_REOPENED_SERVER</a> | Fastify is already started. | - | [#2415](https://github.com/fastify/fastify/pull/2415) |
453
451
  | <a id="fst_err_plugin_version_mismatch">FST_ERR_PLUGIN_VERSION_MISMATCH</a> | Installed Fastify plugin mismatched expected version. | Use a compatible version of the plugin. | [#2549](https://github.com/fastify/fastify/pull/2549) |
@@ -340,6 +340,23 @@ to `302` (if status code is not already set by calling `code`).
340
340
  > [`encodeurl`](https://www.npmjs.com/package/encodeurl). Invalid URLs will
341
341
  > result in a 500 `TypeError` response.
342
342
 
343
+ > ⚠️ Security:
344
+ > Encoding alone does **not** prevent open redirects. Neither `encodeURI`
345
+ > nor `encodeurl` escapes `/`, so a caller-supplied target such as
346
+ > `//evil.com` or `/\evil.com` survives encoding and is resolved by the
347
+ > browser as a new host (a protocol-relative, authority-opening
348
+ > reference). When `dest` can be influenced by request input
349
+ > (`req.query`, `req.body`, `req.params`, cookies, or headers), validate
350
+ > that the resolved target is same-origin or on an explicit allowlist
351
+ > instead of relying on encoding:
352
+ >
353
+ > ```js
354
+ > const target = new URL(dest, 'https://your-site.example')
355
+ > if (target.origin !== 'https://your-site.example') {
356
+ > return reply.code(400).send()
357
+ > }
358
+ > ```
359
+
343
360
  Example (no `reply.code()` call) sets status code to `302` and redirects to
344
361
  `/home`
345
362
  ```js
@@ -122,6 +122,14 @@ to headers on `not found` routes.
122
122
  > Schema validation may mutate the `request.headers` and
123
123
  > `request.raw.headers` objects, causing the headers to become empty.
124
124
 
125
+ > ℹ️ Note:
126
+ > The setter is additive, so it cannot be used to *remove* a header, and a
127
+ > `headers` schema validator's synchronous `{ value }` result is applied through
128
+ > this same setter. `request.headers` is therefore the incoming request's
129
+ > headers (plus any additive values), not a sanitized or authoritative view. If
130
+ > your application needs an allow-listed header subset, build and consume it
131
+ > explicitly from the validated data or from `request.raw.headers`.
132
+
125
133
  ```js
126
134
  fastify.post('/:params', options, function (request, reply) {
127
135
  console.log(request.body)
@@ -32,7 +32,7 @@ fastify.route(options)
32
32
  <a id="options"></a>
33
33
 
34
34
  * `method`: currently it supports `GET`, `HEAD`, `TRACE`, `DELETE`,
35
- `OPTIONS`, `PATCH`, `PUT` and `POST`. To accept more methods,
35
+ `OPTIONS`, `PATCH`, `PUT`, `POST` and `QUERY`. To accept more methods,
36
36
  the [`addHttpMethod`](./Server.md#addHttpMethod) must be used.
37
37
  It could also be an array of methods.
38
38
  * `url`: the path of the URL to match this route (alias: `path`).
@@ -41,7 +41,7 @@ fastify.route(options)
41
41
  [here](./Validation-and-Serialization.md) for more info.
42
42
 
43
43
  * `body`: validates the body of the request if it is a POST, PUT, PATCH,
44
- TRACE, SEARCH, PROPFIND, PROPPATCH or LOCK method.
44
+ TRACE, SEARCH, PROPFIND, PROPPATCH, LOCK or QUERY method.
45
45
  * `querystring` or `query`: validates the querystring. This can be a complete
46
46
  JSON Schema object, with the property `type` of `object` and `properties`
47
47
  object of parameters, or simply the values of what would be contained in the
@@ -198,6 +198,10 @@ The above route declaration is more *Hapi*-like, but if you prefer an
198
198
 
199
199
  `fastify.patch(path, [options], handler)`
200
200
 
201
+ `fastify.query(path, [options], handler)`
202
+
203
+ > The `QUERY` method (RFC 10008) requires a `Content-Type` header.
204
+
201
205
  Example:
202
206
  ```js
203
207
  const opts = {
@@ -213,7 +213,7 @@ ignored.
213
213
 
214
214
  Defines the maximum number of milliseconds for receiving the entire request from
215
215
  the client. See [`server.requestTimeout`
216
- property](https://nodejs.org/dist/latest/docs/api/http.html#servertimeout)
216
+ property](https://nodejs.org/dist/latest/docs/api/http.html#serverrequesttimeout)
217
217
  to understand the effect of this option.
218
218
 
219
219
  When `serverFactory` option is specified, this option is ignored.
@@ -1046,7 +1046,7 @@ be invoked.
1046
1046
 
1047
1047
  This can be useful especially if you have a regex-based route, protecting you
1048
1048
  against [ReDoS
1049
- attacks](https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS).
1049
+ attacks](https://community.owasp.org/attacks/Regular_expression_Denial_of_Service_-_ReDoS).
1050
1050
 
1051
1051
 
1052
1052
  ### `onBadUrl`
@@ -167,6 +167,22 @@ parameters at the top level.
167
167
  > ℹ For using the latest Ajv (v8), refer to the
168
168
  > [`schemaController`](./Server.md#schema-controller) section.
169
169
 
170
+ ##### Header name case
171
+
172
+ Node.js exposes request header names in lowercase. Fastify normalizes header
173
+ names in a `headers` schema to lowercase before compilation, so assertions
174
+ written in the canonical header-name case still match the request, for example
175
+ `required: ['X-Api-Key']`, `dependencies`, and a `propertyNames` deny-list that
176
+ uses `enum` or `const`.
177
+
178
+ Regular-expression constraints on header names are the exception. A
179
+ `patternProperties` key and a `propertyNames` `pattern` are regular expressions
180
+ matched against the already-lowercased header names, and Fastify cannot safely
181
+ rewrite a regular expression. Author them to match lowercase (for example
182
+ `^x-debug-.*$`, not `^X-Debug-.*$`) or make them case-insensitive. A pattern
183
+ written in canonical case will not match, and the assertion it guards will not
184
+ apply.
185
+
170
186
  Example:
171
187
  ```js
172
188
  const bodyJsonSchema = {
@@ -639,6 +655,23 @@ an application.
639
655
  > failure by rejecting (throwing), and it cannot coerce the request part through
640
656
  > a resolved `value`.
641
657
 
658
+ ##### `headers` and the `{ value }` result
659
+
660
+ For the `headers` part specifically, the synchronous success `value` is applied
661
+ through the additive [`request.headers`](./Request.md#headers) setter. That
662
+ setter *adds or overrides* headers on top of the raw request headers; it does
663
+ **not** replace them. Raw request headers remain separately available as
664
+ `request.raw.headers`, and any incoming header that is not present in the
665
+ returned `value` is still visible on `request.headers`.
666
+
667
+ Consequently, a synchronous custom `headers` validator cannot remove a header by
668
+ simply omitting it from `value`. The same applies to the default Ajv validator:
669
+ `request.headers` is the incoming request's headers (plus any additive values)
670
+ and is not a sanitized, authoritative view. If your application needs a
671
+ sanitized or allow-listed header view — for example to drive an authorization
672
+ decision — build and consume that view explicitly from the validated data rather
673
+ than re-reading `request.headers`.
674
+
642
675
  ##### .statusCode property
643
676
 
644
677
  All validation errors have a `.statusCode` property set to `400`, ensuring the
@@ -10,6 +10,7 @@
10
10
  - [Fastify Security Codes](#fastify-security-codes)
11
11
  - [FSTSEC001](#FSTSEC001)
12
12
  - [FSTSEC002](#FSTSEC002)
13
+ - [FSTSEC003](#FSTSEC003)
13
14
  - [Fastify Deprecation Codes](#fastify-deprecation-codes)
14
15
 
15
16
  ## Warnings
@@ -51,6 +52,7 @@ Disabling warnings is not recommended and may cause unexpected behavior.
51
52
  | ---- | ----------- | ------------ | ---------- |
52
53
  | <a id="FSTSEC001">FSTSEC001</a> | A `RegExp` Content-Type is used that may be vulnerable to a CORS bypass, because it does not anchor the essence MIME type. | Start the `RegExp` with `^` or include `;?` so the essence MIME type is detected correctly. | [#4450](https://github.com/fastify/fastify/pull/4450) |
53
54
  | <a id="FSTSEC002">FSTSEC002</a> | A headers schema references an external `$ref` (a schema registered with `addSchema`) that is not reached by header case normalization. Header names in the referenced schema keep their original case and do not match the lowercased request headers, so case-insensitive assertions such as `required` and `dependencies` may not apply. | Inline the header schema instead of referencing it with an external `$ref`. | - |
55
+ | <a id="FSTSEC003">FSTSEC003</a> | A headers schema uses a `$ref` inside `propertyNames` that is not reached by header case normalization. Header names held in the referenced schema keep their original case and do not match the lowercased request headers, so a case-insensitive deny-list or allow-list on header names may not apply. | Inline the `propertyNames` constraint instead of referencing it with a `$ref`. | - |
54
56
 
55
57
  ### Fastify Deprecation Codes
56
58
 
@@ -253,12 +253,12 @@ function rawBody (request, reply, options, parser, done) {
253
253
  }
254
254
 
255
255
  let receivedLength = 0
256
- let body = asString ? '' : []
256
+ const chunks = []
257
257
  const payload = request[kRequestPayloadStream] || request.raw
258
258
 
259
- if (asString) {
260
- payload.setEncoding('utf8')
261
- }
259
+ // Always read raw Buffers and decode once at the end: setEncoding() would
260
+ // allocate a StringDecoder per request and force a Buffer.byteLength()
261
+ // rescan of every decoded chunk just to enforce the byte limit.
262
262
 
263
263
  payload.on('data', onData)
264
264
  payload.on('end', onEnd)
@@ -266,7 +266,11 @@ function rawBody (request, reply, options, parser, done) {
266
266
  payload.resume()
267
267
 
268
268
  function onData (chunk) {
269
- receivedLength += asString ? Buffer.byteLength(chunk) : chunk.length
269
+ // A custom preParsing stream may still emit strings
270
+ if (typeof chunk === 'string') {
271
+ chunk = Buffer.from(chunk)
272
+ }
273
+ receivedLength += chunk.length
270
274
  const { receivedEncodedLength = 0 } = payload
271
275
  // The resulting body length must not exceed bodyLimit (see "zip bomb").
272
276
  // The case when encoded length is larger than received length is rather theoretical,
@@ -279,11 +283,7 @@ function rawBody (request, reply, options, parser, done) {
279
283
  return
280
284
  }
281
285
 
282
- if (asString) {
283
- body += chunk
284
- } else {
285
- body.push(chunk)
286
- }
286
+ chunks.push(chunk)
287
287
  }
288
288
 
289
289
  function onEnd (err) {
@@ -304,8 +304,17 @@ function rawBody (request, reply, options, parser, done) {
304
304
  return
305
305
  }
306
306
 
307
- if (!asString) {
308
- body = Buffer.concat(body)
307
+ let body
308
+ if (asString) {
309
+ // Decoding the whole body at once also keeps multi-byte characters
310
+ // split across chunks intact
311
+ body = chunks.length === 1
312
+ ? chunks[0].toString('utf8')
313
+ : Buffer.concat(chunks, receivedLength).toString('utf8')
314
+ } else {
315
+ // Always copy so the parser never receives a view over the shared
316
+ // socket read buffer
317
+ body = Buffer.concat(chunks, receivedLength)
309
318
  }
310
319
 
311
320
  const result = parser.fn(request, body, done)
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { LruMap: Lru } = require('toad-cache')
3
+ const { FifoMap: Fifo } = require('toad-cache')
4
4
 
5
5
  /**
6
6
  * keyValuePairsReg matches a single `parameter-name "=" parameter-value`
@@ -61,9 +61,9 @@ const subtypeNameReg = /^[\w!#$%&'*+.^`|~-]+\s*$/
61
61
 
62
62
  /**
63
63
  * Content-Type internal shared cache
64
- * @type {Lru<ContentType>}
64
+ * @type {Fifo<ContentType>}
65
65
  */
66
- const cache = new Lru(100)
66
+ const cache = new Fifo(100)
67
67
 
68
68
  /**
69
69
  * ContentType parses and represents the value of the content-type header.
@@ -82,7 +82,7 @@ class ContentType {
82
82
  /**
83
83
  * The shared cache of ContentType instances. The cache is used to avoid
84
84
  * creating multiple instances of ContentType for the same header value.
85
- * @type {Lru<ContentType>}
85
+ * @type {Fifo<ContentType>}
86
86
  */
87
87
  static get cache () { return cache }
88
88
 
package/lib/errors.js CHANGED
@@ -466,11 +466,6 @@ const codes = {
466
466
  "Method '%s' must provide a 'Content-Type' header.",
467
467
  400
468
468
  ),
469
- FST_ERR_ROUTE_MISSING_CONTENT: createError(
470
- 'FST_ERR_ROUTE_MISSING_CONTENT',
471
- "Method '%s' must provide a request body.",
472
- 400
473
- ),
474
469
 
475
470
  /**
476
471
  * again listen when close server
@@ -6,8 +6,7 @@ const { validate: validateSchema } = require('./validation')
6
6
  const { preValidationHookRunner, preHandlerHookRunner } = require('./hooks')
7
7
  const {
8
8
  FST_ERR_CTP_INVALID_MEDIA_TYPE,
9
- FST_ERR_ROUTE_MISSING_CONTENT_TYPE,
10
- FST_ERR_ROUTE_MISSING_CONTENT
9
+ FST_ERR_ROUTE_MISSING_CONTENT_TYPE
11
10
  } = require('./errors')
12
11
  const { setErrorStatusCode } = require('./error-status')
13
12
  const {
@@ -51,20 +50,15 @@ function handleRequest (err, request, reply) {
51
50
  reply.status(400).send(new FST_ERR_ROUTE_MISSING_CONTENT_TYPE(method))
52
51
  return
53
52
  }
54
-
55
- if (isEmptyBody(headers)) {
56
- // https://datatracker.ietf.org/doc/html/rfc10008#section-2
57
- // Servers MUST fail the request if the Content-Type request field
58
- // ([HTTP], Section 8.3) is missing or is inconsistent with the
59
- // request content.
60
- reply[kReplyIsError] = true
61
- reply.status(400).send(new FST_ERR_ROUTE_MISSING_CONTENT(method))
62
- return
63
- }
64
53
  }
65
54
 
66
55
  if (ctHeader === undefined) {
67
- if (isEmptyBody(headers)) {
56
+ const contentLength = headers['content-length']
57
+ const transferEncoding = headers['transfer-encoding']
58
+ const isEmptyBody = transferEncoding === undefined &&
59
+ (contentLength === undefined || contentLength === '0')
60
+
61
+ if (isEmptyBody) {
68
62
  // Request has no body to parse
69
63
  handler(request, reply)
70
64
  return
@@ -109,13 +103,6 @@ function handler (request, reply) {
109
103
  }
110
104
  }
111
105
 
112
- function isEmptyBody (headers) {
113
- const contentLength = headers['content-length']
114
- const transferEncoding = headers['transfer-encoding']
115
- return transferEncoding === undefined &&
116
- (contentLength === undefined || contentLength === '0')
117
- }
118
-
119
106
  function preValidationCallback (err, request, reply) {
120
107
  if (reply.sent === true) return
121
108
 
package/lib/reply.js CHANGED
@@ -95,7 +95,7 @@ Object.defineProperties(Reply.prototype, {
95
95
  },
96
96
  mediaType: {
97
97
  get () {
98
- const contentTypeHeader = this[kReplyHeaders]['content-type']
98
+ const contentTypeHeader = this.getHeader('content-type')
99
99
  return ContentType.from(contentTypeHeader).mediaType
100
100
  }
101
101
  },
@@ -284,6 +284,10 @@ Reply.prototype.header = function (key, value = '') {
284
284
  } else {
285
285
  this[kReplyHeaders][key].push(value)
286
286
  }
287
+ } else if (key === 'set-cookie' && Array.isArray(value)) {
288
+ // Copy the array: later calls push into the stored one, and the caller's
289
+ // array (often a module-level constant) must not grow across requests.
290
+ this[kReplyHeaders][key] = value.slice()
287
291
  } else {
288
292
  this[kReplyHeaders][key] = value
289
293
  }
@@ -370,7 +374,7 @@ Reply.prototype.getSerializationFunction = function (schemaOrStatus, contentType
370
374
  return serialize
371
375
  }
372
376
 
373
- Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null, contentType = null) {
377
+ Reply.prototype.compileSerializationSchema = function (schema, httpStatus, contentType) {
374
378
  const { request } = this
375
379
  const { method, url } = request
376
380
 
package/lib/request.js CHANGED
@@ -122,8 +122,10 @@ function buildRequestWithTrustProxy (R, trustProxy) {
122
122
  host: {
123
123
  get () {
124
124
  const socket = this.raw.socket
125
- if (this.headers['x-forwarded-host'] && socket != null && proxyFn(socket.remoteAddress, 0)) {
126
- return getLastEntryInMultiHeaderValue(this.headers['x-forwarded-host'])
125
+ // The headers getter may allocate a new object, so read it once.
126
+ const headers = this.headers
127
+ if (headers['x-forwarded-host'] && socket != null && proxyFn(socket.remoteAddress, 0)) {
128
+ return getLastEntryInMultiHeaderValue(headers['x-forwarded-host'])
127
129
  }
128
130
  /**
129
131
  * The last fallback supports the following cases:
@@ -131,14 +133,16 @@ function buildRequestWithTrustProxy (R, trustProxy) {
131
133
  * 2. HTTP/1.0 without a Host Header
132
134
  * 3. Headers schema that may remove the Host Header
133
135
  */
134
- return this.headers.host ?? this.headers[':authority'] ?? ''
136
+ return headers.host ?? headers[':authority'] ?? ''
135
137
  }
136
138
  },
137
139
  protocol: {
138
140
  get () {
139
141
  const socket = this.raw.socket
140
- if (this.headers['x-forwarded-proto'] && socket != null && proxyFn(socket.remoteAddress, 0)) {
141
- return getLastEntryInMultiHeaderValue(this.headers['x-forwarded-proto'])
142
+ // The headers getter may allocate a new object, so read it once.
143
+ const headers = this.headers
144
+ if (headers['x-forwarded-proto'] && socket != null && proxyFn(socket.remoteAddress, 0)) {
145
+ return getLastEntryInMultiHeaderValue(headers['x-forwarded-proto'])
142
146
  }
143
147
  if (this.socket) {
144
148
  return this.socket.encrypted ? 'https' : 'http'
package/lib/route.js CHANGED
@@ -466,8 +466,12 @@ function buildRouting (options) {
466
466
  // the child logger creation altogether
467
467
  childLogger = logger
468
468
  } else {
469
- const loggerOpts = {
470
- level: context.logLevel
469
+ const loggerOpts = {}
470
+
471
+ // An empty level means "inherit". Passing it would make pino treat it
472
+ // as a level change and rebuild every log method of each request child.
473
+ if (context.logLevel) {
474
+ loggerOpts.level = context.logLevel
471
475
  }
472
476
 
473
477
  if (context.logSerializers) {