pax_global_header00006660000000000000000000000064133676563430014532gustar00rootroot0000000000000052 comment=9158a8686d64bf567440d030873378c429ad60b0 cors-2.8.5/000077500000000000000000000000001336765634300125145ustar00rootroot00000000000000cors-2.8.5/.eslintrc.yml000066400000000000000000000002241336765634300151360ustar00rootroot00000000000000root: true env: node: true rules: indent: ["error", 2, { "SwitchCase": 1 }] quotes: ["error", "single"] space-in-parens: ["error", "never"] cors-2.8.5/.gitignore000066400000000000000000000001051336765634300145000ustar00rootroot00000000000000.nyc_output/ coverage/ node_modules/ npm-debug.log package-lock.json cors-2.8.5/.travis.yml000066400000000000000000000025441336765634300146320ustar00rootroot00000000000000language: node_js node_js: - "0.10" - "4.9" - "6.14" - "8.12" - "10.11" sudo: false cache: directories: - node_modules before_install: # Configure npm - | # Skip updating shrinkwrap / lock npm config set shrinkwrap false # Setup Node.js version-specific dependencies - | # mocha for testing # - use 3.x for Node.js < 6 if [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 6 ]]; then npm install --save-dev mocha@3.5.3 fi - | # nyc for coverage # - use 10.x for Node.js < 4 # - use 11.x for Node.js < 6 if [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 4 ]]; then npm install --save-dev nyc@10.3.2 elif [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 6 ]]; then npm install --save-dev nyc@11.9.0 fi - | # supertest for http calls # - use 2.0.0 for Node.js < 4 if [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 4 ]]; then npm install --save-dev supertest@2.0.0 fi # Update Node.js modules - | # Prune & rebuild node_modules if [[ -d node_modules ]]; then npm prune npm rebuild fi script: - | # Run test script npm test after_script: - | # Upload coverage to coveralls if exists if [[ -d .nyc_output ]]; then npm install --save-dev coveralls@2 nyc report --reporter=text-lcov | coveralls fi cors-2.8.5/CONTRIBUTING.md000066400000000000000000000020041336765634300147410ustar00rootroot00000000000000# contributing to `cors` CORS is a node.js package for providing a [connect](http://www.senchalabs.org/connect/)/[express](http://expressjs.com/) middleware that can be used to enable [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing) with various options. Learn more about the project in [the README](README.md). ## The CORS Spec [http://www.w3.org/TR/cors/](http://www.w3.org/TR/cors/) ## Pull Requests Welcome * Include `'use strict';` in every javascript file. * 2 space indentation. * Please run the testing steps below before submitting. ## Testing ```bash $ npm install $ npm test ``` ## Interactive Testing Harness [http://node-cors-client.herokuapp.com](http://node-cors-client.herokuapp.com) Related git repositories: * [https://github.com/TroyGoode/node-cors-server](https://github.com/TroyGoode/node-cors-server) * [https://github.com/TroyGoode/node-cors-client](https://github.com/TroyGoode/node-cors-client) ## License [MIT License](http://www.opensource.org/licenses/mit-license.php) cors-2.8.5/HISTORY.md000066400000000000000000000022211336765634300141740ustar00rootroot000000000000002.8.5 / 2018-11-04 ================== * Fix setting `maxAge` option to `0` 2.8.4 / 2017-07-12 ================== * Work-around Safari bug in default pre-flight response 2.8.3 / 2017-03-29 ================== * Fix error when options delegate missing `methods` option 2.8.2 / 2017-03-28 ================== * Fix error when frozen options are passed * Send "Vary: Origin" when using regular expressions * Send "Vary: Access-Control-Request-Headers" when dynamic `allowedHeaders` 2.8.1 / 2016-09-08 ================== This release only changed documentation. 2.8.0 / 2016-08-23 ================== * Add `optionsSuccessStatus` option 2.7.2 / 2016-08-23 ================== * Fix error when Node.js running in strict mode 2.7.1 / 2015-05-28 ================== * Move module into expressjs organization 2.7.0 / 2015-05-28 ================== * Allow array of matching condition as `origin` option * Allow regular expression as `origin` option 2.6.1 / 2015-05-28 ================== * Update `license` in package.json 2.6.0 / 2015-04-27 ================== * Add `preflightContinue` option * Fix "Vary: Origin" header added for "*" cors-2.8.5/LICENSE000066400000000000000000000021071336765634300135210ustar00rootroot00000000000000(The MIT License) Copyright (c) 2013 Troy Goode 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. cors-2.8.5/README.md000066400000000000000000000217661336765634300140070ustar00rootroot00000000000000# cors [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![Build Status][travis-image]][travis-url] [![Test Coverage][coveralls-image]][coveralls-url] CORS is a node.js package for providing a [Connect](http://www.senchalabs.org/connect/)/[Express](http://expressjs.com/) middleware that can be used to enable [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing) with various options. **[Follow me (@troygoode) on Twitter!](https://twitter.com/intent/user?screen_name=troygoode)** * [Installation](#installation) * [Usage](#usage) * [Simple Usage](#simple-usage-enable-all-cors-requests) * [Enable CORS for a Single Route](#enable-cors-for-a-single-route) * [Configuring CORS](#configuring-cors) * [Configuring CORS Asynchronously](#configuring-cors-asynchronously) * [Enabling CORS Pre-Flight](#enabling-cors-pre-flight) * [Configuration Options](#configuration-options) * [Demo](#demo) * [License](#license) * [Author](#author) ## Installation This is a [Node.js](https://nodejs.org/en/) module available through the [npm registry](https://www.npmjs.com/). Installation is done using the [`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): ```sh $ npm install cors ``` ## Usage ### Simple Usage (Enable *All* CORS Requests) ```javascript var express = require('express') var cors = require('cors') var app = express() app.use(cors()) app.get('/products/:id', function (req, res, next) { res.json({msg: 'This is CORS-enabled for all origins!'}) }) app.listen(80, function () { console.log('CORS-enabled web server listening on port 80') }) ``` ### Enable CORS for a Single Route ```javascript var express = require('express') var cors = require('cors') var app = express() app.get('/products/:id', cors(), function (req, res, next) { res.json({msg: 'This is CORS-enabled for a Single Route'}) }) app.listen(80, function () { console.log('CORS-enabled web server listening on port 80') }) ``` ### Configuring CORS ```javascript var express = require('express') var cors = require('cors') var app = express() var corsOptions = { origin: 'http://example.com', optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204 } app.get('/products/:id', cors(corsOptions), function (req, res, next) { res.json({msg: 'This is CORS-enabled for only example.com.'}) }) app.listen(80, function () { console.log('CORS-enabled web server listening on port 80') }) ``` ### Configuring CORS w/ Dynamic Origin ```javascript var express = require('express') var cors = require('cors') var app = express() var whitelist = ['http://example1.com', 'http://example2.com'] var corsOptions = { origin: function (origin, callback) { if (whitelist.indexOf(origin) !== -1) { callback(null, true) } else { callback(new Error('Not allowed by CORS')) } } } app.get('/products/:id', cors(corsOptions), function (req, res, next) { res.json({msg: 'This is CORS-enabled for a whitelisted domain.'}) }) app.listen(80, function () { console.log('CORS-enabled web server listening on port 80') }) ``` If you do not want to block REST tools or server-to-server requests, add a `!origin` check in the origin function like so: ```javascript var corsOptions = { origin: function (origin, callback) { if (whitelist.indexOf(origin) !== -1 || !origin) { callback(null, true) } else { callback(new Error('Not allowed by CORS')) } } } ``` ### Enabling CORS Pre-Flight Certain CORS requests are considered 'complex' and require an initial `OPTIONS` request (called the "pre-flight request"). An example of a 'complex' CORS request is one that uses an HTTP verb other than GET/HEAD/POST (such as DELETE) or that uses custom headers. To enable pre-flighting, you must add a new OPTIONS handler for the route you want to support: ```javascript var express = require('express') var cors = require('cors') var app = express() app.options('/products/:id', cors()) // enable pre-flight request for DELETE request app.del('/products/:id', cors(), function (req, res, next) { res.json({msg: 'This is CORS-enabled for all origins!'}) }) app.listen(80, function () { console.log('CORS-enabled web server listening on port 80') }) ``` You can also enable pre-flight across-the-board like so: ```javascript app.options('*', cors()) // include before other routes ``` ### Configuring CORS Asynchronously ```javascript var express = require('express') var cors = require('cors') var app = express() var whitelist = ['http://example1.com', 'http://example2.com'] var corsOptionsDelegate = function (req, callback) { var corsOptions; if (whitelist.indexOf(req.header('Origin')) !== -1) { corsOptions = { origin: true } // reflect (enable) the requested origin in the CORS response } else { corsOptions = { origin: false } // disable CORS for this request } callback(null, corsOptions) // callback expects two parameters: error and options } app.get('/products/:id', cors(corsOptionsDelegate), function (req, res, next) { res.json({msg: 'This is CORS-enabled for a whitelisted domain.'}) }) app.listen(80, function () { console.log('CORS-enabled web server listening on port 80') }) ``` ## Configuration Options * `origin`: Configures the **Access-Control-Allow-Origin** CORS header. Possible values: - `Boolean` - set `origin` to `true` to reflect the [request origin](http://tools.ietf.org/html/draft-abarth-origin-09), as defined by `req.header('Origin')`, 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. - `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". - `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 (which expects the signature `err [object], allow [bool]`) as the second. * `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']`). * `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. Set to an integer to pass the header, otherwise it is omitted. * `preflightContinue`: Pass the CORS preflight response to the next handler. * `optionsSuccessStatus`: Provides a status code to use for successful `OPTIONS` requests, since some legacy browsers (IE11, various SmartTVs) choke on `204`. The default configuration is the equivalent of: ```json { "origin": "*", "methods": "GET,HEAD,PUT,PATCH,POST,DELETE", "preflightContinue": false, "optionsSuccessStatus": 204 } ``` For details on the effect of each CORS header, read [this](http://www.html5rocks.com/en/tutorials/cors/) article on HTML5 Rocks. ## Demo A demo that illustrates CORS working (and not working) using jQuery is available here: [http://node-cors-client.herokuapp.com/](http://node-cors-client.herokuapp.com/) Code for that demo can be found here: * Client: [https://github.com/TroyGoode/node-cors-client](https://github.com/TroyGoode/node-cors-client) * Server: [https://github.com/TroyGoode/node-cors-server](https://github.com/TroyGoode/node-cors-server) ## License [MIT License](http://www.opensource.org/licenses/mit-license.php) ## Author [Troy Goode](https://github.com/TroyGoode) ([troygoode@gmail.com](mailto:troygoode@gmail.com)) [coveralls-image]: https://img.shields.io/coveralls/expressjs/cors/master.svg [coveralls-url]: https://coveralls.io/r/expressjs/cors?branch=master [downloads-image]: https://img.shields.io/npm/dm/cors.svg [downloads-url]: https://npmjs.org/package/cors [npm-image]: https://img.shields.io/npm/v/cors.svg [npm-url]: https://npmjs.org/package/cors [travis-image]: https://img.shields.io/travis/expressjs/cors/master.svg [travis-url]: https://travis-ci.org/expressjs/cors cors-2.8.5/lib/000077500000000000000000000000001336765634300132625ustar00rootroot00000000000000cors-2.8.5/lib/index.js000066400000000000000000000147371336765634300147430ustar00rootroot00000000000000(function () { 'use strict'; var assign = require('object-assign'); var vary = require('vary'); var defaults = { origin: '*', methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', preflightContinue: false, optionsSuccessStatus: 204 }; function isString(s) { return typeof s === 'string' || s instanceof String; } function isOriginAllowed(origin, allowedOrigin) { if (Array.isArray(allowedOrigin)) { for (var i = 0; i < allowedOrigin.length; ++i) { if (isOriginAllowed(origin, allowedOrigin[i])) { return true; } } return false; } else if (isString(allowedOrigin)) { return origin === allowedOrigin; } else if (allowedOrigin instanceof RegExp) { return allowedOrigin.test(origin); } else { return !!allowedOrigin; } } function configureOrigin(options, req) { var requestOrigin = req.headers.origin, headers = [], isAllowed; if (!options.origin || options.origin === '*') { // allow any origin headers.push([{ key: 'Access-Control-Allow-Origin', value: '*' }]); } else if (isString(options.origin)) { // fixed origin headers.push([{ key: 'Access-Control-Allow-Origin', value: options.origin }]); headers.push([{ key: 'Vary', value: 'Origin' }]); } else { isAllowed = isOriginAllowed(requestOrigin, options.origin); // reflect origin headers.push([{ key: 'Access-Control-Allow-Origin', value: isAllowed ? requestOrigin : false }]); headers.push([{ key: 'Vary', value: 'Origin' }]); } return headers; } function configureMethods(options) { var methods = options.methods; if (methods.join) { methods = options.methods.join(','); // .methods is an array, so turn it into a string } return { key: 'Access-Control-Allow-Methods', value: methods }; } function configureCredentials(options) { if (options.credentials === true) { return { key: 'Access-Control-Allow-Credentials', value: 'true' }; } return null; } function configureAllowedHeaders(options, req) { var allowedHeaders = options.allowedHeaders || options.headers; var headers = []; if (!allowedHeaders) { allowedHeaders = req.headers['access-control-request-headers']; // .headers wasn't specified, so reflect the request headers headers.push([{ key: 'Vary', value: 'Access-Control-Request-Headers' }]); } else if (allowedHeaders.join) { allowedHeaders = allowedHeaders.join(','); // .headers is an array, so turn it into a string } if (allowedHeaders && allowedHeaders.length) { headers.push([{ key: 'Access-Control-Allow-Headers', value: allowedHeaders }]); } return headers; } function configureExposedHeaders(options) { var headers = options.exposedHeaders; if (!headers) { return null; } else if (headers.join) { headers = headers.join(','); // .headers is an array, so turn it into a string } if (headers && headers.length) { return { key: 'Access-Control-Expose-Headers', value: headers }; } return null; } function configureMaxAge(options) { var maxAge = (typeof options.maxAge === 'number' || options.maxAge) && options.maxAge.toString() if (maxAge && maxAge.length) { return { key: 'Access-Control-Max-Age', value: maxAge }; } return null; } function applyHeaders(headers, res) { for (var i = 0, n = headers.length; i < n; i++) { var header = headers[i]; if (header) { if (Array.isArray(header)) { applyHeaders(header, res); } else if (header.key === 'Vary' && header.value) { vary(res, header.value); } else if (header.value) { res.setHeader(header.key, header.value); } } } } function cors(options, req, res, next) { var headers = [], method = req.method && req.method.toUpperCase && req.method.toUpperCase(); if (method === 'OPTIONS') { // preflight headers.push(configureOrigin(options, req)); headers.push(configureCredentials(options, req)); headers.push(configureMethods(options, req)); headers.push(configureAllowedHeaders(options, req)); headers.push(configureMaxAge(options, req)); headers.push(configureExposedHeaders(options, req)); applyHeaders(headers, res); if (options.preflightContinue) { next(); } else { // Safari (and potentially other browsers) need content-length 0, // for 204 or they just hang waiting for a body res.statusCode = options.optionsSuccessStatus; res.setHeader('Content-Length', '0'); res.end(); } } else { // actual response headers.push(configureOrigin(options, req)); headers.push(configureCredentials(options, req)); headers.push(configureExposedHeaders(options, req)); applyHeaders(headers, res); next(); } } function middlewareWrapper(o) { // if options are static (either via defaults or custom options passed in), wrap in a function var optionsCallback = null; if (typeof o === 'function') { optionsCallback = o; } else { optionsCallback = function (req, cb) { cb(null, o); }; } return function corsMiddleware(req, res, next) { optionsCallback(req, function (err, options) { if (err) { next(err); } else { var corsOptions = assign({}, defaults, options); var originCallback = null; if (corsOptions.origin && typeof corsOptions.origin === 'function') { originCallback = corsOptions.origin; } else if (corsOptions.origin) { originCallback = function (origin, cb) { cb(null, corsOptions.origin); }; } if (originCallback) { originCallback(req.headers.origin, function (err2, origin) { if (err2 || !origin) { next(err2); } else { corsOptions.origin = origin; cors(corsOptions, req, res, next); } }); } else { next(); } } }); }; } // can pass either an options hash, an options delegate, or nothing module.exports = middlewareWrapper; }()); cors-2.8.5/package.json000066400000000000000000000015431336765634300150050ustar00rootroot00000000000000{ "name": "cors", "description": "Node.js CORS middleware", "version": "2.8.5", "author": "Troy Goode (https://github.com/troygoode/)", "license": "MIT", "keywords": [ "cors", "express", "connect", "middleware" ], "repository": "expressjs/cors", "main": "./lib/index.js", "dependencies": { "object-assign": "^4", "vary": "^1" }, "devDependencies": { "after": "0.8.2", "eslint": "2.13.1", "express": "4.16.3", "mocha": "5.2.0", "nyc": "13.1.0", "supertest": "3.3.0" }, "files": [ "lib/index.js", "CONTRIBUTING.md", "HISTORY.md", "LICENSE", "README.md" ], "engines": { "node": ">= 0.10" }, "scripts": { "test": "npm run lint && nyc --reporter=html --reporter=text mocha --require test/support/env", "lint": "eslint lib test" } } cors-2.8.5/test/000077500000000000000000000000001336765634300134735ustar00rootroot00000000000000cors-2.8.5/test/.eslintrc.yml000066400000000000000000000000231336765634300161120ustar00rootroot00000000000000env: mocha: true cors-2.8.5/test/error-response.js000066400000000000000000000026511336765634300170220ustar00rootroot00000000000000(function () { 'use strict'; var express = require('express'), supertest = require('supertest'), cors = require('../lib'); var app; /* -------------------------------------------------------------------------- */ app = express(); app.use(cors()); app.post('/five-hundred', function (req, res, next) { next(new Error('nope')); }); app.post('/four-oh-one', function (req, res, next) { next(new Error('401')); }); app.post('/four-oh-four', function (req, res, next) { next(); }); app.use(function (err, req, res, next) { if (err.message === '401') { res.status(401).send('unauthorized'); } else { next(err); } }); /* -------------------------------------------------------------------------- */ describe('error response', function () { it('500', function (done) { supertest(app) .post('/five-hundred') .expect(500) .expect('Access-Control-Allow-Origin', '*') .expect(/Error: nope/) .end(done) }); it('401', function (done) { supertest(app) .post('/four-oh-one') .expect(401) .expect('Access-Control-Allow-Origin', '*') .expect('unauthorized') .end(done) }); it('404', function (done) { supertest(app) .post('/four-oh-four') .expect(404) .expect('Access-Control-Allow-Origin', '*') .end(done) }); }); }()); cors-2.8.5/test/example-app.js000066400000000000000000000041721336765634300162460ustar00rootroot00000000000000(function () { 'use strict'; var express = require('express'), supertest = require('supertest'), cors = require('../lib'); var simpleApp, complexApp; /* -------------------------------------------------------------------------- */ simpleApp = express(); simpleApp.head('/', cors(), function (req, res) { res.status(204).send(); }); simpleApp.get('/', cors(), function (req, res) { res.send('Hello World (Get)'); }); simpleApp.post('/', cors(), function (req, res) { res.send('Hello World (Post)'); }); /* -------------------------------------------------------------------------- */ complexApp = express(); complexApp.options('/', cors()); complexApp.delete('/', cors(), function (req, res) { res.send('Hello World (Delete)'); }); /* -------------------------------------------------------------------------- */ describe('example app(s)', function () { describe('simple methods', function () { it('GET works', function (done) { supertest(simpleApp) .get('/') .expect(200) .expect('Access-Control-Allow-Origin', '*') .expect('Hello World (Get)') .end(done) }); it('HEAD works', function (done) { supertest(simpleApp) .head('/') .expect(204) .expect('Access-Control-Allow-Origin', '*') .end(done) }); it('POST works', function (done) { supertest(simpleApp) .post('/') .expect(200) .expect('Access-Control-Allow-Origin', '*') .expect('Hello World (Post)') .end(done) }); }); describe('complex methods', function () { it('OPTIONS works', function (done) { supertest(complexApp) .options('/') .expect(204) .expect('Access-Control-Allow-Origin', '*') .end(done) }); it('DELETE works', function (done) { supertest(complexApp) .del('/') .expect(200) .expect('Access-Control-Allow-Origin', '*') .expect('Hello World (Delete)') .end(done) }); }); }); }()); cors-2.8.5/test/issue-2.js000066400000000000000000000022401336765634300153160ustar00rootroot00000000000000(function () { 'use strict'; var express = require('express'), supertest = require('supertest'), cors = require('../lib'); var app, corsOptions; /* -------------------------------------------------------------------------- */ app = express(); corsOptions = { origin: true, methods: ['POST'], credentials: true, maxAge: 3600 }; app.options('/api/login', cors(corsOptions)); app.post('/api/login', cors(corsOptions), function (req, res) { res.send('LOGIN'); }); /* -------------------------------------------------------------------------- */ describe('issue #2', function () { it('OPTIONS works', function (done) { supertest(app) .options('/api/login') .set('Origin', 'http://example.com') .expect(204) .expect('Access-Control-Allow-Origin', 'http://example.com') .end(done) }); it('POST works', function (done) { supertest(app) .post('/api/login') .set('Origin', 'http://example.com') .expect(200) .expect('Access-Control-Allow-Origin', 'http://example.com') .expect('LOGIN') .end(done) }); }); }()); cors-2.8.5/test/support/000077500000000000000000000000001336765634300152075ustar00rootroot00000000000000cors-2.8.5/test/support/env.js000066400000000000000000000000401336765634300163270ustar00rootroot00000000000000 process.env.NODE_ENV = 'test'; cors-2.8.5/test/test.js000066400000000000000000000532701336765634300150170ustar00rootroot00000000000000(function () { 'use strict'; var after = require('after') var assert = require('assert') var cors = require('..') var fakeRequest = function (method, headers) { return { method: method, headers: headers || { 'origin': 'request.com', 'access-control-request-headers': 'requestedHeader1,requestedHeader2' } }; }, fakeResponse = function () { var headers = {}; return { getHeader: function (key) { return headers[key]; }, setHeader: function (key, value) { headers[key] = value; return; }, get: function (key) { return headers[key]; } }; }; describe('cors', function () { it('does not alter `options` configuration object', function () { var options = Object.freeze({ origin: 'custom-origin' }); assert.doesNotThrow(function () { cors(options); }) }); it('passes control to next middleware', function (done) { // arrange var req, res, next; req = fakeRequest('GET'); res = fakeResponse(); next = function () { done(); }; // act cors()(req, res, next); }); it('shortcircuits preflight requests', function (done) { // arrange var cb = after(1, done) var req, res, next; req = fakeRequest('OPTIONS'); res = fakeResponse(); res.end = function () { // assert assert.equal(res.statusCode, 204) cb() }; next = function () { // assert cb(new Error('should not be called')) }; // act cors()(req, res, next); }); it('can configure preflight success response status code', function (done) { // arrange var cb = after(1, done) var req, res, next; req = fakeRequest('OPTIONS'); res = fakeResponse(); res.end = function () { // assert assert.equal(res.statusCode, 200) cb() }; next = function () { // assert cb(new Error('should not be called')) }; // act cors({optionsSuccessStatus: 200})(req, res, next); }); it('doesn\'t shortcircuit preflight requests with preflightContinue option', function (done) { // arrange var cb = after(1, done) var req, res, next; req = fakeRequest('OPTIONS'); res = fakeResponse(); res.end = function () { // assert cb(new Error('should not be called')) }; next = function () { // assert cb() }; // act cors({preflightContinue: true})(req, res, next); }); it('normalizes method names', function (done) { // arrange var cb = after(1, done) var req, res, next; req = fakeRequest('options'); res = fakeResponse(); res.end = function () { // assert assert.equal(res.statusCode, 204) cb() }; next = function () { // assert cb(new Error('should not be called')) }; // act cors()(req, res, next); }); it('includes Content-Length response header', function (done) { // arrange var cb = after(1, done) var req, res, next; req = fakeRequest('OPTIONS'); res = fakeResponse(); res.end = function () { // assert assert.equal(res.getHeader('Content-Length'), '0') cb() }; next = function () { // assert cb(new Error('should not be called')) }; // act cors()(req, res, next); }); it('no options enables default CORS to all origins', function (done) { // arrange var req, res, next; req = fakeRequest('GET'); res = fakeResponse(); next = function () { // assert assert.equal(res.getHeader('Access-Control-Allow-Origin'), '*') assert.equal(res.getHeader('Access-Control-Allow-Methods'), undefined) done(); }; // act cors()(req, res, next); }); it('OPTION call with no options enables default CORS to all origins and methods', function (done) { // arrange var cb = after(1, done) var req, res, next; req = fakeRequest('OPTIONS'); res = fakeResponse(); res.end = function () { // assert assert.equal(res.statusCode, 204) assert.equal(res.getHeader('Access-Control-Allow-Origin'), '*') assert.equal(res.getHeader('Access-Control-Allow-Methods'), 'GET,HEAD,PUT,PATCH,POST,DELETE') cb() }; next = function () { // assert cb(new Error('should not be called')) }; // act cors()(req, res, next); }); describe('passing static options', function () { it('overrides defaults', function (done) { // arrange var cb = after(1, done) var req, res, next, options; options = { origin: 'example.com', methods: ['FOO', 'bar'], headers: ['FIZZ', 'buzz'], credentials: true, maxAge: 123 }; req = fakeRequest('OPTIONS'); res = fakeResponse(); res.end = function () { // assert assert.equal(res.statusCode, 204) assert.equal(res.getHeader('Access-Control-Allow-Origin'), 'example.com') assert.equal(res.getHeader('Access-Control-Allow-Methods'), 'FOO,bar') assert.equal(res.getHeader('Access-Control-Allow-Headers'), 'FIZZ,buzz') assert.equal(res.getHeader('Access-Control-Allow-Credentials'), 'true') assert.equal(res.getHeader('Access-Control-Max-Age'), '123') cb() }; next = function () { // assert cb(new Error('should not be called')) }; // act cors(options)(req, res, next); }); it('matches request origin against regexp', function(done) { var req = fakeRequest('GET'); var res = fakeResponse(); var options = { origin: /^(.+\.)?request.com$/ }; cors(options)(req, res, function(err) { assert.ifError(err) assert.equal(res.getHeader('Access-Control-Allow-Origin'), req.headers.origin) assert.equal(res.getHeader('Vary'), 'Origin') return done(); }); }); it('matches request origin against array of origin checks', function(done) { var req = fakeRequest('GET'); var res = fakeResponse(); var options = { origin: [ /foo\.com$/, 'request.com' ] }; cors(options)(req, res, function(err) { assert.ifError(err) assert.equal(res.getHeader('Access-Control-Allow-Origin'), req.headers.origin) assert.equal(res.getHeader('Vary'), 'Origin') return done(); }); }); it('doesn\'t match request origin against array of invalid origin checks', function(done) { var req = fakeRequest('GET'); var res = fakeResponse(); var options = { origin: [ /foo\.com$/, 'bar.com' ] }; cors(options)(req, res, function(err) { assert.ifError(err) assert.equal(res.getHeader('Access-Control-Allow-Origin'), undefined) assert.equal(res.getHeader('Vary'), 'Origin') return done(); }); }); it('origin of false disables cors', function (done) { // arrange var req, res, next, options; options = { origin: false, methods: ['FOO', 'bar'], headers: ['FIZZ', 'buzz'], credentials: true, maxAge: 123 }; req = fakeRequest('GET'); res = fakeResponse(); next = function () { // assert assert.equal(res.getHeader('Access-Control-Allow-Origin'), undefined) assert.equal(res.getHeader('Access-Control-Allow-Methods'), undefined) assert.equal(res.getHeader('Access-Control-Allow-Headers'), undefined) assert.equal(res.getHeader('Access-Control-Allow-Credentials'), undefined) assert.equal(res.getHeader('Access-Control-Max-Age'), undefined) done(); }; // act cors(options)(req, res, next); }); it('can override origin', function (done) { // arrange var req, res, next, options; options = { origin: 'example.com' }; req = fakeRequest('GET'); res = fakeResponse(); next = function () { // assert assert.equal(res.getHeader('Access-Control-Allow-Origin'), 'example.com') done(); }; // act cors(options)(req, res, next); }); it('includes Vary header for specific origins', function (done) { // arrange var req, res, next, options; options = { origin: 'example.com' }; req = fakeRequest('GET'); res = fakeResponse(); next = function () { // assert assert.equal(res.getHeader('Vary'), 'Origin') done(); }; // act cors(options)(req, res, next); }); it('appends to an existing Vary header', function (done) { // arrange var req, res, next, options; options = { origin: 'example.com' }; req = fakeRequest('GET'); res = fakeResponse(); res.setHeader('Vary', 'Foo'); next = function () { // assert assert.equal(res.getHeader('Vary'), 'Foo, Origin') done(); }; // act cors(options)(req, res, next); }); it('origin defaults to *', function (done) { // arrange var req, res, next; req = fakeRequest('GET'); res = fakeResponse(); next = function () { // assert assert.equal(res.getHeader('Access-Control-Allow-Origin'), '*') done(); }; // act cors()(req, res, next); }); it('specifying true for origin reflects requesting origin', function (done) { // arrange var req, res, next, options; options = { origin: true }; req = fakeRequest('GET'); res = fakeResponse(); next = function () { // assert assert.equal(res.getHeader('Access-Control-Allow-Origin'), 'request.com') done(); }; // act cors(options)(req, res, next); }); it('should allow origin when callback returns true', function (done) { var req, res, next, options; options = { origin: function (sentOrigin, cb) { assert.equal(sentOrigin, 'request.com') cb(null, true); } }; req = fakeRequest('GET'); res = fakeResponse(); next = function () { assert.equal(res.getHeader('Access-Control-Allow-Origin'), 'request.com') done(); }; cors(options)(req, res, next); }); it('should not allow origin when callback returns false', function (done) { var req, res, next, options; options = { origin: function (sentOrigin, cb) { assert.equal(sentOrigin, 'request.com') cb(null, false); } }; req = fakeRequest('GET'); res = fakeResponse(); next = function () { assert.equal(res.getHeader('Access-Control-Allow-Origin'), undefined) assert.equal(res.getHeader('Access-Control-Allow-Methods'), undefined) assert.equal(res.getHeader('Access-Control-Allow-Headers'), undefined) assert.equal(res.getHeader('Access-Control-Allow-Credentials'), undefined) assert.equal(res.getHeader('Access-Control-Max-Age'), undefined) done(); }; cors(options)(req, res, next); }); it('should not override options.origin callback', function (done) { var req, res, next, options; options = { origin: function (sentOrigin, cb) { var isValid = sentOrigin === 'request.com'; cb(null, isValid); } }; req = fakeRequest('GET'); res = fakeResponse(); next = function () { assert.equal(res.getHeader('Access-Control-Allow-Origin'), 'request.com') }; cors(options)(req, res, next); req = fakeRequest('GET', { 'origin': 'invalid-request.com' }); res = fakeResponse(); next = function () { assert.equal(res.getHeader('Access-Control-Allow-Origin'), undefined) assert.equal(res.getHeader('Access-Control-Allow-Methods'), undefined) assert.equal(res.getHeader('Access-Control-Allow-Headers'), undefined) assert.equal(res.getHeader('Access-Control-Allow-Credentials'), undefined) assert.equal(res.getHeader('Access-Control-Max-Age'), undefined) done(); }; cors(options)(req, res, next); }); it('can override methods', function (done) { // arrange var cb = after(1, done) var req, res, next, options; options = { methods: ['method1', 'method2'] }; req = fakeRequest('OPTIONS'); res = fakeResponse(); res.end = function () { // assert assert.equal(res.statusCode, 204) assert.equal(res.getHeader('Access-Control-Allow-Methods'), 'method1,method2') cb() }; next = function () { // assert cb(new Error('should not be called')) }; // act cors(options)(req, res, next); }); it('methods defaults to GET, HEAD, PUT, PATCH, POST, DELETE', function (done) { // arrange var cb = after(1, done) var req, res, next; req = fakeRequest('OPTIONS'); res = fakeResponse(); res.end = function () { // assert assert.equal(res.statusCode, 204) assert.equal(res.getHeader('Access-Control-Allow-Methods'), 'GET,HEAD,PUT,PATCH,POST,DELETE') cb() }; next = function () { // assert cb(new Error('should not be called')) }; // act cors()(req, res, next); }); it('can specify allowed headers as array', function (done) { // arrange var req, res, options; options = { allowedHeaders: ['header1', 'header2'] }; req = fakeRequest('OPTIONS'); res = fakeResponse(); res.end = function () { // assert assert.equal(res.getHeader('Access-Control-Allow-Headers'), 'header1,header2') assert.equal(res.getHeader('Vary'), undefined) done(); }; // act cors(options)(req, res, null); }); it('can specify allowed headers as string', function (done) { // arrange var req, res, options; options = { allowedHeaders: 'header1,header2' }; req = fakeRequest('OPTIONS'); res = fakeResponse(); res.end = function () { // assert assert.equal(res.getHeader('Access-Control-Allow-Headers'), 'header1,header2') assert.equal(res.getHeader('Vary'), undefined) done(); }; // act cors(options)(req, res, null); }); it('specifying an empty list or string of allowed headers will result in no response header for allowed headers', function (done) { // arrange var req, res, next, options; options = { allowedHeaders: [] }; req = fakeRequest('GET'); res = fakeResponse(); next = function () { // assert assert.equal(res.getHeader('Access-Control-Allow-Headers'), undefined) assert.equal(res.getHeader('Vary'), undefined) done(); }; // act cors(options)(req, res, next); }); it('if no allowed headers are specified, defaults to requested allowed headers', function (done) { // arrange var req, res; req = fakeRequest('OPTIONS'); res = fakeResponse(); res.end = function () { // assert assert.equal(res.getHeader('Access-Control-Allow-Headers'), 'requestedHeader1,requestedHeader2') assert.equal(res.getHeader('Vary'), 'Access-Control-Request-Headers') done(); }; // act cors()(req, res, null); }); it('can specify exposed headers as array', function (done) { // arrange var req, res, options, next; options = { exposedHeaders: ['custom-header1', 'custom-header2'] }; req = fakeRequest('GET'); res = fakeResponse(); next = function () { // assert assert.equal(res.getHeader('Access-Control-Expose-Headers'), 'custom-header1,custom-header2') done(); }; // act cors(options)(req, res, next); }); it('can specify exposed headers as string', function (done) { // arrange var req, res, options, next; options = { exposedHeaders: 'custom-header1,custom-header2' }; req = fakeRequest('GET'); res = fakeResponse(); next = function () { // assert assert.equal(res.getHeader('Access-Control-Expose-Headers'), 'custom-header1,custom-header2') done(); }; // act cors(options)(req, res, next); }); it('specifying an empty list or string of exposed headers will result in no response header for exposed headers', function (done) { // arrange var req, res, next, options; options = { exposedHeaders: [] }; req = fakeRequest('GET'); res = fakeResponse(); next = function () { // assert assert.equal(res.getHeader('Access-Control-Expose-Headers'), undefined) done(); }; // act cors(options)(req, res, next); }); it('includes credentials if explicitly enabled', function (done) { // arrange var req, res, options; options = { credentials: true }; req = fakeRequest('OPTIONS'); res = fakeResponse(); res.end = function () { // assert assert.equal(res.getHeader('Access-Control-Allow-Credentials'), 'true') done(); }; // act cors(options)(req, res, null); }); it('does not includes credentials unless explicitly enabled', function (done) { // arrange var req, res, next; req = fakeRequest('GET'); res = fakeResponse(); next = function () { // assert assert.equal(res.getHeader('Access-Control-Allow-Credentials'), undefined) done(); }; // act cors()(req, res, next); }); it('includes maxAge when specified', function (done) { // arrange var req, res, options; options = { maxAge: 456 }; req = fakeRequest('OPTIONS'); res = fakeResponse(); res.end = function () { // assert assert.equal(res.getHeader('Access-Control-Max-Age'), '456') done(); }; // act cors(options)(req, res, null); }); it('includes maxAge when specified and equals to zero', function (done) { // arrange var req, res, options options = { maxAge: 0 } req = fakeRequest('OPTIONS') res = fakeResponse() res.end = function () { // assert assert.equal(res.getHeader('Access-Control-Max-Age'), '0') done() } // act cors(options)(req, res, null) }); it('does not includes maxAge unless specified', function (done) { // arrange var req, res, next; req = fakeRequest('GET'); res = fakeResponse(); next = function () { // assert assert.equal(res.getHeader('Access-Control-Max-Age'), undefined) done(); }; // act cors()(req, res, next); }); }); describe('passing a function to build options', function () { it('handles options specified via callback', function (done) { // arrange var req, res, next, delegate; delegate = function (req2, cb) { cb(null, { origin: 'delegate.com' }); }; req = fakeRequest('GET'); res = fakeResponse(); next = function () { // assert assert.equal(res.getHeader('Access-Control-Allow-Origin'), 'delegate.com') done(); }; // act cors(delegate)(req, res, next); }); it('handles options specified via callback for preflight', function (done) { // arrange var req, res, delegate; delegate = function (req2, cb) { cb(null, { origin: 'delegate.com', maxAge: 1000 }); }; req = fakeRequest('OPTIONS'); res = fakeResponse(); res.end = function () { // assert assert.equal(res.getHeader('Access-Control-Allow-Origin'), 'delegate.com') assert.equal(res.getHeader('Access-Control-Max-Age'), '1000') done(); }; // act cors(delegate)(req, res, null); }); it('handles error specified via callback', function (done) { // arrange var req, res, next, delegate; delegate = function (req2, cb) { cb('some error'); }; req = fakeRequest('GET'); res = fakeResponse(); next = function (err) { // assert assert.equal(err, 'some error') done(); }; // act cors(delegate)(req, res, next); }); }); }); }());