├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── Gruntfile.js ├── LICENSE.txt ├── README.md ├── SECURITY.md ├── index.js ├── lib ├── csp.js ├── csrf.js ├── hsts.js ├── nosniff.js ├── p3p.js ├── referrerpolicy.js ├── token.js ├── xframes.js └── xssprotection.js ├── package-lock.json ├── package.json └── test ├── csp.js ├── csrf.js ├── hsts.js ├── index.js ├── mocks ├── app.js ├── config │ ├── all.js │ ├── cspArray.json │ ├── cspEnforce.js │ ├── cspNested.json │ ├── cspReport.js │ ├── cspString.json │ └── nonce.js └── token.js ├── nosniff.js ├── p3p.js ├── referrerpolicy.js ├── xframe.js └── xssprotection.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .settings 3 | .DS_Store 4 | node_modules 5 | coverage 6 | *.iml 7 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | // Whether the scan should stop on first error. 3 | "passfail": false, 4 | // Maximum errors before stopping. 5 | "maxerr": 100, 6 | 7 | 8 | // Predefined globals 9 | 10 | // Whether the standard browser globals should be predefined. 11 | "browser": false, 12 | // Whether the Node.js environment globals should be predefined. 13 | "node": true, 14 | // Whether the Rhino environment globals should be predefined. 15 | "rhino": false, 16 | // Whether CouchDB globals should be predefined. 17 | "couch": false, 18 | // Whether the Windows Scripting Host environment globals should be predefined. 19 | "wsh": false, 20 | 21 | // Whether jQuery globals should be predefined. 22 | "jquery": false, 23 | // Whether Prototype and Scriptaculous globals should be predefined. 24 | "prototypejs": false, 25 | // Whether MooTools globals should be predefined. 26 | "mootools": false, 27 | // Whether Dojo Toolkit globals should be predefined. 28 | "dojo": false, 29 | 30 | // Custom predefined globals. 31 | "predef": [ 32 | "describe", "it" 33 | ], 34 | 35 | // Development 36 | 37 | // Whether debugger statements should be allowed. 38 | "debug": false, 39 | // Whether logging globals should be predefined (console, alert, etc.). 40 | "devel": false, 41 | 42 | 43 | // ECMAScript 5 44 | 45 | // Whether ES6 syntax should be allowed. 46 | "esversion": 6, 47 | // Whether the "use strict"; pragma should be required. 48 | "strict": false, 49 | // Whether global "use strict"; should be allowed (also enables strict). 50 | "globalstrict": true, 51 | 52 | 53 | // The Good Parts 54 | 55 | // Whether automatic semicolon insertion should be allowed. 56 | "asi": false, 57 | // Whether line breaks should not be checked, e.g. `return [\n] x`. 58 | "laxbreak": false, 59 | // Whether bitwise operators (&, |, ^, etc.) should be forbidden. 60 | "bitwise": false, 61 | // Whether assignments inside `if`, `for` and `while` should be allowed. Usually 62 | // conditions and loops are for comparison, not assignments. 63 | "boss": true, 64 | // Whether curly braces around all blocks should be required. 65 | "curly": true, 66 | // Whether `===` and `!==` should be required (instead of `==` and `!=`). 67 | "eqeqeq": true, 68 | // Whether `== null` comparisons should be allowed, even if `eqeqeq` is `true`. 69 | "eqnull": false, 70 | // Whether `eval` should be allowed. 71 | "evil": false, 72 | // Whether ExpressionStatement should be allowed as Programs. 73 | "expr": true, 74 | // Whether `for in` loops must filter with `hasOwnPrototype`. 75 | "forin": false, 76 | // Whether immediate invocations must be wrapped in parens, e.g. 77 | // `( function(){}() );`. 78 | "immed": true, 79 | // Whether use before define should be forbidden. 80 | "latedef": false, 81 | // Whether functions should be allowed to be defined within loops. 82 | "loopfunc": true, 83 | // Whether arguments.caller and arguments.callee should be forbidden. 84 | "noarg": false, 85 | // Whether `.` should be forbidden in regexp literals. 86 | "regexp": false, 87 | // Whether unescaped first/last dash (-) inside brackets in regexps should be allowed. 88 | "regexdash": false, 89 | // Whether script-targeted URLs should be allowed. 90 | "scripturl": false, 91 | // Whether variable shadowing should be allowed. 92 | "shadow": false, 93 | // Whether `new function () { ... };` and `new Object;` should be allowed. 94 | "supernew": false, 95 | // Whether variables must be declared before used. 96 | "undef": true, 97 | // Whether `this` inside a non-constructor function should be allowed. 98 | "validthis": false, 99 | // Whether smarttabs should be allowed 100 | // (http://www.emacswiki.org/emacs/SmartTabs). 101 | "smarttabs": true, 102 | // Whether the `__proto__` property should be allowed. 103 | "proto": false, 104 | // Whether one-case switch statements should be allowed. 105 | "onecase": false, 106 | // Whether non-standard (but widely adopted) globals should be predefined. 107 | "nonstandard": false, 108 | // Allow multiline strings. 109 | "multistr": false, 110 | // Whether line breaks should not be checked around commas. 111 | "laxcomma": false, 112 | // Whether semicolons may be ommitted for the trailing statements inside of a 113 | // one-line blocks. 114 | "lastsemic": false, 115 | // Whether the `__iterator__` property should be allowed. 116 | "iterator": false, 117 | // Whether only function scope should be used for scope tests. 118 | "funcscope": false, 119 | // Whether es.next specific syntax should be allowed. 120 | "esnext": false, 121 | // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`. 122 | "sub": true, 123 | 124 | 125 | // Style preferences 126 | 127 | // Whether constructor names must be capitalized. 128 | "newcap": false, 129 | // Whether empty blocks should be forbidden. 130 | "noempty": false, 131 | // Whether using `new` for side-effects should be forbidden. 132 | "nonew": false, 133 | // Whether names should be checked for leading or trailing underscores 134 | // (object._attribute would be forbidden). 135 | "nomen": false, 136 | // Whether only one var statement per function should be allowed. 137 | "onevar": false, 138 | // Whether increment and decrement (`++` and `--`) should be forbidden. 139 | "plusplus": false, 140 | // Whether trailing whitespace rules apply. 141 | "trailing": false, 142 | // Specify indentation. 143 | "indent": 4, 144 | // Whether strict whitespace rules apply. 145 | "white": true 146 | 147 | // Complexity 148 | 149 | // Maximum number of function parameters. 150 | //"maxparams": 5, 151 | // Maximum block nesting depth. 152 | //"maxdepth": 3, 153 | // Maximum number of statements per function. 154 | //"maxstatements": 25, 155 | // Maximum cyclomatic complexity. 156 | //"maxcomplexity": 6 157 | } 158 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "10" 5 | - "12" 6 | 7 | before_script: 8 | - npm install -g grunt-cli 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ##### Unreleased 2 | 3 | ##### v1.7.0 4 | 5 | * Changes `whitelist`/`blacklist` to `allowlist`/`blocklist` to follow [guidelines](https://chromium.googlesource.com/chromium/src/+/master/styleguide/inclusive_code.md#racially-neutral) 6 | * Updates `allowlist`, `blocklist` csrf config to allow specifying the type of match required 7 | 8 | ##### v1.6.1 9 | 10 | * Fixes issue with multiple `blacklist`/`whitelist` options 11 | * Typo in README 12 | 13 | ##### v1.6.0 14 | 15 | * Adds in `allowlist` and `blocklist` support for `csrf` 16 | 17 | ##### v1.5.2 18 | 19 | * Bugfix: Add style/script directive if nonce is true 20 | 21 | ##### v1.5.1 22 | 23 | * Bugfix: style-src nonce updates properly, speed improvement on match 24 | 25 | 26 | ##### v1.5.0 27 | 28 | * Support for nonce for either style-src, script-src, or both 29 | * Lower case headers for improved performance 30 | * Support for referrer-policy 31 | * Allow CSRF cookie options to be set 32 | * Bugfix: return to suppress promise warning 33 | 34 | 35 | ##### v1.4.1 36 | 37 | * Bugfix: typo in `nosniff` header 38 | 39 | ##### v1.4.0 40 | 41 | * Add `nosniff` middleware 42 | * Add new method signatures for more flexible csp configuration 43 | 44 | ##### v1.3.0 45 | 46 | * Add `req.csrfToken` method to (re)generate token 47 | 48 | ##### v1.2.0 49 | 50 | * Add angular convenience wrapper around CSRF cookie configuration 51 | 52 | ##### v1.1.1 53 | 54 | * Fix csrf header case-sensitivity 55 | 56 | ##### v1.1.0 57 | 58 | * Add `preload` flag to HSTS options 59 | 60 | ##### v0.1.2 - 20131231 61 | 62 | * Add support for HTTP Strict Transport (HSTS) header 63 | 64 | ##### v0.1.1 - 2013xxxx 65 | **Bugs** 66 | - 67 | 68 | **Features** 69 | - 70 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /*───────────────────────────────────────────────────────────────────────────*\ 2 | │ Copyright (C) 2016 PayPal │ 3 | │ │ 4 | │hh ,'""`. │ 5 | │ / _ _ \ Licensed under the Apache License, Version 2.0 (the "License"); │ 6 | │ |(@)(@)| you may not use this file except in compliance with the License. │ 7 | │ ) __ ( You may obtain a copy of the License at │ 8 | │ /,'))((`.\ │ 9 | │(( (( )) )) http://www.apache.org/licenses/LICENSE-2.0 │ 10 | │ `\ `)(' /' │ 11 | │ │ 12 | │ Unless required by applicable law or agreed to in writing, software │ 13 | │ distributed under the License is distributed on an "AS IS" BASIS, │ 14 | │ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ 15 | │ See the License for the specific language governing permissions and │ 16 | │ limitations under the License. │ 17 | \*───────────────────────────────────────────────────────────────────────────*/ 18 | 'use strict'; 19 | 20 | module.exports = function (grunt) { 21 | 22 | grunt.initConfig({ 23 | jshint: { 24 | files: ['Gruntfile.js', 'index.js', 'lib/**/*.js', 'test/**/*.js'], 25 | options: { 26 | jshintrc: '.jshintrc' 27 | } 28 | }, 29 | mochaTest: { 30 | src: ['test/*.js'], 31 | options: { 32 | globals: ['chai'], 33 | timeout: 15000, 34 | ignoreLeaks: false, 35 | ui: 'bdd', 36 | reporter: 'spec', 37 | grep: grunt.option('grep') || 0 38 | } 39 | } 40 | }); 41 | 42 | grunt.loadNpmTasks('grunt-contrib-jshint'); 43 | grunt.loadNpmTasks('grunt-mocha-test'); 44 | 45 | grunt.registerTask('test', ['jshint', 'mochaTest']); 46 | 47 | }; -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*───────────────────────────────────────────────────────────────────────────*\ 2 | │ Copyright (C) 2017 PayPal │ 3 | │ │ 4 | │hh ,'""`. │ 5 | │ / _ _ \ Licensed under the Apache License, Version 2.0 (the "License"); │ 6 | │ |(@)(@)| you may not use this file except in compliance with the License. │ 7 | │ ) __ ( You may obtain a copy of the License at │ 8 | │ /,'))((`.\ │ 9 | │(( (( )) )) http://www.apache.org/licenses/LICENSE-2.0 │ 10 | │ `\ `)(' /' │ 11 | │ │ 12 | │ Unless required by applicable law or agreed to in writing, software │ 13 | │ distributed under the License is distributed on an "AS IS" BASIS, │ 14 | │ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ 15 | │ See the License for the specific language governing permissions and │ 16 | │ limitations under the License. │ 17 | \*───────────────────────────────────────────────────────────────────────────*/ 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lusca 2 | 3 | [![Build Status](https://travis-ci.org/krakenjs/lusca.svg?branch=master)](https://travis-ci.org/krakenjs/lusca) [![NPM version](https://badge.fury.io/js/lusca.svg)](http://badge.fury.io/js/lusca) 4 | 5 | Web application security middleware. 6 | 7 | 8 | ## Usage 9 | 10 | ```js 11 | var express = require('express'), 12 | app = express(), 13 | session = require('express-session'), 14 | lusca = require('lusca'); 15 | 16 | //this or other session management will be required 17 | app.use(session({ 18 | secret: 'abc', 19 | resave: true, 20 | saveUninitialized: true 21 | })); 22 | 23 | app.use(lusca({ 24 | csrf: true, 25 | csp: { /* ... */}, 26 | xframe: 'SAMEORIGIN', 27 | p3p: 'ABCDEF', 28 | hsts: {maxAge: 31536000, includeSubDomains: true, preload: true}, 29 | xssProtection: true, 30 | nosniff: true, 31 | referrerPolicy: 'same-origin' 32 | })); 33 | ``` 34 | 35 | Setting any value to `false` will disable it. Alternately, you can opt into methods one by one: 36 | 37 | ```js 38 | app.use(lusca.csrf()); 39 | app.use(lusca.csp({ /* ... */})); 40 | app.use(lusca.xframe('SAMEORIGIN')); 41 | app.use(lusca.p3p('ABCDEF')); 42 | app.use(lusca.hsts({ maxAge: 31536000 })); 43 | app.use(lusca.xssProtection(true)); 44 | app.use(lusca.nosniff()); 45 | app.use(lusca.referrerPolicy('same-origin')); 46 | ``` 47 | 48 | __Please note that you must use [express-session](https://github.com/expressjs/session), [cookie-session](https://github.com/expressjs/cookie-session), their express 3.x alternatives, or other session object management in order to use lusca.__ 49 | 50 | 51 | ## API 52 | 53 | 54 | ### lusca.csrf(options) 55 | 56 | * `key` String - Optional. The name of the CSRF token added to the model. Defaults to `_csrf`. 57 | * `secret` String - Optional. The key to place on the session object which maps to the server side token. Defaults to `_csrfSecret`. 58 | * `impl` Function - Optional. Custom implementation to generate a token. 59 | * `cookie` String|Object - Optional. If set, a cookie with the name and/or options you provide will be set with the CSRF token. If the value is a string, it'll be used as the cookie name. 60 | * `cookie.name` String - Required if cookie is an object and `angular` is not true. The CSRF cookie name to set. 61 | * `cookie.options` Object - Optional. A valid Express cookie options object. 62 | * `angular` Boolean - Optional. Shorthand setting to set `lusca` up to use the default settings for CSRF validation according to the [AngularJS docs]. Can be used with `cookie.options`. 63 | * `blocklist` Array or String - Optional. Allows defining a set of routes that will not have csrf protection. All others will. 64 | Example configuration: 65 | ``` 66 | blocklist: [{path: '/details', type: 'exact'}, {path: '/summary', type: 'startWith'}] 67 | //If match type is 'exact', route will get blocklisted only if it matches req.path exactly 68 | //If match type is 'startsWith', Lusca will check if req.path starts with the specified path 69 | 70 | For backwards compatiblity, following configuration is supported as well. It will be evaluated using the 'startsWith' match type. 71 | blocklist: '/details'; 72 | blocklist: ['/details', '/summary']; 73 | ``` 74 | * `allowlist` Array or String - Optional. Allows defining a set of routes that will have csrf protection. All others will not. 75 | Configuration is similar to `blocklist` config 76 | 77 | Notes: The app can use either a `blocklist` or a `allowlist`, not both. By default, all post routes are allowlisted. 78 | 79 | [angularjs docs]: https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection 80 | 81 | Enables [Cross Site Request Forgery](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_\(CSRF\)) (CSRF) headers. 82 | 83 | If enabled, the CSRF token must be in the payload when modifying data or you will receive a *403 Forbidden*. To send the token you'll need to echo back the `_csrf` value you received from the previous request. 84 | 85 | Furthermore, parsers must be registered before lusca. 86 | 87 | ### lusca.csp(options) 88 | 89 | * `options.policy` String, Object, or an Array - Object definition of policy. Valid policies examples include: 90 | * `{"default-src": "*"}` 91 | * `"referrer no-referrer"` 92 | * `[{ "img-src": "'self' http:" }, "block-all-mixed-content"]` 93 | * `options.reportOnly` Boolean - Enable report only mode. 94 | * `options.reportUri` String - URI where to send the report data 95 | * `options.styleNonce` Boolean - Enable nonce for inline style-src, access from `res.locals.nonce` 96 | * `options.scriptNonce` Boolean - Enable nonce for inline script-src, access from `res.locals.nonce` 97 | 98 | Enables [Content Security Policy](https://www.owasp.org/index.php/Content_Security_Policy) (CSP) headers. 99 | 100 | #### Example Options 101 | 102 | ```js 103 | // Everything but images can only come from own domain (excluding subdomains) 104 | { 105 | policy: { 106 | 'default-src': '\'self\'', 107 | 'img-src': '*' 108 | } 109 | } 110 | ``` 111 | 112 | See the [MDN CSP usage](https://developer.mozilla.org/en-US/docs/Web/Security/CSP/Using_Content_Security_Policy) page for more information on available policy options. 113 | 114 | ### lusca.xframe(value) 115 | 116 | * `value` String - Required. The value for the header, e.g. DENY, SAMEORIGIN or ALLOW-FROM uri. 117 | 118 | Enables X-FRAME-OPTIONS headers to help prevent [Clickjacking](https://www.owasp.org/index.php/Clickjacking). 119 | 120 | 121 | 122 | ### lusca.p3p(value) 123 | 124 | * `value` String - Required. The compact privacy policy. 125 | 126 | Enables [Platform for Privacy Preferences Project](https://www.w3.org/P3P) (P3P) headers. 127 | 128 | 129 | 130 | ### lusca.hsts(options) 131 | 132 | * `options.maxAge` Number - Required. Number of seconds HSTS is in effect. 133 | * `options.includeSubDomains` Boolean - Optional. Applies HSTS to all subdomains of the host 134 | * `options.preload` Boolean - Optional. Adds preload flag 135 | 136 | Enables [HTTP Strict Transport Security](https://www.owasp.org/index.php/HTTP_Strict_Transport_Security) for the host domain. The preload flag is required for HSTS domain submissions to [Chrome's HSTS preload list](https://hstspreload.appspot.com). 137 | 138 | 139 | ### lusca.xssProtection(options) 140 | 141 | * `options.enabled` Boolean - Optional. If the header is enabled or not (see header docs). Defaults to `1`. 142 | * `options.mode` String - Optional. Mode to set on the header (see header docs). Defaults to `block`. 143 | 144 | Enables [X-XSS-Protection](http://blogs.msdn.com/b/ie/archive/2008/07/02/ie8-security-part-iv-the-xss-filter.aspx) headers to help prevent cross site scripting (XSS) attacks in older IE browsers (IE8) 145 | 146 | 147 | ### lusca.nosniff() 148 | 149 | Enables [X-Content-Type-Options](https://blogs.msdn.microsoft.com/ie/2008/09/02/ie8-security-part-vi-beta-2-update/) header to prevent MIME-sniffing a response away from the declared content-type. 150 | 151 | 152 | ### lusca.referrerPolicy(value) 153 | 154 | * `value` String - Optional. The value for the header, e.g. `origin`, `same-origin`, `no-referrer`. Defaults to `` (empty string). 155 | 156 | Enables [Referrer-Policy](https://www.w3.org/TR/referrer-policy/#intro) header to control the Referer header. -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | We take security very seriously and ask that you follow the following process. 4 | 5 | 6 | ## Contact us 7 | If you think you may have found a security bug we ask that you privately send the details to https://www.paypal.com/us/webapps/mpp/security-tools/reporting-security-issues. Please make sure to use a descriptive title in the email. 8 | 9 | 10 | ## Expectations 11 | We will generally get back to you within **24 hours**, but a more detailed response may take up to **48 hours**. If you feel we're not responding back in time, please send us a message *without detail* on Twitter [@kraken_js](https://twitter.com/kraken_js). 12 | 13 | 14 | ## History 15 | No reported issues 16 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*───────────────────────────────────────────────────────────────────────────*\ 2 | │ Copyright (C) 2016 PayPal │ 3 | │ │ 4 | │hh ,'""`. │ 5 | │ / _ _ \ Licensed under the Apache License, Version 2.0 (the "License"); │ 6 | │ |(@)(@)| you may not use this file except in compliance with the License. │ 7 | │ ) __ ( You may obtain a copy of the License at │ 8 | │ /,'))((`.\ │ 9 | │(( (( )) )) http://www.apache.org/licenses/LICENSE-2.0 │ 10 | │ `\ `)(' /' │ 11 | │ │ 12 | │ Unless required by applicable law or agreed to in writing, software │ 13 | │ distributed under the License is distributed on an "AS IS" BASIS, │ 14 | │ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ 15 | │ See the License for the specific language governing permissions and │ 16 | │ limitations under the License. │ 17 | \*───────────────────────────────────────────────────────────────────────────*/ 18 | 'use strict'; 19 | var crypto = require('crypto'); 20 | 21 | 22 | /** 23 | * Outputs all security headers based on configuration 24 | * @param {Object} options The configuration object. 25 | */ 26 | var lusca = module.exports = function (options) { 27 | var headers = []; 28 | var nonce; 29 | 30 | if (options) { 31 | Object.keys(lusca).forEach(function (key) { 32 | var config = options[key]; 33 | if (key === "csp" && options[key] && (options[key]['styleNonce'] || options[key]['scriptNonce'])) { 34 | nonce = true; 35 | } 36 | 37 | if (config) { 38 | headers.push(lusca[key](config)); 39 | } 40 | }); 41 | } 42 | 43 | return function lusca(req, res, next) { 44 | var chain = next; 45 | 46 | if (nonce) { 47 | Object.defineProperty(res.locals, 'nonce', { 48 | value: crypto.pseudoRandomBytes(36).toString('base64'), 49 | enumerable: true 50 | }); 51 | } 52 | headers.forEach(function (header) { 53 | chain = (function (next) { 54 | return function (err) { 55 | if (err) { 56 | next(err); 57 | return; 58 | } 59 | return header(req, res, next); 60 | }; 61 | }(chain)); 62 | }); 63 | 64 | chain(); 65 | }; 66 | }; 67 | 68 | 69 | lusca.csrf = require('./lib/csrf'); 70 | lusca.csp = require('./lib/csp'); 71 | lusca.hsts = require('./lib/hsts'); 72 | lusca.p3p = require('./lib/p3p'); 73 | lusca.xframe = require('./lib/xframes'); 74 | lusca.xssProtection = require('./lib/xssprotection'); 75 | lusca.nosniff = require('./lib/nosniff'); 76 | lusca.referrerPolicy = require('./lib/referrerpolicy'); 77 | -------------------------------------------------------------------------------- /lib/csp.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Content Security Policy (CSP) 5 | * https://www.owasp.org/index.php/Content_Security_Policy 6 | * @param {Object} options The CSP policy. 7 | */ 8 | module.exports = function (options) { 9 | var policyRules = options && options.policy, 10 | isReportOnly = options && options.reportOnly, 11 | reportUri = options && options.reportUri, 12 | styleNonce = options && options.styleNonce, 13 | scriptNonce = options && options.scriptNonce, 14 | value, name; 15 | 16 | name = 'content-security-policy'; 17 | 18 | if (isReportOnly) { 19 | name += '-report-only'; 20 | } 21 | 22 | if (policyRules && policyRules["default-src"]) { 23 | if (styleNonce && !policyRules["style-src"]) { 24 | policyRules["style-src"] = policyRules["default-src"]; 25 | } 26 | 27 | if (scriptNonce && !policyRules["script-src"]) { 28 | policyRules["script-src"] = policyRules["default-src"]; 29 | } 30 | } 31 | 32 | value = createPolicyString(policyRules); 33 | 34 | if (reportUri) { 35 | if (value !== '') { 36 | value += '; '; 37 | } 38 | value += 'report-uri ' + reportUri; 39 | } 40 | 41 | return function csp(req, res, next) { 42 | if (styleNonce) { 43 | var styleMatch = value.match(/style-src 'nonce-.{48}'/); 44 | if (styleMatch) { 45 | value = value.replace(styleMatch[0], 'style-src \'nonce-' + res.locals.nonce + '\''); 46 | } 47 | else { 48 | value = value.replace('style-src', 'style-src \'nonce-' + res.locals.nonce + '\''); 49 | } 50 | } 51 | if (scriptNonce) { 52 | var scriptMatch = value.match(/script-src 'nonce-.{48}'/); 53 | if (scriptMatch) { 54 | value = value.replace(scriptMatch[0], 'script-src \'nonce-' + res.locals.nonce + '\''); 55 | } 56 | else { 57 | value = value.replace('script-src', 'script-src \'nonce-' + res.locals.nonce + '\''); 58 | } 59 | } 60 | res.header(name, value); 61 | next(); 62 | }; 63 | }; 64 | 65 | var createPolicyString = module.exports.createPolicyString = function (policy) { 66 | var entries; 67 | 68 | if (typeof policy === 'string') { 69 | return policy; 70 | } 71 | 72 | if (Array.isArray(policy)) { 73 | return policy.map(createPolicyString).join('; '); 74 | } 75 | 76 | if (typeof policy === 'object' && policy !== null) { 77 | entries = Object.keys(policy).map(function (directive) { 78 | if (policy[directive] === 0 || policy[directive]) { 79 | directive += ' ' + policy[directive]; 80 | } 81 | return directive; 82 | }); 83 | 84 | return createPolicyString(entries); 85 | } 86 | 87 | throw Error('invalid csp policy - must be array, string, or plain object'); 88 | }; 89 | -------------------------------------------------------------------------------- /lib/csrf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var token = require('./token'), 5 | timeSafeCompare = require('tsscmp'), 6 | assert = require('assert'); 7 | 8 | /** 9 | * CSRF 10 | * https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF) 11 | * @param {Object} options 12 | * key {String} The name of the CSRF token in the model. Default "_csrf". 13 | * impl {Object} An object with create/validate methods for custom tokens. Optional. 14 | * header {String} The name of the response header containing the CSRF token. Default "x-csrf-token". 15 | */ 16 | module.exports = function (options) { 17 | var impl, key, header, secret, cookie, allowlist, blocklist, allowlistStartsWith, 18 | allowlistExact, blocklistStartsWith, blocklistExact; 19 | 20 | options = options || {}; 21 | 22 | if (options.angular) { 23 | options.header = 'X-XSRF-TOKEN'; 24 | options.cookie = { 25 | name: 'XSRF-TOKEN', 26 | options: options.cookie && options.cookie.options 27 | }; 28 | } 29 | 30 | allowlist = options.allowlist || options.whitelist; 31 | 32 | if (typeof allowlist === 'string') { 33 | allowlist = [ { path: allowlist, type: 'startsWith' } ]; 34 | } else if (!Array.isArray(allowlist)) { 35 | // Don't allow non string or array allowlist 36 | allowlist = null; 37 | } 38 | 39 | if (allowlist) { 40 | allowlistStartsWith = new Set(); 41 | allowlistExact = new Set(); 42 | populateSet(allowlist, allowlistStartsWith, allowlistExact); 43 | //preemptively converting to array to use convinience methods in middleware 44 | allowlistStartsWith = Array.from(allowlistStartsWith); 45 | } 46 | 47 | blocklist = options.blocklist || options.blacklist; 48 | 49 | if (typeof blocklist === 'string') { 50 | blocklist = [ { path: blocklist, type: 'startsWith' } ]; 51 | } else if (!Array.isArray(blocklist)) { 52 | // Don't allow non string or array blocklist 53 | blocklist = null; 54 | } 55 | 56 | if (blocklist) { 57 | blocklistStartsWith = new Set(); 58 | blocklistExact = new Set(); 59 | populateSet(blocklist, blocklistStartsWith, blocklistExact); 60 | //preemptively converting to array to use convinience methods in middleware 61 | blocklistStartsWith = Array.from(blocklistStartsWith); 62 | } 63 | 64 | key = options.key || '_csrf'; 65 | impl = options.impl || token; 66 | header = options.header || 'x-csrf-token'; 67 | secret = options.secret || '_csrfSecret'; 68 | 69 | // Check if cookie is string or object 70 | if (typeof options.cookie === 'string') { 71 | cookie = { 72 | name: options.cookie, 73 | }; 74 | } else { 75 | cookie = { 76 | name: options.cookie && options.cookie.name 77 | }; 78 | } 79 | 80 | // Set cookie options 81 | cookie.options = options.cookie && options.cookie.options || {}; 82 | 83 | function getCsrf(req, secret) { 84 | var _impl, validate, _token, _secret; 85 | 86 | _impl = impl.create(req, secret); 87 | validate = impl.validate || _impl.validate; 88 | _token = _impl.token || _impl; 89 | _secret = _impl.secret; 90 | 91 | return { 92 | validate: validate, 93 | token: _token, 94 | secret: _secret 95 | }; 96 | } 97 | 98 | function setToken(res, token) { 99 | res.locals[key] = token; 100 | 101 | if (cookie && cookie.name) { 102 | res.cookie(cookie.name, token, cookie.options || {}); 103 | } 104 | } 105 | 106 | return function checkCsrf(req, res, next) { 107 | 108 | var shouldBypass = false; 109 | 110 | if (blocklist) { 111 | shouldBypass = blocklistExact.has(req.path) || 112 | blocklistStartsWith.some(function (exclusion) { 113 | shouldBypass = req.path.indexOf(exclusion) === 0; 114 | return shouldBypass; 115 | }); 116 | } 117 | 118 | if (allowlist) { 119 | shouldBypass = !allowlistExact.has(req.path) || 120 | allowlistStartsWith.some(function (inclusion) { 121 | shouldBypass = req.path.indexOf(inclusion) !== 0; 122 | return shouldBypass; 123 | }); 124 | } 125 | 126 | if (shouldBypass) { 127 | return next(); 128 | } 129 | 130 | var method, _token, errmsg; 131 | 132 | var csrf = getCsrf(req, secret); 133 | 134 | setToken(res, csrf.token); 135 | 136 | req.csrfToken = function csrfToken() { 137 | var newCsrf = getCsrf(req, secret); 138 | if (csrf.secret && newCsrf.secret && 139 | timeSafeCompare(csrf.secret, newCsrf.secret)) { 140 | return csrf.token; 141 | } 142 | 143 | csrf = newCsrf; 144 | setToken(res, csrf.token); 145 | return csrf.token; 146 | }; 147 | 148 | // Move along for safe verbs 149 | method = req.method; 150 | 151 | if (method === 'GET' || method === 'HEAD' || method === 'OPTIONS') { 152 | return next(); 153 | } 154 | 155 | // Validate token 156 | _token = (req.body && req.body[key]) || req.headers[header.toLowerCase()]; 157 | 158 | if (csrf.validate(req, _token)) { 159 | next(); 160 | } else { 161 | res.statusCode = 403; 162 | 163 | if (!_token) { 164 | errmsg = 'CSRF token missing'; 165 | } else { 166 | errmsg = 'CSRF token mismatch'; 167 | } 168 | 169 | next(new Error(errmsg)); 170 | } 171 | }; 172 | }; 173 | 174 | function populateSet(list, startsWith, exact) { 175 | const validTypesList = ['startsWith', 'exact']; 176 | 177 | list.forEach(function (config) { 178 | if (typeof config === 'string') { 179 | startsWith.add(config); 180 | } else { 181 | const { type, path } = config; 182 | 183 | assert(validTypesList.includes(type), 184 | `Invalid csrf config. type '${type}' is not supported for path '${path}'. Please use one of ${validTypesList.join(', ')}`); 185 | assert(path, 'Invalid csrf config. path is required'); 186 | 187 | if (type === 'startsWith') { 188 | startsWith.add(path); 189 | } else { 190 | exact.add(path); 191 | } 192 | } 193 | }); 194 | } 195 | -------------------------------------------------------------------------------- /lib/hsts.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | /** 5 | * HSTS - Http Strict Transport Security 6 | * https://www.owasp.org/index.php/HTTP_Strict_Transport_Security 7 | * @param {Object} options 8 | * maxAge {Number} The max age of the header. Required. 9 | * includeSubDomains {Boolean} 10 | */ 11 | module.exports = function (options) { 12 | var value; 13 | 14 | options = options || {}; 15 | 16 | value = (options.maxAge !== undefined) ? 'max-age=' + options.maxAge : ''; 17 | value += (value && options.includeSubDomains) ? '; includeSubDomains' : ''; 18 | value += (value && options.preload) ? '; preload' : ''; 19 | 20 | return function hsts(req, res, next) { 21 | if (value) { 22 | res.header('strict-transport-security', value); 23 | } 24 | 25 | next(); 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/nosniff.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * X-Content-Type-Options 5 | * https://blogs.msdn.microsoft.com/ie/2008/09/02/ie8-security-part-vi-beta-2-update/ 6 | */ 7 | module.exports = function nosniff() { 8 | return function nosniff(req, res, next) { 9 | res.header('x-content-type-options', 'nosniff'); 10 | next(); 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /lib/p3p.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | /** 5 | * P3P - Platform for Privacy Preferences Project 6 | * http://support.microsoft.com/kb/290333 7 | * @param {String} value The P3P header value. 8 | */ 9 | module.exports = function (value) { 10 | return function p3p(req, res, next) { 11 | if (value) { 12 | res.header('p3p', value); 13 | } 14 | 15 | next(); 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /lib/referrerpolicy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | /** 5 | * See https://www.w3.org/TR/referrer-policy/#referrer-policies 6 | * @type {Array} 7 | */ 8 | var supportedValues = [ 9 | '', 10 | 'no-referrer', 11 | 'no-referrer-when-downgrade', 12 | 'same-origin', 13 | 'origin', 14 | 'strict-origin', 15 | 'origin-when-cross-origin', 16 | 'strict-origin-when-cross-origin', 17 | 'unsafe-url' 18 | ]; 19 | 20 | /** 21 | * Default value. 22 | * @type {String} 23 | */ 24 | var defaultValue = ''; // Browser should fallback to a Referrer Policy defined via other mechanisms elsewhere 25 | 26 | /** 27 | * Referrer-Policy 28 | * https://scotthelme.co.uk/a-new-security-header-referrer-policy/ 29 | * Specification: https://www.w3.org/TR/referrer-policy/ 30 | * @param {String} value The Referrer-Policy header value, e.g. no-referrer, same-origin, origin. 31 | */ 32 | module.exports = function (value) { 33 | if (supportedValues.indexOf(value) === -1 && process.env.NODE_ENV !== 'production') { 34 | throw Error('Referrer-Policy header doesn\'t support value: ' + value); 35 | } 36 | return function referrerpolicy(req, res, next) { 37 | res.header('referrer-policy', value || defaultValue); 38 | next(); 39 | }; 40 | }; 41 | -------------------------------------------------------------------------------- /lib/token.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var crypto = require('crypto'), 5 | timeSafeCompare = require('tsscmp'); 6 | var LENGTH = 10; 7 | 8 | 9 | 10 | function create(req, secretKey) { 11 | 12 | var session = req.session; 13 | if (session === undefined) { 14 | throw new Error('lusca requires req.session to be available in order to maintain state'); 15 | } 16 | var secret = session[secretKey]; 17 | // Save the secret for validation 18 | if (!secret) { 19 | session[secretKey] = crypto.pseudoRandomBytes(LENGTH).toString('base64'); 20 | secret = session[secretKey]; 21 | } 22 | 23 | return { 24 | secret: secret, 25 | token: tokenize(salt(LENGTH), secret), 26 | validate: function validate(req, token) { 27 | if (typeof token !== 'string') { 28 | return false; 29 | 30 | } 31 | return timeSafeCompare(token, tokenize(token.slice(0, LENGTH), 32 | req.session[secretKey])); 33 | } 34 | }; 35 | } 36 | 37 | function tokenize(salt, secret) { 38 | return salt + crypto.createHash('sha1').update(salt + secret).digest('base64'); 39 | } 40 | 41 | 42 | function salt(len) { 43 | var str = '', 44 | chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 45 | 46 | for (var i = 0; i < len; i++) { 47 | str += chars[Math.floor(Math.random() * chars.length)]; 48 | } 49 | 50 | return str; 51 | } 52 | 53 | 54 | 55 | module.exports = { 56 | create: create 57 | }; 58 | -------------------------------------------------------------------------------- /lib/xframes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | /** 5 | * Xframes 6 | * https://www.owasp.org/index.php/Clickjacking 7 | * @param {String} value The XFRAME header value, e.g. DENY, SAMEORIGIN. 8 | */ 9 | module.exports = function (value) { 10 | return function xframe(req, res, next) { 11 | res.header('x-frame-options', value); 12 | next(); 13 | }; 14 | }; -------------------------------------------------------------------------------- /lib/xssprotection.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * X-XSS-Protection 5 | * http://blogs.msdn.com/b/ie/archive/2008/07/02/ie8-security-part-iv-the-xss-filter.aspx 6 | */ 7 | module.exports = function xssProtection(options) { 8 | options = options || {}; 9 | 10 | // `enabled` should be either `1` or `0` 11 | var enabled = (options.enabled !== undefined) ? +options.enabled : 1; 12 | var mode = options.mode || 'block'; 13 | 14 | return function xssProtection(req, res, next) { 15 | res.header('x-xss-protection', enabled + '; mode=' + mode); 16 | next(); 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lusca", 3 | "version": "1.7.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "abbrev": { 8 | "version": "1.1.1", 9 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/abbrev/-/abbrev-1.1.1.tgz", 10 | "integrity": "sha1-+PLIh60Qv2f2NPAFtph/7TF5qsg=", 11 | "dev": true 12 | }, 13 | "accepts": { 14 | "version": "1.3.7", 15 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/accepts/-/accepts-1.3.7.tgz", 16 | "integrity": "sha1-UxvHJlF6OytB+FACHGzBXqq1B80=", 17 | "dev": true, 18 | "requires": { 19 | "mime-types": "~2.1.24", 20 | "negotiator": "0.6.2" 21 | } 22 | }, 23 | "argparse": { 24 | "version": "0.1.16", 25 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/argparse/-/argparse-0.1.16.tgz", 26 | "integrity": "sha1-z9AeD7uj1srtBJ+9dY1A9lGW9Xw=", 27 | "dev": true, 28 | "requires": { 29 | "underscore": "~1.7.0", 30 | "underscore.string": "~2.4.0" 31 | }, 32 | "dependencies": { 33 | "underscore.string": { 34 | "version": "2.4.0", 35 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/underscore.string/-/underscore.string-2.4.0.tgz", 36 | "integrity": "sha1-jN2PusTi0uoefi6Al8QvRCKA+Fs=", 37 | "dev": true 38 | } 39 | } 40 | }, 41 | "arr-diff": { 42 | "version": "4.0.0", 43 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/arr-diff/-/arr-diff-4.0.0.tgz", 44 | "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", 45 | "dev": true 46 | }, 47 | "arr-flatten": { 48 | "version": "1.1.0", 49 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/arr-flatten/-/arr-flatten-1.1.0.tgz", 50 | "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", 51 | "dev": true 52 | }, 53 | "arr-union": { 54 | "version": "3.1.0", 55 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/arr-union/-/arr-union-3.1.0.tgz", 56 | "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", 57 | "dev": true 58 | }, 59 | "array-each": { 60 | "version": "1.0.1", 61 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/array-each/-/array-each-1.0.1.tgz", 62 | "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", 63 | "dev": true 64 | }, 65 | "array-flatten": { 66 | "version": "1.1.1", 67 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/array-flatten/-/array-flatten-1.1.1.tgz", 68 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", 69 | "dev": true 70 | }, 71 | "array-slice": { 72 | "version": "1.1.0", 73 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/array-slice/-/array-slice-1.1.0.tgz", 74 | "integrity": "sha1-42jqFfibxwaff/uJrsOmx9SsItQ=", 75 | "dev": true 76 | }, 77 | "array-unique": { 78 | "version": "0.3.2", 79 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/array-unique/-/array-unique-0.3.2.tgz", 80 | "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", 81 | "dev": true 82 | }, 83 | "assign-symbols": { 84 | "version": "1.0.0", 85 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/assign-symbols/-/assign-symbols-1.0.0.tgz", 86 | "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", 87 | "dev": true 88 | }, 89 | "async": { 90 | "version": "0.1.22", 91 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/async/-/async-0.1.22.tgz", 92 | "integrity": "sha1-D8GqoIig4+8Ovi2IMbqw3PiEUGE=", 93 | "dev": true 94 | }, 95 | "atob": { 96 | "version": "2.1.2", 97 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/atob/-/atob-2.1.2.tgz", 98 | "integrity": "sha1-bZUX654DDSQ2ZmZR6GvZ9vE1M8k=", 99 | "dev": true 100 | }, 101 | "balanced-match": { 102 | "version": "1.0.0", 103 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/balanced-match/-/balanced-match-1.0.0.tgz", 104 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 105 | "dev": true 106 | }, 107 | "base": { 108 | "version": "0.11.2", 109 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/base/-/base-0.11.2.tgz", 110 | "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", 111 | "dev": true, 112 | "requires": { 113 | "cache-base": "^1.0.1", 114 | "class-utils": "^0.3.5", 115 | "component-emitter": "^1.2.1", 116 | "define-property": "^1.0.0", 117 | "isobject": "^3.0.1", 118 | "mixin-deep": "^1.2.0", 119 | "pascalcase": "^0.1.1" 120 | }, 121 | "dependencies": { 122 | "define-property": { 123 | "version": "1.0.0", 124 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/define-property/-/define-property-1.0.0.tgz", 125 | "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", 126 | "dev": true, 127 | "requires": { 128 | "is-descriptor": "^1.0.0" 129 | } 130 | }, 131 | "is-accessor-descriptor": { 132 | "version": "1.0.0", 133 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", 134 | "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", 135 | "dev": true, 136 | "requires": { 137 | "kind-of": "^6.0.0" 138 | } 139 | }, 140 | "is-data-descriptor": { 141 | "version": "1.0.0", 142 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", 143 | "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", 144 | "dev": true, 145 | "requires": { 146 | "kind-of": "^6.0.0" 147 | } 148 | }, 149 | "is-descriptor": { 150 | "version": "1.0.2", 151 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-descriptor/-/is-descriptor-1.0.2.tgz", 152 | "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", 153 | "dev": true, 154 | "requires": { 155 | "is-accessor-descriptor": "^1.0.0", 156 | "is-data-descriptor": "^1.0.0", 157 | "kind-of": "^6.0.2" 158 | } 159 | } 160 | } 161 | }, 162 | "body-parser": { 163 | "version": "1.19.0", 164 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/body-parser/-/body-parser-1.19.0.tgz", 165 | "integrity": "sha1-lrJwnlfJxOCab9Zqj9l5hE9p8Io=", 166 | "dev": true, 167 | "requires": { 168 | "bytes": "3.1.0", 169 | "content-type": "~1.0.4", 170 | "debug": "2.6.9", 171 | "depd": "~1.1.2", 172 | "http-errors": "1.7.2", 173 | "iconv-lite": "0.4.24", 174 | "on-finished": "~2.3.0", 175 | "qs": "6.7.0", 176 | "raw-body": "2.4.0", 177 | "type-is": "~1.6.17" 178 | } 179 | }, 180 | "brace-expansion": { 181 | "version": "1.1.11", 182 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/brace-expansion/-/brace-expansion-1.1.11.tgz", 183 | "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", 184 | "dev": true, 185 | "requires": { 186 | "balanced-match": "^1.0.0", 187 | "concat-map": "0.0.1" 188 | } 189 | }, 190 | "braces": { 191 | "version": "2.3.2", 192 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/braces/-/braces-2.3.2.tgz", 193 | "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", 194 | "dev": true, 195 | "requires": { 196 | "arr-flatten": "^1.1.0", 197 | "array-unique": "^0.3.2", 198 | "extend-shallow": "^2.0.1", 199 | "fill-range": "^4.0.0", 200 | "isobject": "^3.0.1", 201 | "repeat-element": "^1.1.2", 202 | "snapdragon": "^0.8.1", 203 | "snapdragon-node": "^2.0.1", 204 | "split-string": "^3.0.2", 205 | "to-regex": "^3.0.1" 206 | }, 207 | "dependencies": { 208 | "extend-shallow": { 209 | "version": "2.0.1", 210 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/extend-shallow/-/extend-shallow-2.0.1.tgz", 211 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 212 | "dev": true, 213 | "requires": { 214 | "is-extendable": "^0.1.0" 215 | } 216 | } 217 | } 218 | }, 219 | "bytes": { 220 | "version": "3.1.0", 221 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/bytes/-/bytes-3.1.0.tgz", 222 | "integrity": "sha1-9s95M6Ng4FiPqf3oVlHNx/gF0fY=", 223 | "dev": true 224 | }, 225 | "cache-base": { 226 | "version": "1.0.1", 227 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/cache-base/-/cache-base-1.0.1.tgz", 228 | "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", 229 | "dev": true, 230 | "requires": { 231 | "collection-visit": "^1.0.0", 232 | "component-emitter": "^1.2.1", 233 | "get-value": "^2.0.6", 234 | "has-value": "^1.0.0", 235 | "isobject": "^3.0.1", 236 | "set-value": "^2.0.0", 237 | "to-object-path": "^0.3.0", 238 | "union-value": "^1.0.0", 239 | "unset-value": "^1.0.0" 240 | } 241 | }, 242 | "class-utils": { 243 | "version": "0.3.6", 244 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/class-utils/-/class-utils-0.3.6.tgz", 245 | "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", 246 | "dev": true, 247 | "requires": { 248 | "arr-union": "^3.1.0", 249 | "define-property": "^0.2.5", 250 | "isobject": "^3.0.0", 251 | "static-extend": "^0.1.1" 252 | }, 253 | "dependencies": { 254 | "define-property": { 255 | "version": "0.2.5", 256 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/define-property/-/define-property-0.2.5.tgz", 257 | "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", 258 | "dev": true, 259 | "requires": { 260 | "is-descriptor": "^0.1.0" 261 | } 262 | } 263 | } 264 | }, 265 | "cli": { 266 | "version": "0.4.5", 267 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/cli/-/cli-0.4.5.tgz", 268 | "integrity": "sha1-ePlIXNFhtWbppsctcXDEJw6B22E=", 269 | "dev": true, 270 | "requires": { 271 | "glob": ">= 3.1.4" 272 | } 273 | }, 274 | "coffee-script": { 275 | "version": "1.3.3", 276 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/coffee-script/-/coffee-script-1.3.3.tgz", 277 | "integrity": "sha1-FQ1rTLUiiUNp7+1qIQHCC8f0pPQ=", 278 | "dev": true 279 | }, 280 | "collection-visit": { 281 | "version": "1.0.0", 282 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/collection-visit/-/collection-visit-1.0.0.tgz", 283 | "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", 284 | "dev": true, 285 | "requires": { 286 | "map-visit": "^1.0.0", 287 | "object-visit": "^1.0.0" 288 | } 289 | }, 290 | "colors": { 291 | "version": "0.6.2", 292 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/colors/-/colors-0.6.2.tgz", 293 | "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=", 294 | "dev": true 295 | }, 296 | "combined-stream": { 297 | "version": "0.0.7", 298 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/combined-stream/-/combined-stream-0.0.7.tgz", 299 | "integrity": "sha1-ATfmV7qlp1QcV6w3rF/AfXO03B8=", 300 | "dev": true, 301 | "requires": { 302 | "delayed-stream": "0.0.5" 303 | } 304 | }, 305 | "commander": { 306 | "version": "0.6.1", 307 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/commander/-/commander-0.6.1.tgz", 308 | "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=", 309 | "dev": true 310 | }, 311 | "component-emitter": { 312 | "version": "1.3.0", 313 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/component-emitter/-/component-emitter-1.3.0.tgz", 314 | "integrity": "sha1-FuQHD7qK4ptnnyIVhT7hgasuq8A=", 315 | "dev": true 316 | }, 317 | "concat-map": { 318 | "version": "0.0.1", 319 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/concat-map/-/concat-map-0.0.1.tgz", 320 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 321 | "dev": true 322 | }, 323 | "console-browserify": { 324 | "version": "0.1.6", 325 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/console-browserify/-/console-browserify-0.1.6.tgz", 326 | "integrity": "sha1-0SijwLuINQ61YmxufHGm8P1ImDw=", 327 | "dev": true 328 | }, 329 | "content-disposition": { 330 | "version": "0.5.3", 331 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/content-disposition/-/content-disposition-0.5.3.tgz", 332 | "integrity": "sha1-4TDK9+cnkIfFYWwgB9BIVpiYT70=", 333 | "dev": true, 334 | "requires": { 335 | "safe-buffer": "5.1.2" 336 | } 337 | }, 338 | "content-type": { 339 | "version": "1.0.4", 340 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/content-type/-/content-type-1.0.4.tgz", 341 | "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", 342 | "dev": true 343 | }, 344 | "cookie": { 345 | "version": "0.4.0", 346 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/cookie/-/cookie-0.4.0.tgz", 347 | "integrity": "sha1-vrQ35wIrO21JAZ0IhmUwPr6cFLo=", 348 | "dev": true 349 | }, 350 | "cookie-parser": { 351 | "version": "1.4.5", 352 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/cookie-parser/-/cookie-parser-1.4.5.tgz", 353 | "integrity": "sha1-PlctS3wMgPnGHa9gTkM2gxtdHUk=", 354 | "dev": true, 355 | "requires": { 356 | "cookie": "0.4.0", 357 | "cookie-signature": "1.0.6" 358 | } 359 | }, 360 | "cookie-session": { 361 | "version": "1.4.0", 362 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/cookie-session/-/cookie-session-1.4.0.tgz", 363 | "integrity": "sha1-wyWupoXOucjk/QCwMTpG1Ud0c4A=", 364 | "dev": true, 365 | "requires": { 366 | "cookies": "0.8.0", 367 | "debug": "2.6.9", 368 | "on-headers": "~1.0.2" 369 | } 370 | }, 371 | "cookie-signature": { 372 | "version": "1.0.6", 373 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/cookie-signature/-/cookie-signature-1.0.6.tgz", 374 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", 375 | "dev": true 376 | }, 377 | "cookiejar": { 378 | "version": "1.3.2", 379 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/cookiejar/-/cookiejar-1.3.2.tgz", 380 | "integrity": "sha1-YdMini2iDIWQMiM1ApWKm331gkk=", 381 | "dev": true 382 | }, 383 | "cookies": { 384 | "version": "0.8.0", 385 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/cookies/-/cookies-0.8.0.tgz", 386 | "integrity": "sha1-EpPOSzkXQKhAbjyYcOgoxLVPP5A=", 387 | "dev": true, 388 | "requires": { 389 | "depd": "~2.0.0", 390 | "keygrip": "~1.1.0" 391 | }, 392 | "dependencies": { 393 | "depd": { 394 | "version": "2.0.0", 395 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/depd/-/depd-2.0.0.tgz", 396 | "integrity": "sha1-tpYWPMdXVg0JzyLMj60Vcbeedt8=", 397 | "dev": true 398 | } 399 | } 400 | }, 401 | "copy-descriptor": { 402 | "version": "0.1.1", 403 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/copy-descriptor/-/copy-descriptor-0.1.1.tgz", 404 | "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", 405 | "dev": true 406 | }, 407 | "core-util-is": { 408 | "version": "1.0.2", 409 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/core-util-is/-/core-util-is-1.0.2.tgz", 410 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 411 | "dev": true 412 | }, 413 | "data-driven": { 414 | "version": "1.4.0", 415 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/data-driven/-/data-driven-1.4.0.tgz", 416 | "integrity": "sha1-OreACq0EeUkE4P231A11jLfn1Lg=", 417 | "dev": true 418 | }, 419 | "date-now": { 420 | "version": "0.1.4", 421 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/date-now/-/date-now-0.1.4.tgz", 422 | "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", 423 | "dev": true 424 | }, 425 | "dateformat": { 426 | "version": "1.0.2-1.2.3", 427 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/dateformat/-/dateformat-1.0.2-1.2.3.tgz", 428 | "integrity": "sha1-sCIMAt6YYXQztyhRz0fePfLNvuk=", 429 | "dev": true 430 | }, 431 | "debug": { 432 | "version": "2.6.9", 433 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/debug/-/debug-2.6.9.tgz", 434 | "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", 435 | "dev": true, 436 | "requires": { 437 | "ms": "2.0.0" 438 | } 439 | }, 440 | "decode-uri-component": { 441 | "version": "0.2.0", 442 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/decode-uri-component/-/decode-uri-component-0.2.0.tgz", 443 | "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", 444 | "dev": true 445 | }, 446 | "define-property": { 447 | "version": "2.0.2", 448 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/define-property/-/define-property-2.0.2.tgz", 449 | "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", 450 | "dev": true, 451 | "requires": { 452 | "is-descriptor": "^1.0.2", 453 | "isobject": "^3.0.1" 454 | }, 455 | "dependencies": { 456 | "is-accessor-descriptor": { 457 | "version": "1.0.0", 458 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", 459 | "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", 460 | "dev": true, 461 | "requires": { 462 | "kind-of": "^6.0.0" 463 | } 464 | }, 465 | "is-data-descriptor": { 466 | "version": "1.0.0", 467 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", 468 | "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", 469 | "dev": true, 470 | "requires": { 471 | "kind-of": "^6.0.0" 472 | } 473 | }, 474 | "is-descriptor": { 475 | "version": "1.0.2", 476 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-descriptor/-/is-descriptor-1.0.2.tgz", 477 | "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", 478 | "dev": true, 479 | "requires": { 480 | "is-accessor-descriptor": "^1.0.0", 481 | "is-data-descriptor": "^1.0.0", 482 | "kind-of": "^6.0.2" 483 | } 484 | } 485 | } 486 | }, 487 | "delayed-stream": { 488 | "version": "0.0.5", 489 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/delayed-stream/-/delayed-stream-0.0.5.tgz", 490 | "integrity": "sha1-1LH0OpPoKW3+AmlPRoC8N6MTxz8=", 491 | "dev": true 492 | }, 493 | "depd": { 494 | "version": "1.1.2", 495 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/depd/-/depd-1.1.2.tgz", 496 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", 497 | "dev": true 498 | }, 499 | "destroy": { 500 | "version": "1.0.4", 501 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/destroy/-/destroy-1.0.4.tgz", 502 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", 503 | "dev": true 504 | }, 505 | "detect-file": { 506 | "version": "1.0.0", 507 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/detect-file/-/detect-file-1.0.0.tgz", 508 | "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", 509 | "dev": true 510 | }, 511 | "diff": { 512 | "version": "1.0.7", 513 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/diff/-/diff-1.0.7.tgz", 514 | "integrity": "sha1-JLuwAcSn1VIhaefKvbLCgU7ZHPQ=", 515 | "dev": true 516 | }, 517 | "dom-serializer": { 518 | "version": "0.2.2", 519 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/dom-serializer/-/dom-serializer-0.2.2.tgz", 520 | "integrity": "sha1-GvuB9TNxcXXUeGVd68XjMtn5u1E=", 521 | "dev": true, 522 | "requires": { 523 | "domelementtype": "^2.0.1", 524 | "entities": "^2.0.0" 525 | }, 526 | "dependencies": { 527 | "domelementtype": { 528 | "version": "2.1.0", 529 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/domelementtype/-/domelementtype-2.1.0.tgz", 530 | "integrity": "sha1-qFHAgKbRw9lDRK7RUdmfZp7fWF4=", 531 | "dev": true 532 | }, 533 | "entities": { 534 | "version": "2.2.0", 535 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/entities/-/entities-2.2.0.tgz", 536 | "integrity": "sha1-CY3JDruD2N/6CJ1VJWs1HTTE2lU=", 537 | "dev": true 538 | } 539 | } 540 | }, 541 | "domelementtype": { 542 | "version": "1.3.1", 543 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/domelementtype/-/domelementtype-1.3.1.tgz", 544 | "integrity": "sha1-0EjESzew0Qp/Kj1f7j9DM9eQSB8=", 545 | "dev": true 546 | }, 547 | "domhandler": { 548 | "version": "2.3.0", 549 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/domhandler/-/domhandler-2.3.0.tgz", 550 | "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", 551 | "dev": true, 552 | "requires": { 553 | "domelementtype": "1" 554 | } 555 | }, 556 | "domutils": { 557 | "version": "1.5.1", 558 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/domutils/-/domutils-1.5.1.tgz", 559 | "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", 560 | "dev": true, 561 | "requires": { 562 | "dom-serializer": "0", 563 | "domelementtype": "1" 564 | } 565 | }, 566 | "ee-first": { 567 | "version": "1.1.1", 568 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/ee-first/-/ee-first-1.1.1.tgz", 569 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", 570 | "dev": true 571 | }, 572 | "encodeurl": { 573 | "version": "1.0.2", 574 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/encodeurl/-/encodeurl-1.0.2.tgz", 575 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", 576 | "dev": true 577 | }, 578 | "entities": { 579 | "version": "1.0.0", 580 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/entities/-/entities-1.0.0.tgz", 581 | "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", 582 | "dev": true 583 | }, 584 | "errorhandler": { 585 | "version": "1.5.1", 586 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/errorhandler/-/errorhandler-1.5.1.tgz", 587 | "integrity": "sha1-ubpdF8+QdEzR6FE1em51v4BqmpE=", 588 | "dev": true, 589 | "requires": { 590 | "accepts": "~1.3.7", 591 | "escape-html": "~1.0.3" 592 | } 593 | }, 594 | "escape-html": { 595 | "version": "1.0.3", 596 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/escape-html/-/escape-html-1.0.3.tgz", 597 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", 598 | "dev": true 599 | }, 600 | "esprima": { 601 | "version": "1.0.4", 602 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/esprima/-/esprima-1.0.4.tgz", 603 | "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=", 604 | "dev": true 605 | }, 606 | "etag": { 607 | "version": "1.8.1", 608 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/etag/-/etag-1.8.1.tgz", 609 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", 610 | "dev": true 611 | }, 612 | "eventemitter2": { 613 | "version": "0.4.14", 614 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/eventemitter2/-/eventemitter2-0.4.14.tgz", 615 | "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", 616 | "dev": true 617 | }, 618 | "exit": { 619 | "version": "0.1.2", 620 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/exit/-/exit-0.1.2.tgz", 621 | "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", 622 | "dev": true 623 | }, 624 | "expand-brackets": { 625 | "version": "2.1.4", 626 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/expand-brackets/-/expand-brackets-2.1.4.tgz", 627 | "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", 628 | "dev": true, 629 | "requires": { 630 | "debug": "^2.3.3", 631 | "define-property": "^0.2.5", 632 | "extend-shallow": "^2.0.1", 633 | "posix-character-classes": "^0.1.0", 634 | "regex-not": "^1.0.0", 635 | "snapdragon": "^0.8.1", 636 | "to-regex": "^3.0.1" 637 | }, 638 | "dependencies": { 639 | "define-property": { 640 | "version": "0.2.5", 641 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/define-property/-/define-property-0.2.5.tgz", 642 | "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", 643 | "dev": true, 644 | "requires": { 645 | "is-descriptor": "^0.1.0" 646 | } 647 | }, 648 | "extend-shallow": { 649 | "version": "2.0.1", 650 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/extend-shallow/-/extend-shallow-2.0.1.tgz", 651 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 652 | "dev": true, 653 | "requires": { 654 | "is-extendable": "^0.1.0" 655 | } 656 | } 657 | } 658 | }, 659 | "expand-tilde": { 660 | "version": "2.0.2", 661 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/expand-tilde/-/expand-tilde-2.0.2.tgz", 662 | "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", 663 | "dev": true, 664 | "requires": { 665 | "homedir-polyfill": "^1.0.1" 666 | } 667 | }, 668 | "express": { 669 | "version": "4.17.1", 670 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/express/-/express-4.17.1.tgz", 671 | "integrity": "sha1-RJH8OGBc9R+GKdOcK10Cb5ikwTQ=", 672 | "dev": true, 673 | "requires": { 674 | "accepts": "~1.3.7", 675 | "array-flatten": "1.1.1", 676 | "body-parser": "1.19.0", 677 | "content-disposition": "0.5.3", 678 | "content-type": "~1.0.4", 679 | "cookie": "0.4.0", 680 | "cookie-signature": "1.0.6", 681 | "debug": "2.6.9", 682 | "depd": "~1.1.2", 683 | "encodeurl": "~1.0.2", 684 | "escape-html": "~1.0.3", 685 | "etag": "~1.8.1", 686 | "finalhandler": "~1.1.2", 687 | "fresh": "0.5.2", 688 | "merge-descriptors": "1.0.1", 689 | "methods": "~1.1.2", 690 | "on-finished": "~2.3.0", 691 | "parseurl": "~1.3.3", 692 | "path-to-regexp": "0.1.7", 693 | "proxy-addr": "~2.0.5", 694 | "qs": "6.7.0", 695 | "range-parser": "~1.2.1", 696 | "safe-buffer": "5.1.2", 697 | "send": "0.17.1", 698 | "serve-static": "1.14.1", 699 | "setprototypeof": "1.1.1", 700 | "statuses": "~1.5.0", 701 | "type-is": "~1.6.18", 702 | "utils-merge": "1.0.1", 703 | "vary": "~1.1.2" 704 | } 705 | }, 706 | "express-session": { 707 | "version": "1.17.1", 708 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/express-session/-/express-session-1.17.1.tgz", 709 | "integrity": "sha1-Nuy8cDRWbTjIUJiFwETUYcEb81c=", 710 | "dev": true, 711 | "requires": { 712 | "cookie": "0.4.0", 713 | "cookie-signature": "1.0.6", 714 | "debug": "2.6.9", 715 | "depd": "~2.0.0", 716 | "on-headers": "~1.0.2", 717 | "parseurl": "~1.3.3", 718 | "safe-buffer": "5.2.0", 719 | "uid-safe": "~2.1.5" 720 | }, 721 | "dependencies": { 722 | "depd": { 723 | "version": "2.0.0", 724 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/depd/-/depd-2.0.0.tgz", 725 | "integrity": "sha1-tpYWPMdXVg0JzyLMj60Vcbeedt8=", 726 | "dev": true 727 | }, 728 | "safe-buffer": { 729 | "version": "5.2.0", 730 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/safe-buffer/-/safe-buffer-5.2.0.tgz", 731 | "integrity": "sha1-t02uxJsRSPiMZLaNSbHoFcHy9Rk=", 732 | "dev": true 733 | } 734 | } 735 | }, 736 | "extend": { 737 | "version": "3.0.2", 738 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/extend/-/extend-3.0.2.tgz", 739 | "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=", 740 | "dev": true 741 | }, 742 | "extend-shallow": { 743 | "version": "3.0.2", 744 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/extend-shallow/-/extend-shallow-3.0.2.tgz", 745 | "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", 746 | "dev": true, 747 | "requires": { 748 | "assign-symbols": "^1.0.0", 749 | "is-extendable": "^1.0.1" 750 | }, 751 | "dependencies": { 752 | "is-extendable": { 753 | "version": "1.0.1", 754 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-extendable/-/is-extendable-1.0.1.tgz", 755 | "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", 756 | "dev": true, 757 | "requires": { 758 | "is-plain-object": "^2.0.4" 759 | } 760 | } 761 | } 762 | }, 763 | "extglob": { 764 | "version": "2.0.4", 765 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/extglob/-/extglob-2.0.4.tgz", 766 | "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", 767 | "dev": true, 768 | "requires": { 769 | "array-unique": "^0.3.2", 770 | "define-property": "^1.0.0", 771 | "expand-brackets": "^2.1.4", 772 | "extend-shallow": "^2.0.1", 773 | "fragment-cache": "^0.2.1", 774 | "regex-not": "^1.0.0", 775 | "snapdragon": "^0.8.1", 776 | "to-regex": "^3.0.1" 777 | }, 778 | "dependencies": { 779 | "define-property": { 780 | "version": "1.0.0", 781 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/define-property/-/define-property-1.0.0.tgz", 782 | "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", 783 | "dev": true, 784 | "requires": { 785 | "is-descriptor": "^1.0.0" 786 | } 787 | }, 788 | "extend-shallow": { 789 | "version": "2.0.1", 790 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/extend-shallow/-/extend-shallow-2.0.1.tgz", 791 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 792 | "dev": true, 793 | "requires": { 794 | "is-extendable": "^0.1.0" 795 | } 796 | }, 797 | "is-accessor-descriptor": { 798 | "version": "1.0.0", 799 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", 800 | "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", 801 | "dev": true, 802 | "requires": { 803 | "kind-of": "^6.0.0" 804 | } 805 | }, 806 | "is-data-descriptor": { 807 | "version": "1.0.0", 808 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", 809 | "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", 810 | "dev": true, 811 | "requires": { 812 | "kind-of": "^6.0.0" 813 | } 814 | }, 815 | "is-descriptor": { 816 | "version": "1.0.2", 817 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-descriptor/-/is-descriptor-1.0.2.tgz", 818 | "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", 819 | "dev": true, 820 | "requires": { 821 | "is-accessor-descriptor": "^1.0.0", 822 | "is-data-descriptor": "^1.0.0", 823 | "kind-of": "^6.0.2" 824 | } 825 | } 826 | } 827 | }, 828 | "fill-range": { 829 | "version": "4.0.0", 830 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/fill-range/-/fill-range-4.0.0.tgz", 831 | "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", 832 | "dev": true, 833 | "requires": { 834 | "extend-shallow": "^2.0.1", 835 | "is-number": "^3.0.0", 836 | "repeat-string": "^1.6.1", 837 | "to-regex-range": "^2.1.0" 838 | }, 839 | "dependencies": { 840 | "extend-shallow": { 841 | "version": "2.0.1", 842 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/extend-shallow/-/extend-shallow-2.0.1.tgz", 843 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 844 | "dev": true, 845 | "requires": { 846 | "is-extendable": "^0.1.0" 847 | } 848 | } 849 | } 850 | }, 851 | "finalhandler": { 852 | "version": "1.1.2", 853 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/finalhandler/-/finalhandler-1.1.2.tgz", 854 | "integrity": "sha1-t+fQAP/RGTjQ/bBTUG9uur6fWH0=", 855 | "dev": true, 856 | "requires": { 857 | "debug": "2.6.9", 858 | "encodeurl": "~1.0.2", 859 | "escape-html": "~1.0.3", 860 | "on-finished": "~2.3.0", 861 | "parseurl": "~1.3.3", 862 | "statuses": "~1.5.0", 863 | "unpipe": "~1.0.0" 864 | } 865 | }, 866 | "findup-sync": { 867 | "version": "0.1.3", 868 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/findup-sync/-/findup-sync-0.1.3.tgz", 869 | "integrity": "sha1-fz56l7gjksZTvwZYm9hRkOk8NoM=", 870 | "dev": true, 871 | "requires": { 872 | "glob": "~3.2.9", 873 | "lodash": "~2.4.1" 874 | }, 875 | "dependencies": { 876 | "glob": { 877 | "version": "3.2.11", 878 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/glob/-/glob-3.2.11.tgz", 879 | "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", 880 | "dev": true, 881 | "requires": { 882 | "inherits": "2", 883 | "minimatch": "0.3" 884 | } 885 | }, 886 | "lodash": { 887 | "version": "2.4.2", 888 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/lodash/-/lodash-2.4.2.tgz", 889 | "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", 890 | "dev": true 891 | }, 892 | "minimatch": { 893 | "version": "0.3.0", 894 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/minimatch/-/minimatch-0.3.0.tgz", 895 | "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", 896 | "dev": true, 897 | "requires": { 898 | "lru-cache": "2", 899 | "sigmund": "~1.0.0" 900 | } 901 | } 902 | } 903 | }, 904 | "fined": { 905 | "version": "1.2.0", 906 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/fined/-/fined-1.2.0.tgz", 907 | "integrity": "sha1-0AvszxqitHXRbUI7Aji3E6LEo3s=", 908 | "dev": true, 909 | "requires": { 910 | "expand-tilde": "^2.0.2", 911 | "is-plain-object": "^2.0.3", 912 | "object.defaults": "^1.1.0", 913 | "object.pick": "^1.2.0", 914 | "parse-filepath": "^1.0.1" 915 | } 916 | }, 917 | "flagged-respawn": { 918 | "version": "1.0.1", 919 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/flagged-respawn/-/flagged-respawn-1.0.1.tgz", 920 | "integrity": "sha1-595vEnnd2cqarIpZcdYYYGs6q0E=", 921 | "dev": true 922 | }, 923 | "for-in": { 924 | "version": "1.0.2", 925 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/for-in/-/for-in-1.0.2.tgz", 926 | "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", 927 | "dev": true 928 | }, 929 | "for-own": { 930 | "version": "1.0.0", 931 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/for-own/-/for-own-1.0.0.tgz", 932 | "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", 933 | "dev": true, 934 | "requires": { 935 | "for-in": "^1.0.1" 936 | } 937 | }, 938 | "form-data": { 939 | "version": "0.1.2", 940 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/form-data/-/form-data-0.1.2.tgz", 941 | "integrity": "sha1-EUPCE1eRGnjdeROxibS6tdXVdEU=", 942 | "dev": true, 943 | "requires": { 944 | "async": "~0.2.9", 945 | "combined-stream": "~0.0.4", 946 | "mime": "~1.2.11" 947 | }, 948 | "dependencies": { 949 | "async": { 950 | "version": "0.2.10", 951 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/async/-/async-0.2.10.tgz", 952 | "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", 953 | "dev": true 954 | }, 955 | "mime": { 956 | "version": "1.2.11", 957 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/mime/-/mime-1.2.11.tgz", 958 | "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=", 959 | "dev": true 960 | } 961 | } 962 | }, 963 | "formidable": { 964 | "version": "1.0.14", 965 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/formidable/-/formidable-1.0.14.tgz", 966 | "integrity": "sha1-Kz9MQRy7X91pXESEPiojUUpDIxo=", 967 | "dev": true 968 | }, 969 | "forwarded": { 970 | "version": "0.1.2", 971 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/forwarded/-/forwarded-0.1.2.tgz", 972 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", 973 | "dev": true 974 | }, 975 | "fragment-cache": { 976 | "version": "0.2.1", 977 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/fragment-cache/-/fragment-cache-0.2.1.tgz", 978 | "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", 979 | "dev": true, 980 | "requires": { 981 | "map-cache": "^0.2.2" 982 | } 983 | }, 984 | "fresh": { 985 | "version": "0.5.2", 986 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/fresh/-/fresh-0.5.2.tgz", 987 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", 988 | "dev": true 989 | }, 990 | "fs.realpath": { 991 | "version": "1.0.0", 992 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/fs.realpath/-/fs.realpath-1.0.0.tgz", 993 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 994 | "dev": true 995 | }, 996 | "function-bind": { 997 | "version": "1.1.1", 998 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/function-bind/-/function-bind-1.1.1.tgz", 999 | "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", 1000 | "dev": true 1001 | }, 1002 | "get-value": { 1003 | "version": "2.0.6", 1004 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/get-value/-/get-value-2.0.6.tgz", 1005 | "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", 1006 | "dev": true 1007 | }, 1008 | "getobject": { 1009 | "version": "0.1.0", 1010 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/getobject/-/getobject-0.1.0.tgz", 1011 | "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=", 1012 | "dev": true 1013 | }, 1014 | "glob": { 1015 | "version": "3.1.21", 1016 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/glob/-/glob-3.1.21.tgz", 1017 | "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", 1018 | "dev": true, 1019 | "requires": { 1020 | "graceful-fs": "~1.2.0", 1021 | "inherits": "1", 1022 | "minimatch": "~0.2.11" 1023 | }, 1024 | "dependencies": { 1025 | "inherits": { 1026 | "version": "1.0.2", 1027 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/inherits/-/inherits-1.0.2.tgz", 1028 | "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=", 1029 | "dev": true 1030 | } 1031 | } 1032 | }, 1033 | "global-modules": { 1034 | "version": "1.0.0", 1035 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/global-modules/-/global-modules-1.0.0.tgz", 1036 | "integrity": "sha1-bXcPDrUjrHgWTXK15xqIdyZcw+o=", 1037 | "dev": true, 1038 | "requires": { 1039 | "global-prefix": "^1.0.1", 1040 | "is-windows": "^1.0.1", 1041 | "resolve-dir": "^1.0.0" 1042 | } 1043 | }, 1044 | "global-prefix": { 1045 | "version": "1.0.2", 1046 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/global-prefix/-/global-prefix-1.0.2.tgz", 1047 | "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", 1048 | "dev": true, 1049 | "requires": { 1050 | "expand-tilde": "^2.0.2", 1051 | "homedir-polyfill": "^1.0.1", 1052 | "ini": "^1.3.4", 1053 | "is-windows": "^1.0.1", 1054 | "which": "^1.2.14" 1055 | }, 1056 | "dependencies": { 1057 | "which": { 1058 | "version": "1.3.1", 1059 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/which/-/which-1.3.1.tgz", 1060 | "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", 1061 | "dev": true, 1062 | "requires": { 1063 | "isexe": "^2.0.0" 1064 | } 1065 | } 1066 | } 1067 | }, 1068 | "graceful-fs": { 1069 | "version": "1.2.3", 1070 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/graceful-fs/-/graceful-fs-1.2.3.tgz", 1071 | "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=", 1072 | "dev": true 1073 | }, 1074 | "growl": { 1075 | "version": "1.7.0", 1076 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/growl/-/growl-1.7.0.tgz", 1077 | "integrity": "sha1-3i1mE20ALhErpw8/EMMc98NQsto=", 1078 | "dev": true 1079 | }, 1080 | "grunt": { 1081 | "version": "0.4.5", 1082 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/grunt/-/grunt-0.4.5.tgz", 1083 | "integrity": "sha1-VpN81RlDJK3/bSB2MYMqnWuk5/A=", 1084 | "dev": true, 1085 | "requires": { 1086 | "async": "~0.1.22", 1087 | "coffee-script": "~1.3.3", 1088 | "colors": "~0.6.2", 1089 | "dateformat": "1.0.2-1.2.3", 1090 | "eventemitter2": "~0.4.13", 1091 | "exit": "~0.1.1", 1092 | "findup-sync": "~0.1.2", 1093 | "getobject": "~0.1.0", 1094 | "glob": "~3.1.21", 1095 | "grunt-legacy-log": "~0.1.0", 1096 | "grunt-legacy-util": "~0.2.0", 1097 | "hooker": "~0.2.3", 1098 | "iconv-lite": "~0.2.11", 1099 | "js-yaml": "~2.0.5", 1100 | "lodash": "~0.9.2", 1101 | "minimatch": "~0.2.12", 1102 | "nopt": "~1.0.10", 1103 | "rimraf": "~2.2.8", 1104 | "underscore.string": "~2.2.1", 1105 | "which": "~1.0.5" 1106 | }, 1107 | "dependencies": { 1108 | "iconv-lite": { 1109 | "version": "0.2.11", 1110 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/iconv-lite/-/iconv-lite-0.2.11.tgz", 1111 | "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg=", 1112 | "dev": true 1113 | } 1114 | } 1115 | }, 1116 | "grunt-cli": { 1117 | "version": "1.3.2", 1118 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/grunt-cli/-/grunt-cli-1.3.2.tgz", 1119 | "integrity": "sha1-YPEtEsG1qulK40aca1/iTpYAFOg=", 1120 | "dev": true, 1121 | "requires": { 1122 | "grunt-known-options": "~1.1.0", 1123 | "interpret": "~1.1.0", 1124 | "liftoff": "~2.5.0", 1125 | "nopt": "~4.0.1", 1126 | "v8flags": "~3.1.1" 1127 | }, 1128 | "dependencies": { 1129 | "nopt": { 1130 | "version": "4.0.3", 1131 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/nopt/-/nopt-4.0.3.tgz", 1132 | "integrity": "sha1-o3XK2dAv2SEnjZVMIlTVqlfhXkg=", 1133 | "dev": true, 1134 | "requires": { 1135 | "abbrev": "1", 1136 | "osenv": "^0.1.4" 1137 | } 1138 | } 1139 | } 1140 | }, 1141 | "grunt-contrib-jshint": { 1142 | "version": "0.7.2", 1143 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/grunt-contrib-jshint/-/grunt-contrib-jshint-0.7.2.tgz", 1144 | "integrity": "sha1-KYWd3PQuf2xUxD/nXaPEvZA4So4=", 1145 | "dev": true, 1146 | "requires": { 1147 | "jshint": "~2.3.0" 1148 | }, 1149 | "dependencies": { 1150 | "jshint": { 1151 | "version": "2.3.0", 1152 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/jshint/-/jshint-2.3.0.tgz", 1153 | "integrity": "sha1-GVBEVaLCDEbuGDNh64fzocC33Ec=", 1154 | "dev": true, 1155 | "requires": { 1156 | "cli": "0.4.x", 1157 | "console-browserify": "0.1.x", 1158 | "minimatch": "0.x.x", 1159 | "shelljs": "0.1.x", 1160 | "underscore": "1.4.x" 1161 | } 1162 | }, 1163 | "underscore": { 1164 | "version": "1.4.4", 1165 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/underscore/-/underscore-1.4.4.tgz", 1166 | "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=", 1167 | "dev": true 1168 | } 1169 | } 1170 | }, 1171 | "grunt-known-options": { 1172 | "version": "1.1.1", 1173 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/grunt-known-options/-/grunt-known-options-1.1.1.tgz", 1174 | "integrity": "sha1-bMCIEHvQIZ3F0+V9kZI/RpBZgE0=", 1175 | "dev": true 1176 | }, 1177 | "grunt-legacy-log": { 1178 | "version": "0.1.3", 1179 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/grunt-legacy-log/-/grunt-legacy-log-0.1.3.tgz", 1180 | "integrity": "sha1-7ClCboAwIa9ZAp+H0vnNczWgVTE=", 1181 | "dev": true, 1182 | "requires": { 1183 | "colors": "~0.6.2", 1184 | "grunt-legacy-log-utils": "~0.1.1", 1185 | "hooker": "~0.2.3", 1186 | "lodash": "~2.4.1", 1187 | "underscore.string": "~2.3.3" 1188 | }, 1189 | "dependencies": { 1190 | "lodash": { 1191 | "version": "2.4.2", 1192 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/lodash/-/lodash-2.4.2.tgz", 1193 | "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", 1194 | "dev": true 1195 | }, 1196 | "underscore.string": { 1197 | "version": "2.3.3", 1198 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/underscore.string/-/underscore.string-2.3.3.tgz", 1199 | "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=", 1200 | "dev": true 1201 | } 1202 | } 1203 | }, 1204 | "grunt-legacy-log-utils": { 1205 | "version": "0.1.1", 1206 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/grunt-legacy-log-utils/-/grunt-legacy-log-utils-0.1.1.tgz", 1207 | "integrity": "sha1-wHBrndkGThFvNvI/5OawSGcsD34=", 1208 | "dev": true, 1209 | "requires": { 1210 | "colors": "~0.6.2", 1211 | "lodash": "~2.4.1", 1212 | "underscore.string": "~2.3.3" 1213 | }, 1214 | "dependencies": { 1215 | "lodash": { 1216 | "version": "2.4.2", 1217 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/lodash/-/lodash-2.4.2.tgz", 1218 | "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", 1219 | "dev": true 1220 | }, 1221 | "underscore.string": { 1222 | "version": "2.3.3", 1223 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/underscore.string/-/underscore.string-2.3.3.tgz", 1224 | "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=", 1225 | "dev": true 1226 | } 1227 | } 1228 | }, 1229 | "grunt-legacy-util": { 1230 | "version": "0.2.0", 1231 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz", 1232 | "integrity": "sha1-kzJIhNv343qf98Am3/RR2UqeVUs=", 1233 | "dev": true, 1234 | "requires": { 1235 | "async": "~0.1.22", 1236 | "exit": "~0.1.1", 1237 | "getobject": "~0.1.0", 1238 | "hooker": "~0.2.3", 1239 | "lodash": "~0.9.2", 1240 | "underscore.string": "~2.2.1", 1241 | "which": "~1.0.5" 1242 | } 1243 | }, 1244 | "grunt-mocha-test": { 1245 | "version": "0.7.0", 1246 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/grunt-mocha-test/-/grunt-mocha-test-0.7.0.tgz", 1247 | "integrity": "sha1-mjx/QoOyTBePBost1miQYj6V7rs=", 1248 | "dev": true, 1249 | "requires": { 1250 | "mocha": "~1.13.0" 1251 | } 1252 | }, 1253 | "has": { 1254 | "version": "1.0.3", 1255 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/has/-/has-1.0.3.tgz", 1256 | "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", 1257 | "dev": true, 1258 | "requires": { 1259 | "function-bind": "^1.1.1" 1260 | } 1261 | }, 1262 | "has-value": { 1263 | "version": "1.0.0", 1264 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/has-value/-/has-value-1.0.0.tgz", 1265 | "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", 1266 | "dev": true, 1267 | "requires": { 1268 | "get-value": "^2.0.6", 1269 | "has-values": "^1.0.0", 1270 | "isobject": "^3.0.0" 1271 | } 1272 | }, 1273 | "has-values": { 1274 | "version": "1.0.0", 1275 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/has-values/-/has-values-1.0.0.tgz", 1276 | "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", 1277 | "dev": true, 1278 | "requires": { 1279 | "is-number": "^3.0.0", 1280 | "kind-of": "^4.0.0" 1281 | }, 1282 | "dependencies": { 1283 | "kind-of": { 1284 | "version": "4.0.0", 1285 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/kind-of/-/kind-of-4.0.0.tgz", 1286 | "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", 1287 | "dev": true, 1288 | "requires": { 1289 | "is-buffer": "^1.1.5" 1290 | } 1291 | } 1292 | } 1293 | }, 1294 | "homedir-polyfill": { 1295 | "version": "1.0.3", 1296 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", 1297 | "integrity": "sha1-dDKYzvTlrz4ZQWH7rcwhUdOgWOg=", 1298 | "dev": true, 1299 | "requires": { 1300 | "parse-passwd": "^1.0.0" 1301 | } 1302 | }, 1303 | "hooker": { 1304 | "version": "0.2.3", 1305 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/hooker/-/hooker-0.2.3.tgz", 1306 | "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=", 1307 | "dev": true 1308 | }, 1309 | "htmlparser2": { 1310 | "version": "3.8.3", 1311 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/htmlparser2/-/htmlparser2-3.8.3.tgz", 1312 | "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", 1313 | "dev": true, 1314 | "requires": { 1315 | "domelementtype": "1", 1316 | "domhandler": "2.3", 1317 | "domutils": "1.5", 1318 | "entities": "1.0", 1319 | "readable-stream": "1.1" 1320 | } 1321 | }, 1322 | "http-errors": { 1323 | "version": "1.7.2", 1324 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/http-errors/-/http-errors-1.7.2.tgz", 1325 | "integrity": "sha1-T1ApzxMjnzEDblsuVSkrz7zIXI8=", 1326 | "dev": true, 1327 | "requires": { 1328 | "depd": "~1.1.2", 1329 | "inherits": "2.0.3", 1330 | "setprototypeof": "1.1.1", 1331 | "statuses": ">= 1.5.0 < 2", 1332 | "toidentifier": "1.0.0" 1333 | } 1334 | }, 1335 | "iconv-lite": { 1336 | "version": "0.4.24", 1337 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/iconv-lite/-/iconv-lite-0.4.24.tgz", 1338 | "integrity": "sha1-ICK0sl+93CHS9SSXSkdKr+czkIs=", 1339 | "dev": true, 1340 | "requires": { 1341 | "safer-buffer": ">= 2.1.2 < 3" 1342 | } 1343 | }, 1344 | "inflight": { 1345 | "version": "1.0.6", 1346 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/inflight/-/inflight-1.0.6.tgz", 1347 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 1348 | "dev": true, 1349 | "requires": { 1350 | "once": "^1.3.0", 1351 | "wrappy": "1" 1352 | } 1353 | }, 1354 | "inherits": { 1355 | "version": "2.0.3", 1356 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/inherits/-/inherits-2.0.3.tgz", 1357 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 1358 | "dev": true 1359 | }, 1360 | "ini": { 1361 | "version": "1.3.8", 1362 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/ini/-/ini-1.3.8.tgz", 1363 | "integrity": "sha1-op2kJbSIBvNHZ6Tvzjlyaa8oQyw=", 1364 | "dev": true 1365 | }, 1366 | "interpret": { 1367 | "version": "1.1.0", 1368 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/interpret/-/interpret-1.1.0.tgz", 1369 | "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", 1370 | "dev": true 1371 | }, 1372 | "ipaddr.js": { 1373 | "version": "1.9.1", 1374 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 1375 | "integrity": "sha1-v/OFQ+64mEglB5/zoqjmy9RngbM=", 1376 | "dev": true 1377 | }, 1378 | "is-absolute": { 1379 | "version": "1.0.0", 1380 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-absolute/-/is-absolute-1.0.0.tgz", 1381 | "integrity": "sha1-OV4a6EsR8mrReV5zwXN45IowFXY=", 1382 | "dev": true, 1383 | "requires": { 1384 | "is-relative": "^1.0.0", 1385 | "is-windows": "^1.0.1" 1386 | } 1387 | }, 1388 | "is-accessor-descriptor": { 1389 | "version": "0.1.6", 1390 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", 1391 | "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", 1392 | "dev": true, 1393 | "requires": { 1394 | "kind-of": "^3.0.2" 1395 | }, 1396 | "dependencies": { 1397 | "kind-of": { 1398 | "version": "3.2.2", 1399 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/kind-of/-/kind-of-3.2.2.tgz", 1400 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 1401 | "dev": true, 1402 | "requires": { 1403 | "is-buffer": "^1.1.5" 1404 | } 1405 | } 1406 | } 1407 | }, 1408 | "is-buffer": { 1409 | "version": "1.1.6", 1410 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-buffer/-/is-buffer-1.1.6.tgz", 1411 | "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", 1412 | "dev": true 1413 | }, 1414 | "is-core-module": { 1415 | "version": "2.2.0", 1416 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-core-module/-/is-core-module-2.2.0.tgz", 1417 | "integrity": "sha1-lwN+89UiJNhRY/VZeytj2a/tmBo=", 1418 | "dev": true, 1419 | "requires": { 1420 | "has": "^1.0.3" 1421 | } 1422 | }, 1423 | "is-data-descriptor": { 1424 | "version": "0.1.4", 1425 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", 1426 | "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", 1427 | "dev": true, 1428 | "requires": { 1429 | "kind-of": "^3.0.2" 1430 | }, 1431 | "dependencies": { 1432 | "kind-of": { 1433 | "version": "3.2.2", 1434 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/kind-of/-/kind-of-3.2.2.tgz", 1435 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 1436 | "dev": true, 1437 | "requires": { 1438 | "is-buffer": "^1.1.5" 1439 | } 1440 | } 1441 | } 1442 | }, 1443 | "is-descriptor": { 1444 | "version": "0.1.6", 1445 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-descriptor/-/is-descriptor-0.1.6.tgz", 1446 | "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", 1447 | "dev": true, 1448 | "requires": { 1449 | "is-accessor-descriptor": "^0.1.6", 1450 | "is-data-descriptor": "^0.1.4", 1451 | "kind-of": "^5.0.0" 1452 | }, 1453 | "dependencies": { 1454 | "kind-of": { 1455 | "version": "5.1.0", 1456 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/kind-of/-/kind-of-5.1.0.tgz", 1457 | "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", 1458 | "dev": true 1459 | } 1460 | } 1461 | }, 1462 | "is-extendable": { 1463 | "version": "0.1.1", 1464 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-extendable/-/is-extendable-0.1.1.tgz", 1465 | "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", 1466 | "dev": true 1467 | }, 1468 | "is-extglob": { 1469 | "version": "2.1.1", 1470 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-extglob/-/is-extglob-2.1.1.tgz", 1471 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 1472 | "dev": true 1473 | }, 1474 | "is-glob": { 1475 | "version": "3.1.0", 1476 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-glob/-/is-glob-3.1.0.tgz", 1477 | "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", 1478 | "dev": true, 1479 | "requires": { 1480 | "is-extglob": "^2.1.0" 1481 | } 1482 | }, 1483 | "is-number": { 1484 | "version": "3.0.0", 1485 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-number/-/is-number-3.0.0.tgz", 1486 | "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", 1487 | "dev": true, 1488 | "requires": { 1489 | "kind-of": "^3.0.2" 1490 | }, 1491 | "dependencies": { 1492 | "kind-of": { 1493 | "version": "3.2.2", 1494 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/kind-of/-/kind-of-3.2.2.tgz", 1495 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 1496 | "dev": true, 1497 | "requires": { 1498 | "is-buffer": "^1.1.5" 1499 | } 1500 | } 1501 | } 1502 | }, 1503 | "is-plain-object": { 1504 | "version": "2.0.4", 1505 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-plain-object/-/is-plain-object-2.0.4.tgz", 1506 | "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", 1507 | "dev": true, 1508 | "requires": { 1509 | "isobject": "^3.0.1" 1510 | } 1511 | }, 1512 | "is-relative": { 1513 | "version": "1.0.0", 1514 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-relative/-/is-relative-1.0.0.tgz", 1515 | "integrity": "sha1-obtpNc6MXboei5dUubLcwCDiJg0=", 1516 | "dev": true, 1517 | "requires": { 1518 | "is-unc-path": "^1.0.0" 1519 | } 1520 | }, 1521 | "is-unc-path": { 1522 | "version": "1.0.0", 1523 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-unc-path/-/is-unc-path-1.0.0.tgz", 1524 | "integrity": "sha1-1zHoiY7QkKEsNSrS6u1Qla0yLJ0=", 1525 | "dev": true, 1526 | "requires": { 1527 | "unc-path-regex": "^0.1.2" 1528 | } 1529 | }, 1530 | "is-windows": { 1531 | "version": "1.0.2", 1532 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-windows/-/is-windows-1.0.2.tgz", 1533 | "integrity": "sha1-0YUOuXkezRjmGCzhKjDzlmNLsZ0=", 1534 | "dev": true 1535 | }, 1536 | "isarray": { 1537 | "version": "1.0.0", 1538 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/isarray/-/isarray-1.0.0.tgz", 1539 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 1540 | "dev": true 1541 | }, 1542 | "isexe": { 1543 | "version": "2.0.0", 1544 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/isexe/-/isexe-2.0.0.tgz", 1545 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 1546 | "dev": true 1547 | }, 1548 | "isobject": { 1549 | "version": "3.0.1", 1550 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/isobject/-/isobject-3.0.1.tgz", 1551 | "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", 1552 | "dev": true 1553 | }, 1554 | "jade": { 1555 | "version": "0.26.3", 1556 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/jade/-/jade-0.26.3.tgz", 1557 | "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", 1558 | "dev": true, 1559 | "requires": { 1560 | "commander": "0.6.1", 1561 | "mkdirp": "0.3.0" 1562 | }, 1563 | "dependencies": { 1564 | "mkdirp": { 1565 | "version": "0.3.0", 1566 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/mkdirp/-/mkdirp-0.3.0.tgz", 1567 | "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=", 1568 | "dev": true 1569 | } 1570 | } 1571 | }, 1572 | "js-yaml": { 1573 | "version": "2.0.5", 1574 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/js-yaml/-/js-yaml-2.0.5.tgz", 1575 | "integrity": "sha1-olrmUJmZ6X3yeMZxnaEb0Gh3Q6g=", 1576 | "dev": true, 1577 | "requires": { 1578 | "argparse": "~ 0.1.11", 1579 | "esprima": "~ 1.0.2" 1580 | } 1581 | }, 1582 | "jshint": { 1583 | "version": "2.12.0", 1584 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/jshint/-/jshint-2.12.0.tgz", 1585 | "integrity": "sha1-Uudb0FjVh++BoOL5XlzxjrXcXDc=", 1586 | "dev": true, 1587 | "requires": { 1588 | "cli": "~1.0.0", 1589 | "console-browserify": "1.1.x", 1590 | "exit": "0.1.x", 1591 | "htmlparser2": "3.8.x", 1592 | "lodash": "~4.17.19", 1593 | "minimatch": "~3.0.2", 1594 | "shelljs": "0.3.x", 1595 | "strip-json-comments": "1.0.x" 1596 | }, 1597 | "dependencies": { 1598 | "cli": { 1599 | "version": "1.0.1", 1600 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/cli/-/cli-1.0.1.tgz", 1601 | "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", 1602 | "dev": true, 1603 | "requires": { 1604 | "exit": "0.1.2", 1605 | "glob": "^7.1.1" 1606 | } 1607 | }, 1608 | "console-browserify": { 1609 | "version": "1.1.0", 1610 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/console-browserify/-/console-browserify-1.1.0.tgz", 1611 | "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", 1612 | "dev": true, 1613 | "requires": { 1614 | "date-now": "^0.1.4" 1615 | } 1616 | }, 1617 | "glob": { 1618 | "version": "7.1.6", 1619 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/glob/-/glob-7.1.6.tgz", 1620 | "integrity": "sha1-FB8zuBp8JJLhJVlDB0gMRmeSeKY=", 1621 | "dev": true, 1622 | "requires": { 1623 | "fs.realpath": "^1.0.0", 1624 | "inflight": "^1.0.4", 1625 | "inherits": "2", 1626 | "minimatch": "^3.0.4", 1627 | "once": "^1.3.0", 1628 | "path-is-absolute": "^1.0.0" 1629 | } 1630 | }, 1631 | "lodash": { 1632 | "version": "4.17.20", 1633 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/lodash/-/lodash-4.17.20.tgz", 1634 | "integrity": "sha1-tEqbYpe8tpjxxRo1RaKzs2jVnFI=", 1635 | "dev": true 1636 | }, 1637 | "minimatch": { 1638 | "version": "3.0.4", 1639 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/minimatch/-/minimatch-3.0.4.tgz", 1640 | "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", 1641 | "dev": true, 1642 | "requires": { 1643 | "brace-expansion": "^1.1.7" 1644 | } 1645 | }, 1646 | "shelljs": { 1647 | "version": "0.3.0", 1648 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/shelljs/-/shelljs-0.3.0.tgz", 1649 | "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", 1650 | "dev": true 1651 | } 1652 | } 1653 | }, 1654 | "keygrip": { 1655 | "version": "1.1.0", 1656 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/keygrip/-/keygrip-1.1.0.tgz", 1657 | "integrity": "sha1-hxsWgdXhWcYqRFsMdLYV4JF+ciY=", 1658 | "dev": true, 1659 | "requires": { 1660 | "tsscmp": "1.0.6" 1661 | } 1662 | }, 1663 | "kind-of": { 1664 | "version": "6.0.3", 1665 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/kind-of/-/kind-of-6.0.3.tgz", 1666 | "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", 1667 | "dev": true 1668 | }, 1669 | "liftoff": { 1670 | "version": "2.5.0", 1671 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/liftoff/-/liftoff-2.5.0.tgz", 1672 | "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=", 1673 | "dev": true, 1674 | "requires": { 1675 | "extend": "^3.0.0", 1676 | "findup-sync": "^2.0.0", 1677 | "fined": "^1.0.1", 1678 | "flagged-respawn": "^1.0.0", 1679 | "is-plain-object": "^2.0.4", 1680 | "object.map": "^1.0.0", 1681 | "rechoir": "^0.6.2", 1682 | "resolve": "^1.1.7" 1683 | }, 1684 | "dependencies": { 1685 | "findup-sync": { 1686 | "version": "2.0.0", 1687 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/findup-sync/-/findup-sync-2.0.0.tgz", 1688 | "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", 1689 | "dev": true, 1690 | "requires": { 1691 | "detect-file": "^1.0.0", 1692 | "is-glob": "^3.1.0", 1693 | "micromatch": "^3.0.4", 1694 | "resolve-dir": "^1.0.1" 1695 | } 1696 | } 1697 | } 1698 | }, 1699 | "lodash": { 1700 | "version": "0.9.2", 1701 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/lodash/-/lodash-0.9.2.tgz", 1702 | "integrity": "sha1-jzSZxSRdNG1oLlsNO0B2fgnxqSw=", 1703 | "dev": true 1704 | }, 1705 | "lru-cache": { 1706 | "version": "2.7.3", 1707 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/lru-cache/-/lru-cache-2.7.3.tgz", 1708 | "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", 1709 | "dev": true 1710 | }, 1711 | "make-iterator": { 1712 | "version": "1.0.1", 1713 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/make-iterator/-/make-iterator-1.0.1.tgz", 1714 | "integrity": "sha1-KbM/MSqo9UfEpeSQ9Wr87JkTOtY=", 1715 | "dev": true, 1716 | "requires": { 1717 | "kind-of": "^6.0.2" 1718 | } 1719 | }, 1720 | "map-cache": { 1721 | "version": "0.2.2", 1722 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/map-cache/-/map-cache-0.2.2.tgz", 1723 | "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", 1724 | "dev": true 1725 | }, 1726 | "map-visit": { 1727 | "version": "1.0.0", 1728 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/map-visit/-/map-visit-1.0.0.tgz", 1729 | "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", 1730 | "dev": true, 1731 | "requires": { 1732 | "object-visit": "^1.0.0" 1733 | } 1734 | }, 1735 | "media-typer": { 1736 | "version": "0.3.0", 1737 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/media-typer/-/media-typer-0.3.0.tgz", 1738 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", 1739 | "dev": true 1740 | }, 1741 | "merge-descriptors": { 1742 | "version": "1.0.1", 1743 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 1744 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", 1745 | "dev": true 1746 | }, 1747 | "methods": { 1748 | "version": "1.1.2", 1749 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/methods/-/methods-1.1.2.tgz", 1750 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", 1751 | "dev": true 1752 | }, 1753 | "micromatch": { 1754 | "version": "3.1.10", 1755 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/micromatch/-/micromatch-3.1.10.tgz", 1756 | "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", 1757 | "dev": true, 1758 | "requires": { 1759 | "arr-diff": "^4.0.0", 1760 | "array-unique": "^0.3.2", 1761 | "braces": "^2.3.1", 1762 | "define-property": "^2.0.2", 1763 | "extend-shallow": "^3.0.2", 1764 | "extglob": "^2.0.4", 1765 | "fragment-cache": "^0.2.1", 1766 | "kind-of": "^6.0.2", 1767 | "nanomatch": "^1.2.9", 1768 | "object.pick": "^1.3.0", 1769 | "regex-not": "^1.0.0", 1770 | "snapdragon": "^0.8.1", 1771 | "to-regex": "^3.0.2" 1772 | } 1773 | }, 1774 | "mime": { 1775 | "version": "1.6.0", 1776 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/mime/-/mime-1.6.0.tgz", 1777 | "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=", 1778 | "dev": true 1779 | }, 1780 | "mime-db": { 1781 | "version": "1.45.0", 1782 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/mime-db/-/mime-db-1.45.0.tgz", 1783 | "integrity": "sha1-zO7aIczXw6dF66LezVXUtz54eeo=", 1784 | "dev": true 1785 | }, 1786 | "mime-types": { 1787 | "version": "2.1.28", 1788 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/mime-types/-/mime-types-2.1.28.tgz", 1789 | "integrity": "sha1-EWDEdX6rLFNjiI4AUnPs950qDs0=", 1790 | "dev": true, 1791 | "requires": { 1792 | "mime-db": "1.45.0" 1793 | } 1794 | }, 1795 | "minimatch": { 1796 | "version": "0.2.14", 1797 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/minimatch/-/minimatch-0.2.14.tgz", 1798 | "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", 1799 | "dev": true, 1800 | "requires": { 1801 | "lru-cache": "2", 1802 | "sigmund": "~1.0.0" 1803 | } 1804 | }, 1805 | "mixin-deep": { 1806 | "version": "1.3.2", 1807 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/mixin-deep/-/mixin-deep-1.3.2.tgz", 1808 | "integrity": "sha1-ESC0PcNZp4Xc5ltVuC4lfM9HlWY=", 1809 | "dev": true, 1810 | "requires": { 1811 | "for-in": "^1.0.2", 1812 | "is-extendable": "^1.0.1" 1813 | }, 1814 | "dependencies": { 1815 | "is-extendable": { 1816 | "version": "1.0.1", 1817 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-extendable/-/is-extendable-1.0.1.tgz", 1818 | "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", 1819 | "dev": true, 1820 | "requires": { 1821 | "is-plain-object": "^2.0.4" 1822 | } 1823 | } 1824 | } 1825 | }, 1826 | "mkdirp": { 1827 | "version": "0.3.5", 1828 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/mkdirp/-/mkdirp-0.3.5.tgz", 1829 | "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=", 1830 | "dev": true 1831 | }, 1832 | "mocha": { 1833 | "version": "1.13.0", 1834 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/mocha/-/mocha-1.13.0.tgz", 1835 | "integrity": "sha1-jY+k4xC5TMbv6z7SauypbeqTMHw=", 1836 | "dev": true, 1837 | "requires": { 1838 | "commander": "0.6.1", 1839 | "debug": "*", 1840 | "diff": "1.0.7", 1841 | "glob": "3.2.3", 1842 | "growl": "1.7.x", 1843 | "jade": "0.26.3", 1844 | "mkdirp": "0.3.5" 1845 | }, 1846 | "dependencies": { 1847 | "glob": { 1848 | "version": "3.2.3", 1849 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/glob/-/glob-3.2.3.tgz", 1850 | "integrity": "sha1-4xPusknHr/qlxHUoaw4RW1mDlGc=", 1851 | "dev": true, 1852 | "requires": { 1853 | "graceful-fs": "~2.0.0", 1854 | "inherits": "2", 1855 | "minimatch": "~0.2.11" 1856 | } 1857 | }, 1858 | "graceful-fs": { 1859 | "version": "2.0.3", 1860 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/graceful-fs/-/graceful-fs-2.0.3.tgz", 1861 | "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=", 1862 | "dev": true 1863 | } 1864 | } 1865 | }, 1866 | "ms": { 1867 | "version": "2.0.0", 1868 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/ms/-/ms-2.0.0.tgz", 1869 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 1870 | "dev": true 1871 | }, 1872 | "nanomatch": { 1873 | "version": "1.2.13", 1874 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/nanomatch/-/nanomatch-1.2.13.tgz", 1875 | "integrity": "sha1-uHqKpPwN6P5r6IiVs4mD/yZb0Rk=", 1876 | "dev": true, 1877 | "requires": { 1878 | "arr-diff": "^4.0.0", 1879 | "array-unique": "^0.3.2", 1880 | "define-property": "^2.0.2", 1881 | "extend-shallow": "^3.0.2", 1882 | "fragment-cache": "^0.2.1", 1883 | "is-windows": "^1.0.2", 1884 | "kind-of": "^6.0.2", 1885 | "object.pick": "^1.3.0", 1886 | "regex-not": "^1.0.0", 1887 | "snapdragon": "^0.8.1", 1888 | "to-regex": "^3.0.1" 1889 | } 1890 | }, 1891 | "negotiator": { 1892 | "version": "0.6.2", 1893 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/negotiator/-/negotiator-0.6.2.tgz", 1894 | "integrity": "sha1-/qz3zPUlp3rpY0Q2pkiD/+yjRvs=", 1895 | "dev": true 1896 | }, 1897 | "nopt": { 1898 | "version": "1.0.10", 1899 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/nopt/-/nopt-1.0.10.tgz", 1900 | "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", 1901 | "dev": true, 1902 | "requires": { 1903 | "abbrev": "1" 1904 | } 1905 | }, 1906 | "object-copy": { 1907 | "version": "0.1.0", 1908 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/object-copy/-/object-copy-0.1.0.tgz", 1909 | "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", 1910 | "dev": true, 1911 | "requires": { 1912 | "copy-descriptor": "^0.1.0", 1913 | "define-property": "^0.2.5", 1914 | "kind-of": "^3.0.3" 1915 | }, 1916 | "dependencies": { 1917 | "define-property": { 1918 | "version": "0.2.5", 1919 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/define-property/-/define-property-0.2.5.tgz", 1920 | "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", 1921 | "dev": true, 1922 | "requires": { 1923 | "is-descriptor": "^0.1.0" 1924 | } 1925 | }, 1926 | "kind-of": { 1927 | "version": "3.2.2", 1928 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/kind-of/-/kind-of-3.2.2.tgz", 1929 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 1930 | "dev": true, 1931 | "requires": { 1932 | "is-buffer": "^1.1.5" 1933 | } 1934 | } 1935 | } 1936 | }, 1937 | "object-visit": { 1938 | "version": "1.0.1", 1939 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/object-visit/-/object-visit-1.0.1.tgz", 1940 | "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", 1941 | "dev": true, 1942 | "requires": { 1943 | "isobject": "^3.0.0" 1944 | } 1945 | }, 1946 | "object.defaults": { 1947 | "version": "1.1.0", 1948 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/object.defaults/-/object.defaults-1.1.0.tgz", 1949 | "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", 1950 | "dev": true, 1951 | "requires": { 1952 | "array-each": "^1.0.1", 1953 | "array-slice": "^1.0.0", 1954 | "for-own": "^1.0.0", 1955 | "isobject": "^3.0.0" 1956 | } 1957 | }, 1958 | "object.map": { 1959 | "version": "1.0.1", 1960 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/object.map/-/object.map-1.0.1.tgz", 1961 | "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", 1962 | "dev": true, 1963 | "requires": { 1964 | "for-own": "^1.0.0", 1965 | "make-iterator": "^1.0.0" 1966 | } 1967 | }, 1968 | "object.pick": { 1969 | "version": "1.3.0", 1970 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/object.pick/-/object.pick-1.3.0.tgz", 1971 | "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", 1972 | "dev": true, 1973 | "requires": { 1974 | "isobject": "^3.0.1" 1975 | } 1976 | }, 1977 | "on-finished": { 1978 | "version": "2.3.0", 1979 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/on-finished/-/on-finished-2.3.0.tgz", 1980 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 1981 | "dev": true, 1982 | "requires": { 1983 | "ee-first": "1.1.1" 1984 | } 1985 | }, 1986 | "on-headers": { 1987 | "version": "1.0.2", 1988 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/on-headers/-/on-headers-1.0.2.tgz", 1989 | "integrity": "sha1-dysK5qqlJcOZ5Imt+tkMQD6zwo8=", 1990 | "dev": true 1991 | }, 1992 | "once": { 1993 | "version": "1.4.0", 1994 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/once/-/once-1.4.0.tgz", 1995 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1996 | "dev": true, 1997 | "requires": { 1998 | "wrappy": "1" 1999 | } 2000 | }, 2001 | "os-homedir": { 2002 | "version": "1.0.2", 2003 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/os-homedir/-/os-homedir-1.0.2.tgz", 2004 | "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", 2005 | "dev": true 2006 | }, 2007 | "os-tmpdir": { 2008 | "version": "1.0.2", 2009 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 2010 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", 2011 | "dev": true 2012 | }, 2013 | "osenv": { 2014 | "version": "0.1.5", 2015 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/osenv/-/osenv-0.1.5.tgz", 2016 | "integrity": "sha1-hc36+uso6Gd/QW4odZK18/SepBA=", 2017 | "dev": true, 2018 | "requires": { 2019 | "os-homedir": "^1.0.0", 2020 | "os-tmpdir": "^1.0.0" 2021 | } 2022 | }, 2023 | "parse-filepath": { 2024 | "version": "1.0.2", 2025 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/parse-filepath/-/parse-filepath-1.0.2.tgz", 2026 | "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", 2027 | "dev": true, 2028 | "requires": { 2029 | "is-absolute": "^1.0.0", 2030 | "map-cache": "^0.2.0", 2031 | "path-root": "^0.1.1" 2032 | } 2033 | }, 2034 | "parse-passwd": { 2035 | "version": "1.0.0", 2036 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/parse-passwd/-/parse-passwd-1.0.0.tgz", 2037 | "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", 2038 | "dev": true 2039 | }, 2040 | "parseurl": { 2041 | "version": "1.3.3", 2042 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/parseurl/-/parseurl-1.3.3.tgz", 2043 | "integrity": "sha1-naGee+6NEt/wUT7Vt2lXeTvC6NQ=", 2044 | "dev": true 2045 | }, 2046 | "pascalcase": { 2047 | "version": "0.1.1", 2048 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/pascalcase/-/pascalcase-0.1.1.tgz", 2049 | "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", 2050 | "dev": true 2051 | }, 2052 | "path-is-absolute": { 2053 | "version": "1.0.1", 2054 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 2055 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 2056 | "dev": true 2057 | }, 2058 | "path-parse": { 2059 | "version": "1.0.6", 2060 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/path-parse/-/path-parse-1.0.6.tgz", 2061 | "integrity": "sha1-1i27VnlAXXLEc37FhgDp3c8G0kw=", 2062 | "dev": true 2063 | }, 2064 | "path-root": { 2065 | "version": "0.1.1", 2066 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/path-root/-/path-root-0.1.1.tgz", 2067 | "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", 2068 | "dev": true, 2069 | "requires": { 2070 | "path-root-regex": "^0.1.0" 2071 | } 2072 | }, 2073 | "path-root-regex": { 2074 | "version": "0.1.2", 2075 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/path-root-regex/-/path-root-regex-0.1.2.tgz", 2076 | "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", 2077 | "dev": true 2078 | }, 2079 | "path-to-regexp": { 2080 | "version": "0.1.7", 2081 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 2082 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", 2083 | "dev": true 2084 | }, 2085 | "posix-character-classes": { 2086 | "version": "0.1.1", 2087 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/posix-character-classes/-/posix-character-classes-0.1.1.tgz", 2088 | "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", 2089 | "dev": true 2090 | }, 2091 | "proxy-addr": { 2092 | "version": "2.0.6", 2093 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/proxy-addr/-/proxy-addr-2.0.6.tgz", 2094 | "integrity": "sha1-/cIzZQVEfT8vLGOO0nLK9hS7sr8=", 2095 | "dev": true, 2096 | "requires": { 2097 | "forwarded": "~0.1.2", 2098 | "ipaddr.js": "1.9.1" 2099 | } 2100 | }, 2101 | "qs": { 2102 | "version": "6.7.0", 2103 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/qs/-/qs-6.7.0.tgz", 2104 | "integrity": "sha1-QdwaAV49WB8WIXdr4xr7KHapsbw=", 2105 | "dev": true 2106 | }, 2107 | "random-bytes": { 2108 | "version": "1.0.0", 2109 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/random-bytes/-/random-bytes-1.0.0.tgz", 2110 | "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=", 2111 | "dev": true 2112 | }, 2113 | "range-parser": { 2114 | "version": "1.2.1", 2115 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/range-parser/-/range-parser-1.2.1.tgz", 2116 | "integrity": "sha1-PPNwI9GZ4cJNGlW4SADC8+ZGgDE=", 2117 | "dev": true 2118 | }, 2119 | "raw-body": { 2120 | "version": "2.4.0", 2121 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/raw-body/-/raw-body-2.4.0.tgz", 2122 | "integrity": "sha1-oc5vucm8NWylLoklarWQWeE9AzI=", 2123 | "dev": true, 2124 | "requires": { 2125 | "bytes": "3.1.0", 2126 | "http-errors": "1.7.2", 2127 | "iconv-lite": "0.4.24", 2128 | "unpipe": "1.0.0" 2129 | } 2130 | }, 2131 | "readable-stream": { 2132 | "version": "1.1.14", 2133 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/readable-stream/-/readable-stream-1.1.14.tgz", 2134 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 2135 | "dev": true, 2136 | "requires": { 2137 | "core-util-is": "~1.0.0", 2138 | "inherits": "~2.0.1", 2139 | "isarray": "0.0.1", 2140 | "string_decoder": "~0.10.x" 2141 | }, 2142 | "dependencies": { 2143 | "isarray": { 2144 | "version": "0.0.1", 2145 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/isarray/-/isarray-0.0.1.tgz", 2146 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", 2147 | "dev": true 2148 | } 2149 | } 2150 | }, 2151 | "rechoir": { 2152 | "version": "0.6.2", 2153 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/rechoir/-/rechoir-0.6.2.tgz", 2154 | "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", 2155 | "dev": true, 2156 | "requires": { 2157 | "resolve": "^1.1.6" 2158 | } 2159 | }, 2160 | "reduce-component": { 2161 | "version": "1.0.1", 2162 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/reduce-component/-/reduce-component-1.0.1.tgz", 2163 | "integrity": "sha1-4Mk1QsV0UhvqE98PlIjtgqt3xdo=", 2164 | "dev": true 2165 | }, 2166 | "regex-not": { 2167 | "version": "1.0.2", 2168 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/regex-not/-/regex-not-1.0.2.tgz", 2169 | "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", 2170 | "dev": true, 2171 | "requires": { 2172 | "extend-shallow": "^3.0.2", 2173 | "safe-regex": "^1.1.0" 2174 | } 2175 | }, 2176 | "repeat-element": { 2177 | "version": "1.1.3", 2178 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/repeat-element/-/repeat-element-1.1.3.tgz", 2179 | "integrity": "sha1-eC4NglwMWjuzlzH4Tv7mt0Lmsc4=", 2180 | "dev": true 2181 | }, 2182 | "repeat-string": { 2183 | "version": "1.6.1", 2184 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/repeat-string/-/repeat-string-1.6.1.tgz", 2185 | "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", 2186 | "dev": true 2187 | }, 2188 | "resolve": { 2189 | "version": "1.20.0", 2190 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/resolve/-/resolve-1.20.0.tgz", 2191 | "integrity": "sha1-YpoBP7P3B1XW8LeTXMHCxTeLGXU=", 2192 | "dev": true, 2193 | "requires": { 2194 | "is-core-module": "^2.2.0", 2195 | "path-parse": "^1.0.6" 2196 | } 2197 | }, 2198 | "resolve-dir": { 2199 | "version": "1.0.1", 2200 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/resolve-dir/-/resolve-dir-1.0.1.tgz", 2201 | "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", 2202 | "dev": true, 2203 | "requires": { 2204 | "expand-tilde": "^2.0.0", 2205 | "global-modules": "^1.0.0" 2206 | } 2207 | }, 2208 | "resolve-url": { 2209 | "version": "0.2.1", 2210 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/resolve-url/-/resolve-url-0.2.1.tgz", 2211 | "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", 2212 | "dev": true 2213 | }, 2214 | "ret": { 2215 | "version": "0.1.15", 2216 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/ret/-/ret-0.1.15.tgz", 2217 | "integrity": "sha1-uKSCXVvbH8P29Twrwz+BOIaBx7w=", 2218 | "dev": true 2219 | }, 2220 | "rimraf": { 2221 | "version": "2.2.8", 2222 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/rimraf/-/rimraf-2.2.8.tgz", 2223 | "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", 2224 | "dev": true 2225 | }, 2226 | "safe-buffer": { 2227 | "version": "5.1.2", 2228 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/safe-buffer/-/safe-buffer-5.1.2.tgz", 2229 | "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=", 2230 | "dev": true 2231 | }, 2232 | "safe-regex": { 2233 | "version": "1.1.0", 2234 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/safe-regex/-/safe-regex-1.1.0.tgz", 2235 | "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", 2236 | "dev": true, 2237 | "requires": { 2238 | "ret": "~0.1.10" 2239 | } 2240 | }, 2241 | "safer-buffer": { 2242 | "version": "2.1.2", 2243 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/safer-buffer/-/safer-buffer-2.1.2.tgz", 2244 | "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=", 2245 | "dev": true 2246 | }, 2247 | "send": { 2248 | "version": "0.17.1", 2249 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/send/-/send-0.17.1.tgz", 2250 | "integrity": "sha1-wdiwWfeQD3Rm3Uk4vcROEd2zdsg=", 2251 | "dev": true, 2252 | "requires": { 2253 | "debug": "2.6.9", 2254 | "depd": "~1.1.2", 2255 | "destroy": "~1.0.4", 2256 | "encodeurl": "~1.0.2", 2257 | "escape-html": "~1.0.3", 2258 | "etag": "~1.8.1", 2259 | "fresh": "0.5.2", 2260 | "http-errors": "~1.7.2", 2261 | "mime": "1.6.0", 2262 | "ms": "2.1.1", 2263 | "on-finished": "~2.3.0", 2264 | "range-parser": "~1.2.1", 2265 | "statuses": "~1.5.0" 2266 | }, 2267 | "dependencies": { 2268 | "ms": { 2269 | "version": "2.1.1", 2270 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/ms/-/ms-2.1.1.tgz", 2271 | "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=", 2272 | "dev": true 2273 | } 2274 | } 2275 | }, 2276 | "serve-static": { 2277 | "version": "1.14.1", 2278 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/serve-static/-/serve-static-1.14.1.tgz", 2279 | "integrity": "sha1-Zm5jbcTwEPfvKZcKiKZ0MgiYsvk=", 2280 | "dev": true, 2281 | "requires": { 2282 | "encodeurl": "~1.0.2", 2283 | "escape-html": "~1.0.3", 2284 | "parseurl": "~1.3.3", 2285 | "send": "0.17.1" 2286 | } 2287 | }, 2288 | "set-value": { 2289 | "version": "2.0.1", 2290 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/set-value/-/set-value-2.0.1.tgz", 2291 | "integrity": "sha1-oY1AUw5vB95CKMfe/kInr4ytAFs=", 2292 | "dev": true, 2293 | "requires": { 2294 | "extend-shallow": "^2.0.1", 2295 | "is-extendable": "^0.1.1", 2296 | "is-plain-object": "^2.0.3", 2297 | "split-string": "^3.0.1" 2298 | }, 2299 | "dependencies": { 2300 | "extend-shallow": { 2301 | "version": "2.0.1", 2302 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/extend-shallow/-/extend-shallow-2.0.1.tgz", 2303 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 2304 | "dev": true, 2305 | "requires": { 2306 | "is-extendable": "^0.1.0" 2307 | } 2308 | } 2309 | } 2310 | }, 2311 | "setprototypeof": { 2312 | "version": "1.1.1", 2313 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/setprototypeof/-/setprototypeof-1.1.1.tgz", 2314 | "integrity": "sha1-fpWsskqpL1iF4KvvW6ExMw1K5oM=", 2315 | "dev": true 2316 | }, 2317 | "shelljs": { 2318 | "version": "0.1.4", 2319 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/shelljs/-/shelljs-0.1.4.tgz", 2320 | "integrity": "sha1-37vnjVbDwBaNL7eeEOzR28sH7A4=", 2321 | "dev": true 2322 | }, 2323 | "sigmund": { 2324 | "version": "1.0.1", 2325 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/sigmund/-/sigmund-1.0.1.tgz", 2326 | "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", 2327 | "dev": true 2328 | }, 2329 | "snapdragon": { 2330 | "version": "0.8.2", 2331 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/snapdragon/-/snapdragon-0.8.2.tgz", 2332 | "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", 2333 | "dev": true, 2334 | "requires": { 2335 | "base": "^0.11.1", 2336 | "debug": "^2.2.0", 2337 | "define-property": "^0.2.5", 2338 | "extend-shallow": "^2.0.1", 2339 | "map-cache": "^0.2.2", 2340 | "source-map": "^0.5.6", 2341 | "source-map-resolve": "^0.5.0", 2342 | "use": "^3.1.0" 2343 | }, 2344 | "dependencies": { 2345 | "define-property": { 2346 | "version": "0.2.5", 2347 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/define-property/-/define-property-0.2.5.tgz", 2348 | "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", 2349 | "dev": true, 2350 | "requires": { 2351 | "is-descriptor": "^0.1.0" 2352 | } 2353 | }, 2354 | "extend-shallow": { 2355 | "version": "2.0.1", 2356 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/extend-shallow/-/extend-shallow-2.0.1.tgz", 2357 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 2358 | "dev": true, 2359 | "requires": { 2360 | "is-extendable": "^0.1.0" 2361 | } 2362 | } 2363 | } 2364 | }, 2365 | "snapdragon-node": { 2366 | "version": "2.1.1", 2367 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/snapdragon-node/-/snapdragon-node-2.1.1.tgz", 2368 | "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", 2369 | "dev": true, 2370 | "requires": { 2371 | "define-property": "^1.0.0", 2372 | "isobject": "^3.0.0", 2373 | "snapdragon-util": "^3.0.1" 2374 | }, 2375 | "dependencies": { 2376 | "define-property": { 2377 | "version": "1.0.0", 2378 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/define-property/-/define-property-1.0.0.tgz", 2379 | "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", 2380 | "dev": true, 2381 | "requires": { 2382 | "is-descriptor": "^1.0.0" 2383 | } 2384 | }, 2385 | "is-accessor-descriptor": { 2386 | "version": "1.0.0", 2387 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", 2388 | "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", 2389 | "dev": true, 2390 | "requires": { 2391 | "kind-of": "^6.0.0" 2392 | } 2393 | }, 2394 | "is-data-descriptor": { 2395 | "version": "1.0.0", 2396 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", 2397 | "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", 2398 | "dev": true, 2399 | "requires": { 2400 | "kind-of": "^6.0.0" 2401 | } 2402 | }, 2403 | "is-descriptor": { 2404 | "version": "1.0.2", 2405 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/is-descriptor/-/is-descriptor-1.0.2.tgz", 2406 | "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", 2407 | "dev": true, 2408 | "requires": { 2409 | "is-accessor-descriptor": "^1.0.0", 2410 | "is-data-descriptor": "^1.0.0", 2411 | "kind-of": "^6.0.2" 2412 | } 2413 | } 2414 | } 2415 | }, 2416 | "snapdragon-util": { 2417 | "version": "3.0.1", 2418 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/snapdragon-util/-/snapdragon-util-3.0.1.tgz", 2419 | "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", 2420 | "dev": true, 2421 | "requires": { 2422 | "kind-of": "^3.2.0" 2423 | }, 2424 | "dependencies": { 2425 | "kind-of": { 2426 | "version": "3.2.2", 2427 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/kind-of/-/kind-of-3.2.2.tgz", 2428 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 2429 | "dev": true, 2430 | "requires": { 2431 | "is-buffer": "^1.1.5" 2432 | } 2433 | } 2434 | } 2435 | }, 2436 | "source-map": { 2437 | "version": "0.5.7", 2438 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/source-map/-/source-map-0.5.7.tgz", 2439 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 2440 | "dev": true 2441 | }, 2442 | "source-map-resolve": { 2443 | "version": "0.5.3", 2444 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/source-map-resolve/-/source-map-resolve-0.5.3.tgz", 2445 | "integrity": "sha1-GQhmvs51U+H48mei7oLGBrVQmho=", 2446 | "dev": true, 2447 | "requires": { 2448 | "atob": "^2.1.2", 2449 | "decode-uri-component": "^0.2.0", 2450 | "resolve-url": "^0.2.1", 2451 | "source-map-url": "^0.4.0", 2452 | "urix": "^0.1.0" 2453 | } 2454 | }, 2455 | "source-map-url": { 2456 | "version": "0.4.1", 2457 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/source-map-url/-/source-map-url-0.4.1.tgz", 2458 | "integrity": "sha1-CvZmBadFpaL5HPG7+KevvCg97FY=", 2459 | "dev": true 2460 | }, 2461 | "split-string": { 2462 | "version": "3.1.0", 2463 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/split-string/-/split-string-3.1.0.tgz", 2464 | "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", 2465 | "dev": true, 2466 | "requires": { 2467 | "extend-shallow": "^3.0.0" 2468 | } 2469 | }, 2470 | "static-extend": { 2471 | "version": "0.1.2", 2472 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/static-extend/-/static-extend-0.1.2.tgz", 2473 | "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", 2474 | "dev": true, 2475 | "requires": { 2476 | "define-property": "^0.2.5", 2477 | "object-copy": "^0.1.0" 2478 | }, 2479 | "dependencies": { 2480 | "define-property": { 2481 | "version": "0.2.5", 2482 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/define-property/-/define-property-0.2.5.tgz", 2483 | "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", 2484 | "dev": true, 2485 | "requires": { 2486 | "is-descriptor": "^0.1.0" 2487 | } 2488 | } 2489 | } 2490 | }, 2491 | "statuses": { 2492 | "version": "1.5.0", 2493 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/statuses/-/statuses-1.5.0.tgz", 2494 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", 2495 | "dev": true 2496 | }, 2497 | "string_decoder": { 2498 | "version": "0.10.31", 2499 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/string_decoder/-/string_decoder-0.10.31.tgz", 2500 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", 2501 | "dev": true 2502 | }, 2503 | "strip-json-comments": { 2504 | "version": "1.0.4", 2505 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/strip-json-comments/-/strip-json-comments-1.0.4.tgz", 2506 | "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", 2507 | "dev": true 2508 | }, 2509 | "superagent": { 2510 | "version": "0.18.0", 2511 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/superagent/-/superagent-0.18.0.tgz", 2512 | "integrity": "sha1-nUN1o64sT71V/SDVsSokcNL8j2I=", 2513 | "dev": true, 2514 | "requires": { 2515 | "component-emitter": "1.1.2", 2516 | "cookiejar": "1.3.2", 2517 | "debug": "~0.7.2", 2518 | "extend": "~1.2.1", 2519 | "form-data": "0.1.2", 2520 | "formidable": "1.0.14", 2521 | "methods": "0.0.1", 2522 | "mime": "1.2.5", 2523 | "qs": "0.6.6", 2524 | "readable-stream": "1.0.27-1", 2525 | "reduce-component": "1.0.1" 2526 | }, 2527 | "dependencies": { 2528 | "component-emitter": { 2529 | "version": "1.1.2", 2530 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/component-emitter/-/component-emitter-1.1.2.tgz", 2531 | "integrity": "sha1-KWWU8nU9qmOZbSrwjRWpURbJrsM=", 2532 | "dev": true 2533 | }, 2534 | "debug": { 2535 | "version": "0.7.4", 2536 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/debug/-/debug-0.7.4.tgz", 2537 | "integrity": "sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk=", 2538 | "dev": true 2539 | }, 2540 | "extend": { 2541 | "version": "1.2.1", 2542 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/extend/-/extend-1.2.1.tgz", 2543 | "integrity": "sha1-oPX9bPyDpf5J72mNYOyKYk3UV2w=", 2544 | "dev": true 2545 | }, 2546 | "isarray": { 2547 | "version": "0.0.1", 2548 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/isarray/-/isarray-0.0.1.tgz", 2549 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", 2550 | "dev": true 2551 | }, 2552 | "methods": { 2553 | "version": "0.0.1", 2554 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/methods/-/methods-0.0.1.tgz", 2555 | "integrity": "sha1-J3yQ+L7zlwlkWoNxxRw7bGSOBow=", 2556 | "dev": true 2557 | }, 2558 | "mime": { 2559 | "version": "1.2.5", 2560 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/mime/-/mime-1.2.5.tgz", 2561 | "integrity": "sha1-nu0HMCKov14WyFZsaGe4gyv7+hM=", 2562 | "dev": true 2563 | }, 2564 | "qs": { 2565 | "version": "0.6.6", 2566 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/qs/-/qs-0.6.6.tgz", 2567 | "integrity": "sha1-bgFQmP9RlouKPIGQAdXyyJvEsQc=", 2568 | "dev": true 2569 | }, 2570 | "readable-stream": { 2571 | "version": "1.0.27-1", 2572 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/readable-stream/-/readable-stream-1.0.27-1.tgz", 2573 | "integrity": "sha1-a2eYPCA1fO/QfwFlABoW1xDZEHg=", 2574 | "dev": true, 2575 | "requires": { 2576 | "core-util-is": "~1.0.0", 2577 | "inherits": "~2.0.1", 2578 | "isarray": "0.0.1", 2579 | "string_decoder": "~0.10.x" 2580 | } 2581 | } 2582 | } 2583 | }, 2584 | "supertest": { 2585 | "version": "0.13.0", 2586 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/supertest/-/supertest-0.13.0.tgz", 2587 | "integrity": "sha1-SJK6/ZvqqbvMlf1anwSUmu8c4G8=", 2588 | "dev": true, 2589 | "requires": { 2590 | "methods": "1.0.0", 2591 | "superagent": "0.18.0" 2592 | }, 2593 | "dependencies": { 2594 | "methods": { 2595 | "version": "1.0.0", 2596 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/methods/-/methods-1.0.0.tgz", 2597 | "integrity": "sha1-mnPYY3XfzvJu9hyj5Lii4lOKgOM=", 2598 | "dev": true 2599 | } 2600 | } 2601 | }, 2602 | "to-object-path": { 2603 | "version": "0.3.0", 2604 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/to-object-path/-/to-object-path-0.3.0.tgz", 2605 | "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", 2606 | "dev": true, 2607 | "requires": { 2608 | "kind-of": "^3.0.2" 2609 | }, 2610 | "dependencies": { 2611 | "kind-of": { 2612 | "version": "3.2.2", 2613 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/kind-of/-/kind-of-3.2.2.tgz", 2614 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 2615 | "dev": true, 2616 | "requires": { 2617 | "is-buffer": "^1.1.5" 2618 | } 2619 | } 2620 | } 2621 | }, 2622 | "to-regex": { 2623 | "version": "3.0.2", 2624 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/to-regex/-/to-regex-3.0.2.tgz", 2625 | "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", 2626 | "dev": true, 2627 | "requires": { 2628 | "define-property": "^2.0.2", 2629 | "extend-shallow": "^3.0.2", 2630 | "regex-not": "^1.0.2", 2631 | "safe-regex": "^1.1.0" 2632 | } 2633 | }, 2634 | "to-regex-range": { 2635 | "version": "2.1.1", 2636 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/to-regex-range/-/to-regex-range-2.1.1.tgz", 2637 | "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", 2638 | "dev": true, 2639 | "requires": { 2640 | "is-number": "^3.0.0", 2641 | "repeat-string": "^1.6.1" 2642 | } 2643 | }, 2644 | "toidentifier": { 2645 | "version": "1.0.0", 2646 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/toidentifier/-/toidentifier-1.0.0.tgz", 2647 | "integrity": "sha1-fhvjRw8ed5SLxD2Uo8j013UrpVM=", 2648 | "dev": true 2649 | }, 2650 | "tsscmp": { 2651 | "version": "1.0.6", 2652 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/tsscmp/-/tsscmp-1.0.6.tgz", 2653 | "integrity": "sha1-hbmVg6w1iexL/vgltQAKqRHWBes=" 2654 | }, 2655 | "type-is": { 2656 | "version": "1.6.18", 2657 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/type-is/-/type-is-1.6.18.tgz", 2658 | "integrity": "sha1-TlUs0F3wlGfcvE73Od6J8s83wTE=", 2659 | "dev": true, 2660 | "requires": { 2661 | "media-typer": "0.3.0", 2662 | "mime-types": "~2.1.24" 2663 | } 2664 | }, 2665 | "uid-safe": { 2666 | "version": "2.1.5", 2667 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/uid-safe/-/uid-safe-2.1.5.tgz", 2668 | "integrity": "sha1-Kz1cckDo/C5Y+Komnl7knAhXvTo=", 2669 | "dev": true, 2670 | "requires": { 2671 | "random-bytes": "~1.0.0" 2672 | } 2673 | }, 2674 | "unc-path-regex": { 2675 | "version": "0.1.2", 2676 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/unc-path-regex/-/unc-path-regex-0.1.2.tgz", 2677 | "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", 2678 | "dev": true 2679 | }, 2680 | "underscore": { 2681 | "version": "1.7.0", 2682 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/underscore/-/underscore-1.7.0.tgz", 2683 | "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=", 2684 | "dev": true 2685 | }, 2686 | "underscore.string": { 2687 | "version": "2.2.1", 2688 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/underscore.string/-/underscore.string-2.2.1.tgz", 2689 | "integrity": "sha1-18D6KvXVoaZ/QlPa7pgTLnM/Dxk=", 2690 | "dev": true 2691 | }, 2692 | "union-value": { 2693 | "version": "1.0.1", 2694 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/union-value/-/union-value-1.0.1.tgz", 2695 | "integrity": "sha1-C2/nuDWuzaYcbqTU8CwUIh4QmEc=", 2696 | "dev": true, 2697 | "requires": { 2698 | "arr-union": "^3.1.0", 2699 | "get-value": "^2.0.6", 2700 | "is-extendable": "^0.1.1", 2701 | "set-value": "^2.0.1" 2702 | } 2703 | }, 2704 | "unpipe": { 2705 | "version": "1.0.0", 2706 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/unpipe/-/unpipe-1.0.0.tgz", 2707 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", 2708 | "dev": true 2709 | }, 2710 | "unset-value": { 2711 | "version": "1.0.0", 2712 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/unset-value/-/unset-value-1.0.0.tgz", 2713 | "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", 2714 | "dev": true, 2715 | "requires": { 2716 | "has-value": "^0.3.1", 2717 | "isobject": "^3.0.0" 2718 | }, 2719 | "dependencies": { 2720 | "has-value": { 2721 | "version": "0.3.1", 2722 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/has-value/-/has-value-0.3.1.tgz", 2723 | "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", 2724 | "dev": true, 2725 | "requires": { 2726 | "get-value": "^2.0.3", 2727 | "has-values": "^0.1.4", 2728 | "isobject": "^2.0.0" 2729 | }, 2730 | "dependencies": { 2731 | "isobject": { 2732 | "version": "2.1.0", 2733 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/isobject/-/isobject-2.1.0.tgz", 2734 | "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", 2735 | "dev": true, 2736 | "requires": { 2737 | "isarray": "1.0.0" 2738 | } 2739 | } 2740 | } 2741 | }, 2742 | "has-values": { 2743 | "version": "0.1.4", 2744 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/has-values/-/has-values-0.1.4.tgz", 2745 | "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", 2746 | "dev": true 2747 | } 2748 | } 2749 | }, 2750 | "urix": { 2751 | "version": "0.1.0", 2752 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/urix/-/urix-0.1.0.tgz", 2753 | "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", 2754 | "dev": true 2755 | }, 2756 | "use": { 2757 | "version": "3.1.1", 2758 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/use/-/use-3.1.1.tgz", 2759 | "integrity": "sha1-1QyMrHmhn7wg8pEfVuuXP04QBw8=", 2760 | "dev": true 2761 | }, 2762 | "utils-merge": { 2763 | "version": "1.0.1", 2764 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/utils-merge/-/utils-merge-1.0.1.tgz", 2765 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", 2766 | "dev": true 2767 | }, 2768 | "v8flags": { 2769 | "version": "3.1.3", 2770 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/v8flags/-/v8flags-3.1.3.tgz", 2771 | "integrity": "sha1-/J3CNSHKIMVDP4HMTrmzAzuxBdg=", 2772 | "dev": true, 2773 | "requires": { 2774 | "homedir-polyfill": "^1.0.1" 2775 | } 2776 | }, 2777 | "vary": { 2778 | "version": "1.1.2", 2779 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/vary/-/vary-1.1.2.tgz", 2780 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", 2781 | "dev": true 2782 | }, 2783 | "which": { 2784 | "version": "1.0.9", 2785 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/which/-/which-1.0.9.tgz", 2786 | "integrity": "sha1-RgwdoPgQED0DIam2M6+eV15kSG8=", 2787 | "dev": true 2788 | }, 2789 | "wrappy": { 2790 | "version": "1.0.2", 2791 | "resolved": "http://npm.paypal.com/artifactory/api/npm/npm-all/wrappy/-/wrappy-1.0.2.tgz", 2792 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 2793 | "dev": true 2794 | } 2795 | } 2796 | } 2797 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lusca", 3 | "version": "1.7.0", 4 | "description": "Application security for express.", 5 | "main": "index", 6 | "scripts": { 7 | "test": "grunt test" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/krakenjs/lusca.git" 12 | }, 13 | "author": { 14 | "name": "Jeff Harrell", 15 | "email": "jeharrell@paypal.com" 16 | }, 17 | "publishConfig": { 18 | "registry": "https://registry.npmjs.org" 19 | }, 20 | "licenses": [ 21 | { 22 | "type": "Apache 2.0", 23 | "url": "http://www.apache.org/licenses/LICENSE-2.0.html" 24 | } 25 | ], 26 | "engines": { 27 | "node": ">=0.8.x" 28 | }, 29 | "engineStrict": true, 30 | "devDependencies": { 31 | "body-parser": "^1.6.3", 32 | "cookie-parser": "^1.3.2", 33 | "cookie-session": "^1.0.2", 34 | "data-driven": "^1.0.0", 35 | "errorhandler": "^1.1.1", 36 | "express": "^4.3.8", 37 | "express-session": "^1.7.5", 38 | "grunt": "^0.4.5", 39 | "grunt-cli": "^1.2.0", 40 | "grunt-contrib-jshint": "^3.0.0", 41 | "grunt-mocha-test": "^0.7.0", 42 | "jshint": "*", 43 | "supertest": "^0.13.0" 44 | }, 45 | "dependencies": { 46 | "tsscmp": "^1.0.5" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/csp.js: -------------------------------------------------------------------------------- 1 | /*global describe:false, it:false */ 2 | 'use strict'; 3 | 4 | 5 | var lusca = require('../index'), 6 | request = require('supertest'), 7 | assert = require('assert'), 8 | mock = require('./mocks/app'); 9 | 10 | 11 | describe('CSP', function () { 12 | 13 | it('method', function () { 14 | assert(typeof lusca.csp === 'function'); 15 | }); 16 | 17 | 18 | it('should throw if misconfigured', function () { 19 | assert.throws(function () { 20 | lusca.csp(new Date()); 21 | }, /invalid csp policy/); 22 | }); 23 | 24 | 25 | it('header (report)', function (done) { 26 | var config = require('./mocks/config/cspReport'), 27 | app = mock({ csp: config }); 28 | 29 | app.get('/', function (req, res) { 30 | res.status(200).end(); 31 | }); 32 | 33 | request(app) 34 | .get('/') 35 | .expect('Content-Security-Policy-Report-Only', 'default-src *; report-uri ' + config.reportUri) 36 | .expect(200, done); 37 | }); 38 | 39 | 40 | describe('header (enforce)', function () { 41 | it('object config', function (done) { 42 | var config = require('./mocks/config/cspEnforce'), 43 | app = mock({ csp: config }); 44 | 45 | app.get('/', function (req, res) { 46 | res.status(200).end(); 47 | }); 48 | 49 | request(app) 50 | .get('/') 51 | .expect('Content-Security-Policy', 'default-src *') 52 | .expect(200, done); 53 | }); 54 | 55 | it('string config', function (done) { 56 | var config = require('./mocks/config/cspString'), 57 | app = mock({ csp: config }); 58 | 59 | app.get('/', function (req, res) { 60 | res.status(200).end(); 61 | }); 62 | 63 | request(app) 64 | .get('/') 65 | .expect('Content-Security-Policy', 'default-src *') 66 | .expect(200, done); 67 | }); 68 | 69 | it('array config', function (done) { 70 | var config = require('./mocks/config/cspArray'), 71 | app = mock({ csp: config }); 72 | 73 | app.get('/', function (req, res) { 74 | res.status(200).end(); 75 | }); 76 | 77 | request(app) 78 | .get('/') 79 | .expect('Content-Security-Policy', 'default-src *; img-src *') 80 | .expect(200, done); 81 | }); 82 | 83 | it('nested config', function (done) { 84 | var config = require('./mocks/config/cspNested'), 85 | app = mock({ csp: config }); 86 | 87 | app.get('/', function (req, res) { 88 | res.status(200).end(); 89 | }); 90 | 91 | request(app) 92 | .get('/') 93 | .expect('Content-Security-Policy', 'default-src *; img-src *') 94 | .expect(200, done); 95 | }); 96 | }); 97 | 98 | describe('nonce checks', function () { 99 | it('no nonce specified', function (done) { 100 | var config = require('./mocks/config/cspEnforce'), 101 | app = mock({ csp: config }); 102 | 103 | app.get('/', function (req, res) { 104 | res.status(200).end(); 105 | }); 106 | 107 | request(app) 108 | .get('/') 109 | .expect('Content-Security-Policy', /^(?!.*nonce).*$/) 110 | .expect(200, done); 111 | }); 112 | 113 | it('nonce specified', function (done) { 114 | var config = require('./mocks/config/nonce'), 115 | app = mock({ csp: config }); 116 | 117 | app.get('/', function (req, res) { 118 | res.status(200).end(); 119 | }); 120 | 121 | request(app) 122 | .get('/') 123 | .expect('Content-Security-Policy', /nonce/) 124 | .expect(200, done); 125 | }); 126 | }); 127 | }); 128 | -------------------------------------------------------------------------------- /test/csrf.js: -------------------------------------------------------------------------------- 1 | /*global describe:false, it:false */ 2 | 'use strict'; 3 | 4 | 5 | var lusca = require('../index'), 6 | request = require('supertest'), 7 | assert = require('assert'), 8 | mock = require('./mocks/app'), 9 | dd = require('data-driven'), 10 | sessionOptions = [{ 11 | value: 'session' 12 | }, { 13 | value: 'cookie' 14 | }], 15 | mapCookies = function (cookies) { 16 | return cookies.map(function (r) { 17 | return r.replace("; path=/; httponly", ""); 18 | }).join("; "); 19 | }; 20 | 21 | 22 | describe('CSRF', function () { 23 | it('method', function () { 24 | assert(typeof lusca.csrf === 'function'); 25 | }); 26 | it('expects a thrown error if no session object', function (done) { 27 | var app = mock({ 28 | csrf: true 29 | }, "none"); 30 | 31 | app.get('/', function (req, res) { 32 | res.send(200, { 33 | token: res.locals._csrf 34 | }); 35 | }); 36 | 37 | request(app) 38 | .get('/') 39 | .expect(500) 40 | .end(function (err, res) { 41 | assert(res.text.match("lusca requires req.session to be available")); 42 | done(err); 43 | }); 44 | }); 45 | it('should not require token on post to blocklist', function (done) { 46 | var app = mock({ 47 | csrf: { 48 | blocklist: ['/blocklist1', { path: '/blocklist2', type: 'startsWith' }, { path: '/', type: 'exact' }] 49 | } 50 | }); 51 | 52 | app.post('/blocklist1', function (req, res) { 53 | res.send(200); 54 | }); 55 | 56 | app.post('/blocklist2', function (req, res) { 57 | res.send(200); 58 | }); 59 | 60 | app.post('/notblocklist', function (req, res) { 61 | res.send(200); 62 | }); 63 | 64 | app.post('/', function (req, res) { 65 | res.send(200); 66 | }); 67 | 68 | request(app) 69 | .post('/blocklist1') 70 | .expect(200) 71 | .end(function (err, res) {}); 72 | 73 | request(app) 74 | .post('/blocklist2') 75 | .expect(200) 76 | .end(function (err, res) {}); 77 | 78 | request(app) 79 | .post('/notblocklist') 80 | .expect(403) 81 | .end(function (err, res) {}); 82 | 83 | request(app) 84 | .post('/') 85 | .expect(200) 86 | .end(function (err, res) { 87 | done(err); 88 | }); 89 | }); 90 | it('should not require token on post to blocklist (string config)', function (done) { 91 | var app = mock({ 92 | csrf: { 93 | blocklist: '/blocklist1' 94 | } 95 | }); 96 | 97 | app.post('/blocklist1', function (req, res) { 98 | res.send(200); 99 | }); 100 | 101 | app.post('/notblocklist', function (req, res) { 102 | res.send(200); 103 | }); 104 | 105 | request(app) 106 | .post('/blocklist1') 107 | .expect(200) 108 | .end(function (err, res) {}); 109 | 110 | request(app) 111 | .post('/notblocklist') 112 | .expect(403) 113 | .end(function (err, res) { 114 | done(err); 115 | }); 116 | }); 117 | it('should only require token on post to allowlist', function (done) { 118 | var app = mock({ 119 | csrf: { 120 | allowlist: ['/allowlist1', { path: '/allowlist2', type: 'startsWith' }, { path: '/', type: 'exact' }] 121 | } 122 | }); 123 | 124 | app.post('/allowlist1', function (req, res) { 125 | res.send(200); 126 | }); 127 | 128 | app.post('/allowlist2', function (req, res) { 129 | res.send(200); 130 | }); 131 | 132 | app.post('/notallowlist', function (req, res) { 133 | res.send(200); 134 | }); 135 | 136 | app.post('/', function (req, res) { 137 | res.send(200); 138 | }); 139 | 140 | request(app) 141 | .post('/allowlist1') 142 | .expect(403) 143 | .end(function (err, res) {}); 144 | 145 | request(app) 146 | .post('/allowlist2') 147 | .expect(403) 148 | .end(function (err, res) {}); 149 | 150 | request(app) 151 | .post('/notallowlist') 152 | .expect(200) 153 | .end(function (err, res) { 154 | console.log(err); 155 | }); 156 | 157 | request(app) 158 | .post('/') 159 | .expect(200) 160 | .end(function (err, res) { 161 | done(err); 162 | }); 163 | }); 164 | it('should only require token on post to allowlist (string config)', function (done) { 165 | var app = mock({ 166 | csrf: { 167 | allowlist: '/allowlist1' 168 | } 169 | }); 170 | 171 | app.post('/allowlist1', function (req, res) { 172 | res.send(200); 173 | }); 174 | 175 | app.post('/notallowlist', function (req, res) { 176 | res.send(200); 177 | }); 178 | 179 | request(app) 180 | .post('/allowlist1') 181 | .expect(403) 182 | .end(function (err, res) {}); 183 | 184 | request(app) 185 | .post('/notallowlist') 186 | .expect(200) 187 | .end(function (err, res) { 188 | done(err); 189 | }); 190 | }); 191 | 192 | it('should throw error on invalid allowlist/blocklist config', function() { 193 | assert.throws(function() { 194 | mock({ 195 | csrf: { 196 | allowlist: [{ path: '/allowInvalid', type: 'regex' }] 197 | } 198 | }); 199 | }, /Invalid csrf config. type 'regex' is not supported.*/); 200 | 201 | assert.throws(function() { 202 | mock({ 203 | csrf: { 204 | allowlist: [{ path: '', type: 'startsWith' }] 205 | } 206 | }); 207 | }, /Invalid csrf config. path is required/); 208 | }); 209 | 210 | dd(sessionOptions, function () { 211 | it('GETs have a CSRF token (session type: {value})', function (ctx, done) { 212 | var mockConfig = (ctx.value === 'cookie') ? { 213 | csrf: { 214 | secret: 'csrfSecret' 215 | } 216 | } : { 217 | csrf: true 218 | }, 219 | app = mock(mockConfig, ctx.value); 220 | 221 | app.get('/', function (req, res) { 222 | res.status(200).send({ 223 | token: res.locals._csrf 224 | }); 225 | }); 226 | 227 | request(app) 228 | .get('/') 229 | .expect(200) 230 | .end(function (err, res) { 231 | assert(res.body.token); 232 | done(err); 233 | }); 234 | }); 235 | 236 | 237 | it('POST (200 OK with token) (session type: {value})', function (ctx, done) { 238 | var mockConfig = (ctx.value === 'cookie') ? { 239 | csrf: { 240 | secret: 'csrfSecret' 241 | } 242 | } : { 243 | csrf: true 244 | }, 245 | app = mock(mockConfig, ctx.value); 246 | 247 | app.all('/', function (req, res) { 248 | res.status(200).send({ 249 | token: res.locals._csrf 250 | }); 251 | }); 252 | 253 | request(app) 254 | .get('/') 255 | .end(function (err, res) { 256 | request(app) 257 | .post('/') 258 | .set('Cookie', mapCookies(res.headers['set-cookie'])) 259 | .send({ 260 | _csrf: res.body.token 261 | }) 262 | .expect(200, done); 263 | }); 264 | }); 265 | 266 | it('POST (403 Forbidden on invalid token) (session type: {value})', function (ctx, done) { 267 | var mockConfig = (ctx.value === 'cookie') ? { 268 | csrf: { 269 | secret: 'csrfSecret' 270 | } 271 | } : { 272 | csrf: true 273 | }, 274 | app = mock(mockConfig, ctx.value); 275 | 276 | app.all('/', function (req, res) { 277 | res.status(200).send({ 278 | token: Math.random() 279 | }); 280 | }); 281 | 282 | request(app) 283 | .get('/') 284 | .end(function (err, res) { 285 | request(app) 286 | .post('/') 287 | .set('Cookie', mapCookies(res.headers['set-cookie'])) 288 | .send({ 289 | _csrf: res.body.token 290 | }) 291 | .expect(403) 292 | .end(function (err, res) { 293 | done(err); 294 | }); 295 | }); 296 | }); 297 | 298 | 299 | it('POST (403 Forbidden on no token) (session type: {value})', function (ctx, done) { 300 | var mockConfig = (ctx.value === 'cookie') ? { 301 | csrf: { 302 | secret: 'csrfSecret' 303 | } 304 | } : { 305 | csrf: true 306 | }, 307 | app = mock(mockConfig, ctx.value); 308 | 309 | app.get('/', function (req, res) { 310 | res.status(200).end(); 311 | }); 312 | 313 | request(app) 314 | .post('/') 315 | .expect(403) 316 | .end(function (err, res) { 317 | done(err); 318 | }); 319 | }); 320 | 321 | 322 | it('Should allow custom keys (session type: {value})', function (ctx, done) { 323 | var mockConfig = (ctx.value === 'cookie') ? { 324 | csrf: { 325 | key: 'foobar', 326 | secret: 'csrfSecret' 327 | } 328 | } : { 329 | csrf: { 330 | key: 'foobar' 331 | } 332 | }, 333 | app = mock(mockConfig, ctx.value); 334 | 335 | app.all('/', function (req, res) { 336 | res.status(200).send({ 337 | token: res.locals.foobar 338 | }); 339 | }); 340 | 341 | request(app) 342 | .get('/') 343 | .end(function (err, res) { 344 | request(app) 345 | .post('/') 346 | .set('cookie', mapCookies(res.headers['set-cookie'])) 347 | .send({ 348 | foobar: res.body.token 349 | }) 350 | .expect(200, done); 351 | }); 352 | }); 353 | 354 | it('Token can be sent through header instead of post body (session type: {value})', function (ctx, done) { 355 | var mockConfig = (ctx.value === 'cookie') ? { 356 | csrf: { 357 | secret: 'csrfSecret' 358 | } 359 | } : { 360 | csrf: true 361 | }, 362 | app = mock(mockConfig, ctx.value); 363 | app.all('/', function (req, res) { 364 | res.status(200).send({ 365 | token: res.locals._csrf 366 | }); 367 | }); 368 | 369 | request(app) 370 | .get('/') 371 | .end(function (err, res) { 372 | request(app) 373 | .post('/') 374 | .set('cookie', mapCookies(res.headers['set-cookie'])) 375 | .set('x-csrf-token', res.body.token) 376 | .send({ 377 | name: 'Test' 378 | }) 379 | .expect(200, done); 380 | }); 381 | }); 382 | 383 | it('Should allow custom headers (session type: {value})', function (ctx, done) { 384 | var mockConfig = (ctx.value === 'cookie') ? { 385 | csrf: { 386 | header: 'x-xsrf-token', 387 | secret: 'csrfSecret' 388 | } 389 | } : { 390 | csrf: { 391 | header: 'x-xsrf-token' 392 | } 393 | }, 394 | app = mock(mockConfig, ctx.value); 395 | 396 | app.all('/', function (req, res) { 397 | res.status(200).send({ 398 | token: res.locals._csrf 399 | }); 400 | }); 401 | 402 | request(app) 403 | .get('/') 404 | .end(function (err, res) { 405 | request(app) 406 | .post('/') 407 | .set('cookie', mapCookies(res.headers['set-cookie'])) 408 | .set('x-xsrf-token', res.body.token) 409 | .send({ 410 | name: 'Test' 411 | }) 412 | .expect(200, done); 413 | }); 414 | }); 415 | 416 | it('Should be case-insensitive to custom headers', function (ctx, done) { 417 | var mockConfig = (ctx.value === 'cookie') ? { 418 | csrf: { 419 | header: 'X-XSRF-TOKEN', 420 | secret: 'csrfSecret' 421 | } 422 | } : { 423 | csrf: { 424 | header: 'X-XSRF-TOKEN' 425 | } 426 | }, 427 | app = mock(mockConfig, ctx.value); 428 | 429 | app.all('/', function (req, res) { 430 | res.status(200).send({ 431 | token: res.locals._csrf 432 | }); 433 | }); 434 | 435 | request(app) 436 | .get('/') 437 | .end(function (err, res) { 438 | request(app) 439 | .post('/') 440 | .set('cookie', mapCookies(res.headers['set-cookie'])) 441 | .set('X-xsrf-token', res.body.token) 442 | .send({ 443 | name: 'Test' 444 | }) 445 | .expect(200, done); 446 | }); 447 | }); 448 | 449 | it('Should allow custom secret key (session type: {value})', function (ctx, done) { 450 | var mockConfig = (ctx.value === 'cookie') ? { 451 | csrf: { 452 | secret: 'csrfSecret' 453 | } 454 | } : { 455 | csrf: { 456 | secret: '_csrfSecret' 457 | } 458 | }, 459 | app = mock(mockConfig, ctx.value); 460 | 461 | app.all('/', function (req, res) { 462 | res.status(200).send({ 463 | token: res.locals._csrf 464 | }); 465 | }); 466 | 467 | request(app) 468 | .get('/') 469 | .end(function (err, res) { 470 | request(app) 471 | .post('/') 472 | .set('cookie', mapCookies(res.headers['set-cookie'])) 473 | .send({ 474 | _csrf: res.body.token 475 | }) 476 | .expect(200, done); 477 | }); 478 | }); 479 | 480 | it('Should allow custom functions (session type: {value})', function (ctx, done) { 481 | var myToken = require('./mocks/token'), 482 | mockConfig = { 483 | csrf: { 484 | impl: myToken 485 | } 486 | }, 487 | app = mock(mockConfig, ctx.value); 488 | 489 | app.all('/', function (req, res) { 490 | res.status(200).send({ 491 | token: res.locals._csrf 492 | }); 493 | }); 494 | 495 | request(app) 496 | .get('/') 497 | .end(function (err, res) { 498 | assert(myToken.value === res.body.token); 499 | 500 | request(app) 501 | .post('/') 502 | //.set('cookie', mapCookies(res.headers['set-cookie'])) 503 | .send({ 504 | _csrf: res.body.token 505 | }) 506 | .expect(200, done); 507 | }); 508 | }); 509 | }); 510 | 511 | it('Should not set a cookie without the cookie option', function (done) { 512 | var app = mock({ csrf: {}}); 513 | 514 | app.all('/', function (req, res) { 515 | res.status(200).send({ 516 | token: res.locals._csrf 517 | }); 518 | }); 519 | 520 | request(app) 521 | .get('/') 522 | .end(function (err, res) { 523 | function findToken(cookie) { 524 | cookie = decodeURIComponent(cookie); 525 | return !~cookie.indexOf(res.body.token); 526 | } 527 | assert(res.headers['set-cookie'].some(findToken)); 528 | done(); 529 | }); 530 | }); 531 | it('Should not set a cookie without the cookie name option', function (done) { 532 | var app = mock({ csrf: { 533 | cookie: {} 534 | }}); 535 | 536 | app.all('/', function (req, res) { 537 | res.status(200).send({ 538 | token: res.locals._csrf 539 | }); 540 | }); 541 | 542 | request(app) 543 | .get('/') 544 | .end(function (err, res) { 545 | function findToken(cookie) { 546 | cookie = decodeURIComponent(cookie); 547 | return !~cookie.indexOf(res.body.token); 548 | } 549 | assert(res.headers['set-cookie'].some(findToken)); 550 | done(); 551 | }); 552 | }); 553 | it('Should set a cookie with the cookie option', function (done) { 554 | var app = mock({ csrf: { cookie: 'CSRF' }}); 555 | 556 | app.all('/', function (req, res) { 557 | res.status(200).send({ 558 | token: res.locals._csrf 559 | }); 560 | }); 561 | 562 | request(app) 563 | .get('/') 564 | .end(function (err, res) { 565 | function findToken(cookie) { 566 | cookie = decodeURIComponent(cookie); 567 | return ~cookie.indexOf(res.body.token); 568 | } 569 | assert(res.headers['set-cookie'].some(findToken)); 570 | done(); 571 | }); 572 | }); 573 | it('Should set a cookie with the cookie name option', function (done) { 574 | var app = mock({ csrf: { cookie: { name : 'CSRF' }}}); 575 | 576 | app.all('/', function (req, res) { 577 | res.status(200).send({ 578 | token: res.locals._csrf 579 | }); 580 | }); 581 | 582 | request(app) 583 | .get('/') 584 | .end(function (err, res) { 585 | function findToken(cookie) { 586 | cookie = decodeURIComponent(cookie); 587 | return ~cookie.indexOf(res.body.token); 588 | } 589 | assert(res.headers['set-cookie'].some(findToken)); 590 | done(); 591 | }); 592 | }); 593 | it('Should set cookie httpOnly option correctly', function (done) { 594 | // https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection 595 | var cookieKey = 'XSRF-TOKEN'; 596 | var header = 'X-XSRF-TOKEN'; 597 | var app = mock({ 598 | csrf: { 599 | cookie: { 600 | name: cookieKey, 601 | options: { 602 | httpOnly: true 603 | } 604 | }, 605 | header: header, 606 | secret: '_csrfSecret' 607 | } 608 | }); 609 | 610 | app.all('/', function (req, res) { 611 | res.status(200).send({ 612 | token: res.locals._csrf 613 | }); 614 | }); 615 | 616 | request(app) 617 | .get('/') 618 | .end(function (err, res) { 619 | function findToken(cookie) { 620 | cookie = decodeURIComponent(cookie); 621 | return ~cookie.indexOf(cookieKey + '=' + res.body.token) && 622 | ~cookie.indexOf('HttpOnly'); 623 | } 624 | assert(res.headers['set-cookie'].some(findToken)); 625 | 626 | request(app) 627 | .post('/') 628 | .set('cookie', mapCookies(res.headers['set-cookie'])) 629 | .set(header, res.body.token) 630 | .send({ 631 | cool: 'stuff' 632 | }) 633 | .expect(200, done); 634 | }); 635 | }); 636 | it('Should set cookie secure option correctly', function (done) { 637 | // https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection 638 | var cookieKey = 'XSRF-TOKEN'; 639 | var header = 'X-XSRF-TOKEN'; 640 | var app = mock({ 641 | csrf: { 642 | cookie: { 643 | name: cookieKey, 644 | options: { 645 | secure: true 646 | } 647 | }, 648 | header: header, 649 | secret: '_csrfSecret' 650 | } 651 | }); 652 | 653 | app.all('/', function (req, res) { 654 | res.status(200).send({ 655 | token: res.locals._csrf 656 | }); 657 | }); 658 | 659 | request(app) 660 | .get('/') 661 | .end(function (err, res) { 662 | function findToken(cookie) { 663 | cookie = decodeURIComponent(cookie); 664 | return ~cookie.indexOf(cookieKey + '=' + res.body.token) && 665 | ~cookie.indexOf('Secure'); 666 | } 667 | assert(res.headers['set-cookie'].some(findToken)); 668 | 669 | request(app) 670 | .post('/') 671 | .set('cookie', mapCookies(res.headers['set-cookie'])) 672 | .set(header, res.body.token) 673 | .send({ 674 | cool: 'stuff' 675 | }) 676 | .expect(200, done); 677 | }); 678 | }); 679 | it('Should set cookie secure and httpOnly options correctly', function (done) { 680 | // https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection 681 | var cookieKey = 'XSRF-TOKEN'; 682 | var header = 'X-XSRF-TOKEN'; 683 | var app = mock({ 684 | csrf: { 685 | cookie: { 686 | name: cookieKey, 687 | options: { 688 | httpOnly: true, 689 | secure: true 690 | } 691 | }, 692 | header: header, 693 | secret: '_csrfSecret' 694 | } 695 | }); 696 | 697 | app.all('/', function (req, res) { 698 | res.status(200).send({ 699 | token: res.locals._csrf 700 | }); 701 | }); 702 | 703 | request(app) 704 | .get('/') 705 | .end(function (err, res) { 706 | function findToken(cookie) { 707 | cookie = decodeURIComponent(cookie); 708 | return ~cookie.indexOf(cookieKey + '=' + res.body.token) && 709 | ~cookie.indexOf('HttpOnly') && 710 | ~cookie.indexOf('Secure'); 711 | } 712 | assert(res.headers['set-cookie'].some(findToken)); 713 | 714 | request(app) 715 | .post('/') 716 | .set('cookie', mapCookies(res.headers['set-cookie'])) 717 | .set(header, res.body.token) 718 | .send({ 719 | cool: 'stuff' 720 | }) 721 | .expect(200, done); 722 | }); 723 | }); 724 | it('Should set options correctly with an angular shorthand option', function (done) { 725 | // https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection 726 | var cookieKey = 'XSRF-TOKEN'; 727 | var header = 'X-XSRF-TOKEN'; 728 | var app = mock({ csrf: { angular: true, secret: '_csrfSecret' }}); 729 | 730 | app.all('/', function (req, res) { 731 | res.status(200).send({ 732 | token: res.locals._csrf 733 | }); 734 | }); 735 | 736 | request(app) 737 | .get('/') 738 | .end(function (err, res) { 739 | function findToken(cookie) { 740 | cookie = decodeURIComponent(cookie); 741 | return ~cookie.indexOf(cookieKey + '=' + res.body.token); 742 | } 743 | assert(res.headers['set-cookie'].some(findToken)); 744 | 745 | request(app) 746 | .post('/') 747 | .set('cookie', mapCookies(res.headers['set-cookie'])) 748 | .set(header, res.body.token) 749 | .send({ 750 | cool: 'stuff' 751 | }) 752 | .expect(200, done); 753 | }); 754 | }); 755 | it('Should set cookie httpOnly option correctly with an angular shorthand option', function (done) { 756 | // https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection 757 | var cookieKey = 'XSRF-TOKEN'; 758 | var header = 'X-XSRF-TOKEN'; 759 | var app = mock({ 760 | csrf: { 761 | secret: '_csrfSecret', 762 | angular: true, 763 | cookie: { 764 | options: { 765 | httpOnly: true 766 | } 767 | } 768 | } 769 | }); 770 | 771 | app.all('/', function (req, res) { 772 | res.status(200).send({ 773 | token: res.locals._csrf 774 | }); 775 | }); 776 | 777 | request(app) 778 | .get('/') 779 | .end(function (err, res) { 780 | function findToken(cookie) { 781 | cookie = decodeURIComponent(cookie); 782 | return ~cookie.indexOf(cookieKey + '=' + res.body.token) && 783 | ~cookie.indexOf('HttpOnly'); 784 | } 785 | assert(res.headers['set-cookie'].some(findToken)); 786 | 787 | request(app) 788 | .post('/') 789 | .set('cookie', mapCookies(res.headers['set-cookie'])) 790 | .set(header, res.body.token) 791 | .send({ 792 | cool: 'stuff' 793 | }) 794 | .expect(200, done); 795 | }); 796 | }); 797 | it('Should set cookie secure option correctly with an angular shorthand option', function (done) { 798 | // https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection 799 | var cookieKey = 'XSRF-TOKEN'; 800 | var header = 'X-XSRF-TOKEN'; 801 | var app = mock({ 802 | csrf: { 803 | secret: '_csrfSecret', 804 | angular: true, 805 | cookie: { 806 | options: { 807 | secure: true 808 | } 809 | } 810 | } 811 | }); 812 | 813 | app.all('/', function (req, res) { 814 | res.status(200).send({ 815 | token: res.locals._csrf 816 | }); 817 | }); 818 | 819 | request(app) 820 | .get('/') 821 | .end(function (err, res) { 822 | function findToken(cookie) { 823 | cookie = decodeURIComponent(cookie); 824 | return ~cookie.indexOf(cookieKey + '=' + res.body.token) && 825 | ~cookie.indexOf('Secure'); 826 | } 827 | assert(res.headers['set-cookie'].some(findToken)); 828 | 829 | request(app) 830 | .post('/') 831 | .set('cookie', mapCookies(res.headers['set-cookie'])) 832 | .set(header, res.body.token) 833 | .send({ 834 | cool: 'stuff' 835 | }) 836 | .expect(200, done); 837 | }); 838 | }); 839 | it('Should set cookie secure and httpOnly option correctly with an angular shorthand option', function (done) { 840 | // https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection 841 | var cookieKey = 'XSRF-TOKEN'; 842 | var header = 'X-XSRF-TOKEN'; 843 | var app = mock({ 844 | csrf: { 845 | secret: '_csrfSecret', 846 | angular: true, 847 | cookie: { 848 | options: { 849 | httpOnly: true, 850 | secure: true 851 | } 852 | } 853 | } 854 | }); 855 | 856 | app.all('/', function (req, res) { 857 | res.status(200).send({ 858 | token: res.locals._csrf 859 | }); 860 | }); 861 | 862 | request(app) 863 | .get('/') 864 | .end(function (err, res) { 865 | function findToken(cookie) { 866 | cookie = decodeURIComponent(cookie); 867 | return ~cookie.indexOf(cookieKey + '=' + res.body.token) && 868 | ~cookie.indexOf('HttpOnly') && 869 | ~cookie.indexOf('Secure'); 870 | } 871 | assert(res.headers['set-cookie'].some(findToken)); 872 | 873 | request(app) 874 | .post('/') 875 | .set('cookie', mapCookies(res.headers['set-cookie'])) 876 | .set(header, res.body.token) 877 | .send({ 878 | cool: 'stuff' 879 | }) 880 | .expect(200, done); 881 | }); 882 | }); 883 | dd(sessionOptions, function () { 884 | it('Should return the cached token for valid session on req.csrfToken', function (ctx, done) { 885 | var key = 'foo'; 886 | var mockConfig = (ctx.value === 'cookie') ? { 887 | csrf: { 888 | key: key, 889 | secret: 'csrfSecret' 890 | } 891 | } : { 892 | csrf: {key: key} 893 | }, 894 | app = mock(mockConfig, ctx.value); 895 | 896 | function callCsrfToken(req, res, next) { 897 | var token = res.locals[key]; 898 | assert(req.csrfToken() === token, 'req.csrfToken should use cached token'); 899 | assert(res.locals[key] === token, 'req.csrfToken should not mutate token'); 900 | next(); 901 | } 902 | 903 | app.get('/', callCsrfToken, function (req, res) { 904 | res.status(200).send({ 905 | token: res.locals[key] 906 | }); 907 | }); 908 | 909 | app.post('/', function (req, res) { 910 | res.status(200).send({ 911 | token: res.locals[key] 912 | }); 913 | }); 914 | 915 | request(app) 916 | .get('/') 917 | .end(function (err, res) { 918 | var obj = {}; 919 | obj[key] = res.body.token; 920 | 921 | request(app) 922 | .post('/') 923 | .set('Cookie', mapCookies(res.headers['set-cookie'])) 924 | .send(obj) 925 | .expect(200, done); 926 | }); 927 | }); 928 | it('Should generate a new token for invalid session on req.csrfToken', function (ctx, done) { 929 | var key = 'foo', 930 | secret = 'csrfSecret', 931 | mockConfig = { 932 | csrf: { 933 | key: key, 934 | secret: secret 935 | } 936 | }, 937 | app = mock(mockConfig, ctx.value); 938 | 939 | function destroy(req, res, next) { 940 | delete req.session[secret]; 941 | next(); 942 | } 943 | 944 | function callCsrfToken(req, res, next) { 945 | var token = res.locals[key]; 946 | assert(req.csrfToken() !== token, 'req.csrfToken should not use cached token'); 947 | assert(res.locals[key] !== token, 'req.csrfToken should mutate token'); 948 | token = res.locals[key]; 949 | assert(req.csrfToken() === token, 'subsequent req.csrfToken should use cached token'); 950 | next(); 951 | } 952 | 953 | app.get('/', destroy, callCsrfToken, function (req, res) { 954 | res.status(200).send({ 955 | token: res.locals[key] 956 | }); 957 | }); 958 | 959 | app.post('/', function (req, res) { 960 | res.status(200).send({ 961 | token: res.locals[key] 962 | }); 963 | }); 964 | 965 | request(app) 966 | .get('/') 967 | .end(function (err, res) { 968 | var obj = {}; 969 | obj[key] = res.body.token; 970 | 971 | request(app) 972 | .post('/') 973 | .set('Cookie', mapCookies(res.headers['set-cookie'])) 974 | .send(obj) 975 | .expect(200, done); 976 | }); 977 | }); 978 | }); 979 | }); -------------------------------------------------------------------------------- /test/hsts.js: -------------------------------------------------------------------------------- 1 | /*global describe:false, it:false */ 2 | 'use strict'; 3 | 4 | 5 | var lusca = require('../index'), 6 | request = require('supertest'), 7 | assert = require('assert'), 8 | mock = require('./mocks/app'); 9 | 10 | 11 | describe('HSTS', function () { 12 | 13 | it('method', function () { 14 | assert(typeof lusca.hsts === 'function'); 15 | }); 16 | 17 | 18 | it('header (maxAge)', function (done) { 19 | var config = { hsts: { maxAge: 31536000 } }, 20 | app = mock(config); 21 | 22 | app.get('/', function (req, res) { 23 | res.status(200).end(); 24 | }); 25 | 26 | request(app) 27 | .get('/') 28 | .expect('Strict-Transport-Security', 'max-age=' + config.hsts.maxAge) 29 | .expect(200, done); 30 | }); 31 | 32 | 33 | it('header (maxAge 0)', function (done) { 34 | var config = { hsts: { maxAge: 0 } }, 35 | app = mock(config); 36 | 37 | app.get('/', function (req, res) { 38 | res.status(200).end(); 39 | }); 40 | 41 | request(app) 42 | .get('/') 43 | .expect('Strict-Transport-Security', 'max-age=' + config.hsts.maxAge) 44 | .expect(200, done); 45 | }); 46 | 47 | 48 | it('header (maxAge; includeSubDomains)', function (done) { 49 | var config = { hsts: { maxAge: 31536000, includeSubDomains: true } }, 50 | app = mock(config); 51 | 52 | app.get('/', function (req, res) { 53 | res.status(200).end(); 54 | }); 55 | 56 | request(app) 57 | .get('/') 58 | .expect('Strict-Transport-Security', 'max-age=' + config.hsts.maxAge + '; includeSubDomains') 59 | .expect(200, done); 60 | }); 61 | 62 | 63 | it('header (maxAge; includeSubDomains; preload)', function (done) { 64 | var config = { hsts: { maxAge: 31536000, includeSubDomains: true, preload: true} }, 65 | app = mock(config); 66 | 67 | app.get('/', function (req, res) { 68 | res.status(200).end(); 69 | }); 70 | 71 | request(app) 72 | .get('/') 73 | .expect( 74 | 'Strict-Transport-Security', 75 | 'max-age=' + config.hsts.maxAge + 76 | '; includeSubDomains' + 77 | '; preload' 78 | ) 79 | .expect(200, done); 80 | }); 81 | 82 | 83 | it('header (missing maxAge)', function (done) { 84 | var config = { hsts: {} }, 85 | app = mock(config); 86 | 87 | app.get('/', function (req, res) { 88 | res.status(200).end(); 89 | }); 90 | 91 | request(app) 92 | .get('/') 93 | .expect(200) 94 | .end(function (err, res) { 95 | assert(res.headers['Strict-Transport-Security'] === undefined); 96 | done(err); 97 | }); 98 | 99 | }); 100 | 101 | }); 102 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | /*global describe:false, it:false */ 2 | 'use strict'; 3 | 4 | 5 | var lusca = require('../index'), 6 | request = require('supertest'), 7 | assert = require('assert'), 8 | mock = require('./mocks/app'); 9 | 10 | 11 | describe('All', function () { 12 | 13 | it('method', function () { 14 | assert(typeof lusca === 'function'); 15 | }); 16 | 17 | 18 | it('headers', function (done) { 19 | var config = require('./mocks/config/all'), 20 | app = mock(config); 21 | 22 | app.get('/', function (req, res) { 23 | res.status(200).end(); 24 | }); 25 | 26 | request(app) 27 | .get('/') 28 | .expect('X-FRAME-OPTIONS', config.xframe) 29 | .expect('P3P', config.p3p) 30 | .expect('Strict-Transport-Security', 'max-age=' + config.hsts.maxAge) 31 | .expect('Content-Security-Policy-Report-Only', 'default-src *; report-uri ' + config.csp.reportUri) 32 | .expect('X-XSS-Protection', '1; mode=block') 33 | .expect(200, done); 34 | }); 35 | 36 | }); 37 | -------------------------------------------------------------------------------- /test/mocks/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var express = require('express'), 5 | cookieParser = require('cookie-parser'), 6 | cookieSession = require('cookie-session'), 7 | session = require('express-session'), 8 | bodyParser = require('body-parser'), 9 | errorHandler = require('errorhandler'), 10 | lusca = require('../..'); 11 | 12 | 13 | module.exports = function (config, sessionType) { 14 | var app = express(); 15 | 16 | app.use(cookieParser()); 17 | if (sessionType === undefined || sessionType === 'session') { 18 | app.use(session({ 19 | secret: 'abc', 20 | resave: true, 21 | saveUninitialized: true 22 | })); 23 | } else if (sessionType === "cookie") { 24 | app.use(cookieSession({ 25 | secret: 'abc' 26 | })); 27 | } 28 | 29 | app.use(bodyParser.json()); 30 | app.use(bodyParser.urlencoded({ 31 | extended: false 32 | })); 33 | (config !== undefined) ? app.use(lusca(config)) : console.log('no lusca'); 34 | app.use(errorHandler()); 35 | 36 | return app; 37 | }; -------------------------------------------------------------------------------- /test/mocks/config/all.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var cspPolicy = require('./cspReport'); 5 | 6 | 7 | module.exports = { 8 | csrf: true, 9 | xframe: 'SAMEORIGIN', 10 | p3p: 'MY_P3P_VALUE', 11 | hsts: { maxAge: 31536000 }, 12 | csp: cspPolicy, 13 | xssProtection: true 14 | }; -------------------------------------------------------------------------------- /test/mocks/config/cspArray.json: -------------------------------------------------------------------------------- 1 | { 2 | "policy": ["default-src *", "img-src *"] 3 | } 4 | -------------------------------------------------------------------------------- /test/mocks/config/cspEnforce.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | module.exports = { 5 | reportOnly: false, 6 | policy: { 7 | "default-src": "*" 8 | } 9 | }; -------------------------------------------------------------------------------- /test/mocks/config/cspNested.json: -------------------------------------------------------------------------------- 1 | { 2 | "policy": [ 3 | {"default-src": "*"}, 4 | "img-src *" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test/mocks/config/cspReport.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | module.exports = { 5 | reportOnly: true, 6 | reportUri: 'http://www.example.com', 7 | policy: { 8 | "default-src": "*" 9 | } 10 | }; -------------------------------------------------------------------------------- /test/mocks/config/cspString.json: -------------------------------------------------------------------------------- 1 | { "policy": "default-src *" } 2 | -------------------------------------------------------------------------------- /test/mocks/config/nonce.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | module.exports = { 5 | reportOnly: false, 6 | scriptNonce: true, 7 | policy: { 8 | "default-src": "*", 9 | "script-src": "'unsafe-inline" 10 | } 11 | }; -------------------------------------------------------------------------------- /test/mocks/token.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var tokenModule = module.exports = { 5 | 6 | value: 'tokenAllTheThings', 7 | 8 | create: function (req) { 9 | return tokenModule.value; 10 | }, 11 | 12 | validate: function (req, token) { 13 | return token === tokenModule.value; 14 | } 15 | 16 | }; -------------------------------------------------------------------------------- /test/nosniff.js: -------------------------------------------------------------------------------- 1 | /*global describe:false, it:false */ 2 | 'use strict'; 3 | 4 | 5 | var lusca = require('../index'), 6 | request = require('supertest'), 7 | assert = require('assert'), 8 | mock = require('./mocks/app'); 9 | 10 | 11 | 12 | describe('nosniff', function () { 13 | 14 | it('method', function () { 15 | assert(typeof lusca.nosniff === 'function'); 16 | }); 17 | 18 | it('header (enabled)', function (done) { 19 | var config = { nosniff: true }, 20 | app = mock(config); 21 | 22 | app.get('/', function (req, res) { 23 | res.status(200).end(); 24 | }); 25 | 26 | request(app) 27 | .get('/') 28 | .expect('X-Content-Type-Options', 'nosniff') 29 | .expect(200, done); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/p3p.js: -------------------------------------------------------------------------------- 1 | /*global describe:false, it:false */ 2 | 'use strict'; 3 | 4 | 5 | var lusca = require('../index'), 6 | request = require('supertest'), 7 | assert = require('assert'), 8 | mock = require('./mocks/app'); 9 | 10 | 11 | describe('P3P', function () { 12 | 13 | it('method', function () { 14 | assert(typeof lusca.p3p === 'function'); 15 | }); 16 | 17 | 18 | it('header', function (done) { 19 | var config = { p3p: 'MY_P3P_VALUE' }, 20 | app = mock(config); 21 | 22 | app.get('/', function (req, res) { 23 | res.status(200).end(); 24 | }); 25 | 26 | request(app) 27 | .get('/') 28 | .expect('P3P', config.p3p) 29 | .expect(200, done); 30 | }); 31 | 32 | }); 33 | -------------------------------------------------------------------------------- /test/referrerpolicy.js: -------------------------------------------------------------------------------- 1 | /*global describe:false, it:false */ 2 | 'use strict'; 3 | 4 | 5 | var lusca = require('../index'), 6 | request = require('supertest'), 7 | assert = require('assert'), 8 | mock = require('./mocks/app'); 9 | 10 | 11 | 12 | describe('referrerPolicy', function () { 13 | 14 | it('method', function () { 15 | assert(typeof lusca.referrerPolicy === 'function'); 16 | }); 17 | 18 | it('header (enabled)', function (done) { 19 | var config = { referrerPolicy: 'no-referrer-when-downgrade' }, 20 | app = mock(config); 21 | 22 | app.get('/', function (req, res) { 23 | res.status(200).end(); 24 | }); 25 | 26 | request(app) 27 | .get('/') 28 | .expect('referrer-policy', 'no-referrer-when-downgrade') 29 | .expect(200, done); 30 | }); 31 | 32 | it('header invalid value', function () { 33 | assert.throws(function () { 34 | lusca.referrerPolicy('value-with-error'); 35 | }, /Referrer-Policy header doesn't support/); 36 | }); 37 | 38 | it('header invalid value in production doesn\'t throw error', function (done) { 39 | process.env.NODE_ENV = 'production'; 40 | var config = { referrerPolicy: 'invalid-value' }, 41 | app = mock(config); 42 | 43 | app.get('/', function (req, res) { 44 | res.status(200).end(); 45 | }); 46 | 47 | request(app) 48 | .get('/') 49 | .expect('referrer-policy', 'invalid-value') 50 | .expect(200, done); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /test/xframe.js: -------------------------------------------------------------------------------- 1 | /*global describe:false, it:false */ 2 | 'use strict'; 3 | 4 | 5 | var lusca = require('../index'), 6 | request = require('supertest'), 7 | assert = require('assert'), 8 | mock = require('./mocks/app'); 9 | 10 | 11 | describe('XFRAME', function () { 12 | 13 | it('method', function () { 14 | assert(typeof lusca.xframe === 'function'); 15 | }); 16 | 17 | 18 | it('header (deny)', function (done) { 19 | var config = { xframe: 'DENY' }, 20 | app = mock(config); 21 | 22 | app.get('/', function (req, res) { 23 | res.status(200).end(); 24 | }); 25 | 26 | request(app) 27 | .get('/') 28 | .expect('X-FRAME-OPTIONS', config.xframe) 29 | .expect(200, done); 30 | }); 31 | 32 | 33 | it('header (sameorigin)', function (done) { 34 | var config = { xframe: 'SAMEORIGIN' }, 35 | app = mock(config); 36 | 37 | app.get('/', function (req, res) { 38 | res.status(200).end(); 39 | }); 40 | 41 | request(app) 42 | .get('/') 43 | .expect('X-FRAME-OPTIONS', config.xframe) 44 | .expect(200, done); 45 | }); 46 | 47 | }); -------------------------------------------------------------------------------- /test/xssprotection.js: -------------------------------------------------------------------------------- 1 | /*global describe:false, it:false */ 2 | 'use strict'; 3 | 4 | 5 | var lusca = require('../index'), 6 | request = require('supertest'), 7 | assert = require('assert'), 8 | mock = require('./mocks/app'); 9 | 10 | 11 | 12 | describe('xssProtection', function () { 13 | 14 | it('method', function () { 15 | assert(typeof lusca.xssProtection === 'function'); 16 | }); 17 | 18 | it('header (enabled)', function (done) { 19 | var config = { xssProtection: true }, 20 | app = mock(config); 21 | 22 | app.get('/', function (req, res) { 23 | res.status(200).end(); 24 | }); 25 | 26 | request(app) 27 | .get('/') 28 | .expect('X-XSS-Protection', '1; mode=block') 29 | .expect(200, done); 30 | }); 31 | 32 | it('header (enabled; custom mode)', function (done) { 33 | var config = { xssProtection: { enabled: 1, mode: 'foo' } }, 34 | app = mock(config); 35 | 36 | app.get('/', function (req, res) { 37 | res.status(200).end(); 38 | }); 39 | 40 | request(app) 41 | .get('/') 42 | .expect('X-XSS-Protection', '1; mode=foo') 43 | .expect(200, done); 44 | }); 45 | 46 | it('header (enabled is boolean; custom mode)', function (done) { 47 | var config = { xssProtection: { enabled: true } }, 48 | app = mock(config); 49 | 50 | app.get('/', function (req, res) { 51 | res.status(200).end(); 52 | }); 53 | 54 | request(app) 55 | .get('/') 56 | .expect('X-XSS-Protection', '1; mode=block') 57 | .expect(200, done); 58 | }); 59 | 60 | it('header (!enabled)', function (done) { 61 | var config = { xssProtection: { enabled: 0 } }, 62 | app = mock(config); 63 | 64 | app.get('/', function (req, res) { 65 | res.status(200).end(); 66 | }); 67 | 68 | request(app) 69 | .get('/') 70 | .expect('X-XSS-Protection', '0; mode=block') 71 | .expect(200, done); 72 | }); 73 | }); 74 | --------------------------------------------------------------------------------