Skip to content

Commit 8340081

Browse files
authored
Stricter options (#101)
* feat: stricter options handling * chore: clarify origin documentation * test: falsy non false origin option
1 parent abafaa1 commit 8340081

File tree

3 files changed

+56
-5
lines changed

3 files changed

+56
-5
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ You can use it as is without passing any option, or you can configure it as expl
3232
### Options
3333
* `origin`: Configures the **Access-Control-Allow-Origin** CORS header. The value of origin could be of different types:
3434
- `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.
35-
- `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.
35+
- `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.
3636
- `RegExp` - set `origin` to a regular expression pattern which will be used to test the request origin. If it's 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".
3737
- `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".
3838
- `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:

index.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,20 @@ function fastifyCors (fastify, opts, next) {
5252
return next(error)
5353
}
5454

55-
req.corsPreflightEnabled = resolvedOriginOption
56-
5755
// Disable CORS and preflight if false
5856
if (resolvedOriginOption === false) {
57+
req.corsPreflightEnabled = false
5958
return next()
6059
}
6160

61+
// Falsy values are invalid
62+
if (!resolvedOriginOption) {
63+
return next(new Error('Invalid CORS origin option'))
64+
}
65+
66+
// Enable preflight
67+
req.corsPreflightEnabled = true
68+
6269
reply.header('Access-Control-Allow-Origin',
6370
getAccessControlAllowOriginHeader(req.headers.origin, resolvedOriginOption))
6471

@@ -78,7 +85,7 @@ function fastifyCors (fastify, opts, next) {
7885
}
7986

8087
function preflightHandler (req, reply) {
81-
// Do not handle preflight requests if the origin was not allowed
88+
// Do not handle preflight requests if the origin option disabled CORS
8289
if (!req.corsPreflightEnabled) {
8390
reply.code(404).type('text/plain').send('Not Found')
8491
return
@@ -132,7 +139,7 @@ function fastifyCors (fastify, opts, next) {
132139
}
133140

134141
function getAccessControlAllowOriginHeader (reqOrigin, originOption) {
135-
if (!originOption || originOption === '*') {
142+
if (originOption === '*') {
136143
// allow any origin
137144
return '*'
138145
}

test/cors.test.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,26 @@ test('Dynamic origin resolution (errored)', t => {
170170
})
171171
})
172172

173+
test('Dynamic origin resolution (invalid result)', t => {
174+
t.plan(3)
175+
176+
const fastify = Fastify()
177+
const origin = (header, cb) => {
178+
t.strictEqual(header, 'example.com')
179+
cb(null, undefined)
180+
}
181+
fastify.register(cors, { origin })
182+
183+
fastify.inject({
184+
method: 'GET',
185+
url: '/',
186+
headers: { origin: 'example.com' }
187+
}, (err, res) => {
188+
t.error(err)
189+
t.strictEqual(res.statusCode, 500)
190+
})
191+
})
192+
173193
test('Dynamic origin resolution (valid origin - promises)', t => {
174194
t.plan(5)
175195

@@ -308,6 +328,30 @@ test('Should reply 404 without cors headers other than `vary` when origin is fal
308328
})
309329
})
310330

331+
test('Server error if origin option is falsy but not false', t => {
332+
t.plan(4)
333+
334+
const fastify = Fastify()
335+
fastify.register(cors, { origin: '' })
336+
337+
fastify.inject({
338+
method: 'GET',
339+
url: '/',
340+
headers: { origin: 'example.com' }
341+
}, (err, res) => {
342+
t.error(err)
343+
delete res.headers.date
344+
t.strictEqual(res.statusCode, 500)
345+
t.deepEqual(res.json(), { statusCode: 500, error: 'Internal Server Error', message: 'Invalid CORS origin option' })
346+
t.deepEqual(res.headers, {
347+
'content-length': '89',
348+
'content-type': 'application/json; charset=utf-8',
349+
connection: 'keep-alive',
350+
vary: 'Origin'
351+
})
352+
})
353+
})
354+
311355
test('Allow only request from a specific origin', t => {
312356
t.plan(4)
313357

0 commit comments

Comments
 (0)