-
Notifications
You must be signed in to change notification settings - Fork 65
New generic mode that always returns * (less secure, better caching) #32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,8 @@ | ||
|
||
exports.generic = function (list) { | ||
return list.length === 1 && list[0] === '*' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a case where we would want to explicitly match some domains, and wildcard the rest? Would it be surprising that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that's quite strange, not 100% happy with that design. Do you think it would make more sense to have some sort of option flag like {
// specify one or more origins, wildcard supported
// will only return the matched origin, with a Vary header
origins: ['https://myapp.com', 'https://*.myapp.com']
// or use the generic mode
// which always returns "Accept", and can be cached across the board
// but has security implications
allowAllOrigins: true
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the |
||
} | ||
|
||
exports.create = function (allowedOrigins) { | ||
// pre-compile list of matchers, so regexes are only built once | ||
var matchers = allowedOrigins.map(createMatcher) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,42 +3,70 @@ var constants = require('./constants.js') | |
|
||
exports.handler = function (options) { | ||
var matcher = originMatcher.create(options.origins) | ||
return function (req, res, next) { | ||
if (req.method !== 'OPTIONS') return next() | ||
|
||
// 6.2.1 and 6.2.2 | ||
var originHeader = req.headers['origin'] | ||
if (!matcher(originHeader)) return next() | ||
if (originMatcher.generic(options.origins)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment as above, but to a much greater extent. Is there a way we can do this that doesn't treat the whole block as a special case? We are adding an incredibly amount of complexity here to support this. |
||
// | ||
// If origins = ['*'] then we always set generic CORS headers | ||
// This is the simplest case, similar to what restify.fullResponse() used to do | ||
// Must must keep the headers generic because they can be cached by reverse proxies | ||
// | ||
|
||
// 6.2.3 | ||
var requestedMethod = req.headers[constants['AC_REQ_METHOD']] | ||
if (!requestedMethod) return next() | ||
return function (req, res, next) { | ||
if (req.method !== 'OPTIONS') return next() | ||
res.once('header', function () { | ||
res.header(constants['AC_ALLOW_ORIGIN'], '*') | ||
res.header(constants['AC_ALLOW_CREDS'], false) // not compatible with * | ||
res.header(constants['AC_ALLOW_METHODS'], 'GET, PUT, POST, DELETE, OPTIONS') | ||
res.header(constants['AC_ALLOW_HEADERS'], options.allowHeaders.join(', ')) | ||
}) | ||
res.send(constants['HTTP_NO_CONTENT']) | ||
} | ||
} else { | ||
// | ||
// Full CORS mode | ||
// This is the "better" option where we have a list of origins | ||
// In this case, we return customised CORS headers for each request | ||
// And must set the "Vary: Origin" header | ||
// | ||
|
||
// 6.2.4 | ||
// var requestedHeaders = req.headers[constants['AC_REQ_HEADERS']] | ||
// requestedHeaders = requestedHeaders ? requestedHeaders.split(', ') : [] | ||
return function (req, res, next) { | ||
if (req.method !== 'OPTIONS') return next() | ||
|
||
var allowedMethods = [requestedMethod, 'OPTIONS'] | ||
var allowedHeaders = constants['ALLOW_HEADERS'] | ||
.concat(options.allowHeaders) | ||
// 6.2.1 and 6.2.2 | ||
var originHeader = req.headers['origin'] | ||
if (!matcher(originHeader)) return next() | ||
|
||
res.once('header', function () { | ||
// 6.2.7 | ||
res.header(constants['AC_ALLOW_ORIGIN'], originHeader) | ||
res.header(constants['AC_ALLOW_CREDS'], true) | ||
// 6.2.3 | ||
var requestedMethod = req.headers[constants['AC_REQ_METHOD']] | ||
if (!requestedMethod) return next() | ||
|
||
// 6.2.8 | ||
if (options.preflightMaxAge) { | ||
res.header(constants['AC_MAX_AGE'], options.preflightMaxAge) | ||
} | ||
// 6.2.4 | ||
// var requestedHeaders = req.headers[constants['AC_REQ_HEADERS']] | ||
// requestedHeaders = requestedHeaders ? requestedHeaders.split(', ') : [] | ||
var allowedMethods = [requestedMethod, 'OPTIONS'] | ||
var allowedHeaders = constants['ALLOW_HEADERS'].concat(options.allowHeaders) | ||
|
||
// 6.2.9 | ||
res.header(constants['AC_ALLOW_METHODS'], allowedMethods.join(', ')) | ||
res.once('header', function () { | ||
// 6.2.7 | ||
res.header(constants['AC_ALLOW_ORIGIN'], originHeader) | ||
res.header(constants['AC_ALLOW_CREDS'], true) | ||
|
||
// 6.2.10 | ||
res.header(constants['AC_ALLOW_HEADERS'], allowedHeaders.join(', ')) | ||
}) | ||
// 6.2.8 | ||
if (options.preflightMaxAge) { | ||
res.header(constants['AC_MAX_AGE'], options.preflightMaxAge) | ||
} | ||
|
||
res.send(constants['HTTP_NO_CONTENT']) | ||
// 6.2.9 | ||
res.header(constants['AC_ALLOW_METHODS'], allowedMethods.join(', ')) | ||
|
||
// 6.2.10 | ||
res.header(constants['AC_ALLOW_HEADERS'], allowedHeaders.join(', ')) | ||
|
||
// 6.4 | ||
res.header('Vary', 'Origin') | ||
}) | ||
|
||
res.send(constants['HTTP_NO_CONTENT']) | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to handle this without treating the whole block as a special case? A nested
if/else
of this size increases the complexity of the code base quite a lot and fragments related changes across multiple lines in the code base.