From 34d82020e99e1fe28ff02af8826d76cc30c2b068 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 26 May 2019 12:14:12 +0300 Subject: [PATCH 01/15] Added a notification when infection is done. --- monkey/monkey_island/cc/ui/package-lock.json | 85 ++++++++----------- monkey/monkey_island/cc/ui/package.json | 3 +- .../cc/ui/src/components/Main.js | 15 ++++ 3 files changed, 54 insertions(+), 49 deletions(-) diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index 58208ef24..b13e2ac35 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -621,7 +621,6 @@ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, - "optional": true, "requires": { "kind-of": "^3.0.2", "longest": "^1.0.1", @@ -3293,7 +3292,7 @@ }, "yargs": { "version": "11.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", + "resolved": "http://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", "dev": true, "requires": { @@ -3397,6 +3396,16 @@ "sha.js": "^2.4.8" } }, + "create-react-class": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.2.tgz", + "integrity": "sha1-zx7RXxKq1/FO9fLf4F5sQvke8Co=", + "requires": { + "fbjs": "^0.8.9", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } + }, "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", @@ -5452,8 +5461,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.1.1", @@ -5504,8 +5512,7 @@ "balanced-match": { "version": "0.4.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "bcrypt-pbkdf": { "version": "1.0.1", @@ -5544,8 +5551,7 @@ "buffer-shims": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "caseless": { "version": "0.12.0", @@ -5816,8 +5822,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.4", @@ -6012,7 +6017,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -6322,8 +6326,7 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -6408,7 +6411,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, - "optional": true, "requires": { "is-glob": "^2.0.0" } @@ -7649,8 +7651,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true, - "optional": true + "dev": true }, "is-finite": { "version": "1.0.2", @@ -7672,7 +7673,6 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, - "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -7739,8 +7739,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true, - "optional": true + "dev": true }, "is-promise": { "version": "2.1.0", @@ -9388,8 +9387,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true, - "optional": true + "dev": true }, "loose-envify": { "version": "1.3.1", @@ -13876,6 +13874,14 @@ "prop-types": "^15.5.10" } }, + "react-desktop-notification": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/react-desktop-notification/-/react-desktop-notification-1.0.9.tgz", + "integrity": "sha512-2nG+3V3n+dnIks4a+jXWYod8k6DgUt/ZDslce667QTimhbQy3+Z2OOYz4G4WjFMyDFrN6QxR28aphOCnA9x7hA==", + "requires": { + "create-react-class": "15.6.2" + } + }, "react-dimensions": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/react-dimensions/-/react-dimensions-1.3.1.tgz", @@ -18362,8 +18368,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -18391,7 +18396,6 @@ "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "1.0.0", "concat-map": "0.0.1" @@ -18406,8 +18410,7 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", @@ -18418,8 +18421,7 @@ "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -18536,8 +18538,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -18549,7 +18550,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -18564,22 +18564,19 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -18598,7 +18595,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -18679,8 +18675,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -18692,7 +18687,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -18778,8 +18772,7 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -18815,7 +18808,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -18835,7 +18827,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -18879,14 +18870,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index 8218b89ae..8def053f8 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -95,6 +95,7 @@ "redux": "^4.0.0", "sha3": "^2.0.0", "react-spinners": "^0.5.4", - "@emotion/core": "^10.0.10" + "@emotion/core": "^10.0.10", + "react-desktop-notification": "^1.0.9" } } diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js index 8229133e6..e6fa1cadc 100644 --- a/monkey/monkey_island/cc/ui/src/components/Main.js +++ b/monkey/monkey_island/cc/ui/src/components/Main.js @@ -14,6 +14,8 @@ import ReportPage from 'components/pages/ReportPage'; import LicensePage from 'components/pages/LicensePage'; import AuthComponent from 'components/AuthComponent'; import LoginPageComponent from 'components/pages/LoginPage'; +import Notifier from "react-desktop-notification" + import 'normalize.css/normalize.css'; import 'react-data-components/css/table-twbs.css'; @@ -105,6 +107,8 @@ class AppComponent extends AuthComponent { } render() { + this.showInfectionDoneNotification(); + return ( @@ -194,6 +198,17 @@ class AppComponent extends AuthComponent { ); } + + showInfectionDoneNotification() { + if (this.state.completedSteps.infection_done) { + console.log("Trying to show notification..."); + Notifier.start( + "Monkey Island", + "Infection is done. Click to see results", + "https://localhost:5000/report", + {logoImage}); + } + } } AppComponent.defaultProps = {}; From 30e892b1c7a4335dfae99ebaf6ee5a382055fb46 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 26 May 2019 13:08:30 +0300 Subject: [PATCH 02/15] Logo doesn't work - leave it for now, also move the show notification to only when changed. also added dev mode to package.json - this helps with debugging JS errors live in chrome. use with 'npm run dev' --- monkey/monkey_island/cc/ui/package.json | 1 + monkey/monkey_island/cc/ui/src/components/Main.js | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index 8def053f8..b0f206bca 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -6,6 +6,7 @@ "clean": "rimraf dist/*", "copy": "copyfiles -f ./src/index.html ./src/favicon.ico ./dist", "dist": "webpack --mode production", + "dev": "webpack --mode development", "lint": "eslint ./src", "posttest": "npm run lint", "release:major": "npm version major && npm publish && git push --follow-tags", diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js index e6fa1cadc..863e814ba 100644 --- a/monkey/monkey_island/cc/ui/src/components/Main.js +++ b/monkey/monkey_island/cc/ui/src/components/Main.js @@ -52,6 +52,7 @@ class AppComponent extends AuthComponent { } if (isChanged) { this.setState({completedSteps: res['completed_steps']}); + this.showInfectionDoneNotification(); } }); } @@ -107,8 +108,6 @@ class AppComponent extends AuthComponent { } render() { - this.showInfectionDoneNotification(); - return ( @@ -206,7 +205,7 @@ class AppComponent extends AuthComponent { "Monkey Island", "Infection is done. Click to see results", "https://localhost:5000/report", - {logoImage}); + ""); } } } From 026cb41064150191609b05805dd6d24a06a13b3c Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 30 May 2019 09:17:24 +0300 Subject: [PATCH 03/15] WIP - initial code, just trying to make a POST request work --- monkey/monkey_island/cc/resources/netmap.py | 31 +++++++++++++++++++ .../cc/ui/src/components/pages/ReportPage.js | 12 ++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/resources/netmap.py b/monkey/monkey_island/cc/resources/netmap.py index ed83414f5..96ea38b28 100644 --- a/monkey/monkey_island/cc/resources/netmap.py +++ b/monkey/monkey_island/cc/resources/netmap.py @@ -1,4 +1,7 @@ +import json + import flask_restful +from flask import request from monkey_island.cc.auth import jwt_required from monkey_island.cc.services.edge import EdgeService @@ -28,4 +31,32 @@ class NetMap(flask_restful.Resource): "edges": edges } + @jwt_required() + def post(self, **kw): + post_data = json.loads(request.data) + print(post_data) + + monkeys = [NodeService.monkey_to_net_node(x) for x in mongo.db.monkey.find({})] + nodes = [NodeService.node_to_net_node(x) for x in mongo.db.node.find({})] + edges = [EdgeService.edge_to_net_edge(x) for x in mongo.db.edge.find({})] + + if NodeService.get_monkey_island_monkey() is None: + monkey_island = [NodeService.get_monkey_island_pseudo_net_node()] + edges += EdgeService.get_monkey_island_pseudo_edges() + else: + monkey_island = [] + edges += EdgeService.get_infected_monkey_island_pseudo_edges() + + all_nodes = monkeys + nodes + monkey_island + def filter_linux(machine): + if machine["os"] == "linux": + return False + return True + all_nodes = filter(filter_linux, all_nodes) + + return \ + { + "nodes": all_nodes, + "edges": edges + } diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js index eb94792ab..65a8b9003 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -103,7 +103,17 @@ class ReportPageComponent extends AuthComponent { }; updateMapFromServer = () => { - this.authFetch('/api/netmap') + this.authFetch('/api/netmap', { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + firstParam: 'yourValue', + secondParam: 'yourOtherValue', + }) + }) .then(res => res.json()) .then(res => { res.edges.forEach(edge => { From 64fcf4425b2660adaad98ed455423568a859bb0f Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 5 Jun 2019 16:20:04 +0300 Subject: [PATCH 04/15] Fixed URL and added ICON icon doesn't work yet - not passing a URL to the react notification package --- monkey/monkey_island/cc/ui/package-lock.json | 1060 +++++++++++++---- .../cc/ui/src/components/Main.js | 11 +- .../src/images/notification-logo-512x512.png | Bin 0 -> 19755 bytes 3 files changed, 837 insertions(+), 234 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/images/notification-logo-512x512.png diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index b13e2ac35..b169afb2a 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -518,8 +518,7 @@ "abbrev": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", - "dev": true + "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=" }, "accepts": { "version": "1.3.5", @@ -630,8 +629,7 @@ "amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" }, "ansi-colors": { "version": "3.1.0", @@ -654,14 +652,12 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" }, "anymatch": { "version": "1.3.2", @@ -677,8 +673,50 @@ "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "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" + } + }, + "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" + } + } + } }, "argparse": { "version": "1.0.9", @@ -713,8 +751,7 @@ "array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" }, "array-flatten": { "version": "2.1.1", @@ -779,8 +816,7 @@ "asn1": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", - "dev": true + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" }, "asn1.js": { "version": "4.10.1", @@ -822,8 +858,7 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, "assertion-error": { "version": "1.1.0", @@ -855,6 +890,11 @@ "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", "dev": true }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=" + }, "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", @@ -864,8 +904,7 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "atob": { "version": "2.1.1", @@ -876,8 +915,7 @@ "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { "version": "1.7.0", @@ -2201,8 +2239,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base": { "version": "0.11.2", @@ -2303,7 +2340,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "dev": true, "optional": true, "requires": { "tweetnacl": "^0.14.3" @@ -2344,6 +2380,14 @@ "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=", "dev": true }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "requires": { + "inherits": "~2.0.0" + } + }, "bluebird": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", @@ -2442,7 +2486,6 @@ "version": "1.1.8", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2617,8 +2660,7 @@ "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 + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" }, "builtin-status-codes": { "version": "3.0.0", @@ -2743,7 +2785,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "dev": true, "requires": { "camelcase": "^2.0.0", "map-obj": "^1.0.0" @@ -2752,8 +2793,7 @@ "camelcase": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" } } }, @@ -2766,8 +2806,7 @@ "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "center-align": { "version": "0.1.3", @@ -2798,7 +2837,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", @@ -2952,6 +2990,32 @@ } } }, + "clone-deep": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-2.0.2.tgz", + "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==", + "requires": { + "for-own": "^1.0.0", + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.0", + "shallow-clone": "^1.0.0" + }, + "dependencies": { + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "requires": { + "for-in": "^1.0.1" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + } + } + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -2960,8 +3024,7 @@ "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "collection-visit": { "version": "1.0.0", @@ -3007,7 +3070,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", - "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -3094,8 +3156,7 @@ "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 + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { "version": "1.6.0", @@ -3178,6 +3239,11 @@ "date-now": "^0.1.4" } }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, "constants-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", @@ -3320,8 +3386,7 @@ "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=", - "dev": true + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cosmiconfig": { "version": "5.2.0", @@ -3557,7 +3622,6 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, "requires": { "array-find-index": "^1.0.1" } @@ -3587,7 +3651,6 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -3626,8 +3689,7 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, "decode-uri-component": { "version": "0.2.0", @@ -3782,8 +3844,12 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" }, "depd": { "version": "1.1.2", @@ -4030,7 +4096,6 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "dev": true, "optional": true, "requires": { "jsbn": "~0.1.0" @@ -4076,8 +4141,7 @@ "emojis-list": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" }, "encodeurl": { "version": "1.0.2", @@ -5010,8 +5074,7 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-deep-equal": { "version": "1.1.0", @@ -5203,7 +5266,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, "requires": { "path-exists": "^2.0.0", "pinkie-promise": "^2.0.0" @@ -5297,8 +5359,7 @@ "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" }, "for-own": { "version": "0.1.5", @@ -5313,14 +5374,12 @@ "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", - "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "1.0.6", @@ -5428,8 +5487,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "1.1.2", @@ -6330,6 +6388,17 @@ } } }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -6342,11 +6411,53 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "requires": { + "globule": "^1.0.0" + } + }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" }, "get-func-name": { "version": "2.0.0", @@ -6357,8 +6468,7 @@ "get-stdin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" }, "get-stream": { "version": "3.0.0", @@ -6376,7 +6486,6 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -6385,7 +6494,6 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -6451,11 +6559,20 @@ "pinkie-promise": "^2.0.0" } }, + "globule": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", + "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", + "requires": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + } + }, "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" }, "growl": { "version": "1.10.5", @@ -6500,8 +6617,7 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { "version": "5.0.3", @@ -6540,7 +6656,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6580,6 +6695,11 @@ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", "dev": true }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -6735,8 +6855,7 @@ "hosted-git-info": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", - "dev": true + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" }, "hpack.js": { "version": "2.1.6", @@ -7239,7 +7358,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -7389,11 +7507,15 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "in-publish": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", + "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=" + }, "indent-string": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true, "requires": { "repeating": "^2.0.0" } @@ -7408,7 +7530,6 @@ "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" @@ -7417,8 +7538,7 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "inquirer": { "version": "6.2.0", @@ -7520,8 +7640,7 @@ "invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" }, "ip": { "version": "1.1.5", @@ -7574,7 +7693,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "dev": true, "requires": { "builtin-modules": "^1.0.0" } @@ -7644,8 +7762,7 @@ "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" }, "is-extglob": { "version": "1.0.0", @@ -7657,7 +7774,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, "requires": { "number-is-nan": "^1.0.0" } @@ -7665,8 +7781,7 @@ "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "is-glob": { "version": "2.0.1", @@ -7715,7 +7830,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, "requires": { "isobject": "^3.0.1" }, @@ -7723,8 +7837,7 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" } } }, @@ -7776,14 +7889,12 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" }, "is-windows": { "version": "1.0.2", @@ -7814,8 +7925,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isobject": { "version": "2.1.0", @@ -7848,8 +7958,7 @@ "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "istanbul": { "version": "0.4.5", @@ -7903,6 +8012,11 @@ } } }, + "js-base64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", + "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==" + }, "js-file-download": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/js-file-download/-/js-file-download-0.4.4.tgz", @@ -7927,7 +8041,6 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true, "optional": true }, "jsesc": { @@ -7949,8 +8062,7 @@ "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, "json-schema-traverse": { "version": "0.3.1", @@ -7966,8 +8078,7 @@ "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "json3": { "version": "3.3.2", @@ -7994,7 +8105,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -8294,8 +8404,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -8316,14 +8425,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8338,20 +8445,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -8468,8 +8572,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -8481,7 +8584,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -8496,7 +8598,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -8504,14 +8605,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -8530,7 +8629,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -8611,8 +8709,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -8624,7 +8721,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -8710,8 +8806,7 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -8747,7 +8842,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -8767,7 +8861,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -8811,14 +8904,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -9152,7 +9243,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, "requires": { "invert-kv": "^1.0.0" } @@ -9171,7 +9261,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, "requires": { "graceful-fs": "^4.1.2", "parse-json": "^2.2.0", @@ -9184,7 +9273,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, "requires": { "is-utf8": "^0.2.0" } @@ -9286,6 +9374,11 @@ "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", "integrity": "sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o=" }, + "lodash.tail": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz", + "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=" + }, "lodash.topath": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", @@ -9401,7 +9494,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true, "requires": { "currently-unhandled": "^0.4.1", "signal-exit": "^3.0.0" @@ -9417,7 +9509,6 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", - "dev": true, "requires": { "pseudomap": "^1.0.2", "yallist": "^2.1.2" @@ -9458,8 +9549,7 @@ "map-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" }, "map-visit": { "version": "1.0.0", @@ -9556,7 +9646,6 @@ "version": "3.7.0", "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "dev": true, "requires": { "camelcase-keys": "^2.0.0", "decamelize": "^1.1.2", @@ -9623,14 +9712,12 @@ "mime-db": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.29.0.tgz", - "integrity": "sha1-SNJtI1WJZRcErFkWygYAGRQmaHg=", - "dev": true + "integrity": "sha1-SNJtI1WJZRcErFkWygYAGRQmaHg=" }, "mime-types": { "version": "2.1.16", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.16.tgz", "integrity": "sha1-K4WKUuXs1RbbiXrCvodIeDBpjiM=", - "dev": true, "requires": { "mime-db": "~1.29.0" } @@ -9666,7 +9753,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -9674,8 +9760,7 @@ "minimist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "mississippi": { "version": "2.0.0", @@ -9716,11 +9801,26 @@ } } }, + "mixin-object": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", + "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", + "requires": { + "for-in": "^0.1.3", + "is-extendable": "^0.1.1" + }, + "dependencies": { + "for-in": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", + "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=" + } + } + }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, "requires": { "minimist": "0.0.8" }, @@ -9728,8 +9828,7 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" } } }, @@ -9900,8 +9999,7 @@ "neo-async": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.5.2.tgz", - "integrity": "sha512-vdqTKI9GBIYcAEbFAcpKPErKINfPF5zIuz3/niBfq8WUZjpT2tytLlFVrBgWdOtqI4uaA/Rb6No0hux39XXDuw==", - "dev": true + "integrity": "sha512-vdqTKI9GBIYcAEbFAcpKPErKINfPF5zIuz3/niBfq8WUZjpT2tytLlFVrBgWdOtqI4uaA/Rb6No0hux39XXDuw==" }, "next-tick": { "version": "1.0.0", @@ -9939,6 +10037,141 @@ "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==", "dev": true }, + "node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "ajv": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", + "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "requires": { + "mime-db": "1.40.0" + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "psl": { + "version": "1.1.32", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.32.tgz", + "integrity": "sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g==" + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "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==" + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, "node-libs-browser": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", @@ -10014,6 +10247,160 @@ } } }, + "node-sass": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz", + "integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==", + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash": "^4.17.11", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.13.2", + "node-gyp": "^3.8.0", + "npmlog": "^4.0.0", + "request": "^2.88.0", + "sass-graph": "^2.2.4", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + }, + "dependencies": { + "ajv": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", + "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "cross-spawn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "requires": { + "mime-db": "1.40.0" + } + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "psl": { + "version": "1.1.32", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.32.tgz", + "integrity": "sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g==" + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "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==" + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, "noms": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", @@ -10028,7 +10415,6 @@ "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, "requires": { "abbrev": "1" } @@ -10037,7 +10423,6 @@ "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", "integrity": "sha1-EvlaMH1YNSB1oEkHuErIvpisAS8=", - "dev": true, "requires": { "hosted-git-info": "^2.1.4", "is-builtin-module": "^1.0.0", @@ -12764,6 +13149,17 @@ "path-key": "^2.0.0" } }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, "nth-check": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", @@ -12782,8 +13178,7 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, "oauth-sign": { "version": "0.8.2", @@ -12928,7 +13323,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -13013,8 +13407,7 @@ "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" }, "os-locale": { "version": "2.1.0", @@ -13030,8 +13423,16 @@ "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } }, "output-file-sync": { "version": "1.1.2", @@ -13186,7 +13587,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, "requires": { "error-ex": "^1.2.0" } @@ -13237,7 +13637,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, "requires": { "pinkie-promise": "^2.0.0" } @@ -13245,8 +13644,7 @@ "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 + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { "version": "1.0.2", @@ -13275,7 +13673,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, "requires": { "graceful-fs": "^4.1.2", "pify": "^2.0.0", @@ -13310,8 +13707,7 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "phantomjs-prebuilt": { "version": "2.1.16", @@ -13333,20 +13729,17 @@ "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" }, "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" }, "pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, "requires": { "pinkie": "^2.0.0" } @@ -13599,8 +13992,7 @@ "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, "psl": { "version": "1.1.20", @@ -13653,8 +14045,7 @@ "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" }, "pure-color": { "version": "1.3.0", @@ -13676,8 +14067,7 @@ "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, "querystring": { "version": "0.2.0", @@ -14156,6 +14546,14 @@ "classnames": "^2.2.5" } }, + "react-tooltip-lite": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/react-tooltip-lite/-/react-tooltip-lite-1.9.3.tgz", + "integrity": "sha512-nRtludE/JfaT+ZJdyW2MHJgaj8ZbyljlJDhdFTedScfHDbErLBDqinH4RZHIJ/SRg+7gpf+9r3QRpxo99JdEQw==", + "requires": { + "prop-types": "^15.5.8" + } + }, "react-transition-group": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.5.0.tgz", @@ -14181,7 +14579,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, "requires": { "load-json-file": "^1.0.0", "normalize-package-data": "^2.3.2", @@ -14192,7 +14589,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, "requires": { "find-up": "^1.0.0", "read-pkg": "^1.0.0" @@ -14291,7 +14687,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true, "requires": { "indent-string": "^2.1.0", "strip-indent": "^1.0.1" @@ -14448,7 +14843,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, "requires": { "is-finite": "^1.0.0" } @@ -14518,14 +14912,12 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, "require-main-filename": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" }, "require-uncached": { "version": "1.0.3", @@ -14621,7 +15013,6 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "dev": true, "requires": { "glob": "^7.0.5" } @@ -14666,8 +15057,7 @@ "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=", - "dev": true + "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=" }, "safe-regex": { "version": "1.1.0", @@ -14684,6 +15074,141 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "sass-graph": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", + "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", + "requires": { + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^7.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + } + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "requires": { + "camelcase": "^3.0.0" + } + } + } + }, + "sass-loader": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.1.0.tgz", + "integrity": "sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w==", + "requires": { + "clone-deep": "^2.0.1", + "loader-utils": "^1.0.1", + "lodash.tail": "^4.1.1", + "neo-async": "^2.5.0", + "pify": "^3.0.0", + "semver": "^5.5.0" + }, + "dependencies": { + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + } + } + }, "scheduler": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.11.2.tgz", @@ -14703,6 +15228,25 @@ "ajv-keywords": "^3.1.0" } }, + "scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", + "requires": { + "js-base64": "^2.1.8", + "source-map": "^0.4.2" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -14721,8 +15265,7 @@ "semver": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", - "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", - "dev": true + "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=" }, "send": { "version": "0.16.2", @@ -14830,8 +15373,7 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "set-immediate-shim": { "version": "1.0.1", @@ -14902,6 +15444,23 @@ } } }, + "shallow-clone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-1.0.0.tgz", + "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==", + "requires": { + "is-extendable": "^0.1.1", + "kind-of": "^5.0.0", + "mixin-object": "^2.0.1" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, "shallowequal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", @@ -14926,8 +15485,7 @@ "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "slash": { "version": "1.0.0", @@ -15232,7 +15790,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", - "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -15241,14 +15798,12 @@ "spdx-exceptions": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", - "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", - "dev": true + "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==" }, "spdx-expression-parse": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, "requires": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -15257,8 +15812,7 @@ "spdx-license-ids": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", - "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", - "dev": true + "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==" }, "spdy": { "version": "3.4.7", @@ -15345,7 +15899,6 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", - "dev": true, "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -15393,6 +15946,48 @@ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", "dev": true }, + "stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "requires": { + "readable-stream": "^2.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "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" + } + }, + "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" + } + } + } + }, "stream-browserify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", @@ -15571,7 +16166,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" @@ -15580,14 +16174,12 @@ "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, "requires": { "ansi-regex": "^3.0.0" } @@ -15610,7 +16202,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, "requires": { "ansi-regex": "^2.0.0" } @@ -15625,7 +16216,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true, "requires": { "get-stdin": "^4.0.1" } @@ -15662,8 +16252,7 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" }, "symbol-observable": { "version": "1.2.0", @@ -15727,6 +16316,16 @@ "integrity": "sha512-dQRhbNQkRnaqauC7WqSJ21EEksgT0fYZX2lqXzGkpo8JNig9zGZTYoMGvyI2nWmXlE2VSVXVDu7wLVGu/mQEsg==", "dev": true }, + "tar": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", + "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", + "requires": { + "block-stream": "*", + "fstream": "^1.0.12", + "inherits": "2" + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -15900,8 +16499,7 @@ "trim-newlines": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" }, "trim-right": { "version": "1.0.1", @@ -15909,6 +16507,14 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, + "true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "requires": { + "glob": "^7.1.2" + } + }, "tslib": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", @@ -15925,7 +16531,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, "requires": { "safe-buffer": "^5.0.1" } @@ -15934,7 +16539,6 @@ "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, "optional": true }, "type-check": { @@ -16200,7 +16804,6 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, "requires": { "punycode": "^2.1.0" }, @@ -16208,8 +16811,7 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" } } }, @@ -16324,8 +16926,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "util.promisify": { "version": "1.0.0", @@ -16382,7 +16983,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, "requires": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -16403,7 +17003,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -19291,7 +19890,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", "integrity": "sha1-/wS9/AEO5UfXgL7DjhrBwnd9JTo=", - "dev": true, "requires": { "isexe": "^2.0.0" } @@ -19302,6 +19900,14 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, "window-size": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", @@ -19328,7 +19934,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, "requires": { "string-width": "^1.0.1", "strip-ansi": "^3.0.1" @@ -19338,7 +19943,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, "requires": { "number-is-nan": "^1.0.0" } @@ -19347,7 +19951,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -19359,8 +19962,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write": { "version": "0.2.1", @@ -19403,14 +20005,12 @@ "y18n": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" }, "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" }, "yargs": { "version": "3.10.0", diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js index 863e814ba..08fcf125a 100644 --- a/monkey/monkey_island/cc/ui/src/components/Main.js +++ b/monkey/monkey_island/cc/ui/src/components/Main.js @@ -23,6 +23,7 @@ import 'styles/App.css'; import 'react-toggle/style.css'; import 'react-table/react-table.css'; import VersionComponent from "./side-menu/VersionComponent"; +import notificationIcon from '../images/notification-logo-512x512.png'; let logoImage = require('../images/monkey-icon.svg'); let infectionMonkeyImage = require('../images/infection-monkey.svg'); @@ -200,12 +201,14 @@ class AppComponent extends AuthComponent { showInfectionDoneNotification() { if (this.state.completedSteps.infection_done) { - console.log("Trying to show notification..."); + let hostname = window.location.hostname; + let url = `https://${hostname}:5000/report`; + console.log("Trying to show notification. URL: " + url + " | icon: " + {notificationIcon}); Notifier.start( "Monkey Island", - "Infection is done. Click to see results", - "https://localhost:5000/report", - ""); + "Infection is done! Click here to go to the report page.", + url, + {notificationIcon}); } } } diff --git a/monkey/monkey_island/cc/ui/src/images/notification-logo-512x512.png b/monkey/monkey_island/cc/ui/src/images/notification-logo-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..3875818584745cbdeedec71fa7383a399218500d GIT binary patch literal 19755 zcmb5WWn3Fi)CRf<0fM_*a4E&LSg_(!C@t=kQXGm~pcHp0ZY@$O6n8BUq)@EI9g0J6 zmz)24-}iSv-w)Z%?w&cbGntul=A7qAw63-a0WK{r000DPs!DnQ07icW15hmV>D2en zHTs0(rfTE~0C=STeL&+2!M6Ya1Jsn{pZn(S=kdQ~Q2Ve^(~NuiQLjo;P5o6jZys!z zx5~&idYDDy8}^K~uqwNRD(P&^*1&6@WZU|8Y&B*{DkI;ht|BiDW&ZzhP=&Xz4Rf%$#tF6<7%)6HIQ#g8 z=LQvEV6e{{w5@DlF!ANfD+7bJA76Hg9I|OF2o#RrgAqyif=EM*5IuvxkEm}24doB( zzkJClb)XE?fezr|0t!OlhzBhLgC8$*kNd3SAG{0)ZBElv=Lyl_pPry|XP`Ob?B zr=f_L9TY%t)sLN*O95}MT)X`NLT)frjqE=qN})%tMNk;qGcEwChTn6`#M*c_Wz`M3 z8Nz|FJ-+YUR95xSB&T`6of3NH#t@za3LME%R((QkJ zs$(~%N0dxK8r>gJ!TD0=EBMF|M{CCW-&UN`0+dsZ9O1qZUYS3-!Y-(^5C^>Z>j*wHl`p`cme-UlWE+x(#=_ zT+=NtvyvqG7O{eAD7tuxxZIOKsf+^l|B*r0!M4^^`_JpjC;^k>f8@6M>?(irJ-H5XZ2EQ8wC4b>A#t?skcE)B!~`AeqH1yk1>=ve?*EXT@ z+`9qaD0_7zKpI2ahK;TWfH`(WAEvL_iaWku{4V8{61;5Gi2SGMKDfD$m4Bh3GG^a< zX(EePO^6E|6a1`LrVOAp+70HI6GR4T7WP)0T}pr2w5K->U}GZ()`g=UX-n&P)UAM}p=h7iew`V2V?uGK zF|_HNYqXz?9O!X@r$RY@Y22C%;ZI54$x43O4WTx~$OsMDmz-iOrv`K7Xb(4_Qu{bu zvP;LD{`?6YzEPU-I+7$*>hi2DLMX=6dlX?8I`Og6h5W+NVSOj5Kk48a@wuV@xP0m=6e^}f*Ly!giS5RRd>TL6)_z~7ng@`B z-XAJ%ix;kT|pOqr0ub}%=xAsT#HsybyET1{P&Ah zYpA*2e0iZra3Q`aRn5XBcHOqJkHpku`>c*P?a?--FV*hN&!o~!`kiY+IsEo$hnxV2&`DR{n(U8rXqbRREw(wrmp&<^H6c&?4;GcJ8N@Uh;GMVrYgco3$qJtG3IN<&r+hJF?KpDb1oN?bu3$yRoPMyq2OUx zXB0tnkYmV8wh9!zqwBw*Do0XB*Q5qZf}u8Uh&BdqWP?K4F-ETDT>DAK3xhM^`w+DZ zuX4VAyLTbhanUj%a?}`R4?1i~Cju0-l{RwSF?%#|Z(GjK!*gD}r{+;6N4FO0o!WzB zwks|X&sz;WGO^<##HEc7{MFO(eXk-bhO3MB6PsQ&W&|*}`tg*mFxQK7i7J_2DoXxo zZoucy#Vy8^2PByCAYDoyUewq?gwe%cRh?K{31mxIAGN25&{F8Y8;>E}V-X4+WwcNf ztNzNat$y%RC(tqlTYAFqb>hrN330y6I}1v3;Qg4$uojEU#P~6un5b3^BN@{iVe{r@ zbI+ar;p>t^J~>NQ*nf3#l;JdL$ErcPYg^)%e4;?+FH5(hF6~#P7Nq zA$rv00Oq*JFz)^uMG+1*gWkxyR6kJ+5`eyPA1nNV^pOn8xqmS+Y6 z+HVd(>zvJyk=XD(+LE{sma>q2)FJmdSPzC)_AayF6$xxiTKTa+xGWZj%+z&O7_vjX z*eGqo=Q7Rxp}Gj*%1wZdW|A#;CpoOn>jzFpj=76XyG3fyJn$sr0`aKBFl51Va-jH{ z1x!eh|GD}MC@gEa>||pDu8ApH_>P6N|)?c zYsT+yt_9&UGvKFoCGzs$mdYYO*}Ww-DLZq!p#8per?#^SJ9R0Mbp>8fn+YRzuF-DU z_WK3hBvv=tF4pBw&-u8(k6jzw89#3X+8ZndH{Q6uuelW$<7C~rPO%Wk(F_r_+SzisJVp4V^C|hss}%-<6`Uq{8M$~l8Uh)h2gesfJ6@0poEykT z608blUUR*wyQ9i|H5Wc)&B5Vqcq;k5TgrV#vAEdxTFxU$s`5G_J58f_?HIo|$oFR*wLVaQi(yoN;_EOOt2 zhxUNz`t>f13K+dAakL9vrO%od`QYSEf#u(vX)Q$jOcgM$;5yMdhQ5HzM-OXs|*Cf#T4om*pE%2{_zJg z?bvwSMGE~-(0=zKjsUftjYH<6s3RukU%3%-W^85;dNPcY$-MyYM><@96f*SwB8!ca z2fVx}6C(ji?q_)+OWS#AGS$({LzTjW4@%O_WqEZFOU7o)&^#m?*|1u&Nsu3IkHsu* z4EVl2tR-x|S0mm6!MdQj6uK+LK7z$cl@sO{6lS+G!rki~In0XyTTnjs>ki%*`KtY5 zT!5;8Ti7-A=g2#J(KYd1X)yKt@snk0laH}w?ciNwkPj`v`@y?P)V44c5hb%?Apn!_ z(mWq6@zlR{h}^l23xwpKt6^4MR-Oj$DfizkZx=w&!ybZp;9bxJgclDAe;4Lc&95XO z#=#Yns262lcf|U~M$7`j1fsi47QGbO!yLmPk<0)orp=HFav8_ZdQxOqcoCPB)*n#i zo&Fb>q~rEDiAxtqG93M<*3a2UsY;IS$uR-sE#+rUfZ5ncMC*jCfG`>)1F&(7y}Ba% z2&n*(5}^As^`iBr*sT#uRRq0@V-8OOqdjp!s}WDVC#D!|wB%@UO@OVWTNM5gJq*zT z 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 literal 0 HcmV?d00001 From bf0b812f7330f8ae4f518d2de720e7598e419c63 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 5 Jun 2019 17:18:52 +0300 Subject: [PATCH 05/15] Revert "WIP - initial code, just trying to make a POST request work" This reverts commit 026cb41064150191609b05805dd6d24a06a13b3c. --- monkey/monkey_island/cc/resources/netmap.py | 31 ------------------- .../cc/ui/src/components/pages/ReportPage.js | 12 +------ 2 files changed, 1 insertion(+), 42 deletions(-) diff --git a/monkey/monkey_island/cc/resources/netmap.py b/monkey/monkey_island/cc/resources/netmap.py index 96ea38b28..ed83414f5 100644 --- a/monkey/monkey_island/cc/resources/netmap.py +++ b/monkey/monkey_island/cc/resources/netmap.py @@ -1,7 +1,4 @@ -import json - import flask_restful -from flask import request from monkey_island.cc.auth import jwt_required from monkey_island.cc.services.edge import EdgeService @@ -31,32 +28,4 @@ class NetMap(flask_restful.Resource): "edges": edges } - @jwt_required() - def post(self, **kw): - post_data = json.loads(request.data) - print(post_data) - - monkeys = [NodeService.monkey_to_net_node(x) for x in mongo.db.monkey.find({})] - nodes = [NodeService.node_to_net_node(x) for x in mongo.db.node.find({})] - edges = [EdgeService.edge_to_net_edge(x) for x in mongo.db.edge.find({})] - - if NodeService.get_monkey_island_monkey() is None: - monkey_island = [NodeService.get_monkey_island_pseudo_net_node()] - edges += EdgeService.get_monkey_island_pseudo_edges() - else: - monkey_island = [] - edges += EdgeService.get_infected_monkey_island_pseudo_edges() - - all_nodes = monkeys + nodes + monkey_island - def filter_linux(machine): - if machine["os"] == "linux": - return False - return True - all_nodes = filter(filter_linux, all_nodes) - - return \ - { - "nodes": all_nodes, - "edges": edges - } diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js index 65a8b9003..eb94792ab 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -103,17 +103,7 @@ class ReportPageComponent extends AuthComponent { }; updateMapFromServer = () => { - this.authFetch('/api/netmap', { - method: 'POST', - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - firstParam: 'yourValue', - secondParam: 'yourOtherValue', - }) - }) + this.authFetch('/api/netmap') .then(res => res.json()) .then(res => { res.edges.forEach(edge => { From 833af00421ea91b168cdb931564b79375b5cabcc Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 5 Jun 2019 18:28:00 +0300 Subject: [PATCH 06/15] Added the IslandTestCase class for better testing + moved get_os to the monkey model --- monkey/monkey_island/cc/models/monkey.py | 8 +++++ monkey/monkey_island/cc/models/test_monkey.py | 29 +++++++++++++++++-- .../cc/testing/IslandTestCase.py | 12 ++++++++ monkey/monkey_island/cc/testing/__init__.py | 0 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 monkey/monkey_island/cc/testing/IslandTestCase.py create mode 100644 monkey/monkey_island/cc/testing/__init__.py diff --git a/monkey/monkey_island/cc/models/monkey.py b/monkey/monkey_island/cc/models/monkey.py index bb018caa9..3e1e3d7c5 100644 --- a/monkey/monkey_island/cc/models/monkey.py +++ b/monkey/monkey_island/cc/models/monkey.py @@ -55,6 +55,14 @@ class Monkey(Document): monkey_is_dead = True return monkey_is_dead + def get_os(self): + os = "unknown" + if self.description.lower().find("linux") != -1: + os = "linux" + elif self.description.lower().find("windows") != -1: + os = "windows" + return os + class MonkeyNotFoundError(Exception): pass diff --git a/monkey/monkey_island/cc/models/test_monkey.py b/monkey/monkey_island/cc/models/test_monkey.py index 008fb0ce6..a744db6b6 100644 --- a/monkey/monkey_island/cc/models/test_monkey.py +++ b/monkey/monkey_island/cc/models/test_monkey.py @@ -1,13 +1,13 @@ import uuid from time import sleep -from unittest import TestCase from monkey import Monkey from monkey_island.cc.models.monkey import MonkeyNotFoundError +from monkey_island.cc.testing.IslandTestCase import IslandTestCase from monkey_ttl import MonkeyTtl -class TestMonkey(TestCase): +class TestMonkey(IslandTestCase): """ Make sure to set server environment to `testing` in server.json! Otherwise this will mess up your mongo instance and won't work. @@ -15,7 +15,11 @@ class TestMonkey(TestCase): Also, the working directory needs to be the working directory from which you usually run the island so the server.json file is found and loaded. """ + def test_is_dead(self): + self.fail_if_not_testing_env() + self.clean_monkey_db() + # Arrange alive_monkey_ttl = MonkeyTtl.create_ttl_expire_in(30) alive_monkey_ttl.save() @@ -43,6 +47,9 @@ class TestMonkey(TestCase): self.assertFalse(alive_monkey.is_dead()) def test_get_single_monkey_by_id(self): + self.fail_if_not_testing_env() + self.clean_monkey_db() + # Arrange a_monkey = Monkey(guid=str(uuid.uuid4())) a_monkey.save() @@ -52,3 +59,21 @@ class TestMonkey(TestCase): self.assertIsNotNone(Monkey.get_single_monkey_by_id(a_monkey.id)) # Raise on non-existent monkey self.assertRaises(MonkeyNotFoundError, Monkey.get_single_monkey_by_id, "abcdefabcdefabcdefabcdef") + + def test_get_os(self): + self.fail_if_not_testing_env() + self.clean_monkey_db() + + linux_monkey = Monkey(guid=str(uuid.uuid4()), + description="Linux shay-Virtual-Machine 4.15.0-50-generic #54-Ubuntu SMP Mon May 6 18:46:08 UTC 2019 x86_64 x86_64") + windows_monkey = Monkey(guid=str(uuid.uuid4()), + description="Windows bla bla bla") + unknown_monkey = Monkey(guid=str(uuid.uuid4()), + description="bla bla bla") + linux_monkey.save() + windows_monkey.save() + unknown_monkey.save() + + self.assertEquals(1, len(filter(lambda m: m.get_os() == "windows", Monkey.objects()))) + self.assertEquals(1, len(filter(lambda m: m.get_os() == "linux", Monkey.objects()))) + self.assertEquals(1, len(filter(lambda m: m.get_os() == "unknown", Monkey.objects()))) diff --git a/monkey/monkey_island/cc/testing/IslandTestCase.py b/monkey/monkey_island/cc/testing/IslandTestCase.py new file mode 100644 index 000000000..e894f13df --- /dev/null +++ b/monkey/monkey_island/cc/testing/IslandTestCase.py @@ -0,0 +1,12 @@ +import unittest +from monkey_island.cc.environment.environment import env +from monkey_island.cc.models import Monkey + + +class IslandTestCase(unittest.TestCase): + def fail_if_not_testing_env(self): + self.failIf(not env.testing, "Change server_config.json to testing environment.") + + @staticmethod + def clean_monkey_db(): + Monkey.objects().delete() diff --git a/monkey/monkey_island/cc/testing/__init__.py b/monkey/monkey_island/cc/testing/__init__.py new file mode 100644 index 000000000..e69de29bb From 712ce4622d145c47fc3c38e1527f03dc1ab6fa6e Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 5 Jun 2019 18:28:54 +0300 Subject: [PATCH 07/15] Refactored the GenerateMapNodes function to use the DAL and now it filters windows nodes --- .../monkey_island/cc/services/pth_report.py | 13 ++-- .../cc/services/test_PTHReportService.py | 69 +++++++++++++++++++ 2 files changed, 76 insertions(+), 6 deletions(-) create mode 100644 monkey/monkey_island/cc/services/test_PTHReportService.py diff --git a/monkey/monkey_island/cc/services/pth_report.py b/monkey/monkey_island/cc/services/pth_report.py index 93fd51989..9f3b9769f 100644 --- a/monkey/monkey_island/cc/services/pth_report.py +++ b/monkey/monkey_island/cc/services/pth_report.py @@ -1,6 +1,7 @@ from itertools import product from monkey_island.cc.database import mongo +from monkey_island.cc.models import Monkey from bson import ObjectId from monkey_island.cc.services.groups_and_users_consts import USERTYPE @@ -216,15 +217,15 @@ class PTHReportService(object): @staticmethod def generate_map_nodes(): - monkeys = mongo.db.monkey.find({}, {'_id': 1, 'hostname': 1, 'critical_services': 1, 'ip_addresses': 1}) + monkeys = filter(lambda m: m.get_os() == "windows", Monkey.objects()) return [ { - 'id': monkey['_id'], - 'label': '{0} : {1}'.format(monkey['hostname'], monkey['ip_addresses'][0]), - 'group': 'critical' if monkey.get('critical_services', []) else 'normal', - 'services': monkey.get('critical_services', []), - 'hostname': monkey['hostname'] + 'id': monkey.guid, + 'label': '{0} : {1}'.format(monkey.hostname, monkey.ip_addresses[0]), + 'group': 'critical' if monkey.critical_services is not None else 'normal', + 'services': monkey.critical_services, + 'hostname': monkey.hostname } for monkey in monkeys ] diff --git a/monkey/monkey_island/cc/services/test_PTHReportService.py b/monkey/monkey_island/cc/services/test_PTHReportService.py new file mode 100644 index 000000000..24e560ff0 --- /dev/null +++ b/monkey/monkey_island/cc/services/test_PTHReportService.py @@ -0,0 +1,69 @@ +import uuid + +from monkey_island.cc.models import Monkey +from monkey_island.cc.services.pth_report import PTHReportService +from monkey_island.cc.testing.IslandTestCase import IslandTestCase + + +class TestPTHReportServiceGenerateMapNodes(IslandTestCase): + def test_generate_map_nodes(self): + self.fail_if_not_testing_env() + self.clean_monkey_db() + + self.assertEqual(PTHReportService.generate_map_nodes(), []) + + windows_monkey_with_services = Monkey( + guid=str(uuid.uuid4()), + hostname="A_Windows_PC_1", + critical_services=["aCriticalService", "Domain Controller"], + ip_addresses=["1.1.1.1", "2.2.2.2"], + description="windows 10" + ) + windows_monkey_with_services.save() + + windows_monkey_with_no_services = Monkey( + guid=str(uuid.uuid4()), + hostname="A_Windows_PC_2", + critical_services=[], + ip_addresses=["3.3.3.3"], + description="windows 10" + ) + windows_monkey_with_no_services.save() + + linux_monkey = Monkey( + guid=str(uuid.uuid4()), + hostname="A_Linux_PC", + ip_addresses=["4.4.4.4"], + description="linux ubuntu" + ) + linux_monkey.save() + + map_nodes = PTHReportService.generate_map_nodes() + + self.assertEquals(2, len(map_nodes)) + + def test_generate_map_nodes_parsing(self): + self.fail_if_not_testing_env() + self.clean_monkey_db() + + monkey_id = str(uuid.uuid4()) + hostname = "A_Windows_PC_1" + windows_monkey_with_services = Monkey( + guid=monkey_id, + hostname=hostname, + critical_services=["aCriticalService", "Domain Controller"], + ip_addresses=["1.1.1.1"], + description="windows 10" + ) + windows_monkey_with_services.save() + + map_nodes = PTHReportService.generate_map_nodes() + + self.assertEquals(map_nodes[0]["id"], monkey_id) + self.assertEquals(map_nodes[0]["label"], "A_Windows_PC_1 : 1.1.1.1") + self.assertEquals(map_nodes[0]["group"], "critical") + self.assertEquals(len(map_nodes[0]["services"]), 2) + self.assertEquals(map_nodes[0]["hostname"], hostname) + + + From cbcc640acee79a1c6609c355d961623208a1d1c7 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 5 Jun 2019 19:07:42 +0300 Subject: [PATCH 08/15] Icon works in the notification --- monkey/monkey_island/cc/ui/src/components/Main.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js index 08fcf125a..619d5e922 100644 --- a/monkey/monkey_island/cc/ui/src/components/Main.js +++ b/monkey/monkey_island/cc/ui/src/components/Main.js @@ -23,11 +23,11 @@ import 'styles/App.css'; import 'react-toggle/style.css'; import 'react-table/react-table.css'; import VersionComponent from "./side-menu/VersionComponent"; -import notificationIcon from '../images/notification-logo-512x512.png'; let logoImage = require('../images/monkey-icon.svg'); let infectionMonkeyImage = require('../images/infection-monkey.svg'); let guardicoreLogoImage = require('../images/guardicore-logo.png'); +let notificationIcon = require('../images/notification-logo-512x512.png'); class AppComponent extends AuthComponent { updateStatus = () => { @@ -203,12 +203,13 @@ class AppComponent extends AuthComponent { if (this.state.completedSteps.infection_done) { let hostname = window.location.hostname; let url = `https://${hostname}:5000/report`; - console.log("Trying to show notification. URL: " + url + " | icon: " + {notificationIcon}); + console.log("Trying to show notification. URL: " + url + " | icon: " + notificationIcon); + Notifier.start( "Monkey Island", "Infection is done! Click here to go to the report page.", url, - {notificationIcon}); + notificationIcon); } } } From 28f2d32579924b99dec4fd54ec3aa84f0ee4d40d Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 5 Jun 2019 19:25:09 +0300 Subject: [PATCH 09/15] Added assertion of mongo db version. Also refactoed a bit to make the main shorter. --- monkey/monkey_island/cc/database.py | 11 ++++++++++ monkey/monkey_island/cc/main.py | 32 ++++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/monkey/monkey_island/cc/database.py b/monkey/monkey_island/cc/database.py index 8fb3b120b..082553e5f 100644 --- a/monkey/monkey_island/cc/database.py +++ b/monkey/monkey_island/cc/database.py @@ -25,3 +25,14 @@ def is_db_server_up(mongo_url): return True except ServerSelectionTimeoutError: return False + + +def get_db_version(mongo_url): + """ + Return the mongo db version + :param mongo_url: Which mongo to check. + :return: version as a tuple (e.g. `(u'4', u'0', u'8')`) + """ + client = MongoClient(mongo_url, serverSelectionTimeoutMS=100) + server_version = tuple(client.server_info()['version'].split('.')) + return server_version diff --git a/monkey/monkey_island/cc/main.py b/monkey/monkey_island/cc/main.py index 412c3c399..103280a05 100644 --- a/monkey/monkey_island/cc/main.py +++ b/monkey/monkey_island/cc/main.py @@ -6,6 +6,8 @@ import sys import time import logging +MINIMUM_MONGO_DB_VERSION_REQUIRED = "4.0.0" + BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) if BASE_PATH not in sys.path: @@ -22,7 +24,7 @@ from monkey_island.cc.app import init_app from monkey_island.cc.exporter_init import populate_exporter_list from monkey_island.cc.utils import local_ip_addresses from monkey_island.cc.environment.environment import env -from monkey_island.cc.database import is_db_server_up +from monkey_island.cc.database import is_db_server_up, get_db_version def main(): @@ -31,10 +33,8 @@ def main(): from tornado.ioloop import IOLoop mongo_url = os.environ.get('MONGO_URL', env.get_mongo_url()) - - while not is_db_server_up(mongo_url): - logger.info('Waiting for MongoDB server') - time.sleep(1) + wait_for_mongo_db_server(mongo_url) + assert_mongo_db_version(mongo_url) populate_exporter_list() app = init_app(mongo_url) @@ -55,5 +55,27 @@ def main(): IOLoop.instance().start() +def wait_for_mongo_db_server(mongo_url): + while not is_db_server_up(mongo_url): + logger.info('Waiting for MongoDB server on {0}'.format(mongo_url)) + time.sleep(1) + + +def assert_mongo_db_version(mongo_url): + """ + Checks if the mongodb version is new enough for running the app. + If the DB is too old, quits. + :param mongo_url: URL to the mongo the Island will use + """ + required_version = tuple(MINIMUM_MONGO_DB_VERSION_REQUIRED.split(".")) + server_version = get_db_version(mongo_url) + if server_version < required_version: + logger.error( + 'Mongo DB version too old. {0} is required, but got {1}'.format(str(required_version), str(server_version))) + sys.exit(-1) + else: + logger.info('Mongo DB version OK. Got {0}'.format(str(server_version))) + + if __name__ == '__main__': main() From f2d25c44810d3c19464396a1eb9fafd11c36dcec Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 12 Jun 2019 11:51:20 +0300 Subject: [PATCH 10/15] fixed PR comments --- .../attack/technique_reports/T1110.py | 38 ++++++++++++------- .../attack/technique_reports/__init__.py | 9 +++++ 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py index 040b05be2..977fb860a 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py @@ -35,11 +35,11 @@ class T1110(AttackTechnique): result['successful_creds'].append(T1110.parse_creds(attempt)) if succeeded: - data = {'message': T1110.used_msg, 'status': ScanStatus.USED.name} + data = T1110.get_message_and_status(T1110, ScanStatus.USED) elif attempts: - data = {'message': T1110.scanned_msg, 'status': ScanStatus.SCANNED.name} + data = T1110.get_message_and_status(T1110, ScanStatus.SCANNED) else: - data = {'message': T1110.unscanned_msg, 'status': ScanStatus.UNSCANNED.name} + data = T1110.get_message_and_status(T1110, ScanStatus.UNSCANNED) data.update({'services': attempts, 'title': T1110.technique_title(T1110.tech_id)}) return data @@ -51,21 +51,33 @@ class T1110(AttackTechnique): :return: string with username and used password/hash """ username = attempt['user'] - if attempt['lm_hash']: - return '%s ; LM hash %s ...' % (username, encryptor.dec(attempt['lm_hash'])[0:5]) - if attempt['ntlm_hash']: - return '%s ; NTLM hash %s ...' % (username, encryptor.dec(attempt['ntlm_hash'])[0:20]) - if attempt['ssh_key']: - return '%s ; SSH key %s ...' % (username, encryptor.dec(attempt['ssh_key'])[0:15]) - if attempt['password']: - return '%s : %s' % (username, T1110.obfuscate_password(encryptor.dec(attempt['password']))) + creds = {'lm_hash': {'type': 'LM hash', 'shown_chars': 5, 'funct': T1110.censor_hash}, + 'ntlm_hash': {'type': 'NTLM hash', 'shown_chars': 20, 'funct': T1110.censor_hash}, + 'ssh_key': {'type': 'SSH key', 'shown_chars': 15, 'funct': T1110.censor_hash}, + 'password': {'type': 'Plaintext password', 'shown_chars': 3, 'funct': T1110.censor_password}} + for key, cred in creds.items(): + if attempt[key]: + return '%s ; %s : %s' % (username, + cred['type'], + cred['funct'](encryptor.dec(attempt[key]), cred['shown_chars'])) @staticmethod - def obfuscate_password(password, plain_chars=3): + def censor_password(password, plain_chars=3, secret_chars=5): """ Obfuscates password by changing characters to * :param password: Password or string to obfuscate :param plain_chars: How many plain-text characters should be kept at the start of the string + :param secret_chars: How many * symbols should be used to hide the remainder of the password :return: Obfuscated string e.g. Pass**** """ - return password[0:plain_chars] + '*' * (len(password) - plain_chars) + return password[0:plain_chars] + '*' * secret_chars + + @staticmethod + def censor_hash(hash_, plain_chars=5): + """ + Obfuscates hash by only showing a part of it + :param hash_: Hash to obfuscate + :param plain_chars: How many chars of hash should be shown + :return: Obfuscated string + """ + return hash_[0: plain_chars] + ' ...' diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py index 0346a1857..782ca389d 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -60,6 +60,15 @@ class AttackTechnique(object): else: return ScanStatus.UNSCANNED + @staticmethod + def get_message_and_status(technique, status): + if status == ScanStatus.UNSCANNED: + return {'message': technique.unscanned_msg, 'status': ScanStatus.UNSCANNED.name} + elif status == ScanStatus.SCANNED: + return {'message': technique.scanned_msg, 'status': ScanStatus.SCANNED.name} + else: + return {'message': technique.used_msg, 'status': ScanStatus.USED.name} + @staticmethod def technique_title(technique): """ From 922a129f2f1a55c019951aa467b2da778512551e Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 12 Jun 2019 17:08:33 +0300 Subject: [PATCH 11/15] Now using tempfile to create monkey directory Instead of predetermined tmp paths. --- monkey/infection_monkey/config.py | 5 ++--- monkey/infection_monkey/example.conf | 4 +--- monkey/infection_monkey/exploit/mssqlexec.py | 17 +++++++++-------- monkey/infection_monkey/utils.py | 15 ++++++--------- .../monkey_island/cc/services/config_schema.py | 14 ++++---------- 5 files changed, 22 insertions(+), 33 deletions(-) diff --git a/monkey/infection_monkey/config.py b/monkey/infection_monkey/config.py index 97b66690e..f7e4cfae4 100644 --- a/monkey/infection_monkey/config.py +++ b/monkey/infection_monkey/config.py @@ -157,9 +157,8 @@ class Configuration(object): keep_tunnel_open_time = 60 - # Monkey files directories - monkey_dir_linux = '/tmp/monkey_dir' - monkey_dir_windows = r'C:\Windows\Temp\monkey_dir' + # Monkey files directory name + monkey_dir_name = 'monkey_dir' ########################### # scanners config diff --git a/monkey/infection_monkey/example.conf b/monkey/infection_monkey/example.conf index 8c8668bef..8dba50352 100644 --- a/monkey/infection_monkey/example.conf +++ b/monkey/infection_monkey/example.conf @@ -29,9 +29,7 @@ "dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe", "dropper_target_path_linux": "/tmp/monkey", - monkey_dir_linux = '/tmp/monkey_dir', - monkey_dir_windows = r'C:\Windows\Temp\monkey_dir', - + "monkey_dir_name": "monkey_dir", "kill_file_path_linux": "/var/run/monkey.not", "kill_file_path_windows": "%windir%\\monkey.not", diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 3f8af40fc..3d3f199f5 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -1,15 +1,16 @@ -import os import logging -from time import sleep -import pymssql +import os import textwrap +from time import sleep + +import pymssql -from infection_monkey.exploit import HostExploiter, tools from common.utils.exploit_enum import ExploitType +from infection_monkey.exploit import HostExploiter, tools from infection_monkey.exploit.tools import HTTPTools -from infection_monkey.config import WormConfiguration -from infection_monkey.model import DROPPER_ARG from infection_monkey.exploit.tools import get_monkey_dest_path +from infection_monkey.model import DROPPER_ARG +from infection_monkey.utils import get_monkey_dir_path LOG = logging.getLogger(__name__) @@ -52,10 +53,10 @@ class MSSQLExploiter(HostExploiter): LOG.info("Started http server on %s", http_path) dst_path = get_monkey_dest_path(http_path) - tmp_file_path = os.path.join(WormConfiguration.monkey_dir_windows, MSSQLExploiter.TMP_FILE_NAME) + tmp_file_path = os.path.join(get_monkey_dir_path(), MSSQLExploiter.TMP_FILE_NAME) # Create monkey dir. - commands = ["xp_cmdshell \"mkdir %s\"" % WormConfiguration.monkey_dir_windows] + commands = ["xp_cmdshell \"mkdir %s\"" % get_monkey_dir_path()] MSSQLExploiter.execute_command(cursor, commands) # Form download command in a file diff --git a/monkey/infection_monkey/utils.py b/monkey/infection_monkey/utils.py index 0e08203c2..6eb3aefb5 100644 --- a/monkey/infection_monkey/utils.py +++ b/monkey/infection_monkey/utils.py @@ -1,8 +1,9 @@ import os -import sys import shutil import struct -import datetime +import sys +import tempfile + from infection_monkey.config import WormConfiguration @@ -17,10 +18,9 @@ def get_dropper_log_path(): def is_64bit_windows_os(): - ''' + """ Checks for 64 bit Windows OS using environment variables. - :return: - ''' + """ return 'PROGRAMFILES(X86)' in os.environ @@ -54,7 +54,4 @@ def remove_monkey_dir(): def get_monkey_dir_path(): - if is_windows_os(): - return WormConfiguration.monkey_dir_windows - else: - return WormConfiguration.monkey_dir_linux + return os.path.join(tempfile.gettempdir(), WormConfiguration.monkey_dir_name) diff --git a/monkey/monkey_island/cc/services/config_schema.py b/monkey/monkey_island/cc/services/config_schema.py index 8a96a0d78..46129266c 100644 --- a/monkey/monkey_island/cc/services/config_schema.py +++ b/monkey/monkey_island/cc/services/config_schema.py @@ -499,17 +499,11 @@ SCHEMA = { "default": 60, "description": "Time to keep tunnel open before going down after last exploit (in seconds)" }, - "monkey_dir_windows": { - "title": "Monkey's windows directory", + "monkey_dir_name": { + "title": "Monkey's directory name", "type": "string", - "default": r"C:\Windows\temp\monkey_dir", - "description": "Directory containing all monkey files on windows" - }, - "monkey_dir_linux": { - "title": "Monkey's linux directory", - "type": "string", - "default": "/tmp/monkey_dir", - "description": "Directory containing all monkey files on linux" + "default": r"monkey_dir", + "description": "Directory name for the directory which will contain all of the monkey files" }, } }, From 65ca86f6c03e40179156fac67bdf1d02b16444f8 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 12 Jun 2019 18:28:25 +0300 Subject: [PATCH 12/15] Changed minimum mongo version to 3.6 Because Ubuntu 18.04 still provides only 3.6 and people may install that way. https://launchpad.net/ubuntu/bionic/+source/mongodb --- monkey/monkey_island/cc/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/main.py b/monkey/monkey_island/cc/main.py index 103280a05..5b9bda8cb 100644 --- a/monkey/monkey_island/cc/main.py +++ b/monkey/monkey_island/cc/main.py @@ -6,7 +6,7 @@ import sys import time import logging -MINIMUM_MONGO_DB_VERSION_REQUIRED = "4.0.0" +MINIMUM_MONGO_DB_VERSION_REQUIRED = "3.6.0" BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) From 74042b4f29127fb8062aab3680b3f1dc7ad2ea79 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 14 Jun 2019 09:15:55 +0300 Subject: [PATCH 13/15] _EXPLOITED_SERVICE var added to win_ms08_067 exploiter --- monkey/infection_monkey/exploit/win_ms08_067.py | 1 + 1 file changed, 1 insertion(+) diff --git a/monkey/infection_monkey/exploit/win_ms08_067.py b/monkey/infection_monkey/exploit/win_ms08_067.py index 41b3820d5..d576af3cd 100644 --- a/monkey/infection_monkey/exploit/win_ms08_067.py +++ b/monkey/infection_monkey/exploit/win_ms08_067.py @@ -153,6 +153,7 @@ class SRVSVC_Exploit(object): class Ms08_067_Exploiter(HostExploiter): _TARGET_OS_TYPE = ['windows'] + _EXPLOITED_SERVICE = 'Microsoft Server Service' _windows_versions = {'Windows Server 2003 3790 Service Pack 2': WindowsVersion.Windows2003_SP2, 'Windows Server 2003 R2 3790 Service Pack 2': WindowsVersion.Windows2003_SP2} From 0422cd32dbecfb3e33635af945af5ce84604dc31 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 14 Jun 2019 15:52:49 +0300 Subject: [PATCH 14/15] Bugfix --- .../attack/technique_reports/T1110.py | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py index 977fb860a..7fe5ac90f 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py @@ -40,6 +40,10 @@ class T1110(AttackTechnique): data = T1110.get_message_and_status(T1110, ScanStatus.SCANNED) else: data = T1110.get_message_and_status(T1110, ScanStatus.UNSCANNED) + + # Remove data with no successful brute force attempts + attempts = [attempt for attempt in attempts if attempt['attempts']] + data.update({'services': attempts, 'title': T1110.technique_title(T1110.tech_id)}) return data @@ -51,33 +55,39 @@ class T1110(AttackTechnique): :return: string with username and used password/hash """ username = attempt['user'] - creds = {'lm_hash': {'type': 'LM hash', 'shown_chars': 5, 'funct': T1110.censor_hash}, - 'ntlm_hash': {'type': 'NTLM hash', 'shown_chars': 20, 'funct': T1110.censor_hash}, - 'ssh_key': {'type': 'SSH key', 'shown_chars': 15, 'funct': T1110.censor_hash}, - 'password': {'type': 'Plaintext password', 'shown_chars': 3, 'funct': T1110.censor_password}} + creds = {'lm_hash': {'type': 'LM hash', 'output': T1110.censor_hash(attempt['lm_hash'])}, + 'ntlm_hash': {'type': 'NTLM hash', 'output': T1110.censor_hash(attempt['ntlm_hash'], 20)}, + 'ssh_key': {'type': 'SSH key', 'output': attempt['ssh_key']}, + 'password': {'type': 'Plaintext password', 'output': T1110.censor_password(attempt['password'])}} for key, cred in creds.items(): if attempt[key]: return '%s ; %s : %s' % (username, cred['type'], - cred['funct'](encryptor.dec(attempt[key]), cred['shown_chars'])) + cred['output']) @staticmethod def censor_password(password, plain_chars=3, secret_chars=5): """ - Obfuscates password by changing characters to * + Decrypts and obfuscates password by changing characters to * :param password: Password or string to obfuscate :param plain_chars: How many plain-text characters should be kept at the start of the string :param secret_chars: How many * symbols should be used to hide the remainder of the password :return: Obfuscated string e.g. Pass**** """ + if not password: + return "" + password = encryptor.dec(password) return password[0:plain_chars] + '*' * secret_chars @staticmethod def censor_hash(hash_, plain_chars=5): """ - Obfuscates hash by only showing a part of it + Decrypts and obfuscates hash by only showing a part of it :param hash_: Hash to obfuscate :param plain_chars: How many chars of hash should be shown :return: Obfuscated string """ + if not hash_: + return "" + hash_ = encryptor.dec(hash_) return hash_[0: plain_chars] + ' ...' From dc2755173d6416a6530aea343f271cbb1d4fd17c Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 18 Jun 2019 14:15:13 +0300 Subject: [PATCH 15/15] Refactored technique report basic data generation methods --- .../attack/technique_reports/__init__.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py index 782ca389d..8d60e963f 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -62,12 +62,16 @@ class AttackTechnique(object): @staticmethod def get_message_and_status(technique, status): + return {'message': technique.get_message_by_status(technique, status), 'status': status.name} + + @staticmethod + def get_message_by_status(technique, status): if status == ScanStatus.UNSCANNED: - return {'message': technique.unscanned_msg, 'status': ScanStatus.UNSCANNED.name} + return technique.unscanned_msg elif status == ScanStatus.SCANNED: - return {'message': technique.scanned_msg, 'status': ScanStatus.SCANNED.name} + return technique.scanned_msg else: - return {'message': technique.used_msg, 'status': ScanStatus.USED.name} + return technique.used_msg @staticmethod def technique_title(technique): @@ -87,11 +91,7 @@ class AttackTechnique(object): data = {} status = AttackTechnique.technique_status(technique.tech_id) title = AttackTechnique.technique_title(technique.tech_id) - data.update({'status': status.name, 'title': title}) - if status == ScanStatus.UNSCANNED: - data.update({'message': technique.unscanned_msg}) - elif status == ScanStatus.SCANNED: - data.update({'message': technique.scanned_msg}) - else: - data.update({'message': technique.used_msg}) + data.update({'status': status.name, + 'title': title, + 'message': technique.get_message_by_status(technique, status)}) return data