Projektstart

This commit is contained in:
2026-01-22 15:49:12 +01:00
parent 7212eb6f7a
commit 57e5f652f8
10637 changed files with 2598792 additions and 64 deletions

8
backend/node_modules/@fastify/cors/.editorconfig generated vendored Normal file
View File

@@ -0,0 +1,8 @@
root = true
[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = false

5
backend/node_modules/@fastify/cors/.gitattributes generated vendored Normal file
View File

@@ -0,0 +1,5 @@
# Set the default behavior, in case people don't have core.autocrlf set
* text=auto
# Require Unix line endings
* text eol=lf

View File

@@ -0,0 +1,16 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 10
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
ignore:
- dependency-name: tap
update-types: ["version-update:semver-major"]

21
backend/node_modules/@fastify/cors/.github/stale.yml generated vendored Normal file
View File

@@ -0,0 +1,21 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 15
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- "discussion"
- "feature request"
- "bug"
- "help wanted"
- "plugin suggestion"
- "good first issue"
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

View File

@@ -0,0 +1,23 @@
name: CI
on:
push:
branches:
- main
- master
- next
- 'v*'
paths-ignore:
- 'docs/**'
- '*.md'
pull_request:
paths-ignore:
- 'docs/**'
- '*.md'
jobs:
test:
uses: fastify/workflows/.github/workflows/plugins-ci.yml@v3
with:
lint: true
license-check: true

4
backend/node_modules/@fastify/cors/.taprc generated vendored Normal file
View File

@@ -0,0 +1,4 @@
ts: false
jsx: false
coverage: true
flow: true

21
backend/node_modules/@fastify/cors/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Fastify
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

169
backend/node_modules/@fastify/cors/README.md generated vendored Normal file
View File

@@ -0,0 +1,169 @@
# @fastify/cors
![CI](https://github.com/fastify/fastify-cors/workflows/CI/badge.svg)
[![NPM version](https://img.shields.io/npm/v/@fastify/cors.svg?style=flat)](https://www.npmjs.com/package/@fastify/cors)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://standardjs.com/)
`@fastify/cors` enables the use of [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) in a Fastify application.
## Install
```
npm i @fastify/cors
```
### Compatibility
| Plugin version | Fastify version |
| -------------- |---------------- |
| `^8.0.0` | `^4.0.0` |
| `^7.0.0` | `^3.0.0` |
| `^3.0.0` | `^2.0.0` |
| `^1.0.0` | `^1.0.0` |
Please note that if a Fastify version is out of support, then so are the corresponding version(s) of this plugin
in the table above.
See [Fastify's LTS policy](https://github.com/fastify/fastify/blob/main/docs/Reference/LTS.md) for more details.
## Usage
Require `@fastify/cors` and register it as any other plugin, it will add an `onRequest` hook and a [wildcard options route](https://github.com/fastify/fastify/issues/326#issuecomment-411360862).
```js
import Fastify from 'fastify'
import cors from '@fastify/cors'
const fastify = Fastify()
await fastify.register(cors, {
// put your options here
})
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
await fastify.listen({ port: 3000 })
```
You can use it as is without passing any option or you can configure it as explained below.
### Options
* `origin`: Configures the **Access-Control-Allow-Origin** CORS header. The value of origin could be of different types:
- `Boolean` - set `origin` to `true` to reflect the [request origin](http://tools.ietf.org/html/draft-abarth-origin-09), or set it to `false` to disable CORS.
- `String` - set `origin` to a specific origin. For example if you set it to `"http://example.com"` only requests from "http://example.com" will be allowed. The special `*` value (default) allows any origin.
- `RegExp` - set `origin` to a regular expression pattern that will be used to test the request origin. If it is a match, the request origin will be reflected. For example, the pattern `/example\.com$/` will reflect any request that is coming from an origin ending with "example.com".
- `Array` - set `origin` to an array of valid origins. Each origin can be a `String` or a `RegExp`. For example `["http://example1.com", /\.example2\.com$/]` will accept any request from "http://example1.com" or from a subdomain of "example2.com".
- `Function` - set `origin` to a function implementing some custom logic. The function takes the request origin as the first parameter and a callback as a second (which expects the signature `err [Error | null], origin`), where `origin` is a non-function value of the origin option. *Async-await* and promises are supported as well. The Fastify instance is bound to function call and you may access via `this`. For example:
```js
origin: (origin, cb) => {
const hostname = new URL(origin).hostname
if(hostname === "localhost"){
// Request from localhost will pass
cb(null, true)
return
}
// Generate an error on other origins, disabling access
cb(new Error("Not allowed"), false)
}
```
* `methods`: Configures the **Access-Control-Allow-Methods** CORS header. Expects a comma-delimited string (ex: 'GET,PUT,POST') or an array (ex: `['GET', 'PUT', 'POST']`).
* `hook`: See the section `Custom Fastify hook name` (default: `onRequest`)
* `allowedHeaders`: Configures the **Access-Control-Allow-Headers** CORS header. Expects a comma-delimited string (ex: `'Content-Type,Authorization'`) or an array (ex: `['Content-Type', 'Authorization']`). If not specified, defaults to reflecting the headers specified in the request's **Access-Control-Request-Headers** header.
* `exposedHeaders`: Configures the **Access-Control-Expose-Headers** CORS header. Expects a comma-delimited string (ex: `'Content-Range,X-Content-Range'`) or an array (ex: `['Content-Range', 'X-Content-Range']`). If not specified, no custom headers are exposed.
* `credentials`: Configures the **Access-Control-Allow-Credentials** CORS header. Set to `true` to pass the header, otherwise it is omitted.
* `maxAge`: Configures the **Access-Control-Max-Age** CORS header. In seconds. Set to an integer to pass the header, otherwise it is omitted.
* `cacheControl`: Configures the **Cache-Control** header for CORS preflight responses. Set to an integer to pass the header as `Cache-Control: max-age=${cacheControl}`, or set to a string to pass the header as `Cache-Control: ${cacheControl}` (fully define the header value), otherwise the header is omitted.
* `preflightContinue`: Pass the CORS preflight response to the route handler (default: `false`).
* `optionsSuccessStatus`: Provides a status code to use for successful `OPTIONS` requests, since some legacy browsers (IE11, various SmartTVs) choke on `204`.
* `preflight`: if needed you can entirely disable preflight by passing `false` here (default: `true`).
* `strictPreflight`: Enforces strict requirement of the CORS preflight request headers (**Access-Control-Request-Method** and **Origin**) as defined by the [W3C CORS specification](https://www.w3.org/TR/2020/SPSD-cors-20200602/#resource-preflight-requests) (the current [fetch living specification](https://fetch.spec.whatwg.org/) does not define server behavior for missing headers). Preflight requests without the required headers will result in 400 errors when set to `true` (default: `true`).
* `hideOptionsRoute`: hide options route from the documentation built using [@fastify/swagger](https://github.com/fastify/fastify-swagger) (default: `true`).
### Configuring CORS Asynchronously
```js
const fastify = require('fastify')()
fastify.register(require('@fastify/cors'), (instance) => {
return (req, callback) => {
const corsOptions = {
// This is NOT recommended for production as it enables reflection exploits
origin: true
};
// do not include CORS headers for requests from localhost
if (/^localhost$/m.test(req.headers.origin)) {
corsOptions.origin = false
}
// callback expects two parameters: error and options
callback(null, corsOptions)
}
})
fastify.register(async function (fastify) {
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
})
fastify.listen({ port: 3000 })
```
### Custom Fastify hook name
By default, `@fastify/cors` adds a `onRequest` hook where the validation and header injection are executed. This can be customized by passing `hook` in the options. Valid values are `onRequest`, `preParsing`, `preValidation`, `preHandler`, `preSerialization`, and `onSend`.
```js
import Fastify from 'fastify'
import cors from '@fastify/cors'
const fastify = Fastify()
await fastify.register(cors, {
hook: 'preHandler',
})
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
await fastify.listen({ port: 3000 })
```
When configuring CORS asynchronously, an object with `delegator` key is expected:
```js
const fastify = require('fastify')()
fastify.register(require('@fastify/cors'), {
hook: 'preHandler',
delegator: (req, callback) => {
const corsOptions = {
// This is NOT recommended for production as it enables reflection exploits
origin: true
};
// do not include CORS headers for requests from localhost
if (/^localhost$/m.test(req.headers.origin)) {
corsOptions.origin = false
}
// callback expects two parameters: error and options
callback(null, corsOptions)
},
})
fastify.register(async function (fastify) {
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
})
fastify.listen({ port: 3000 })
```
## Acknowledgements
The code is a port for Fastify of [`expressjs/cors`](https://github.com/expressjs/cors).
## License
Licensed under [MIT](./LICENSE).<br/>
[`expressjs/cors` license](https://github.com/expressjs/cors/blob/master/LICENSE)

17
backend/node_modules/@fastify/cors/bench.js generated vendored Normal file
View File

@@ -0,0 +1,17 @@
'use strict'
const fastify = require('fastify')()
fastify.register((instance, opts, next) => {
instance.register(require('./index'))
instance.get('/fastify', (req, reply) => reply.send('ok'))
next()
})
fastify.register((instance, opts, next) => {
instance.use(require('cors')())
instance.get('/express', (req, reply) => reply.send('ok'))
next()
})
fastify.listen({ port: 3000 })

View File

@@ -0,0 +1,15 @@
{
"name": "benchmark",
"version": "1.0.0",
"description": "",
"main": "vary.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"benchmark": "^2.1.4",
"vary": "^1.1.2"
}
}

34
backend/node_modules/@fastify/cors/benchmark/vary.js generated vendored Normal file
View File

@@ -0,0 +1,34 @@
'use strict'
const benchmark = require('benchmark')
const vary = require('vary')
const createAddFieldnameToVary = require('../vary').createAddFieldnameToVary
const replyMock = (header) => ({
getHeader () { return header },
setHeader () { },
header () { }
})
const addAcceptToVary = createAddFieldnameToVary('Accept')
const addWildcardToVary = createAddFieldnameToVary('*')
const addAcceptEncodingToVary = createAddFieldnameToVary('Accept-Encoding')
const addXFooToVary = createAddFieldnameToVary('X-Foo')
new benchmark.Suite()
.add('vary - field to undefined', function () { vary(replyMock(undefined), 'Accept-Encoding') }, { minSamples: 100 })
.add('vary - field to *', function () { vary(replyMock('*'), 'Accept-Encoding') }, { minSamples: 100 })
.add('vary - * to field', function () { vary(replyMock('Accept-Encoding'), '*') }, { minSamples: 100 })
.add('vary - field to empty', function () { vary(replyMock(''), 'Accept-Encoding') }, { minSamples: 100 })
.add('vary - fields string to empty', function () { vary(replyMock(''), 'Accept') }, { minSamples: 100 })
.add('vary - field to fields', function () { vary(replyMock('Accept, Accept-Encoding, Accept-Language'), 'X-Foo') }, { minSamples: 100 })
.add('cors - field to undefined', function () { addAcceptEncodingToVary(replyMock(undefined)) }, { minSamples: 100 })
.add('cors - field to *', function () { addAcceptEncodingToVary(replyMock('*')) }, { minSamples: 100 })
.add('cors - * to field', function () { addWildcardToVary(replyMock('Accept-Encoding')) }, { minSamples: 100 })
.add('cors - field to empty', function () { addAcceptEncodingToVary(replyMock('')) }, { minSamples: 100 })
.add('cors - fields string to empty', function () { addAcceptToVary(replyMock('')) }, { minSamples: 100 })
.add('cors - field to fields', function () { addXFooToVary(replyMock('Accept, Accept-Encoding, Accept-Language')) }, { minSamples: 100 })
.on('cycle', function onCycle (event) { console.log(String(event.target)) })
.run({ async: false })

307
backend/node_modules/@fastify/cors/index.js generated vendored Normal file
View File

@@ -0,0 +1,307 @@
'use strict'
const fp = require('fastify-plugin')
const {
addAccessControlRequestHeadersToVaryHeader,
addOriginToVaryHeader
} = require('./vary')
const defaultOptions = {
origin: '*',
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
hook: 'onRequest',
preflightContinue: false,
optionsSuccessStatus: 204,
credentials: false,
exposedHeaders: null,
allowedHeaders: null,
maxAge: null,
preflight: true,
strictPreflight: true
}
const validHooks = [
'onRequest',
'preParsing',
'preValidation',
'preHandler',
'preSerialization',
'onSend'
]
const hookWithPayload = [
'preSerialization',
'preParsing',
'onSend'
]
function validateHook (value, next) {
if (validHooks.indexOf(value) !== -1) {
return
}
next(new TypeError('@fastify/cors: Invalid hook option provided.'))
}
function fastifyCors (fastify, opts, next) {
fastify.decorateRequest('corsPreflightEnabled', false)
let hideOptionsRoute = true
if (typeof opts === 'function') {
handleCorsOptionsDelegator(opts, fastify, { hook: defaultOptions.hook }, next)
} else if (opts.delegator) {
const { delegator, ...options } = opts
handleCorsOptionsDelegator(delegator, fastify, options, next)
} else {
if (opts.hideOptionsRoute !== undefined) hideOptionsRoute = opts.hideOptionsRoute
const corsOptions = normalizeCorsOptions(opts)
validateHook(corsOptions.hook, next)
if (hookWithPayload.indexOf(corsOptions.hook) !== -1) {
fastify.addHook(corsOptions.hook, function handleCors (req, reply, payload, next) {
addCorsHeadersHandler(fastify, corsOptions, req, reply, next)
})
} else {
fastify.addHook(corsOptions.hook, function handleCors (req, reply, next) {
addCorsHeadersHandler(fastify, corsOptions, req, reply, next)
})
}
}
// The preflight reply must occur in the hook. This allows fastify-cors to reply to
// preflight requests BEFORE possible authentication plugins. If the preflight reply
// occurred in this handler, other plugins may deny the request since the browser will
// remove most headers (such as the Authentication header).
//
// This route simply enables fastify to accept preflight requests.
fastify.options('*', { schema: { hide: hideOptionsRoute } }, (req, reply) => {
if (!req.corsPreflightEnabled) {
// Do not handle preflight requests if the origin option disabled CORS
reply.callNotFound()
return
}
reply.send()
})
next()
}
function handleCorsOptionsDelegator (optionsResolver, fastify, opts, next) {
const hook = (opts && opts.hook) || defaultOptions.hook
validateHook(hook, next)
if (optionsResolver.length === 2) {
if (hookWithPayload.indexOf(hook) !== -1) {
fastify.addHook(hook, function handleCors (req, reply, payload, next) {
handleCorsOptionsCallbackDelegator(optionsResolver, fastify, req, reply, next)
})
} else {
fastify.addHook(hook, function handleCors (req, reply, next) {
handleCorsOptionsCallbackDelegator(optionsResolver, fastify, req, reply, next)
})
}
} else {
if (hookWithPayload.indexOf(hook) !== -1) {
// handle delegator based on Promise
fastify.addHook(hook, function handleCors (req, reply, payload, next) {
const ret = optionsResolver(req)
if (ret && typeof ret.then === 'function') {
ret.then(options => addCorsHeadersHandler(fastify, normalizeCorsOptions(options, true), req, reply, next)).catch(next)
return
}
next(new Error('Invalid CORS origin option'))
})
} else {
// handle delegator based on Promise
fastify.addHook(hook, function handleCors (req, reply, next) {
const ret = optionsResolver(req)
if (ret && typeof ret.then === 'function') {
ret.then(options => addCorsHeadersHandler(fastify, normalizeCorsOptions(options, true), req, reply, next)).catch(next)
return
}
next(new Error('Invalid CORS origin option'))
})
}
}
}
function handleCorsOptionsCallbackDelegator (optionsResolver, fastify, req, reply, next) {
optionsResolver(req, (err, options) => {
if (err) {
next(err)
} else {
addCorsHeadersHandler(fastify, normalizeCorsOptions(options, true), req, reply, next)
}
})
}
/**
* @param {import('./types').FastifyCorsOptions} opts
*/
function normalizeCorsOptions (opts, dynamic) {
const corsOptions = { ...defaultOptions, ...opts }
if (Array.isArray(opts.origin) && opts.origin.indexOf('*') !== -1) {
corsOptions.origin = '*'
}
if (Number.isInteger(corsOptions.cacheControl)) {
// integer numbers are formatted this way
corsOptions.cacheControl = `max-age=${corsOptions.cacheControl}`
} else if (typeof corsOptions.cacheControl !== 'string') {
// strings are applied directly and any other value is ignored
corsOptions.cacheControl = null
}
corsOptions.dynamic = dynamic || false
return corsOptions
}
function addCorsHeadersHandler (fastify, options, req, reply, next) {
if ((typeof options.origin !== 'string' && options.origin !== false) || options.dynamic) {
// Always set Vary header for non-static origin option
// https://fetch.spec.whatwg.org/#cors-protocol-and-http-caches
addOriginToVaryHeader(reply)
}
const resolveOriginOption = typeof options.origin === 'function' ? resolveOriginWrapper(fastify, options.origin) : (_, cb) => cb(null, options.origin)
resolveOriginOption(req, (error, resolvedOriginOption) => {
if (error !== null) {
return next(error)
}
// Disable CORS and preflight if false
if (resolvedOriginOption === false) {
return next()
}
// Falsy values are invalid
if (!resolvedOriginOption) {
return next(new Error('Invalid CORS origin option'))
}
addCorsHeaders(req, reply, resolvedOriginOption, options)
if (req.raw.method === 'OPTIONS' && options.preflight === true) {
// Strict mode enforces the required headers for preflight
if (options.strictPreflight === true && (!req.headers.origin || !req.headers['access-control-request-method'])) {
reply.status(400).type('text/plain').send('Invalid Preflight Request')
return
}
req.corsPreflightEnabled = true
addPreflightHeaders(req, reply, options)
if (!options.preflightContinue) {
// Do not call the hook callback and terminate the request
// Safari (and potentially other browsers) need content-length 0,
// for 204 or they just hang waiting for a body
reply
.code(options.optionsSuccessStatus)
.header('Content-Length', '0')
.send()
return
}
}
return next()
})
}
function addCorsHeaders (req, reply, originOption, corsOptions) {
const origin = getAccessControlAllowOriginHeader(req.headers.origin, originOption)
// In the case of origin not allowed the header is not
// written in the response.
// https://github.com/fastify/fastify-cors/issues/127
if (origin) {
reply.header('Access-Control-Allow-Origin', origin)
}
if (corsOptions.credentials) {
reply.header('Access-Control-Allow-Credentials', 'true')
}
if (corsOptions.exposedHeaders !== null) {
reply.header(
'Access-Control-Expose-Headers',
Array.isArray(corsOptions.exposedHeaders) ? corsOptions.exposedHeaders.join(', ') : corsOptions.exposedHeaders
)
}
}
function addPreflightHeaders (req, reply, corsOptions) {
reply.header(
'Access-Control-Allow-Methods',
Array.isArray(corsOptions.methods) ? corsOptions.methods.join(', ') : corsOptions.methods
)
if (corsOptions.allowedHeaders === null) {
addAccessControlRequestHeadersToVaryHeader(reply)
const reqAllowedHeaders = req.headers['access-control-request-headers']
if (reqAllowedHeaders !== undefined) {
reply.header('Access-Control-Allow-Headers', reqAllowedHeaders)
}
} else {
reply.header(
'Access-Control-Allow-Headers',
Array.isArray(corsOptions.allowedHeaders) ? corsOptions.allowedHeaders.join(', ') : corsOptions.allowedHeaders
)
}
if (corsOptions.maxAge !== null) {
reply.header('Access-Control-Max-Age', String(corsOptions.maxAge))
}
if (corsOptions.cacheControl) {
reply.header('Cache-Control', corsOptions.cacheControl)
}
}
function resolveOriginWrapper (fastify, origin) {
return function (req, cb) {
const result = origin.call(fastify, req.headers.origin, cb)
// Allow for promises
if (result && typeof result.then === 'function') {
result.then(res => cb(null, res), cb)
}
}
}
function getAccessControlAllowOriginHeader (reqOrigin, originOption) {
if (typeof originOption === 'string') {
// fixed or any origin ('*')
return originOption
}
// reflect origin
return isRequestOriginAllowed(reqOrigin, originOption) ? reqOrigin : false
}
function isRequestOriginAllowed (reqOrigin, allowedOrigin) {
if (Array.isArray(allowedOrigin)) {
for (let i = 0; i < allowedOrigin.length; ++i) {
if (isRequestOriginAllowed(reqOrigin, allowedOrigin[i])) {
return true
}
}
return false
} else if (typeof allowedOrigin === 'string') {
return reqOrigin === allowedOrigin
} else if (allowedOrigin instanceof RegExp) {
allowedOrigin.lastIndex = 0
return allowedOrigin.test(reqOrigin)
} else {
return !!allowedOrigin
}
}
const _fastifyCors = fp(fastifyCors, {
fastify: '4.x',
name: '@fastify/cors'
})
/**
* These export configurations enable JS and TS developers
* to consumer fastify in whatever way best suits their needs.
*/
module.exports = _fastifyCors
module.exports.fastifyCors = _fastifyCors
module.exports.default = _fastifyCors

59
backend/node_modules/@fastify/cors/package.json generated vendored Normal file
View File

@@ -0,0 +1,59 @@
{
"name": "@fastify/cors",
"version": "9.0.1",
"description": "Fastify CORS",
"main": "index.js",
"type": "commonjs",
"types": "types/index.d.ts",
"scripts": {
"coverage": "tap --cov --coverage-report=html test",
"lint": "standard",
"lint:fix": "standard --fix",
"test": "npm run test:unit && npm run test:typescript",
"test:typescript": "tsd",
"test:unit": "tap test/*.test.js"
},
"keywords": [
"fastify",
"cors",
"headers",
"access",
"control"
],
"author": "Tomas Della Vedova - @delvedor (http://delved.org)",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/fastify/fastify-cors.git"
},
"bugs": {
"url": "https://github.com/fastify/fastify-cors/issues"
},
"homepage": "https://github.com/fastify/fastify-cors#readme",
"devDependencies": {
"@fastify/pre-commit": "^2.0.2",
"@types/node": "^20.1.0",
"@typescript-eslint/eslint-plugin": "^6.4.1",
"@typescript-eslint/parser": "^6.4.0",
"cors": "^2.8.5",
"fastify": "^4.0.0-rc.2",
"standard": "^17.0.0",
"tap": "16.3.9",
"tsd": "^0.30.0",
"typescript": "^5.0.2"
},
"dependencies": {
"fastify-plugin": "^4.0.0",
"mnemonist": "0.39.6"
},
"tsd": {
"directory": "test"
},
"publishConfig": {
"access": "public"
},
"pre-commit": [
"lint",
"test"
]
}

941
backend/node_modules/@fastify/cors/test/cors.test.js generated vendored Normal file
View File

@@ -0,0 +1,941 @@
'use strict'
const { test } = require('tap')
const { createReadStream, statSync, readFileSync } = require('node:fs')
const Fastify = require('fastify')
const cors = require('../')
const { resolve } = require('node:path')
test('Should add cors headers', t => {
t.plan(4)
const fastify = Fastify()
fastify.register(cors)
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': '*'
})
})
})
test('Should add cors headers when payload is a stream', t => {
t.plan(4)
const fastify = Fastify()
fastify.register(cors)
const filePath = resolve(__dirname, __filename)
fastify.get('/', (req, reply) => {
const stream = createReadStream(filePath)
reply
.type('application/json')
.header('Content-Length', statSync(filePath).size)
.send(stream)
})
const fileContent = readFileSync(filePath, 'utf-8')
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, fileContent)
t.match(res.headers, {
'access-control-allow-origin': '*',
'content-length': statSync(filePath).size
})
})
})
test('Should add cors headers (custom values)', t => {
t.plan(10)
const fastify = Fastify()
fastify.register(cors, {
origin: 'example.com',
methods: 'GET',
credentials: true,
exposedHeaders: ['foo', 'bar'],
allowedHeaders: ['baz', 'woo'],
maxAge: 123,
cacheControl: 321
})
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 204)
t.equal(res.payload, '')
t.match(res.headers, {
'access-control-allow-origin': 'example.com',
'access-control-allow-credentials': 'true',
'access-control-expose-headers': 'foo, bar',
'access-control-allow-methods': 'GET',
'access-control-allow-headers': 'baz, woo',
'access-control-max-age': '123',
'cache-control': 'max-age=321',
'content-length': '0'
})
t.notMatch(res.headers, { vary: 'Origin' })
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': 'example.com',
'access-control-allow-credentials': 'true',
'access-control-expose-headers': 'foo, bar',
'content-length': '2'
})
t.notMatch(res.headers, { vary: 'Origin' })
})
})
test('Should support dynamic config (callback)', t => {
t.plan(16)
const configs = [{
origin: 'example.com',
methods: 'GET',
credentials: true,
exposedHeaders: ['foo', 'bar'],
allowedHeaders: ['baz', 'woo'],
maxAge: 123,
cacheControl: 456
}, {
origin: 'sample.com',
methods: 'GET',
credentials: true,
exposedHeaders: ['zoo', 'bar'],
allowedHeaders: ['baz', 'foo'],
maxAge: 321,
cacheControl: '456'
}]
const fastify = Fastify()
let requestId = 0
const configDelegation = function (req, cb) {
// request should have id
t.ok(req.id)
// request should not have send
t.notOk(req.send)
const config = configs[requestId]
requestId++
if (config) {
cb(null, config)
} else {
cb(new Error('ouch'))
}
}
fastify.register(cors, () => configDelegation)
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': 'example.com',
'access-control-allow-credentials': 'true',
'access-control-expose-headers': 'foo, bar',
'content-length': '2',
vary: 'Origin'
})
})
fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 204)
t.equal(res.payload, '')
t.match(res.headers, {
'access-control-allow-origin': 'sample.com',
'access-control-allow-credentials': 'true',
'access-control-expose-headers': 'zoo, bar',
'access-control-allow-methods': 'GET',
'access-control-allow-headers': 'baz, foo',
'access-control-max-age': '321',
'cache-control': '456',
'content-length': '0',
vary: 'Origin'
})
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
})
})
test('Should support dynamic config (Promise)', t => {
t.plan(23)
const configs = [{
origin: 'example.com',
methods: 'GET',
credentials: true,
exposedHeaders: ['foo', 'bar'],
allowedHeaders: ['baz', 'woo'],
maxAge: 123,
cacheControl: 456
}, {
origin: 'sample.com',
methods: 'GET',
credentials: true,
exposedHeaders: ['zoo', 'bar'],
allowedHeaders: ['baz', 'foo'],
maxAge: 321,
cacheControl: true // Invalid value should be ignored
}, {
origin: 'sample.com',
methods: 'GET',
credentials: true,
exposedHeaders: ['zoo', 'bar'],
allowedHeaders: ['baz', 'foo'],
maxAge: 321,
cacheControl: 'public, max-age=456'
}]
const fastify = Fastify()
let requestId = 0
const configDelegation = function (req) {
// request should have id
t.ok(req.id)
// request should not have send
t.notOk(req.send)
const config = configs[requestId]
requestId++
if (config) {
return Promise.resolve(config)
} else {
return Promise.reject(new Error('ouch'))
}
}
fastify.register(cors, () => configDelegation)
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': 'example.com',
'access-control-allow-credentials': 'true',
'access-control-expose-headers': 'foo, bar',
'content-length': '2',
vary: 'Origin'
})
})
fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'sample.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 204)
t.equal(res.payload, '')
t.match(res.headers, {
'access-control-allow-origin': 'sample.com',
'access-control-allow-credentials': 'true',
'access-control-expose-headers': 'zoo, bar',
'access-control-allow-methods': 'GET',
'access-control-allow-headers': 'baz, foo',
'access-control-max-age': '321',
'content-length': '0',
vary: 'Origin'
})
t.equal(res.headers['cache-control'], undefined, 'cache-control omitted (invalid value)')
})
fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 204)
t.equal(res.payload, '')
t.match(res.headers, {
'access-control-allow-origin': 'sample.com',
'access-control-allow-credentials': 'true',
'access-control-expose-headers': 'zoo, bar',
'access-control-allow-methods': 'GET',
'access-control-allow-headers': 'baz, foo',
'access-control-max-age': '321',
'cache-control': 'public, max-age=456', // cache-control included (custom string)
'content-length': '0',
vary: 'Origin'
})
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
})
})
test('Should support dynamic config. (Invalid function)', t => {
t.plan(2)
const fastify = Fastify()
fastify.register(cors, () => (a, b, c) => {})
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
})
})
test('Dynamic origin resolution (valid origin)', t => {
t.plan(6)
const fastify = Fastify()
const origin = function (header, cb) {
t.equal(header, 'example.com')
t.same(this, fastify)
cb(null, true)
}
fastify.register(cors, { origin })
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'GET',
url: '/',
headers: { origin: 'example.com' }
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': 'example.com',
vary: 'Origin'
})
})
})
test('Dynamic origin resolution (not valid origin)', t => {
t.plan(5)
const fastify = Fastify()
const origin = (header, cb) => {
t.equal(header, 'example.com')
cb(null, false)
}
fastify.register(cors, { origin })
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'GET',
url: '/',
headers: { origin: 'example.com' }
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.same(res.headers, {
'content-length': '2',
'content-type': 'text/plain; charset=utf-8',
connection: 'keep-alive',
vary: 'Origin'
})
})
})
test('Dynamic origin resolution (errored)', t => {
t.plan(3)
const fastify = Fastify()
const origin = (header, cb) => {
t.equal(header, 'example.com')
cb(new Error('ouch'))
}
fastify.register(cors, { origin })
fastify.inject({
method: 'GET',
url: '/',
headers: { origin: 'example.com' }
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
})
})
test('Dynamic origin resolution (invalid result)', t => {
t.plan(3)
const fastify = Fastify()
const origin = (header, cb) => {
t.equal(header, 'example.com')
cb(null, undefined)
}
fastify.register(cors, { origin })
fastify.inject({
method: 'GET',
url: '/',
headers: { origin: 'example.com' }
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
})
})
test('Dynamic origin resolution (valid origin - promises)', t => {
t.plan(5)
const fastify = Fastify()
const origin = (header, cb) => {
return new Promise((resolve, reject) => {
t.equal(header, 'example.com')
resolve(true)
})
}
fastify.register(cors, { origin })
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'GET',
url: '/',
headers: { origin: 'example.com' }
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': 'example.com',
vary: 'Origin'
})
})
})
test('Dynamic origin resolution (not valid origin - promises)', t => {
t.plan(5)
const fastify = Fastify()
const origin = (header, cb) => {
return new Promise((resolve, reject) => {
t.equal(header, 'example.com')
resolve(false)
})
}
fastify.register(cors, { origin })
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'GET',
url: '/',
headers: { origin: 'example.com' }
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.same(res.headers, {
'content-length': '2',
'content-type': 'text/plain; charset=utf-8',
connection: 'keep-alive',
vary: 'Origin'
})
})
})
test('Dynamic origin resolution (errored - promises)', t => {
t.plan(3)
const fastify = Fastify()
const origin = (header, cb) => {
return new Promise((resolve, reject) => {
t.equal(header, 'example.com')
reject(new Error('ouch'))
})
}
fastify.register(cors, { origin })
fastify.inject({
method: 'GET',
url: '/',
headers: { origin: 'example.com' }
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
})
})
test('Should reply 404 without cors headers when origin is false', t => {
t.plan(8)
const fastify = Fastify()
fastify.register(cors, {
origin: false,
methods: 'GET',
credentials: true,
exposedHeaders: ['foo', 'bar'],
allowedHeaders: ['baz', 'woo'],
maxAge: 123
})
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'OPTIONS',
url: '/'
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 404)
t.equal(res.payload, '{"message":"Route OPTIONS:/ not found","error":"Not Found","statusCode":404}')
t.same(res.headers, {
'content-length': '76',
'content-type': 'application/json; charset=utf-8',
connection: 'keep-alive'
})
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.same(res.headers, {
'content-length': '2',
'content-type': 'text/plain; charset=utf-8',
connection: 'keep-alive'
})
})
})
test('Server error if origin option is falsy but not false', t => {
t.plan(4)
const fastify = Fastify()
fastify.register(cors, { origin: '' })
fastify.inject({
method: 'GET',
url: '/',
headers: { origin: 'example.com' }
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 500)
t.same(res.json(), { statusCode: 500, error: 'Internal Server Error', message: 'Invalid CORS origin option' })
t.same(res.headers, {
'content-length': '89',
'content-type': 'application/json; charset=utf-8',
connection: 'keep-alive'
})
})
})
test('Allow only request from a specific origin', t => {
t.plan(5)
const fastify = Fastify()
fastify.register(cors, { origin: 'other.io' })
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'GET',
url: '/',
headers: { origin: 'example.com' }
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': 'other.io'
})
t.notMatch(res.headers, { vary: 'Origin' })
})
})
test('Allow only request from multiple specific origin', t => {
t.plan(9)
const fastify = Fastify()
fastify.register(cors, { origin: ['other.io', 'example.com'] })
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'GET',
url: '/',
headers: { origin: 'other.io' }
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': 'other.io',
vary: 'Origin'
})
})
fastify.inject({
method: 'GET',
url: '/',
headers: { origin: 'foo.com' }
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
vary: 'Origin'
})
t.equal(res.headers['access-control-allow-origin'], undefined)
})
})
test('Allow only request from a specific origin using regex', t => {
t.plan(8)
const fastify = Fastify()
fastify.register(cors, { origin: /(?:example|other)\.com/giu })
fastify.get('/', (req, reply) => {
reply.send('ok')
})
// .test was previously used, which caused 2 consecutive requests to return
// different results with global (e.g. /g) regexes. Therefore, check this
// twice to check consistency
for (let i = 0; i < 2; i++) {
fastify.inject({
method: 'GET',
url: '/',
headers: { origin: 'https://www.example.com/' }
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': 'https://www.example.com/',
vary: 'Origin'
})
})
}
})
test('Disable preflight', t => {
t.plan(7)
const fastify = Fastify()
fastify.register(cors, { preflight: false })
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'OPTIONS',
url: '/hello'
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 404)
t.match(res.headers, {
'access-control-allow-origin': '*'
})
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': '*'
})
})
})
test('Should always add vary header to `Origin` for reflected origin', t => {
t.plan(12)
const fastify = Fastify()
fastify.register(cors, { origin: true })
fastify.get('/', (req, reply) => {
reply.send('ok')
})
// Invalid Preflight
fastify.inject({
method: 'OPTIONS',
url: '/'
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 400)
t.equal(res.payload, 'Invalid Preflight Request')
t.match(res.headers, {
vary: 'Origin'
})
})
// Valid Preflight
fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 204)
t.equal(res.payload, '')
t.match(res.headers, {
vary: 'Origin'
})
})
// Other Route
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
vary: 'Origin'
})
})
})
test('Should always add vary header to `Origin` for reflected origin (vary is array)', t => {
t.plan(4)
const fastify = Fastify()
// Mock getHeader function
fastify.decorateReply('getHeader', (name) => ['foo', 'bar'])
fastify.register(cors, { origin: true })
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
vary: 'foo, bar, Origin'
})
})
})
test('Allow only request from with specific headers', t => {
t.plan(8)
const fastify = Fastify()
fastify.register(cors, {
allowedHeaders: 'foo',
exposedHeaders: 'bar'
})
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 204)
t.match(res.headers, {
'access-control-allow-headers': 'foo'
})
t.notMatch(res.headers, { vary: 'Origin' })
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-expose-headers': 'bar'
})
})
})
test('Should support wildcard config /1', t => {
t.plan(4)
const fastify = Fastify()
fastify.register(cors, { origin: '*' })
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.equal(res.headers['access-control-allow-origin'], '*')
})
})
test('Should support wildcard config /2', t => {
t.plan(4)
const fastify = Fastify()
fastify.register(cors, { origin: ['*'] })
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.equal(res.headers['access-control-allow-origin'], '*')
})
})

706
backend/node_modules/@fastify/cors/test/hooks.test.js generated vendored Normal file
View File

@@ -0,0 +1,706 @@
'use strict'
const { test } = require('tap')
const Fastify = require('fastify')
const kFastifyContext = require('fastify/lib/symbols').kRouteContext
const cors = require('..')
test('Should error on invalid hook option', async (t) => {
t.plan(1)
const fastify = Fastify()
t.rejects(fastify.register(cors, { hook: 'invalid' }), new TypeError('@fastify/cors: Invalid hook option provided.'))
})
test('Should set hook onRequest if hook option is not set', async (t) => {
t.plan(10)
const fastify = Fastify()
fastify.register(cors)
fastify.addHook('onResponse', (request, reply, done) => {
t.equal(request[kFastifyContext].onError, null)
t.equal(request[kFastifyContext].onRequest.length, 1)
t.equal(request[kFastifyContext].onSend, null)
t.equal(request[kFastifyContext].preHandler, null)
t.equal(request[kFastifyContext].preParsing, null)
t.equal(request[kFastifyContext].preSerialization, null)
t.equal(request[kFastifyContext].preValidation, null)
done()
})
fastify.get('/', (req, reply) => {
reply.send('ok')
})
await fastify.ready()
const res = await fastify.inject({
method: 'GET',
url: '/'
})
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': '*'
})
})
test('Should set hook onRequest if hook option is set to onRequest', async (t) => {
t.plan(10)
const fastify = Fastify()
fastify.register(cors, {
hook: 'onRequest'
})
fastify.addHook('onResponse', (request, reply, done) => {
t.equal(request[kFastifyContext].onError, null)
t.equal(request[kFastifyContext].onRequest.length, 1)
t.equal(request[kFastifyContext].onSend, null)
t.equal(request[kFastifyContext].preHandler, null)
t.equal(request[kFastifyContext].preParsing, null)
t.equal(request[kFastifyContext].preSerialization, null)
t.equal(request[kFastifyContext].preValidation, null)
done()
})
fastify.get('/', (req, reply) => {
reply.send('ok')
})
await fastify.ready()
const res = await fastify.inject({
method: 'GET',
url: '/'
})
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': '*'
})
})
test('Should set hook preParsing if hook option is set to preParsing', async (t) => {
t.plan(11)
const fastify = Fastify()
fastify.register(cors, {
hook: 'preParsing'
})
fastify.addHook('onResponse', (request, reply, done) => {
t.equal(request[kFastifyContext].onError, null)
t.equal(request[kFastifyContext].onRequest, null)
t.equal(request[kFastifyContext].onSend, null)
t.equal(request[kFastifyContext].preHandler, null)
t.equal(request[kFastifyContext].preParsing.length, 1)
t.equal(request[kFastifyContext].preSerialization, null)
t.equal(request[kFastifyContext].preValidation, null)
done()
})
fastify.get('/', (req, reply) => {
reply.send('ok')
})
await fastify.ready()
const res = await fastify.inject({
method: 'GET',
url: '/'
})
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': '*'
})
t.notMatch(res.headers, { vary: 'Origin' })
})
test('Should set hook preValidation if hook option is set to preValidation', async (t) => {
t.plan(11)
const fastify = Fastify()
fastify.register(cors, {
hook: 'preValidation'
})
fastify.addHook('onResponse', (request, reply, done) => {
t.equal(request[kFastifyContext].onError, null)
t.equal(request[kFastifyContext].onRequest, null)
t.equal(request[kFastifyContext].onSend, null)
t.equal(request[kFastifyContext].preHandler, null)
t.equal(request[kFastifyContext].preParsing, null)
t.equal(request[kFastifyContext].preSerialization, null)
t.equal(request[kFastifyContext].preValidation.length, 1)
done()
})
fastify.get('/', (req, reply) => {
reply.send('ok')
})
await fastify.ready()
const res = await fastify.inject({
method: 'GET',
url: '/'
})
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': '*'
})
t.notMatch(res.headers, { vary: 'Origin' })
})
test('Should set hook preParsing if hook option is set to preParsing', async (t) => {
t.plan(11)
const fastify = Fastify()
fastify.register(cors, {
hook: 'preParsing'
})
fastify.addHook('onResponse', (request, reply, done) => {
t.equal(request[kFastifyContext].onError, null)
t.equal(request[kFastifyContext].onRequest, null)
t.equal(request[kFastifyContext].onSend, null)
t.equal(request[kFastifyContext].preHandler, null)
t.equal(request[kFastifyContext].preParsing.length, 1)
t.equal(request[kFastifyContext].preSerialization, null)
t.equal(request[kFastifyContext].preValidation, null)
done()
})
fastify.get('/', (req, reply) => {
reply.send('ok')
})
await fastify.ready()
const res = await fastify.inject({
method: 'GET',
url: '/'
})
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': '*'
})
t.notMatch(res.headers, { vary: 'Origin' })
})
test('Should set hook preHandler if hook option is set to preHandler', async (t) => {
t.plan(11)
const fastify = Fastify()
fastify.register(cors, {
hook: 'preHandler'
})
fastify.addHook('onResponse', (request, reply, done) => {
t.equal(request[kFastifyContext].onError, null)
t.equal(request[kFastifyContext].onRequest, null)
t.equal(request[kFastifyContext].onSend, null)
t.equal(request[kFastifyContext].preHandler.length, 1)
t.equal(request[kFastifyContext].preParsing, null)
t.equal(request[kFastifyContext].preSerialization, null)
t.equal(request[kFastifyContext].preValidation, null)
done()
})
fastify.get('/', (req, reply) => {
reply.send('ok')
})
await fastify.ready()
const res = await fastify.inject({
method: 'GET',
url: '/'
})
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': '*'
})
t.notMatch(res.headers, { vary: 'Origin' })
})
test('Should set hook onSend if hook option is set to onSend', async (t) => {
t.plan(11)
const fastify = Fastify()
fastify.register(cors, {
hook: 'onSend'
})
fastify.addHook('onResponse', (request, reply, done) => {
t.equal(request[kFastifyContext].onError, null)
t.equal(request[kFastifyContext].onRequest, null)
t.equal(request[kFastifyContext].onSend.length, 1)
t.equal(request[kFastifyContext].preHandler, null)
t.equal(request[kFastifyContext].preParsing, null)
t.equal(request[kFastifyContext].preSerialization, null)
t.equal(request[kFastifyContext].preValidation, null)
done()
})
fastify.get('/', (req, reply) => {
reply.send('ok')
})
await fastify.ready()
const res = await fastify.inject({
method: 'GET',
url: '/'
})
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': '*'
})
t.notMatch(res.headers, { vary: 'Origin' })
})
test('Should set hook preSerialization if hook option is set to preSerialization', async (t) => {
t.plan(11)
const fastify = Fastify()
fastify.register(cors, {
hook: 'preSerialization'
})
fastify.addHook('onResponse', (request, reply, done) => {
t.equal(request[kFastifyContext].onError, null)
t.equal(request[kFastifyContext].onRequest, null)
t.equal(request[kFastifyContext].onSend, null)
t.equal(request[kFastifyContext].preHandler, null)
t.equal(request[kFastifyContext].preParsing, null)
t.equal(request[kFastifyContext].preSerialization.length, 1)
t.equal(request[kFastifyContext].preValidation, null)
done()
})
fastify.get('/', (req, reply) => {
reply.send({ nonString: true })
})
await fastify.ready()
const res = await fastify.inject({
method: 'GET',
url: '/'
})
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, '{"nonString":true}')
t.match(res.headers, {
'access-control-allow-origin': '*'
})
t.notMatch(res.headers, { vary: 'Origin' })
})
test('Should support custom hook with dynamic config', t => {
t.plan(16)
const configs = [{
origin: 'example.com',
methods: 'GET',
credentials: true,
exposedHeaders: ['foo', 'bar'],
allowedHeaders: ['baz', 'woo'],
maxAge: 123
}, {
origin: 'sample.com',
methods: 'GET',
credentials: true,
exposedHeaders: ['zoo', 'bar'],
allowedHeaders: ['baz', 'foo'],
maxAge: 321
}]
const fastify = Fastify()
let requestId = 0
const configDelegation = function (req, cb) {
// request should have id
t.ok(req.id)
// request should not have send
t.notOk(req.send)
const config = configs[requestId]
requestId++
if (config) {
cb(null, config)
} else {
cb(new Error('ouch'))
}
}
fastify.register(cors, {
hook: 'preHandler',
delegator: configDelegation
})
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': 'example.com',
'access-control-allow-credentials': 'true',
'access-control-expose-headers': 'foo, bar',
'content-length': '2',
vary: 'Origin'
})
})
fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 204)
t.equal(res.payload, '')
t.match(res.headers, {
'access-control-allow-origin': 'sample.com',
'access-control-allow-credentials': 'true',
'access-control-expose-headers': 'zoo, bar',
'access-control-allow-methods': 'GET',
'access-control-allow-headers': 'baz, foo',
'access-control-max-age': '321',
'content-length': '0',
vary: 'Origin'
})
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
})
})
test('Should support custom hook with dynamic config (callback)', t => {
t.plan(16)
const configs = [{
origin: 'example.com',
methods: 'GET',
credentials: true,
exposedHeaders: ['foo', 'bar'],
allowedHeaders: ['baz', 'woo'],
maxAge: 123
}, {
origin: 'sample.com',
methods: 'GET',
credentials: true,
exposedHeaders: ['zoo', 'bar'],
allowedHeaders: ['baz', 'foo'],
maxAge: 321
}]
const fastify = Fastify()
let requestId = 0
const configDelegation = function (req, cb) {
// request should have id
t.ok(req.id)
// request should not have send
t.notOk(req.send)
const config = configs[requestId]
requestId++
if (config) {
cb(null, config)
} else {
cb(new Error('ouch'))
}
}
fastify.register(cors, {
hook: 'preParsing',
delegator: configDelegation
})
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': 'example.com',
'access-control-allow-credentials': 'true',
'access-control-expose-headers': 'foo, bar',
'content-length': '2',
vary: 'Origin'
})
})
fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 204)
t.equal(res.payload, '')
t.match(res.headers, {
'access-control-allow-origin': 'sample.com',
'access-control-allow-credentials': 'true',
'access-control-expose-headers': 'zoo, bar',
'access-control-allow-methods': 'GET',
'access-control-allow-headers': 'baz, foo',
'access-control-max-age': '321',
'content-length': '0',
vary: 'Origin'
})
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
})
})
test('Should support custom hook with dynamic config (Promise)', t => {
t.plan(16)
const configs = [{
origin: 'example.com',
methods: 'GET',
credentials: true,
exposedHeaders: ['foo', 'bar'],
allowedHeaders: ['baz', 'woo'],
maxAge: 123
}, {
origin: 'sample.com',
methods: 'GET',
credentials: true,
exposedHeaders: ['zoo', 'bar'],
allowedHeaders: ['baz', 'foo'],
maxAge: 321
}]
const fastify = Fastify()
let requestId = 0
const configDelegation = function (req) {
// request should have id
t.ok(req.id)
// request should not have send
t.notOk(req.send)
const config = configs[requestId]
requestId++
if (config) {
return Promise.resolve(config)
} else {
return Promise.reject(new Error('ouch'))
}
}
fastify.register(cors, {
hook: 'preParsing',
delegator: configDelegation
})
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': 'example.com',
'access-control-allow-credentials': 'true',
'access-control-expose-headers': 'foo, bar',
'content-length': '2',
vary: 'Origin'
})
})
fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 204)
t.equal(res.payload, '')
t.match(res.headers, {
'access-control-allow-origin': 'sample.com',
'access-control-allow-credentials': 'true',
'access-control-expose-headers': 'zoo, bar',
'access-control-allow-methods': 'GET',
'access-control-allow-headers': 'baz, foo',
'access-control-max-age': '321',
'content-length': '0',
vary: 'Origin'
})
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
})
})
test('Should support custom hook with dynamic config (Promise), but should error /1', t => {
t.plan(6)
const fastify = Fastify()
const configDelegation = function () {
return false
}
fastify.register(cors, {
hook: 'preParsing',
delegator: configDelegation
})
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 500)
t.equal(res.payload, '{"statusCode":500,"error":"Internal Server Error","message":"Invalid CORS origin option"}')
t.match(res.headers, {
'content-length': '89'
})
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
})
})
test('Should support custom hook with dynamic config (Promise), but should error /2', t => {
t.plan(6)
const fastify = Fastify()
const configDelegation = function () {
return false
}
fastify.register(cors, {
delegator: configDelegation
})
fastify.get('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 500)
t.equal(res.payload, '{"statusCode":500,"error":"Internal Server Error","message":"Invalid CORS origin option"}')
t.match(res.headers, {
'content-length': '89'
})
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 500)
})
})

View File

@@ -0,0 +1,435 @@
'use strict'
const { test } = require('tap')
const Fastify = require('fastify')
const cors = require('../')
test('Should reply to preflight requests', t => {
t.plan(4)
const fastify = Fastify()
fastify.register(cors)
fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 204)
t.equal(res.payload, '')
t.match(res.headers, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
vary: 'Access-Control-Request-Headers',
'content-length': '0'
})
})
})
test('Should add access-control-allow-headers to response if preflight req has access-control-request-headers', t => {
t.plan(4)
const fastify = Fastify()
fastify.register(cors)
fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-headers': 'x-requested-with',
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 204)
t.equal(res.payload, '')
t.match(res.headers, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
'access-control-allow-headers': 'x-requested-with',
vary: 'Access-Control-Request-Headers',
'content-length': '0'
})
})
})
test('Should reply to preflight requests with custom status code', t => {
t.plan(4)
const fastify = Fastify()
fastify.register(cors, { optionsSuccessStatus: 200 })
fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, '')
t.match(res.headers, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
vary: 'Access-Control-Request-Headers',
'content-length': '0'
})
})
})
test('Should be able to override preflight response with a route', t => {
t.plan(5)
const fastify = Fastify()
fastify.register(cors, { preflightContinue: true })
fastify.options('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
// Only the base cors headers and no preflight headers
'access-control-allow-origin': '*'
})
t.notMatch(res.headers, { vary: 'Origin' })
})
})
test('Should reply to all options requests', t => {
t.plan(4)
const fastify = Fastify()
fastify.register(cors)
fastify.inject({
method: 'OPTIONS',
url: '/hello',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 204)
t.equal(res.payload, '')
t.match(res.headers, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
vary: 'Access-Control-Request-Headers',
'content-length': '0'
})
})
})
test('Should support a prefix for preflight requests', t => {
t.plan(6)
const fastify = Fastify()
fastify.register((instance, opts, next) => {
instance.register(cors)
next()
}, { prefix: '/subsystem' })
fastify.inject({
method: 'OPTIONS',
url: '/hello'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 404)
})
fastify.inject({
method: 'OPTIONS',
url: '/subsystem/hello',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 204)
t.equal(res.payload, '')
t.match(res.headers, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
vary: 'Access-Control-Request-Headers',
'content-length': '0'
})
})
})
test('hide options route by default', t => {
t.plan(2)
const fastify = Fastify()
fastify.addHook('onRoute', (route) => {
if (route.method === 'OPTIONS' && route.url === '*') {
t.equal(route.schema.hide, true)
}
})
fastify.register(cors)
fastify.ready(err => {
t.error(err)
})
})
test('show options route', t => {
t.plan(2)
const fastify = Fastify()
fastify.addHook('onRoute', (route) => {
if (route.method === 'OPTIONS' && route.url === '*') {
t.equal(route.schema.hide, false)
}
})
fastify.register(cors, { hideOptionsRoute: false })
fastify.ready(err => {
t.error(err)
})
})
test('Allow only request from with specific methods', t => {
t.plan(4)
const fastify = Fastify()
fastify.register(cors, { methods: ['GET', 'POST'] })
fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 204)
t.match(res.headers, {
'access-control-allow-methods': 'GET, POST'
})
t.notMatch(res.headers, { vary: 'Origin' })
})
})
test('Should reply with 400 error to OPTIONS requests missing origin header when default strictPreflight', t => {
t.plan(3)
const fastify = Fastify()
fastify.register(cors)
fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 400)
t.equal(res.payload, 'Invalid Preflight Request')
})
})
test('Should reply with 400 to OPTIONS requests when missing Access-Control-Request-Method header when default strictPreflight', t => {
t.plan(3)
const fastify = Fastify()
fastify.register(cors, {
strictPreflight: true
})
fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 400)
t.equal(res.payload, 'Invalid Preflight Request')
})
})
test('Should reply to all preflight requests when strictPreflight is disabled', t => {
t.plan(4)
const fastify = Fastify()
fastify.register(cors, { strictPreflight: false })
fastify.inject({
method: 'OPTIONS',
url: '/'
// No access-control-request-method or origin headers
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 204)
t.equal(res.payload, '')
t.match(res.headers, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
vary: 'Access-Control-Request-Headers',
'content-length': '0'
})
})
})
test('Default empty 200 response with preflightContinue on OPTIONS routes', t => {
t.plan(4)
const fastify = Fastify()
fastify.register(cors, { preflightContinue: true })
fastify.inject({
method: 'OPTIONS',
url: '/doesnotexist',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, '')
t.match(res.headers, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
vary: 'Access-Control-Request-Headers'
})
})
})
test('Can override preflight response with preflightContinue', t => {
t.plan(4)
const fastify = Fastify()
fastify.register(cors, { preflightContinue: true })
fastify.options('/', (req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 200)
t.equal(res.payload, 'ok')
t.match(res.headers, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
vary: 'Access-Control-Request-Headers'
})
})
})
test('Should support ongoing prefix ', t => {
t.plan(12)
const fastify = Fastify()
fastify.register(async (instance) => {
instance.register(cors)
}, { prefix: '/prefix' })
// support prefixed route
fastify.inject({
method: 'OPTIONS',
url: '/prefix',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 204)
t.equal(res.payload, '')
t.match(res.headers, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
vary: 'Access-Control-Request-Headers',
'content-length': '0'
})
})
// support prefixed route without / continue
fastify.inject({
method: 'OPTIONS',
url: '/prefixfoo',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 204)
t.equal(res.payload, '')
t.match(res.headers, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
vary: 'Access-Control-Request-Headers',
'content-length': '0'
})
})
// support prefixed route with / continue
fastify.inject({
method: 'OPTIONS',
url: '/prefix/foo',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.error(err)
delete res.headers.date
t.equal(res.statusCode, 204)
t.equal(res.payload, '')
t.match(res.headers, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
vary: 'Access-Control-Request-Headers',
'content-length': '0'
})
})
})

229
backend/node_modules/@fastify/cors/test/vary.test.js generated vendored Normal file
View File

@@ -0,0 +1,229 @@
'use strict'
const test = require('tap').test
const createAddFieldnameToVary = require('../vary').createAddFieldnameToVary
const parse = require('../vary').parse
test('Should set * even if we set a specific field', t => {
t.plan(1)
const addOriginToVary = createAddFieldnameToVary('Origin')
const replyMock = {
getHeader (name) {
return '*'
},
header (name, value) {
t.fail('Should not be here')
}
}
addOriginToVary(replyMock)
t.pass()
})
test('Should set * even if we set a specific field', t => {
t.plan(2)
const addWildcardToVary = createAddFieldnameToVary('*')
const replyMock = {
getHeader (name) {
return 'Origin'
},
header (name, value) {
t.same(name, 'Vary')
t.same(value, '*')
}
}
addWildcardToVary(replyMock)
})
test('Should set * when field contains a *', t => {
t.plan(3)
const addOriginToVary = createAddFieldnameToVary('Origin')
const replyMock = {
getHeader (name) {
return ['Origin', '*', 'Access-Control-Request-Headers']
},
header (name, value) {
t.same(name, 'Vary')
t.same(value, '*')
}
}
addOriginToVary(replyMock)
t.pass()
})
test('Should concat vary values', t => {
t.plan(3)
const addOriginToVary = createAddFieldnameToVary('Origin')
const replyMock = {
getHeader (name) {
return 'Access-Control-Request-Headers'
},
header (name, value) {
t.same(name, 'Vary')
t.same(value, 'Access-Control-Request-Headers, Origin')
}
}
addOriginToVary(replyMock)
t.pass()
})
test('Should concat vary values ignoring consecutive commas', t => {
t.plan(3)
const addOriginToVary = createAddFieldnameToVary('Origin')
const replyMock = {
getHeader (name) {
return ' Access-Control-Request-Headers,Access-Control-Request-Method'
},
header (name, value) {
t.same(name, 'Vary')
t.same(value, ' Access-Control-Request-Headers,Access-Control-Request-Method, Origin')
}
}
addOriginToVary(replyMock)
t.pass()
})
test('Should concat vary values ignoring whitespace', t => {
t.plan(3)
const addOriginToVary = createAddFieldnameToVary('Origin')
const replyMock = {
getHeader (name) {
return ' Access-Control-Request-Headers ,Access-Control-Request-Method'
},
header (name, value) {
t.same(name, 'Vary')
t.same(value, ' Access-Control-Request-Headers ,Access-Control-Request-Method, Origin')
}
}
addOriginToVary(replyMock)
t.pass()
})
test('Should set the field as value for vary if no vary is defined', t => {
t.plan(2)
const addOriginToVary = createAddFieldnameToVary('Origin')
const replyMock = {
getHeader (name) {
return undefined
},
header (name, value) {
t.same(name, 'Vary')
t.same(value, 'Origin')
}
}
addOriginToVary(replyMock)
})
test('Should set * as value for vary if vary contains *', t => {
t.plan(2)
const addOriginToVary = createAddFieldnameToVary('Origin')
const replyMock = {
getHeader (name) {
return 'Accept,*'
},
header (name, value) {
t.same(name, 'Vary')
t.same(value, '*')
}
}
addOriginToVary(replyMock)
})
test('Should set Accept-Encoding as value for vary if vary is empty string', t => {
t.plan(2)
const addAcceptEncodingToVary = createAddFieldnameToVary('Accept-Encoding')
const replyMock = {
getHeader (name) {
return ''
},
header (name, value) {
t.same(name, 'Vary')
t.same(value, 'Accept-Encoding')
}
}
addAcceptEncodingToVary(replyMock)
})
test('Should have no issues with values containing dashes', t => {
t.plan(2)
const addXFooToVary = createAddFieldnameToVary('X-Foo')
const replyMock = {
value: 'Accept-Encoding',
getHeader (name) {
return this.value
},
header (name, value) {
t.same(name, 'Vary')
t.same(value, 'Accept-Encoding, X-Foo')
this.value = value
}
}
addXFooToVary(replyMock)
addXFooToVary(replyMock)
})
test('Should ignore the header as value for vary if it is already in vary', t => {
t.plan(1)
const addOriginToVary = createAddFieldnameToVary('Origin')
const replyMock = {
getHeader (name) {
return 'Origin'
},
header (name, value) {
t.fail('Should not be here')
}
}
addOriginToVary(replyMock)
addOriginToVary(replyMock)
t.pass()
})
test('parse', t => {
t.plan(18)
t.same(parse(''), [])
t.same(parse('a'), ['a'])
t.same(parse('a,b'), ['a', 'b'])
t.same(parse(' a,b'), ['a', 'b'])
t.same(parse('a,b '), ['a', 'b'])
t.same(parse('a,b,c'), ['a', 'b', 'c'])
t.same(parse('A,b,c'), ['a', 'b', 'c'])
t.same(parse('a,b,c,'), ['a', 'b', 'c'])
t.same(parse('a,b,c, '), ['a', 'b', 'c'])
t.same(parse(',a,b,c'), ['a', 'b', 'c'])
t.same(parse(' ,a,b,c'), ['a', 'b', 'c'])
t.same(parse('a,,b,c'), ['a', 'b', 'c'])
t.same(parse('a,,,b,,c'), ['a', 'b', 'c'])
t.same(parse('a, b,c'), ['a', 'b', 'c'])
t.same(parse('a, b,c'), ['a', 'b', 'c'])
t.same(parse('a, , b,c'), ['a', 'b', 'c'])
t.same(parse('a, , b,c'), ['a', 'b', 'c'])
// one for the cache
t.same(parse('A,b,c'), ['a', 'b', 'c'])
})
test('createAddFieldnameToVary', t => {
t.plan(2)
t.same(typeof createAddFieldnameToVary('valid-header'), 'function')
t.throws(() => createAddFieldnameToVary('invalid:header'), TypeError, 'Field contains invalid characters.')
})

116
backend/node_modules/@fastify/cors/types/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,116 @@
/// <reference types="node" />
import { FastifyInstance, FastifyPluginCallback, FastifyRequest } from 'fastify';
type OriginCallback = (err: Error | null, origin: ValueOrArray<OriginType>) => void;
type OriginType = string | boolean | RegExp;
type ValueOrArray<T> = T | ArrayOfValueOrArray<T>;
interface ArrayOfValueOrArray<T> extends Array<ValueOrArray<T>> {
}
type FastifyCorsPlugin = FastifyPluginCallback<
NonNullable<fastifyCors.FastifyCorsOptions> | fastifyCors.FastifyCorsOptionsDelegate
>;
type FastifyCorsHook =
| 'onRequest'
| 'preParsing'
| 'preValidation'
| 'preHandler'
| 'preSerialization'
| 'onSend'
declare namespace fastifyCors {
export type OriginFunction = (origin: string | undefined, callback: OriginCallback) => void;
export interface FastifyCorsOptions {
/**
* Configures the Lifecycle Hook.
*/
hook?: FastifyCorsHook;
/**
* Configures the delegate function.
*/
delegator?: FastifyCorsOptionsDelegate;
/**
* Configures the Access-Control-Allow-Origin CORS header.
*/
origin?: ValueOrArray<OriginType> | fastifyCors.OriginFunction;
/**
* Configures the Access-Control-Allow-Credentials CORS header.
* Set to true to pass the header, otherwise it is omitted.
*/
credentials?: boolean;
/**
* Configures the Access-Control-Expose-Headers CORS header.
* Expects a comma-delimited string (ex: 'Content-Range,X-Content-Range')
* or an array (ex: ['Content-Range', 'X-Content-Range']).
* If not specified, no custom headers are exposed.
*/
exposedHeaders?: string | string[];
/**
* Configures the Access-Control-Allow-Headers CORS header.
* Expects a comma-delimited string (ex: 'Content-Type,Authorization')
* or an array (ex: ['Content-Type', 'Authorization']). If not
* specified, defaults to reflecting the headers specified in the
* request's Access-Control-Request-Headers header.
*/
allowedHeaders?: string | string[];
/**
* Configures the Access-Control-Allow-Methods CORS header.
* Expects a comma-delimited string (ex: 'GET,PUT,POST') or an array (ex: ['GET', 'PUT', 'POST']).
*/
methods?: string | string[];
/**
* Configures the Access-Control-Max-Age CORS header.
* Set to an integer to pass the header, otherwise it is omitted.
*/
maxAge?: number;
/**
* Configures the Cache-Control header for CORS preflight responses.
* Set to an integer to pass the header as `Cache-Control: max-age=${cacheControl}`,
* or set to a string to pass the header as `Cache-Control: ${cacheControl}` (fully define
* the header value), otherwise the header is omitted.
*/
cacheControl?: number | string;
/**
* Pass the CORS preflight response to the route handler (default: false).
*/
preflightContinue?: boolean;
/**
* Provides a status code to use for successful OPTIONS requests,
* since some legacy browsers (IE11, various SmartTVs) choke on 204.
*/
optionsSuccessStatus?: number;
/**
* Pass the CORS preflight response to the route handler (default: false).
*/
preflight?: boolean;
/**
* Enforces strict requirement of the CORS preflight request headers (Access-Control-Request-Method and Origin).
* Preflight requests without the required headers will result in 400 errors when set to `true` (default: `true`).
*/
strictPreflight?: boolean;
/**
* Hide options route from the documentation built using fastify-swagger (default: true).
*/
hideOptionsRoute?: boolean;
}
export interface FastifyCorsOptionsDelegateCallback { (req: FastifyRequest, cb: (error: Error | null, corsOptions?: FastifyCorsOptions) => void): void }
export interface FastifyCorsOptionsDelegatePromise { (req: FastifyRequest): Promise<FastifyCorsOptions> }
export type FastifyCorsOptionsDelegate = FastifyCorsOptionsDelegateCallback | FastifyCorsOptionsDelegatePromise
export type FastifyPluginOptionsDelegate<T = FastifyCorsOptionsDelegate> = (instance: FastifyInstance) => T;
export const fastifyCors: FastifyCorsPlugin
export { fastifyCors as default };
}
declare function fastifyCors(
...params: Parameters<FastifyCorsPlugin>
): ReturnType<FastifyCorsPlugin>;
export = fastifyCors;

View File

@@ -0,0 +1,355 @@
import fastify, { FastifyRequest } from 'fastify'
import { expectType } from 'tsd'
import fastifyCors, {
FastifyCorsOptions,
FastifyCorsOptionsDelegate,
FastifyCorsOptionsDelegatePromise,
FastifyPluginOptionsDelegate,
OriginFunction
} from '..'
const app = fastify()
app.register(fastifyCors)
app.register(fastifyCors, {
origin: true,
allowedHeaders: 'authorization,content-type',
methods: 'GET,POST,PUT,PATCH,DELETE,OPTIONS',
credentials: true,
exposedHeaders: 'authorization',
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
app.register(fastifyCors, {
origin: true,
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 'public, max-age=3500',
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
app.register(fastifyCors, {
origin: '*',
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
app.register(fastifyCors, {
origin: /\*/,
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
app.register(fastifyCors, {
origin: ['*', 'something'],
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
app.register(fastifyCors, {
origin: [/\*/, /something/],
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
const corsDelegate: OriginFunction = (origin, cb) => {
if (origin === undefined || /localhost/.test(origin)) {
cb(null, true)
return
}
cb(new Error(), false)
}
app.register(fastifyCors, {
origin: corsDelegate,
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
app.register(fastifyCors, {
origin: (origin, cb) => cb(null, true)
})
app.register(fastifyCors, {
origin: (origin, cb) => cb(null, '*')
})
app.register(fastifyCors, {
origin: (origin, cb) => cb(null, /\*/)
})
const appHttp2 = fastify({ http2: true })
appHttp2.register(fastifyCors)
appHttp2.register(fastifyCors, {
origin: true,
allowedHeaders: 'authorization,content-type',
methods: 'GET,POST,PUT,PATCH,DELETE,OPTIONS',
credentials: true,
exposedHeaders: 'authorization',
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
appHttp2.register(fastifyCors, {
origin: true,
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
appHttp2.register(fastifyCors, {
origin: '*',
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
appHttp2.register(fastifyCors, {
origin: /\*/,
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
appHttp2.register(fastifyCors, {
origin: ['*', 'something'],
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
appHttp2.register(fastifyCors, {
origin: [/\*/, /something/],
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
appHttp2.register(fastifyCors, {
origin: (origin: string | undefined, cb: (err: Error | null, allow: boolean) => void) => {
if (origin === undefined || /localhost/.test(origin)) {
cb(null, true)
return
}
cb(new Error(), false)
},
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
appHttp2.register(fastifyCors, (): FastifyCorsOptionsDelegate => (req, cb) => {
cb(null, {
origin: [/\*/, /something/],
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
})
appHttp2.register(fastifyCors, (): FastifyCorsOptionsDelegatePromise => (req) => {
return Promise.resolve({
origin: [/\*/, /something/],
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
})
const delegate: FastifyPluginOptionsDelegate<FastifyCorsOptionsDelegatePromise> = () => async (req) => {
return {
origin: [/\*/, /something/],
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
}
}
appHttp2.register(fastifyCors, {
hook: 'onRequest'
})
appHttp2.register(fastifyCors, {
hook: 'preParsing'
})
appHttp2.register(fastifyCors, {
hook: 'preValidation'
})
appHttp2.register(fastifyCors, {
hook: 'preHandler'
})
appHttp2.register(fastifyCors, {
hook: 'preSerialization'
})
appHttp2.register(fastifyCors, {
hook: 'onSend'
})
appHttp2.register(fastifyCors, {
hook: 'preParsing',
delegator: (req, cb) => {
if (req.url.startsWith('/some-value')) {
cb(new Error())
}
cb(null, {
origin: [/\*/, /something/],
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 12000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
}
})
appHttp2.register(fastifyCors, {
hook: 'preParsing',
delegator: async (req: FastifyRequest): Promise<FastifyCorsOptions> => {
return {
origin: [/\*/, /something/],
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 'public, max-age=3500',
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
}
}
})
appHttp2.register(fastifyCors, delegate)
appHttp2.register(fastifyCors, {
hook: 'preParsing',
origin: function (origin) {
expectType<string|undefined>(origin)
},
})

116
backend/node_modules/@fastify/cors/vary.js generated vendored Normal file
View File

@@ -0,0 +1,116 @@
'use strict'
const LRUCache = require('mnemonist/lru-cache')
/**
* Field Value Components
* Most HTTP header field values are defined using common syntax
* components (token, quoted-string, and comment) separated by
* whitespace or specific delimiting characters. Delimiters are chosen
* from the set of US-ASCII visual characters not allowed in a token
* (DQUOTE and "(),/:;<=>?@[\]{}").
*
* field-name = token
* token = 1*tchar
* tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
* / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
* / DIGIT / ALPHA
* ; any VCHAR, except delimiters
*
* @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6
*/
const validFieldnameRE = /^[!#$%&'*+\-.^\w`|~]+$/u
function validateFieldname (fieldname) {
if (validFieldnameRE.test(fieldname) === false) {
throw new TypeError('Fieldname contains invalid characters.')
}
}
function parse (header) {
header = header.trim().toLowerCase()
const result = []
if (header.length === 0) {
// pass through
} else if (header.indexOf(',') === -1) {
result.push(header)
} else {
const il = header.length
let i = 0
let pos = 0
let char
// tokenize the header
for (i = 0; i < il; ++i) {
char = header[i]
// when we have whitespace set the pos to the next position
if (char === ' ') {
pos = i + 1
// `,` is the separator of vary-values
} else if (char === ',') {
// if pos and current position are not the same we have a valid token
if (pos !== i) {
result.push(header.slice(pos, i))
}
// reset the positions
pos = i + 1
}
}
if (pos !== i) {
result.push(header.slice(pos, i))
}
}
return result
}
function createAddFieldnameToVary (fieldname) {
const headerCache = new LRUCache(1000)
validateFieldname(fieldname)
return function (reply) {
let header = reply.getHeader('Vary')
if (!header) {
reply.header('Vary', fieldname)
return
}
if (header === '*') {
return
}
if (fieldname === '*') {
reply.header('Vary', '*')
return
}
if (Array.isArray(header)) {
header = header.join(', ')
}
if (!headerCache.has(header)) {
const vals = parse(header)
if (vals.indexOf('*') !== -1) {
headerCache.set(header, '*')
} else if (vals.indexOf(fieldname.toLowerCase()) === -1) {
headerCache.set(header, header + ', ' + fieldname)
} else {
headerCache.set(header, null)
}
}
const cached = headerCache.get(header)
if (cached !== null) {
reply.header('Vary', cached)
}
}
}
module.exports.createAddFieldnameToVary = createAddFieldnameToVary
module.exports.addOriginToVaryHeader = createAddFieldnameToVary('Origin')
module.exports.addAccessControlRequestHeadersToVaryHeader = createAddFieldnameToVary('Access-Control-Request-Headers')
module.exports.parse = parse