From e3f1e204b2d7b4dd577dd9072a5f5b8ad11a6e2f Mon Sep 17 00:00:00 2001 From: electronic Max Date: Fri, 14 Dec 2018 00:31:42 +0000 Subject: [PATCH 01/55] adding requirements.txt ala readme.md --- requirements.txt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..03d35df --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +psycopg2 +scapy +pandas +sklearn +ipdata +Pyshark From 64c8ed84f40ff7a7fdc85490eeac19cfc390c7a2 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Sun, 23 Dec 2018 12:09:59 -0700 Subject: [PATCH 02/55] added command line flags --- requirements.txt | 2 ++ scripts/api.py | 1 + scripts/capture.py | 43 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index 03d35df..8de506c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,5 @@ pandas sklearn ipdata Pyshark +libxml + diff --git a/scripts/api.py b/scripts/api.py index 1502d7b..6acae8c 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -162,6 +162,7 @@ def UpdateImpact(mac, ip, impact): def GetImpact(mac, ip): global impacts if mac in impacts: + # mac is shadowing parent variable here, what if ip in impacts[mac]: return impacts[mac][ip] else: diff --git a/scripts/capture.py b/scripts/capture.py index 47d86a3..549c105 100755 --- a/scripts/capture.py +++ b/scripts/capture.py @@ -4,9 +4,11 @@ import datetime import psycopg2 import re +import argparse #constants COMMIT_INTERVAL = 5 +DEBUG = False #initialise vars timestamp = 0 @@ -79,10 +81,39 @@ def QueuedCommit(packet): queue = [] timestamp = 0 -#configure capture object -capture = pyshark.LiveCapture(interface='1') -capture.set_debug() +def log(*args): + if DEBUG: + print(*args) + + +if __name__=='__main__': + + parser = argparse.ArgumentParser() + parser.add_argument('--interface', dest="interface", type=str, help="Interface to listen to") + parser.add_argument('--cinterval', dest="cinterval", type=int, help="Commit interval in seconds", default=5) + parser.add_argument('--debug', dest='debug', action='store_true') + args = parser.parse_args() + + DEBUG = args.debug + + if args.interface is None: + print(parser.print_help()) + sys.exit(-1) + + log("Configuring capture on ", args.interface) + + if args.cinterval is not None: + COMMIT_INTERVAL = args.cinterval + log("Setting commit interval as ", COMMIT_INTERVAL) + + capture = pyshark.LiveCapture(interface=args.interface) + + if DEBUG: + capture.set_debug() -#start capturing -capture.apply_on_packets(QueuedCommit) #, timeout=30) -capture.close(); + log("Starting capture") + + capture.apply_on_packets(QueuedCommit) #, timeout=30) + capture.close(); + + From 39bebcbcedbbcc2a3cba9e32752d60bebd73cc9a Mon Sep 17 00:00:00 2001 From: electronic Max Date: Sun, 23 Dec 2018 20:34:58 -0700 Subject: [PATCH 03/55] tuple check and updated requirements.txt --- requirements.txt | 3 ++- scripts/capture.py | 6 +----- scripts/loop.py | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/requirements.txt b/requirements.txt index 8de506c..830efce 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,9 @@ -psycopg2 +psycopg2-binary scapy pandas sklearn ipdata Pyshark libxml +requests diff --git a/scripts/capture.py b/scripts/capture.py index 549c105..de582af 100755 --- a/scripts/capture.py +++ b/scripts/capture.py @@ -1,10 +1,6 @@ #! /usr/bin/env python3 -import pyshark -import datetime -import psycopg2 -import re -import argparse +import pyshark, datetime, psycopg2, re, argparse, sys #constants COMMIT_INTERVAL = 5 diff --git a/scripts/loop.py b/scripts/loop.py index 0d15c5f..30253f4 100755 --- a/scripts/loop.py +++ b/scripts/loop.py @@ -150,7 +150,7 @@ def processMacs(): if "errors" not in manufacturer: DB_MANAGER.execute("INSERT INTO devices VALUES(%s, %s, 'unknown')", (mac[0], manufacturer[:20])) else: - DB_MANAGER.execute("INSERT INTO devices VALUES(%s, 'unknown', 'unknown')", (mac[0])) + DB_MANAGER.execute("INSERT INTO devices VALUES(%s, 'unknown', 'unknown')", (mac[0],)) #============ #loop control From 09f9a659efb69459a96f0b0219769572f8d74da2 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Mon, 24 Dec 2018 17:18:34 -0700 Subject: [PATCH 04/55] api debugging --- requirements.txt | 3 + scripts/api.py | 31 +- ui/package-lock.json | 3893 +++++++++++++++++++++--------------------- 3 files changed, 1971 insertions(+), 1956 deletions(-) diff --git a/requirements.txt b/requirements.txt index 830efce..7ba6890 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,7 @@ ipdata Pyshark libxml requests +ipwhois +flask +flask_restful diff --git a/scripts/api.py b/scripts/api.py index 6acae8c..ede02d6 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -99,10 +99,13 @@ def GetBursts(days): #get impact (traffic) of every device/external ip combination for the given time period (in days) def GetImpacts(days): + global geos, ID_POINTER, lastDays + print("GetImpacts: days::", days, " ID>::", ID_POINTER, " lastDays::", lastDays) #we can only keep the cache if we're looking at the same packets as the previous request if days is not lastDays: + print("ResetImpactCache()") ResetImpactCache() #get all packets from the database (if we have cached impacts from before, then only get new packets) @@ -112,8 +115,10 @@ def GetImpacts(days): for packet in packets: #determine if the src or dst is the external ip address - ip_src = local_ip_mask.match(packet[2]) is not None - ip_dst = local_ip_mask.match(packet[3]) is not None + pkt_id, pkt_time, pkt_src, pkt_dst, pkt_mac, pkt_len, pkt_proto, pkt_burst = packet + + ip_src = local_ip_mask.match(pkt_src) is not None + ip_dst = local_ip_mask.match(pkt_dst) is not None ext_ip = None if (ip_src and ip_dst) or (not ip_src and not ip_dst): @@ -121,40 +126,47 @@ def GetImpacts(days): #remember which ip address was external elif ip_src: - ext_ip = packet[3] + ext_ip = pkt_dst else: - ext_ip = packet[2] + ext_ip = pkt_src #make sure we have geo data, then update the impact if ext_ip not in geos: geos[ext_ip] = GetGeo(ext_ip) - UpdateImpact(packet[4], ext_ip, packet[5]) + + print("UpdateImpact ", pkt_mac, ext_ip, pkt_len) + UpdateImpact(pkt_mac, ext_ip, pkt_len) #fast forward the id pointer so we know this packet is cached - if ID_POINTER < packet[0]: - ID_POINTER = packet[0] + if ID_POINTER < pkt_id: + ID_POINTER = pkt_id #build a list of all device/ip impacts and geo data for ip,geo in geos.items(): for mac,_ in ManDev().items(): - item = geo + item = geo.copy() # emax added .copy here() this is so gross item['impact'] = GetImpact(mac, ip) + print("Calling getimpact mac::", mac, " ip::", ip, 'impact result ', item['impact']); item['companyid'] = ip item['appid'] = mac if item['impact'] > 0: result.append(item) lastDays = days + print("result ", json.dumps(result)) return result #shipit #setter method for impacts def UpdateImpact(mac, ip, impact): - global impacts if mac in impacts: + print("updateimpact existing mac ", mac) if ip in impacts[mac]: + print("updateimpact existing ip, updating impact for mac ", mac, " ip ", ip, " impact: ", impacts[mac][ip]) impacts[mac][ip] += impact else: + print("updateimpact no existing ip for mac ", mac, " ip ", ip, " impact: ", impact) impacts[mac][ip] = impact #impact did not exist else: + print("updateimpact unknown mac, creating new entry for ", mac, ip) impacts[mac] = dict() impacts[mac][ip] = impact #impact did not exist @@ -162,7 +174,6 @@ def UpdateImpact(mac, ip, impact): def GetImpact(mac, ip): global impacts if mac in impacts: - # mac is shadowing parent variable here, what if ip in impacts[mac]: return impacts[mac][ip] else: diff --git a/ui/package-lock.json b/ui/package-lock.json index 0071222..6b59842 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -9,7 +9,7 @@ "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-4.3.3.tgz", "integrity": "sha1-tx3dRTZzkp9VCxccypmVKzqqgxw=", "requires": { - "tslib": "1.7.1" + "tslib": "^1.7.1" } }, "@angular/cli": { @@ -20,62 +20,62 @@ "requires": { "@ngtools/json-schema": "1.1.0", "@ngtools/webpack": "1.4.2", - "autoprefixer": "6.7.7", - "chalk": "1.1.3", - "common-tags": "1.8.0", - "css-loader": "0.28.11", - "cssnano": "3.10.0", - "debug": "2.6.8", - "denodeify": "1.2.1", - "diff": "3.3.0", - "ember-cli-normalize-entity-name": "1.0.0", - "ember-cli-string-utils": "1.1.0", - "exports-loader": "0.6.4", - "extract-text-webpack-plugin": "2.1.2", - "file-loader": "0.10.1", - "fs-extra": "2.1.2", - "get-caller-file": "1.0.2", - "glob": "7.1.2", - "html-webpack-plugin": "2.30.1", - "inflection": "1.12.0", - "inquirer": "3.3.0", - "isbinaryfile": "3.0.2", - "istanbul-instrumenter-loader": "2.0.0", - "json-loader": "0.5.7", - "less": "2.7.3", - "less-loader": "4.1.0", - "lodash": "4.17.4", - "memory-fs": "0.4.1", - "minimatch": "3.0.4", - "node-modules-path": "1.0.1", - "node-sass": "4.9.0", - "nopt": "4.0.1", + "autoprefixer": "^6.5.3", + "chalk": "^1.1.3", + "common-tags": "^1.3.1", + "css-loader": "^0.28.1", + "cssnano": "^3.10.0", + "debug": "^2.1.3", + "denodeify": "^1.2.1", + "diff": "^3.1.0", + "ember-cli-normalize-entity-name": "^1.0.0", + "ember-cli-string-utils": "^1.0.0", + "exports-loader": "^0.6.3", + "extract-text-webpack-plugin": "^2.1.0", + "file-loader": "^0.10.0", + "fs-extra": "^2.0.0", + "get-caller-file": "^1.0.0", + "glob": "^7.0.3", + "html-webpack-plugin": "^2.19.0", + "inflection": "^1.7.0", + "inquirer": "^3.0.0", + "isbinaryfile": "^3.0.0", + "istanbul-instrumenter-loader": "^2.0.0", + "json-loader": "^0.5.4", + "less": "^2.7.2", + "less-loader": "^4.0.2", + "lodash": "^4.11.1", + "memory-fs": "^0.4.1", + "minimatch": "^3.0.3", + "node-modules-path": "^1.0.0", + "node-sass": "^4.3.0", + "nopt": "^4.0.1", "opn": "4.0.2", - "portfinder": "1.0.13", - "postcss-loader": "1.3.3", - "postcss-url": "5.1.2", - "raw-loader": "0.5.1", - "resolve": "1.4.0", - "rimraf": "2.6.1", - "rsvp": "3.6.2", - "rxjs": "5.4.2", - "sass-loader": "6.0.7", - "script-loader": "0.7.2", - "semver": "5.4.1", - "silent-error": "1.1.0", - "source-map-loader": "0.2.3", - "style-loader": "0.13.2", - "stylus": "0.54.5", - "stylus-loader": "3.0.2", + "portfinder": "~1.0.12", + "postcss-loader": "^1.3.3", + "postcss-url": "^5.1.2", + "raw-loader": "^0.5.1", + "resolve": "^1.1.7", + "rimraf": "^2.5.3", + "rsvp": "^3.0.17", + "rxjs": "^5.0.1", + "sass-loader": "^6.0.3", + "script-loader": "^0.7.0", + "semver": "^5.1.0", + "silent-error": "^1.0.0", + "source-map-loader": "^0.2.0", + "style-loader": "^0.13.1", + "stylus": "^0.54.5", + "stylus-loader": "^3.0.1", "temp": "0.8.3", - "typescript": "2.3.4", - "url-loader": "0.5.9", - "walk-sync": "0.3.2", - "webpack": "2.4.1", - "webpack-dev-middleware": "1.12.2", - "webpack-dev-server": "2.4.5", - "webpack-merge": "2.6.1", - "zone.js": "0.8.16" + "typescript": ">=2.0.0 <2.4.0", + "url-loader": "^0.5.7", + "walk-sync": "^0.3.1", + "webpack": "~2.4.0", + "webpack-dev-middleware": "^1.10.2", + "webpack-dev-server": "~2.4.5", + "webpack-merge": "^2.4.0", + "zone.js": "^0.8.4" } }, "@angular/common": { @@ -83,7 +83,7 @@ "resolved": "https://registry.npmjs.org/@angular/common/-/common-4.3.3.tgz", "integrity": "sha1-H6++ozr066TN3YZ371eg92pQSIw=", "requires": { - "tslib": "1.7.1" + "tslib": "^1.7.1" } }, "@angular/compiler": { @@ -91,7 +91,7 @@ "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-4.3.3.tgz", "integrity": "sha1-jBWC/iinhEATJeUaBKmza2cSAz4=", "requires": { - "tslib": "1.7.1" + "tslib": "^1.7.1" } }, "@angular/compiler-cli": { @@ -101,8 +101,8 @@ "dev": true, "requires": { "@angular/tsc-wrapped": "4.3.3", - "minimist": "1.2.0", - "reflect-metadata": "0.1.10" + "minimist": "^1.2.0", + "reflect-metadata": "^0.1.2" }, "dependencies": { "minimist": { @@ -118,7 +118,7 @@ "resolved": "https://registry.npmjs.org/@angular/core/-/core-4.3.3.tgz", "integrity": "sha1-jmp2kUZh20B/otiN0kQcTAFv9iU=", "requires": { - "tslib": "1.7.1" + "tslib": "^1.7.1" } }, "@angular/forms": { @@ -126,7 +126,7 @@ "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-4.3.3.tgz", "integrity": "sha1-CRKuu4GKF29OCLffDsn9QCHvy2s=", "requires": { - "tslib": "1.7.1" + "tslib": "^1.7.1" } }, "@angular/http": { @@ -134,7 +134,7 @@ "resolved": "https://registry.npmjs.org/@angular/http/-/http-4.3.3.tgz", "integrity": "sha1-y+NjkBA2K2gQdvC2Bmc6DmL9lA0=", "requires": { - "tslib": "1.7.1" + "tslib": "^1.7.1" } }, "@angular/language-service": { @@ -148,7 +148,7 @@ "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-4.3.3.tgz", "integrity": "sha1-Yjtll5TweQltT3aFtL68tJ47mnE=", "requires": { - "tslib": "1.7.1" + "tslib": "^1.7.1" } }, "@angular/platform-browser-dynamic": { @@ -156,7 +156,7 @@ "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.3.3.tgz", "integrity": "sha1-sWwJ3+l7W4PaoFgbIz9eo8Pu+zo=", "requires": { - "tslib": "1.7.1" + "tslib": "^1.7.1" } }, "@angular/router": { @@ -164,7 +164,7 @@ "resolved": "https://registry.npmjs.org/@angular/router/-/router-4.3.3.tgz", "integrity": "sha1-8/kATsMbT+TRVHfh5j4RjfNq1G0=", "requires": { - "tslib": "1.7.1" + "tslib": "^1.7.1" } }, "@angular/tsc-wrapped": { @@ -173,7 +173,7 @@ "integrity": "sha1-xYkPdDZkvmS3nCAK5unaXSqAH1s=", "dev": true, "requires": { - "tsickle": "0.21.6" + "tsickle": "^0.21.0" } }, "@ngtools/json-schema": { @@ -188,10 +188,10 @@ "integrity": "sha1-E2qbOVnpUCnXy2f6z/8QoD0t3b8=", "dev": true, "requires": { - "enhanced-resolve": "3.4.1", - "loader-utils": "1.1.0", - "magic-string": "0.19.1", - "source-map": "0.5.6" + "enhanced-resolve": "^3.1.0", + "loader-utils": "^1.0.2", + "magic-string": "^0.19.0", + "source-map": "^0.5.6" } }, "@types/jasmine": { @@ -230,7 +230,7 @@ "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", "dev": true, "requires": { - "mime-types": "2.1.16", + "mime-types": "~2.1.11", "negotiator": "0.6.1" } }, @@ -246,7 +246,7 @@ "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", "dev": true, "requires": { - "acorn": "4.0.13" + "acorn": "^4.0.3" }, "dependencies": { "acorn": { @@ -275,8 +275,8 @@ "integrity": "sha1-1t4Q1a9hMtW9aSQn1G/FOFOQlMc=", "dev": true, "requires": { - "extend": "3.0.1", - "semver": "5.0.3" + "extend": "~3.0.0", + "semver": "~5.0.1" }, "dependencies": { "semver": { @@ -293,10 +293,10 @@ "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "dev": true, "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.1.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" } }, "ajv-keywords": { @@ -311,9 +311,9 @@ "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, "requires": { - "kind-of": "3.2.2", - "longest": "1.0.1", - "repeat-string": "1.6.1" + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" } }, "alphanum-sort": { @@ -358,8 +358,8 @@ "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", "dev": true, "requires": { - "micromatch": "2.3.11", - "normalize-path": "2.1.1" + "micromatch": "^2.1.5", + "normalize-path": "^2.0.0" } }, "app-root-path": { @@ -374,7 +374,7 @@ "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", "dev": true, "requires": { - "default-require-extensions": "1.0.0" + "default-require-extensions": "^1.0.0" } }, "aproba": { @@ -389,8 +389,8 @@ "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.3" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, "argparse": { @@ -399,7 +399,7 @@ "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", "dev": true, "requires": { - "sprintf-js": "1.0.3" + "sprintf-js": "~1.0.2" } }, "arr-diff": { @@ -408,7 +408,7 @@ "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, "requires": { - "arr-flatten": "1.1.0" + "arr-flatten": "^1.0.1" } }, "arr-flatten": { @@ -448,7 +448,7 @@ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", "dev": true, "requires": { - "array-uniq": "1.0.3" + "array-uniq": "^1.0.1" } }, "array-uniq": { @@ -494,9 +494,9 @@ "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", "dev": true, "requires": { - "bn.js": "4.11.8", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.1" + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" } }, "assert": { @@ -543,7 +543,7 @@ "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", "dev": true, "requires": { - "lodash": "4.17.4" + "lodash": "^4.14.0" } }, "async-each": { @@ -577,12 +577,12 @@ "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", "dev": true, "requires": { - "browserslist": "1.7.7", - "caniuse-db": "1.0.30000859", - "normalize-range": "0.1.2", - "num2fraction": "1.2.2", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" + "browserslist": "^1.7.6", + "caniuse-db": "^1.0.30000634", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^5.2.16", + "postcss-value-parser": "^3.2.3" } }, "aws-sign2": { @@ -603,9 +603,9 @@ "integrity": "sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ=", "dev": true, "requires": { - "chalk": "1.1.3", - "esutils": "2.0.2", - "js-tokens": "3.0.2" + "chalk": "^1.1.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" } }, "babel-generator": { @@ -614,14 +614,14 @@ "integrity": "sha1-M6GvcNXyiQrrRlpKd5PB32qeqfw=", "dev": true, "requires": { - "babel-messages": "6.23.0", - "babel-runtime": "6.25.0", - "babel-types": "6.25.0", - "detect-indent": "4.0.0", - "jsesc": "1.3.0", - "lodash": "4.17.4", - "source-map": "0.5.6", - "trim-right": "1.0.1" + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-types": "^6.25.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.2.0", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" }, "dependencies": { "jsesc": { @@ -638,7 +638,7 @@ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "dev": true, "requires": { - "babel-runtime": "6.25.0" + "babel-runtime": "^6.22.0" } }, "babel-runtime": { @@ -647,8 +647,8 @@ "integrity": "sha1-M7mOql1IK7AajRqmtDetKwGuxBw=", "dev": true, "requires": { - "core-js": "2.4.1", - "regenerator-runtime": "0.10.5" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.10.0" } }, "babel-template": { @@ -657,11 +657,11 @@ "integrity": "sha1-ZlJBFmt8KqTGGdceGSlpVSsQwHE=", "dev": true, "requires": { - "babel-runtime": "6.25.0", - "babel-traverse": "6.25.0", - "babel-types": "6.25.0", - "babylon": "6.17.4", - "lodash": "4.17.4" + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.25.0", + "babel-types": "^6.25.0", + "babylon": "^6.17.2", + "lodash": "^4.2.0" } }, "babel-traverse": { @@ -670,15 +670,15 @@ "integrity": "sha1-IldJfi/NGbie3BPEyROB+VEklvE=", "dev": true, "requires": { - "babel-code-frame": "6.22.0", - "babel-messages": "6.23.0", - "babel-runtime": "6.25.0", - "babel-types": "6.25.0", - "babylon": "6.17.4", - "debug": "2.6.8", - "globals": "9.18.0", - "invariant": "2.2.2", - "lodash": "4.17.4" + "babel-code-frame": "^6.22.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-types": "^6.25.0", + "babylon": "^6.17.2", + "debug": "^2.2.0", + "globals": "^9.0.0", + "invariant": "^2.2.0", + "lodash": "^4.2.0" } }, "babel-types": { @@ -687,10 +687,10 @@ "integrity": "sha1-cK+ySNVmDl0Y+BHZHIMDtUE0oY4=", "dev": true, "requires": { - "babel-runtime": "6.25.0", - "esutils": "2.0.2", - "lodash": "4.17.4", - "to-fast-properties": "1.0.3" + "babel-runtime": "^6.22.0", + "esutils": "^2.0.2", + "lodash": "^4.2.0", + "to-fast-properties": "^1.0.1" } }, "babylon": { @@ -717,13 +717,13 @@ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, "requires": { - "cache-base": "1.0.1", - "class-utils": "0.3.6", - "component-emitter": "1.2.1", - "define-property": "1.0.0", - "isobject": "3.0.1", - "mixin-deep": "1.3.1", - "pascalcase": "0.1.1" + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" }, "dependencies": { "component-emitter": { @@ -738,7 +738,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "is-accessor-descriptor": { @@ -747,7 +747,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -756,7 +756,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -765,9 +765,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "kind-of": { @@ -809,7 +809,7 @@ "dev": true, "optional": true, "requires": { - "tweetnacl": "0.14.5" + "tweetnacl": "^0.14.3" } }, "better-assert": { @@ -846,7 +846,7 @@ "dev": true, "optional": true, "requires": { - "inherits": "2.0.3" + "inherits": "~2.0.0" } }, "blocking-proxy": { @@ -855,7 +855,7 @@ "integrity": "sha1-RikF4Nz76pcPQao3Ij3anAexkSs=", "dev": true, "requires": { - "minimist": "1.2.0" + "minimist": "^1.2.0" }, "dependencies": { "minimist": { @@ -885,15 +885,15 @@ "dev": true, "requires": { "bytes": "2.4.0", - "content-type": "1.0.2", + "content-type": "~1.0.2", "debug": "2.6.7", - "depd": "1.1.1", - "http-errors": "1.6.1", + "depd": "~1.1.0", + "http-errors": "~1.6.1", "iconv-lite": "0.4.15", - "on-finished": "2.3.0", + "on-finished": "~2.3.0", "qs": "6.4.0", - "raw-body": "2.2.0", - "type-is": "1.6.15" + "raw-body": "~2.2.0", + "type-is": "~1.6.15" }, "dependencies": { "bytes": { @@ -931,7 +931,7 @@ "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", "dev": true, "requires": { - "hoek": "2.16.3" + "hoek": "2.x.x" } }, "brace-expansion": { @@ -940,7 +940,7 @@ "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", "dev": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" }, "dependencies": { @@ -958,9 +958,9 @@ "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" } }, "brorand": { @@ -975,12 +975,12 @@ "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { - "buffer-xor": "1.0.3", - "cipher-base": "1.0.4", - "create-hash": "1.2.0", - "evp_bytestokey": "1.0.3", - "inherits": "2.0.3", - "safe-buffer": "5.1.1" + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, "browserify-cipher": { @@ -989,9 +989,9 @@ "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", "dev": true, "requires": { - "browserify-aes": "1.2.0", - "browserify-des": "1.0.1", - "evp_bytestokey": "1.0.3" + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" } }, "browserify-des": { @@ -1000,9 +1000,9 @@ "integrity": "sha512-zy0Cobe3hhgpiOM32Tj7KQ3Vl91m0njwsjzZQK1L+JDf11dzP9qIvjreVinsvXrgfjhStXwUWAEpB9D7Gwmayw==", "dev": true, "requires": { - "cipher-base": "1.0.4", - "des.js": "1.0.0", - "inherits": "2.0.3" + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1" } }, "browserify-rsa": { @@ -1011,8 +1011,8 @@ "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { - "bn.js": "4.11.8", - "randombytes": "2.0.6" + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" } }, "browserify-sign": { @@ -1021,13 +1021,13 @@ "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", "dev": true, "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.2.0", - "create-hmac": "1.1.7", - "elliptic": "6.4.0", - "inherits": "2.0.3", - "parse-asn1": "5.1.1" + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" } }, "browserify-zlib": { @@ -1036,7 +1036,7 @@ "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", "dev": true, "requires": { - "pako": "1.0.6" + "pako": "~1.0.5" } }, "browserslist": { @@ -1045,8 +1045,8 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "1.0.30000859", - "electron-to-chromium": "1.3.50" + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" } }, "buffer": { @@ -1055,9 +1055,9 @@ "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { - "base64-js": "1.3.0", - "ieee754": "1.1.12", - "isarray": "1.0.0" + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" } }, "buffer-xor": { @@ -1090,15 +1090,15 @@ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, "requires": { - "collection-visit": "1.0.0", - "component-emitter": "1.2.1", - "get-value": "2.0.6", - "has-value": "1.0.0", - "isobject": "3.0.1", - "set-value": "2.0.0", - "to-object-path": "0.3.0", - "union-value": "1.0.0", - "unset-value": "1.0.0" + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" }, "dependencies": { "component-emitter": { @@ -1121,8 +1121,8 @@ "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", "dev": true, "requires": { - "no-case": "2.3.2", - "upper-case": "1.1.3" + "no-case": "^2.2.0", + "upper-case": "^1.1.1" } }, "camelcase": { @@ -1139,8 +1139,8 @@ "dev": true, "optional": true, "requires": { - "camelcase": "2.1.1", - "map-obj": "1.0.1" + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" } }, "caniuse-api": { @@ -1149,10 +1149,10 @@ "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", "dev": true, "requires": { - "browserslist": "1.7.7", - "caniuse-db": "1.0.30000859", - "lodash.memoize": "4.1.2", - "lodash.uniq": "4.5.0" + "browserslist": "^1.3.6", + "caniuse-db": "^1.0.30000529", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" } }, "caniuse-db": { @@ -1173,8 +1173,8 @@ "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", "dev": true, "requires": { - "align-text": "0.1.4", - "lazy-cache": "1.0.4" + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" }, "dependencies": { "lazy-cache": { @@ -1191,11 +1191,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" }, "dependencies": { "supports-color": { @@ -1218,15 +1218,15 @@ "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", "dev": true, "requires": { - "anymatch": "1.3.2", - "async-each": "1.0.1", - "fsevents": "1.1.2", - "glob-parent": "2.0.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "2.0.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0" + "anymatch": "^1.3.0", + "async-each": "^1.0.0", + "fsevents": "^1.0.0", + "glob-parent": "^2.0.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^2.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0" } }, "cipher-base": { @@ -1235,8 +1235,8 @@ "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", "dev": true, "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, "clap": { @@ -1245,7 +1245,7 @@ "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", "dev": true, "requires": { - "chalk": "1.1.3" + "chalk": "^1.1.3" } }, "class-utils": { @@ -1254,10 +1254,10 @@ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "requires": { - "arr-union": "3.1.0", - "define-property": "0.2.5", - "isobject": "3.0.1", - "static-extend": "0.1.2" + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" }, "dependencies": { "define-property": { @@ -1266,7 +1266,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } } } @@ -1277,7 +1277,7 @@ "integrity": "sha1-Ls3xRaujj1R0DybO/Q/z4D4SXWo=", "dev": true, "requires": { - "source-map": "0.5.6" + "source-map": "0.5.x" } }, "cli-cursor": { @@ -1286,7 +1286,7 @@ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", "dev": true, "requires": { - "restore-cursor": "2.0.0" + "restore-cursor": "^2.0.0" } }, "cli-width": { @@ -1301,9 +1301,9 @@ "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "dev": true, "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" }, "dependencies": { "is-fullwidth-code-point": { @@ -1312,7 +1312,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "string-width": { @@ -1321,9 +1321,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } } } @@ -1340,10 +1340,10 @@ "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==", "dev": true, "requires": { - "for-own": "1.0.0", - "is-plain-object": "2.0.4", - "kind-of": "6.0.2", - "shallow-clone": "1.0.0" + "for-own": "^1.0.0", + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.0", + "shallow-clone": "^1.0.0" }, "dependencies": { "kind-of": { @@ -1366,7 +1366,7 @@ "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", "dev": true, "requires": { - "q": "1.5.1" + "q": "^1.1.2" } }, "code-point-at": { @@ -1381,12 +1381,12 @@ "integrity": "sha1-uma3sqpWT+n0XWAEtAA60s8RaCg=", "dev": true, "requires": { - "app-root-path": "2.0.1", - "css-selector-tokenizer": "0.7.0", - "cssauron": "1.4.0", - "semver-dsl": "1.0.1", - "source-map": "0.5.6", - "sprintf-js": "1.0.3" + "app-root-path": "^2.0.1", + "css-selector-tokenizer": "^0.7.0", + "cssauron": "^1.4.0", + "semver-dsl": "^1.0.1", + "source-map": "^0.5.6", + "sprintf-js": "^1.0.3" } }, "collection-visit": { @@ -1395,8 +1395,8 @@ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { - "map-visit": "1.0.0", - "object-visit": "1.0.1" + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" } }, "color": { @@ -1405,9 +1405,9 @@ "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", "dev": true, "requires": { - "clone": "1.0.4", - "color-convert": "1.9.2", - "color-string": "0.3.0" + "clone": "^1.0.2", + "color-convert": "^1.3.0", + "color-string": "^0.3.0" } }, "color-convert": { @@ -1431,7 +1431,7 @@ "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", "dev": true, "requires": { - "color-name": "1.1.1" + "color-name": "^1.0.0" } }, "colorbrewer": { @@ -1445,9 +1445,9 @@ "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", "dev": true, "requires": { - "color": "0.11.4", + "color": "^0.11.0", "css-color-names": "0.0.4", - "has": "1.0.3" + "has": "^1.0.1" } }, "colors": { @@ -1462,7 +1462,7 @@ "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=", "dev": true, "requires": { - "lodash": "4.17.4" + "lodash": "^4.5.0" } }, "combined-stream": { @@ -1471,7 +1471,7 @@ "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", "dev": true, "requires": { - "delayed-stream": "1.0.0" + "delayed-stream": "~1.0.0" } }, "commander": { @@ -1509,7 +1509,7 @@ "integrity": "sha1-MmxfUH+7BV9UEWeCuWmoG2einac=", "dev": true, "requires": { - "mime-db": "1.34.0" + "mime-db": ">= 1.34.0 < 2" }, "dependencies": { "mime-db": { @@ -1526,13 +1526,13 @@ "integrity": "sha1-qv+81qr4VLROuygDU9WtFlH1mmk=", "dev": true, "requires": { - "accepts": "1.3.5", + "accepts": "~1.3.4", "bytes": "3.0.0", - "compressible": "2.0.14", + "compressible": "~2.0.13", "debug": "2.6.9", - "on-headers": "1.0.1", + "on-headers": "~1.0.1", "safe-buffer": "5.1.1", - "vary": "1.1.2" + "vary": "~1.1.2" }, "dependencies": { "accepts": { @@ -1541,7 +1541,7 @@ "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "dev": true, "requires": { - "mime-types": "2.1.18", + "mime-types": "~2.1.18", "negotiator": "0.6.1" } }, @@ -1566,7 +1566,7 @@ "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "dev": true, "requires": { - "mime-db": "1.33.0" + "mime-db": "~1.33.0" } } } @@ -1585,7 +1585,7 @@ "requires": { "debug": "2.6.8", "finalhandler": "1.0.4", - "parseurl": "1.3.1", + "parseurl": "~1.3.1", "utils-merge": "1.0.0" } }, @@ -1601,7 +1601,7 @@ "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", "dev": true, "requires": { - "date-now": "0.1.4" + "date-now": "^0.1.4" } }, "console-control-strings": { @@ -1669,13 +1669,13 @@ "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", "dev": true, "requires": { - "is-directory": "0.3.1", - "js-yaml": "3.7.0", - "minimist": "1.2.0", - "object-assign": "4.1.1", - "os-homedir": "1.0.2", - "parse-json": "2.2.0", - "require-from-string": "1.2.1" + "is-directory": "^0.3.1", + "js-yaml": "^3.4.3", + "minimist": "^1.2.0", + "object-assign": "^4.1.0", + "os-homedir": "^1.0.1", + "parse-json": "^2.2.0", + "require-from-string": "^1.1.0" }, "dependencies": { "minimist": { @@ -1692,8 +1692,8 @@ "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", "dev": true, "requires": { - "bn.js": "4.11.8", - "elliptic": "6.4.0" + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" } }, "create-hash": { @@ -1702,11 +1702,11 @@ "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { - "cipher-base": "1.0.4", - "inherits": "2.0.3", - "md5.js": "1.3.4", - "ripemd160": "2.0.2", - "sha.js": "2.4.11" + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" } }, "create-hmac": { @@ -1715,12 +1715,12 @@ "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { - "cipher-base": "1.0.4", - "create-hash": "1.2.0", - "inherits": "2.0.3", - "ripemd160": "2.0.2", - "safe-buffer": "5.1.1", - "sha.js": "2.4.11" + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" } }, "cross-spawn": { @@ -1730,8 +1730,8 @@ "dev": true, "optional": true, "requires": { - "lru-cache": "4.1.3", - "which": "1.3.0" + "lru-cache": "^4.0.1", + "which": "^1.2.9" } }, "cryptiles": { @@ -1740,7 +1740,7 @@ "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", "dev": true, "requires": { - "boom": "2.10.1" + "boom": "2.x.x" } }, "crypto-browserify": { @@ -1749,17 +1749,17 @@ "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", "dev": true, "requires": { - "browserify-cipher": "1.0.1", - "browserify-sign": "4.0.4", - "create-ecdh": "4.0.3", - "create-hash": "1.2.0", - "create-hmac": "1.1.7", - "diffie-hellman": "5.0.3", - "inherits": "2.0.3", - "pbkdf2": "3.0.16", - "public-encrypt": "4.0.2", - "randombytes": "2.0.6", - "randomfill": "1.0.4" + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" } }, "css-color-names": { @@ -1774,20 +1774,20 @@ "integrity": "sha512-wovHgjAx8ZIMGSL8pTys7edA1ClmzxHeY6n/d97gg5odgsxEgKjULPR0viqyC+FWMCL9sfqoC/QCUBo62tLvPg==", "dev": true, "requires": { - "babel-code-frame": "6.26.0", - "css-selector-tokenizer": "0.7.0", - "cssnano": "3.10.0", - "icss-utils": "2.1.0", - "loader-utils": "1.1.0", - "lodash.camelcase": "4.3.0", - "object-assign": "4.1.1", - "postcss": "5.2.18", - "postcss-modules-extract-imports": "1.2.0", - "postcss-modules-local-by-default": "1.2.0", - "postcss-modules-scope": "1.1.0", - "postcss-modules-values": "1.3.0", - "postcss-value-parser": "3.3.0", - "source-list-map": "2.0.0" + "babel-code-frame": "^6.26.0", + "css-selector-tokenizer": "^0.7.0", + "cssnano": "^3.10.0", + "icss-utils": "^2.1.0", + "loader-utils": "^1.0.2", + "lodash.camelcase": "^4.3.0", + "object-assign": "^4.1.1", + "postcss": "^5.0.6", + "postcss-modules-extract-imports": "^1.2.0", + "postcss-modules-local-by-default": "^1.2.0", + "postcss-modules-scope": "^1.1.0", + "postcss-modules-values": "^1.3.0", + "postcss-value-parser": "^3.3.0", + "source-list-map": "^2.0.0" }, "dependencies": { "babel-code-frame": { @@ -1796,9 +1796,9 @@ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { - "chalk": "1.1.3", - "esutils": "2.0.2", - "js-tokens": "3.0.2" + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" } } } @@ -1815,10 +1815,10 @@ "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { - "boolbase": "1.0.0", - "css-what": "2.1.0", + "boolbase": "~1.0.0", + "css-what": "2.1", "domutils": "1.5.1", - "nth-check": "1.0.1" + "nth-check": "~1.0.1" } }, "css-selector-tokenizer": { @@ -1827,9 +1827,9 @@ "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", "dev": true, "requires": { - "cssesc": "0.1.0", - "fastparse": "1.1.1", - "regexpu-core": "1.0.0" + "cssesc": "^0.1.0", + "fastparse": "^1.1.1", + "regexpu-core": "^1.0.0" } }, "css-what": { @@ -1844,7 +1844,7 @@ "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", "dev": true, "requires": { - "through": "2.3.8" + "through": "X.X.X" } }, "cssesc": { @@ -1859,38 +1859,38 @@ "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", "dev": true, "requires": { - "autoprefixer": "6.7.7", - "decamelize": "1.2.0", - "defined": "1.0.0", - "has": "1.0.3", - "object-assign": "4.1.1", - "postcss": "5.2.18", - "postcss-calc": "5.3.1", - "postcss-colormin": "2.2.2", - "postcss-convert-values": "2.6.1", - "postcss-discard-comments": "2.0.4", - "postcss-discard-duplicates": "2.1.0", - "postcss-discard-empty": "2.1.0", - "postcss-discard-overridden": "0.1.1", - "postcss-discard-unused": "2.2.3", - "postcss-filter-plugins": "2.0.3", - "postcss-merge-idents": "2.1.7", - "postcss-merge-longhand": "2.0.2", - "postcss-merge-rules": "2.1.2", - "postcss-minify-font-values": "1.0.5", - "postcss-minify-gradients": "1.0.5", - "postcss-minify-params": "1.2.2", - "postcss-minify-selectors": "2.1.1", - "postcss-normalize-charset": "1.1.1", - "postcss-normalize-url": "3.0.8", - "postcss-ordered-values": "2.2.3", - "postcss-reduce-idents": "2.4.0", - "postcss-reduce-initial": "1.0.1", - "postcss-reduce-transforms": "1.0.4", - "postcss-svgo": "2.1.6", - "postcss-unique-selectors": "2.0.2", - "postcss-value-parser": "3.3.0", - "postcss-zindex": "2.2.0" + "autoprefixer": "^6.3.1", + "decamelize": "^1.1.2", + "defined": "^1.0.0", + "has": "^1.0.1", + "object-assign": "^4.0.1", + "postcss": "^5.0.14", + "postcss-calc": "^5.2.0", + "postcss-colormin": "^2.1.8", + "postcss-convert-values": "^2.3.4", + "postcss-discard-comments": "^2.0.4", + "postcss-discard-duplicates": "^2.0.1", + "postcss-discard-empty": "^2.0.1", + "postcss-discard-overridden": "^0.1.1", + "postcss-discard-unused": "^2.2.1", + "postcss-filter-plugins": "^2.0.0", + "postcss-merge-idents": "^2.1.5", + "postcss-merge-longhand": "^2.0.1", + "postcss-merge-rules": "^2.0.3", + "postcss-minify-font-values": "^1.0.2", + "postcss-minify-gradients": "^1.0.1", + "postcss-minify-params": "^1.0.4", + "postcss-minify-selectors": "^2.0.4", + "postcss-normalize-charset": "^1.1.0", + "postcss-normalize-url": "^3.0.7", + "postcss-ordered-values": "^2.1.0", + "postcss-reduce-idents": "^2.2.2", + "postcss-reduce-initial": "^1.0.0", + "postcss-reduce-transforms": "^1.0.3", + "postcss-svgo": "^2.1.1", + "postcss-unique-selectors": "^2.0.2", + "postcss-value-parser": "^3.2.3", + "postcss-zindex": "^2.0.1" } }, "csso": { @@ -1899,8 +1899,8 @@ "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", "dev": true, "requires": { - "clap": "1.2.3", - "source-map": "0.5.6" + "clap": "^1.0.9", + "source-map": "^0.5.3" } }, "currently-unhandled": { @@ -1910,7 +1910,7 @@ "dev": true, "optional": true, "requires": { - "array-find-index": "1.0.2" + "array-find-index": "^1.0.1" } }, "custom-event": { @@ -1971,11 +1971,11 @@ "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.4.tgz", "integrity": "sha1-AMLyOAGfJPbAoZSibUGhUw/+e8Q=", "requires": { - "d3-dispatch": "1.0.3", - "d3-drag": "1.1.1", - "d3-interpolate": "1.1.5", - "d3-selection": "1.1.0", - "d3-transition": "1.1.0" + "d3-dispatch": "1", + "d3-drag": "1", + "d3-interpolate": "1", + "d3-selection": "1", + "d3-transition": "1" } }, "d3-chord": { @@ -1983,8 +1983,8 @@ "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.4.tgz", "integrity": "sha1-fexPC6iG9xP+ERxF92NBT290yiw=", "requires": { - "d3-array": "1.2.0", - "d3-path": "1.0.5" + "d3-array": "1", + "d3-path": "1" } }, "d3-collection": { @@ -2007,8 +2007,8 @@ "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.1.1.tgz", "integrity": "sha512-51aazbUuZZhPZzXv9xxwPOJTeDSVv8cXNd8oFxqJyR8ZBD9yLd09CFGSDSm3ArViHg2D5Wo1qCaKl7Efj/qchg==", "requires": { - "d3-dispatch": "1.0.3", - "d3-selection": "1.1.0" + "d3-dispatch": "1", + "d3-selection": "1" } }, "d3-dsv": { @@ -2016,9 +2016,9 @@ "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.0.5.tgz", "integrity": "sha1-QZ99tH9ih4n8P9tjbmeESdCCETY=", "requires": { - "commander": "2.11.0", - "iconv-lite": "0.4.18", - "rw": "1.3.3" + "commander": "2", + "iconv-lite": "0.4", + "rw": "1" } }, "d3-ease": { @@ -2031,10 +2031,10 @@ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.0.6.tgz", "integrity": "sha1-6n4bdzDiZkzTFPWU1nGMV8wTK3k=", "requires": { - "d3-collection": "1.0.4", - "d3-dispatch": "1.0.3", - "d3-quadtree": "1.0.3", - "d3-timer": "1.0.6" + "d3-collection": "1", + "d3-dispatch": "1", + "d3-quadtree": "1", + "d3-timer": "1" } }, "d3-format": { @@ -2047,7 +2047,7 @@ "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.6.4.tgz", "integrity": "sha1-8g4eRhyxhF9ai+Vatvh2VCp+MZk=", "requires": { - "d3-array": "1.2.0" + "d3-array": "1" } }, "d3-hierarchy": { @@ -2060,7 +2060,7 @@ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.1.5.tgz", "integrity": "sha1-aeCZ/zkhRxblY8muw+qdHqS4p58=", "requires": { - "d3-color": "1.0.3" + "d3-color": "1" } }, "d3-path": { @@ -2093,10 +2093,10 @@ "resolved": "https://registry.npmjs.org/d3-request/-/d3-request-1.0.5.tgz", "integrity": "sha1-TarpRtHdDVff4B8CKVY1SVjVHyM=", "requires": { - "d3-collection": "1.0.4", - "d3-dispatch": "1.0.3", - "d3-dsv": "1.0.5", - "xmlhttprequest": "1.8.0" + "d3-collection": "1", + "d3-dispatch": "1", + "d3-dsv": "1", + "xmlhttprequest": "1" } }, "d3-scale": { @@ -2104,13 +2104,13 @@ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.6.tgz", "integrity": "sha1-vOGdqA06DPQiyVQ64zIghiILNO0=", "requires": { - "d3-array": "1.2.0", - "d3-collection": "1.0.4", - "d3-color": "1.0.3", - "d3-format": "1.2.0", - "d3-interpolate": "1.1.5", - "d3-time": "1.0.7", - "d3-time-format": "2.0.5" + "d3-array": "^1.2.0", + "d3-collection": "1", + "d3-color": "1", + "d3-format": "1", + "d3-interpolate": "1", + "d3-time": "1", + "d3-time-format": "2" } }, "d3-selection": { @@ -2123,7 +2123,7 @@ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.2.0.tgz", "integrity": "sha1-RdAVOPBkuv0F6j1tLLdI/YxB93c=", "requires": { - "d3-path": "1.0.5" + "d3-path": "1" } }, "d3-time": { @@ -2136,7 +2136,7 @@ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.0.5.tgz", "integrity": "sha1-nXeAIE98kRnJFwsaVttN6aivly4=", "requires": { - "d3-time": "1.0.7" + "d3-time": "1" } }, "d3-timer": { @@ -2149,12 +2149,12 @@ "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.1.0.tgz", "integrity": "sha1-z8hcdOUjkyQpBUZiNXKZBWDDlm8=", "requires": { - "d3-color": "1.0.3", - "d3-dispatch": "1.0.3", - "d3-ease": "1.0.3", - "d3-interpolate": "1.1.5", - "d3-selection": "1.1.0", - "d3-timer": "1.0.6" + "d3-color": "1", + "d3-dispatch": "1", + "d3-ease": "1", + "d3-interpolate": "1", + "d3-selection": "^1.1.0", + "d3-timer": "1" } }, "d3-voronoi": { @@ -2167,11 +2167,11 @@ "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.5.0.tgz", "integrity": "sha512-tc/ONeSUVuwHczjjK4jQPd0T1iZ+lfsz8TbguAAceY5qs057hp4WLglkPWValkuVjCyeGpqiA2iTm8S++NJ84w==", "requires": { - "d3-dispatch": "1.0.3", - "d3-drag": "1.1.1", - "d3-interpolate": "1.1.5", - "d3-selection": "1.1.0", - "d3-transition": "1.1.0" + "d3-dispatch": "1", + "d3-drag": "1", + "d3-interpolate": "1", + "d3-selection": "1", + "d3-transition": "1" } }, "dashdash": { @@ -2180,7 +2180,7 @@ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" }, "dependencies": { "assert-plus": { @@ -2224,7 +2224,7 @@ "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", "dev": true, "requires": { - "strip-bom": "2.0.0" + "strip-bom": "^2.0.0" } }, "define-property": { @@ -2233,8 +2233,8 @@ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "requires": { - "is-descriptor": "1.0.2", - "isobject": "3.0.1" + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" }, "dependencies": { "is-accessor-descriptor": { @@ -2243,7 +2243,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -2252,7 +2252,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -2261,9 +2261,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "kind-of": { @@ -2286,13 +2286,13 @@ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", "dev": true, "requires": { - "globby": "5.0.0", - "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.0", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "rimraf": "2.6.1" + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" } }, "delayed-stream": { @@ -2325,8 +2325,8 @@ "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", "dev": true, "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.1" + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" } }, "destroy": { @@ -2341,7 +2341,7 @@ "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", "dev": true, "requires": { - "repeating": "2.0.1" + "repeating": "^2.0.0" } }, "detect-node": { @@ -2368,9 +2368,9 @@ "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { - "bn.js": "4.11.8", - "miller-rabin": "4.0.1", - "randombytes": "2.0.6" + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" } }, "directory-encoder": { @@ -2379,9 +2379,9 @@ "integrity": "sha1-WbTiqk8lQi9sY7UntGL14tDdLFg=", "dev": true, "requires": { - "fs-extra": "0.23.1", - "handlebars": "1.3.0", - "img-stats": "0.5.2" + "fs-extra": "^0.23.1", + "handlebars": "^1.3.0", + "img-stats": "^0.5.2" }, "dependencies": { "fs-extra": { @@ -2390,10 +2390,10 @@ "integrity": "sha1-ZhHbpq3yq43Jxp+rN83fiBgVfj0=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "jsonfile": "2.4.0", - "path-is-absolute": "1.0.1", - "rimraf": "2.6.1" + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" } } } @@ -2404,7 +2404,7 @@ "integrity": "sha1-pF71cnuJDJv/5tfIduexnLDhfzs=", "dev": true, "requires": { - "utila": "0.3.3" + "utila": "~0.3" }, "dependencies": { "utila": { @@ -2421,10 +2421,10 @@ "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", "dev": true, "requires": { - "custom-event": "1.0.1", - "ent": "2.2.0", - "extend": "3.0.1", - "void-elements": "2.0.1" + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" } }, "dom-serializer": { @@ -2433,8 +2433,8 @@ "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", "dev": true, "requires": { - "domelementtype": "1.1.3", - "entities": "1.1.1" + "domelementtype": "~1.1.1", + "entities": "~1.1.1" }, "dependencies": { "domelementtype": { @@ -2463,7 +2463,7 @@ "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=", "dev": true, "requires": { - "domelementtype": "1.3.0" + "domelementtype": "1" } }, "domutils": { @@ -2472,8 +2472,8 @@ "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", "dev": true, "requires": { - "dom-serializer": "0.1.0", - "domelementtype": "1.3.0" + "dom-serializer": "0", + "domelementtype": "1" } }, "ecc-jsbn": { @@ -2483,7 +2483,7 @@ "dev": true, "optional": true, "requires": { - "jsbn": "0.1.1" + "jsbn": "~0.1.0" } }, "ee-first": { @@ -2504,13 +2504,13 @@ "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", "dev": true, "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0", - "hash.js": "1.1.4", - "hmac-drbg": "1.0.1", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.1", - "minimalistic-crypto-utils": "1.0.1" + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" } }, "ember-cli-normalize-entity-name": { @@ -2519,7 +2519,7 @@ "integrity": "sha1-CxT3vLxZmqEXtf3cgeT9A8S61bc=", "dev": true, "requires": { - "silent-error": "1.1.0" + "silent-error": "^1.0.0" } }, "ember-cli-string-utils": { @@ -2634,10 +2634,10 @@ "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "memory-fs": "0.4.1", - "object-assign": "4.1.1", - "tapable": "0.2.8" + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "object-assign": "^4.0.1", + "tapable": "^0.2.7" } }, "ensure-posix-path": { @@ -2664,7 +2664,7 @@ "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", "dev": true, "requires": { - "prr": "1.0.1" + "prr": "~1.0.1" } }, "error-ex": { @@ -2673,7 +2673,7 @@ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "requires": { - "is-arrayish": "0.2.1" + "is-arrayish": "^0.2.1" } }, "escape-html": { @@ -2724,7 +2724,7 @@ "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=", "dev": true, "requires": { - "original": "1.0.1" + "original": ">=0.0.5" } }, "evp_bytestokey": { @@ -2733,8 +2733,8 @@ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "dev": true, "requires": { - "md5.js": "1.3.4", - "safe-buffer": "5.1.1" + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" } }, "exit": { @@ -2749,9 +2749,9 @@ "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=", "dev": true, "requires": { - "array-slice": "0.2.3", - "array-unique": "0.2.1", - "braces": "0.1.5" + "array-slice": "^0.2.3", + "array-unique": "^0.2.1", + "braces": "^0.1.2" }, "dependencies": { "braces": { @@ -2760,7 +2760,7 @@ "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=", "dev": true, "requires": { - "expand-range": "0.1.1" + "expand-range": "^0.1.0" } }, "expand-range": { @@ -2769,8 +2769,8 @@ "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=", "dev": true, "requires": { - "is-number": "0.1.1", - "repeat-string": "0.2.2" + "is-number": "^0.1.1", + "repeat-string": "^0.2.2" } }, "is-number": { @@ -2793,7 +2793,7 @@ "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, "requires": { - "is-posix-bracket": "0.1.1" + "is-posix-bracket": "^0.1.0" } }, "expand-range": { @@ -2802,7 +2802,7 @@ "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "dev": true, "requires": { - "fill-range": "2.2.3" + "fill-range": "^2.1.0" } }, "exports-loader": { @@ -2811,8 +2811,8 @@ "integrity": "sha1-1w/GEhl1s1/BKDDPUnVL4nQPyIY=", "dev": true, "requires": { - "loader-utils": "1.1.0", - "source-map": "0.5.6" + "loader-utils": "^1.0.2", + "source-map": "0.5.x" } }, "express": { @@ -2821,36 +2821,36 @@ "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", "dev": true, "requires": { - "accepts": "1.3.5", + "accepts": "~1.3.5", "array-flatten": "1.1.1", "body-parser": "1.18.2", "content-disposition": "0.5.2", - "content-type": "1.0.4", + "content-type": "~1.0.4", "cookie": "0.3.1", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "1.1.2", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "etag": "1.8.1", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", "finalhandler": "1.1.1", "fresh": "0.5.2", "merge-descriptors": "1.0.1", - "methods": "1.1.2", - "on-finished": "2.3.0", - "parseurl": "1.3.2", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "2.0.3", + "proxy-addr": "~2.0.3", "qs": "6.5.1", - "range-parser": "1.2.0", + "range-parser": "~1.2.0", "safe-buffer": "5.1.1", "send": "0.16.2", "serve-static": "1.13.2", "setprototypeof": "1.1.0", - "statuses": "1.4.0", - "type-is": "1.6.16", + "statuses": "~1.4.0", + "type-is": "~1.6.16", "utils-merge": "1.0.1", - "vary": "1.1.2" + "vary": "~1.1.2" }, "dependencies": { "accepts": { @@ -2859,7 +2859,7 @@ "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "dev": true, "requires": { - "mime-types": "2.1.18", + "mime-types": "~2.1.18", "negotiator": "0.6.1" } }, @@ -2870,15 +2870,15 @@ "dev": true, "requires": { "bytes": "3.0.0", - "content-type": "1.0.4", + "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "1.1.2", - "http-errors": "1.6.3", + "depd": "~1.1.1", + "http-errors": "~1.6.2", "iconv-lite": "0.4.19", - "on-finished": "2.3.0", + "on-finished": "~2.3.0", "qs": "6.5.1", "raw-body": "2.3.2", - "type-is": "1.6.16" + "type-is": "~1.6.15" } }, "content-type": { @@ -2915,12 +2915,12 @@ "dev": true, "requires": { "debug": "2.6.9", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "statuses": "1.4.0", - "unpipe": "1.0.0" + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" } }, "http-errors": { @@ -2929,10 +2929,10 @@ "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { - "depd": "1.1.2", + "depd": "~1.1.2", "inherits": "2.0.3", "setprototypeof": "1.1.0", - "statuses": "1.4.0" + "statuses": ">= 1.4.0 < 2" } }, "iconv-lite": { @@ -2953,7 +2953,7 @@ "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "dev": true, "requires": { - "mime-db": "1.33.0" + "mime-db": "~1.33.0" } }, "parseurl": { @@ -2995,7 +2995,7 @@ "depd": "1.1.1", "inherits": "2.0.3", "setprototypeof": "1.0.3", - "statuses": "1.4.0" + "statuses": ">= 1.3.1 < 2" } }, "setprototypeof": { @@ -3025,7 +3025,7 @@ "dev": true, "requires": { "media-typer": "0.3.0", - "mime-types": "2.1.18" + "mime-types": "~2.1.18" } }, "utils-merge": { @@ -3048,8 +3048,8 @@ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { - "assign-symbols": "1.0.0", - "is-extendable": "1.0.1" + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" }, "dependencies": { "is-extendable": { @@ -3058,7 +3058,7 @@ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "2.0.4" + "is-plain-object": "^2.0.4" } } } @@ -3069,9 +3069,9 @@ "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", "dev": true, "requires": { - "chardet": "0.4.2", - "iconv-lite": "0.4.18", - "tmp": "0.0.33" + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" }, "dependencies": { "tmp": { @@ -3080,7 +3080,7 @@ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "requires": { - "os-tmpdir": "1.0.2" + "os-tmpdir": "~1.0.2" } } } @@ -3091,7 +3091,7 @@ "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, "requires": { - "is-extglob": "1.0.0" + "is-extglob": "^1.0.0" } }, "extract-text-webpack-plugin": { @@ -3100,10 +3100,10 @@ "integrity": "sha1-dW7076gVXDaBgz+8NNpTuUF0bWw=", "dev": true, "requires": { - "async": "2.5.0", - "loader-utils": "1.1.0", - "schema-utils": "0.3.0", - "webpack-sources": "1.1.0" + "async": "^2.1.2", + "loader-utils": "^1.0.2", + "schema-utils": "^0.3.0", + "webpack-sources": "^1.0.1" } }, "extsprintf": { @@ -3136,7 +3136,7 @@ "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", "dev": true, "requires": { - "websocket-driver": "0.7.0" + "websocket-driver": ">=0.5.1" } }, "figures": { @@ -3145,7 +3145,7 @@ "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", "dev": true, "requires": { - "escape-string-regexp": "1.0.5" + "escape-string-regexp": "^1.0.5" } }, "file-loader": { @@ -3154,7 +3154,7 @@ "integrity": "sha1-gVA0EZiR/GRB+1pkwRvJPCLd2EI=", "dev": true, "requires": { - "loader-utils": "1.1.0" + "loader-utils": "^1.0.2" } }, "filename-regex": { @@ -3169,8 +3169,8 @@ "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", "dev": true, "requires": { - "glob": "7.1.2", - "minimatch": "3.0.4" + "glob": "^7.0.3", + "minimatch": "^3.0.3" } }, "fill-range": { @@ -3179,11 +3179,11 @@ "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", "dev": true, "requires": { - "is-number": "2.1.0", - "isobject": "2.1.0", - "randomatic": "1.1.7", - "repeat-element": "1.1.2", - "repeat-string": "1.6.1" + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^1.1.3", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" }, "dependencies": { "isobject": { @@ -3204,12 +3204,12 @@ "dev": true, "requires": { "debug": "2.6.8", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.1", - "statuses": "1.3.1", - "unpipe": "1.0.0" + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.1", + "statuses": "~1.3.1", + "unpipe": "~1.0.0" } }, "find-up": { @@ -3218,8 +3218,8 @@ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "flatten": { @@ -3240,7 +3240,7 @@ "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", "dev": true, "requires": { - "for-in": "1.0.2" + "for-in": "^1.0.1" } }, "forever-agent": { @@ -3255,9 +3255,9 @@ "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", "dev": true, "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.16" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.12" } }, "forwarded": { @@ -3272,7 +3272,7 @@ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "requires": { - "map-cache": "0.2.2" + "map-cache": "^0.2.2" } }, "fresh": { @@ -3287,7 +3287,7 @@ "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", "dev": true, "requires": { - "null-check": "1.0.0" + "null-check": "^1.0.0" } }, "fs-extra": { @@ -3296,8 +3296,8 @@ "integrity": "sha1-BGxwFjzvmq1GsOSn+kZ/si1x3jU=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "jsonfile": "2.4.0" + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0" } }, "fs.realpath": { @@ -3313,8 +3313,8 @@ "dev": true, "optional": true, "requires": { - "nan": "2.10.0", - "node-pre-gyp": "0.6.36" + "nan": "^2.3.0", + "node-pre-gyp": "^0.6.36" }, "dependencies": { "abbrev": { @@ -3329,8 +3329,8 @@ "dev": true, "optional": true, "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" } }, "ansi-regex": { @@ -3350,8 +3350,8 @@ "dev": true, "optional": true, "requires": { - "delegates": "1.0.0", - "readable-stream": "2.2.9" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, "asn1": { @@ -3395,7 +3395,7 @@ "dev": true, "optional": true, "requires": { - "tweetnacl": "0.14.5" + "tweetnacl": "^0.14.3" } }, "block-stream": { @@ -3403,7 +3403,7 @@ "bundled": true, "dev": true, "requires": { - "inherits": "2.0.3" + "inherits": "~2.0.0" } }, "boom": { @@ -3411,7 +3411,7 @@ "bundled": true, "dev": true, "requires": { - "hoek": "2.16.3" + "hoek": "2.x.x" } }, "brace-expansion": { @@ -3419,7 +3419,7 @@ "bundled": true, "dev": true, "requires": { - "balanced-match": "0.4.2", + "balanced-match": "^0.4.1", "concat-map": "0.0.1" } }, @@ -3450,7 +3450,7 @@ "bundled": true, "dev": true, "requires": { - "delayed-stream": "1.0.0" + "delayed-stream": "~1.0.0" } }, "concat-map": { @@ -3474,7 +3474,7 @@ "dev": true, "optional": true, "requires": { - "boom": "2.10.1" + "boom": "2.x.x" } }, "dashdash": { @@ -3483,7 +3483,7 @@ "dev": true, "optional": true, "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" }, "dependencies": { "assert-plus": { @@ -3526,7 +3526,7 @@ "dev": true, "optional": true, "requires": { - "jsbn": "0.1.1" + "jsbn": "~0.1.0" } }, "extend": { @@ -3552,9 +3552,9 @@ "dev": true, "optional": true, "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.15" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.12" } }, "fs.realpath": { @@ -3567,10 +3567,10 @@ "bundled": true, "dev": true, "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.1" + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" } }, "fstream-ignore": { @@ -3579,9 +3579,9 @@ "dev": true, "optional": true, "requires": { - "fstream": "1.0.11", - "inherits": "2.0.3", - "minimatch": "3.0.4" + "fstream": "^1.0.0", + "inherits": "2", + "minimatch": "^3.0.0" } }, "gauge": { @@ -3590,14 +3590,14 @@ "dev": true, "optional": true, "requires": { - "aproba": "1.1.1", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" + "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" } }, "getpass": { @@ -3606,7 +3606,7 @@ "dev": true, "optional": true, "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" }, "dependencies": { "assert-plus": { @@ -3622,12 +3622,12 @@ "bundled": true, "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "graceful-fs": { @@ -3647,8 +3647,8 @@ "dev": true, "optional": true, "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" + "ajv": "^4.9.1", + "har-schema": "^1.0.5" } }, "has-unicode": { @@ -3663,10 +3663,10 @@ "dev": true, "optional": true, "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" + "boom": "2.x.x", + "cryptiles": "2.x.x", + "hoek": "2.x.x", + "sntp": "1.x.x" } }, "hoek": { @@ -3680,9 +3680,9 @@ "dev": true, "optional": true, "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.0", - "sshpk": "1.13.0" + "assert-plus": "^0.2.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" } }, "inflight": { @@ -3690,8 +3690,8 @@ "bundled": true, "dev": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -3710,7 +3710,7 @@ "bundled": true, "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "is-typedarray": { @@ -3736,13 +3736,14 @@ "dev": true, "optional": true, "requires": { - "jsbn": "0.1.1" + "jsbn": "~0.1.0" } }, "jsbn": { "version": "0.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "json-schema": { "version": "0.2.3", @@ -3756,7 +3757,7 @@ "dev": true, "optional": true, "requires": { - "jsonify": "0.0.0" + "jsonify": "~0.0.0" } }, "json-stringify-safe": { @@ -3801,7 +3802,7 @@ "bundled": true, "dev": true, "requires": { - "mime-db": "1.27.0" + "mime-db": "~1.27.0" } }, "minimatch": { @@ -3809,7 +3810,7 @@ "bundled": true, "dev": true, "requires": { - "brace-expansion": "1.1.7" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -3837,15 +3838,15 @@ "dev": true, "optional": true, "requires": { - "mkdirp": "0.5.1", - "nopt": "4.0.1", - "npmlog": "4.1.0", - "rc": "1.2.1", - "request": "2.81.0", - "rimraf": "2.6.1", - "semver": "5.3.0", - "tar": "2.2.1", - "tar-pack": "3.4.0" + "mkdirp": "^0.5.1", + "nopt": "^4.0.1", + "npmlog": "^4.0.2", + "rc": "^1.1.7", + "request": "^2.81.0", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^2.2.1", + "tar-pack": "^3.4.0" } }, "nopt": { @@ -3854,8 +3855,8 @@ "dev": true, "optional": true, "requires": { - "abbrev": "1.1.0", - "osenv": "0.1.4" + "abbrev": "1", + "osenv": "^0.1.4" } }, "npmlog": { @@ -3864,10 +3865,10 @@ "dev": true, "optional": true, "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, "number-is-nan": { @@ -3892,7 +3893,7 @@ "bundled": true, "dev": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "os-homedir": { @@ -3913,8 +3914,8 @@ "dev": true, "optional": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, "path-is-absolute": { @@ -3951,10 +3952,10 @@ "dev": true, "optional": true, "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" + "deep-extend": "~0.4.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { @@ -3970,13 +3971,13 @@ "bundled": true, "dev": true, "requires": { - "buffer-shims": "1.0.0", - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "1.0.1", - "util-deprecate": "1.0.2" + "buffer-shims": "~1.0.0", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~1.0.0", + "util-deprecate": "~1.0.1" } }, "request": { @@ -3985,28 +3986,28 @@ "dev": true, "optional": true, "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.15", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.0.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.6.0", - "uuid": "3.0.1" + "aws-sign2": "~0.6.0", + "aws4": "^1.2.1", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.0", + "forever-agent": "~0.6.1", + "form-data": "~2.1.1", + "har-validator": "~4.2.1", + "hawk": "~3.1.3", + "http-signature": "~1.1.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.7", + "oauth-sign": "~0.8.1", + "performance-now": "^0.2.0", + "qs": "~6.4.0", + "safe-buffer": "^5.0.1", + "stringstream": "~0.0.4", + "tough-cookie": "~2.3.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.0.0" } }, "rimraf": { @@ -4014,7 +4015,7 @@ "bundled": true, "dev": true, "requires": { - "glob": "7.1.2" + "glob": "^7.0.5" } }, "safe-buffer": { @@ -4046,7 +4047,7 @@ "dev": true, "optional": true, "requires": { - "hoek": "2.16.3" + "hoek": "2.x.x" } }, "sshpk": { @@ -4055,15 +4056,15 @@ "dev": true, "optional": true, "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jodid25519": "1.0.2", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jodid25519": "^1.0.0", + "jsbn": "~0.1.0", + "tweetnacl": "~0.14.0" }, "dependencies": { "assert-plus": { @@ -4079,9 +4080,9 @@ "bundled": true, "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "string_decoder": { @@ -4089,7 +4090,7 @@ "bundled": true, "dev": true, "requires": { - "safe-buffer": "5.0.1" + "safe-buffer": "^5.0.1" } }, "stringstream": { @@ -4103,7 +4104,7 @@ "bundled": true, "dev": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "strip-json-comments": { @@ -4117,9 +4118,9 @@ "bundled": true, "dev": true, "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" } }, "tar-pack": { @@ -4128,14 +4129,14 @@ "dev": true, "optional": true, "requires": { - "debug": "2.6.8", - "fstream": "1.0.11", - "fstream-ignore": "1.0.5", - "once": "1.4.0", - "readable-stream": "2.2.9", - "rimraf": "2.6.1", - "tar": "2.2.1", - "uid-number": "0.0.6" + "debug": "^2.2.0", + "fstream": "^1.0.10", + "fstream-ignore": "^1.0.5", + "once": "^1.3.3", + "readable-stream": "^2.1.4", + "rimraf": "^2.5.1", + "tar": "^2.2.1", + "uid-number": "^0.0.6" } }, "tough-cookie": { @@ -4144,7 +4145,7 @@ "dev": true, "optional": true, "requires": { - "punycode": "1.4.1" + "punycode": "^1.4.1" } }, "tunnel-agent": { @@ -4153,7 +4154,7 @@ "dev": true, "optional": true, "requires": { - "safe-buffer": "5.0.1" + "safe-buffer": "^5.0.1" } }, "tweetnacl": { @@ -4194,7 +4195,7 @@ "dev": true, "optional": true, "requires": { - "string-width": "1.0.2" + "string-width": "^1.0.2" } }, "wrappy": { @@ -4210,10 +4211,10 @@ "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.1" + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" } }, "function-bind": { @@ -4228,14 +4229,14 @@ "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.3" + "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": { @@ -4244,7 +4245,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "string-width": { @@ -4253,9 +4254,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } } } @@ -4267,7 +4268,7 @@ "dev": true, "optional": true, "requires": { - "globule": "1.2.1" + "globule": "^1.0.0" } }, "generate-function": { @@ -4284,7 +4285,7 @@ "dev": true, "optional": true, "requires": { - "is-property": "1.0.2" + "is-property": "^1.0.0" } }, "get-caller-file": { @@ -4311,7 +4312,7 @@ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "dev": true, "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" }, "dependencies": { "assert-plus": { @@ -4328,12 +4329,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "glob-base": { @@ -4342,8 +4343,8 @@ "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", "dev": true, "requires": { - "glob-parent": "2.0.0", - "is-glob": "2.0.1" + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" } }, "glob-parent": { @@ -4352,7 +4353,7 @@ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { - "is-glob": "2.0.1" + "is-glob": "^2.0.0" } }, "globals": { @@ -4367,12 +4368,12 @@ "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", "dev": true, "requires": { - "array-union": "1.0.2", - "arrify": "1.0.1", - "glob": "7.1.2", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "globule": { @@ -4382,9 +4383,9 @@ "dev": true, "optional": true, "requires": { - "glob": "7.1.2", - "lodash": "4.17.10", - "minimatch": "3.0.4" + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" }, "dependencies": { "lodash": { @@ -4414,8 +4415,8 @@ "integrity": "sha1-npsTCpPjiUkTItl1zz7BgYw3zjQ=", "dev": true, "requires": { - "optimist": "0.3.7", - "uglify-js": "2.3.6" + "optimist": "~0.3", + "uglify-js": "~2.3" }, "dependencies": { "async": { @@ -4432,7 +4433,7 @@ "dev": true, "optional": true, "requires": { - "amdefine": "1.0.1" + "amdefine": ">=0.0.4" } }, "uglify-js": { @@ -4442,9 +4443,9 @@ "dev": true, "optional": true, "requires": { - "async": "0.2.10", - "optimist": "0.3.7", - "source-map": "0.1.43" + "async": "~0.2.6", + "optimist": "~0.3.5", + "source-map": "~0.1.7" } } } @@ -4461,8 +4462,8 @@ "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", "dev": true, "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" + "ajv": "^4.9.1", + "har-schema": "^1.0.5" }, "dependencies": { "ajv": { @@ -4471,8 +4472,8 @@ "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", "dev": true, "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" } } } @@ -4483,7 +4484,7 @@ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { - "function-bind": "1.1.1" + "function-bind": "^1.1.1" } }, "has-ansi": { @@ -4492,7 +4493,7 @@ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "has-binary": { @@ -4536,9 +4537,9 @@ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { - "get-value": "2.0.6", - "has-values": "1.0.0", - "isobject": "3.0.1" + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" } }, "has-values": { @@ -4547,8 +4548,8 @@ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" + "is-number": "^3.0.0", + "kind-of": "^4.0.0" }, "dependencies": { "is-number": { @@ -4557,7 +4558,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -4566,7 +4567,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.5" + "is-buffer": "^1.1.5" } } } @@ -4577,7 +4578,7 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "1.1.5" + "is-buffer": "^1.1.5" } } } @@ -4588,8 +4589,8 @@ "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", "dev": true, "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, "hash.js": { @@ -4598,8 +4599,8 @@ "integrity": "sha512-A6RlQvvZEtFS5fLU43IDu0QUmBy+fDO9VMdTXvufKwIkt/rFfvICAViCax5fbDO4zdNzaC3/27ZhKUok5bAJyw==", "dev": true, "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.1" + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.0" } }, "hawk": { @@ -4608,10 +4609,10 @@ "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", "dev": true, "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" + "boom": "2.x.x", + "cryptiles": "2.x.x", + "hoek": "2.x.x", + "sntp": "1.x.x" } }, "he": { @@ -4626,9 +4627,9 @@ "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", "dev": true, "requires": { - "hash.js": "1.1.4", - "minimalistic-assert": "1.0.1", - "minimalistic-crypto-utils": "1.0.1" + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" } }, "hoek": { @@ -4649,10 +4650,10 @@ "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", "dev": true, "requires": { - "inherits": "2.0.3", - "obuf": "1.1.2", - "readable-stream": "2.3.3", - "wbuf": "1.7.3" + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" } }, "html-comment-regex": { @@ -4673,13 +4674,13 @@ "integrity": "sha512-O+StuKL0UWfwX5Zv4rFxd60DPcT5DVjGq1AlnP6VQ8wzudft/W4hx5Wl98aSYNwFBHY6XWJreRw/BehX4l+diQ==", "dev": true, "requires": { - "camel-case": "3.0.0", - "clean-css": "4.1.11", - "commander": "2.15.1", - "he": "1.1.1", - "param-case": "2.1.1", - "relateurl": "0.2.7", - "uglify-js": "3.4.2" + "camel-case": "3.0.x", + "clean-css": "4.1.x", + "commander": "2.15.x", + "he": "1.1.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" }, "dependencies": { "commander": { @@ -4696,12 +4697,12 @@ "integrity": "sha1-f5xCG36pHsRg9WUn1430hO51N9U=", "dev": true, "requires": { - "bluebird": "3.5.0", - "html-minifier": "3.5.17", - "loader-utils": "0.2.17", - "lodash": "4.17.4", - "pretty-error": "2.1.1", - "toposort": "1.0.7" + "bluebird": "^3.4.7", + "html-minifier": "^3.2.3", + "loader-utils": "^0.2.16", + "lodash": "^4.17.3", + "pretty-error": "^2.0.2", + "toposort": "^1.0.0" }, "dependencies": { "loader-utils": { @@ -4710,10 +4711,10 @@ "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", "dev": true, "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1", - "object-assign": "4.1.1" + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" } } } @@ -4724,10 +4725,10 @@ "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", "dev": true, "requires": { - "domelementtype": "1.3.0", - "domhandler": "2.1.0", - "domutils": "1.1.6", - "readable-stream": "1.0.34" + "domelementtype": "1", + "domhandler": "2.1", + "domutils": "1.1", + "readable-stream": "1.0" }, "dependencies": { "domutils": { @@ -4736,7 +4737,7 @@ "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=", "dev": true, "requires": { - "domelementtype": "1.3.0" + "domelementtype": "1" } }, "isarray": { @@ -4751,10 +4752,10 @@ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", "isarray": "0.0.1", - "string_decoder": "0.10.31" + "string_decoder": "~0.10.x" } }, "string_decoder": { @@ -4780,7 +4781,7 @@ "depd": "1.1.0", "inherits": "2.0.3", "setprototypeof": "1.0.3", - "statuses": "1.3.1" + "statuses": ">= 1.3.1 < 2" }, "dependencies": { "depd": { @@ -4803,8 +4804,8 @@ "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=", "dev": true, "requires": { - "eventemitter3": "1.2.0", - "requires-port": "1.0.0" + "eventemitter3": "1.x.x", + "requires-port": "1.x.x" } }, "http-proxy-middleware": { @@ -4813,10 +4814,10 @@ "integrity": "sha1-ZC6ISIUdZvCdTxJJEoRtuutBuDM=", "dev": true, "requires": { - "http-proxy": "1.16.2", - "is-glob": "3.1.0", - "lodash": "4.17.4", - "micromatch": "2.3.11" + "http-proxy": "^1.16.2", + "is-glob": "^3.1.0", + "lodash": "^4.17.2", + "micromatch": "^2.3.11" }, "dependencies": { "is-extglob": { @@ -4831,7 +4832,7 @@ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "is-extglob": "2.1.1" + "is-extglob": "^2.1.0" } } } @@ -4842,9 +4843,9 @@ "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", "dev": true, "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" + "assert-plus": "^0.2.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" } }, "https-browserify": { @@ -4859,9 +4860,9 @@ "integrity": "sha1-NffabEjOTdv6JkiRrFk+5f+GceY=", "dev": true, "requires": { - "agent-base": "2.1.1", - "debug": "2.6.8", - "extend": "3.0.1" + "agent-base": "2", + "debug": "2", + "extend": "3" } }, "iconv-lite": { @@ -4881,7 +4882,7 @@ "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", "dev": true, "requires": { - "postcss": "6.0.23" + "postcss": "^6.0.1" }, "dependencies": { "ansi-styles": { @@ -4890,7 +4891,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.2" + "color-convert": "^1.9.0" } }, "chalk": { @@ -4899,9 +4900,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "has-flag": { @@ -4916,9 +4917,9 @@ "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", "dev": true, "requires": { - "chalk": "2.4.1", - "source-map": "0.6.1", - "supports-color": "5.4.0" + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" } }, "source-map": { @@ -4933,7 +4934,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -4957,7 +4958,7 @@ "integrity": "sha1-wgNJbELy2esuWrgjL6dWurMsnis=", "dev": true, "requires": { - "xmldom": "0.1.27" + "xmldom": "^0.1.19" } }, "in-publish": { @@ -4974,7 +4975,7 @@ "dev": true, "optional": true, "requires": { - "repeating": "2.0.1" + "repeating": "^2.0.0" } }, "indexes-of": { @@ -5001,8 +5002,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -5023,20 +5024,20 @@ "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", "dev": true, "requires": { - "ansi-escapes": "3.1.0", - "chalk": "2.4.1", - "cli-cursor": "2.1.0", - "cli-width": "2.2.0", - "external-editor": "2.2.0", - "figures": "2.0.0", - "lodash": "4.17.4", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.0.4", + "figures": "^2.0.0", + "lodash": "^4.3.0", "mute-stream": "0.0.7", - "run-async": "2.3.0", - "rx-lite": "4.0.8", - "rx-lite-aggregates": "4.0.8", - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "through": "2.3.8" + "run-async": "^2.2.0", + "rx-lite": "^4.0.8", + "rx-lite-aggregates": "^4.0.8", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" }, "dependencies": { "ansi-regex": { @@ -5051,7 +5052,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.2" + "color-convert": "^1.9.0" } }, "chalk": { @@ -5060,9 +5061,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "has-flag": { @@ -5077,7 +5078,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } }, "supports-color": { @@ -5086,7 +5087,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -5103,7 +5104,7 @@ "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", "dev": true, "requires": { - "loose-envify": "1.3.1" + "loose-envify": "^1.0.0" } }, "invert-kv": { @@ -5130,7 +5131,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } }, "is-arrayish": { @@ -5145,7 +5146,7 @@ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { - "binary-extensions": "1.9.0" + "binary-extensions": "^1.0.0" } }, "is-buffer": { @@ -5160,7 +5161,7 @@ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { - "builtin-modules": "1.1.1" + "builtin-modules": "^1.0.0" } }, "is-data-descriptor": { @@ -5169,7 +5170,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } }, "is-descriptor": { @@ -5178,9 +5179,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" }, "dependencies": { "kind-of": { @@ -5209,7 +5210,7 @@ "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", "dev": true, "requires": { - "is-primitive": "2.0.0" + "is-primitive": "^2.0.0" } }, "is-extendable": { @@ -5230,7 +5231,7 @@ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "is-fullwidth-code-point": { @@ -5245,7 +5246,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "1.0.0" + "is-extglob": "^1.0.0" } }, "is-my-ip-valid": { @@ -5262,11 +5263,11 @@ "dev": true, "optional": true, "requires": { - "generate-function": "2.0.0", - "generate-object-property": "1.2.0", - "is-my-ip-valid": "1.0.0", - "jsonpointer": "4.0.1", - "xtend": "4.0.1" + "generate-function": "^2.0.0", + "generate-object-property": "^1.1.0", + "is-my-ip-valid": "^1.0.0", + "jsonpointer": "^4.0.0", + "xtend": "^4.0.0" } }, "is-number": { @@ -5275,7 +5276,7 @@ "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } }, "is-odd": { @@ -5284,7 +5285,7 @@ "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", "dev": true, "requires": { - "is-number": "4.0.0" + "is-number": "^4.0.0" }, "dependencies": { "is-number": { @@ -5307,7 +5308,7 @@ "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", "dev": true, "requires": { - "is-path-inside": "1.0.0" + "is-path-inside": "^1.0.0" } }, "is-path-inside": { @@ -5316,7 +5317,7 @@ "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", "dev": true, "requires": { - "path-is-inside": "1.0.2" + "path-is-inside": "^1.0.1" } }, "is-plain-obj": { @@ -5331,7 +5332,7 @@ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.1" } }, "is-posix-bracket": { @@ -5365,7 +5366,7 @@ "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", "dev": true, "requires": { - "html-comment-regex": "1.1.1" + "html-comment-regex": "^1.1.0" } }, "is-typedarray": { @@ -5422,17 +5423,17 @@ "integrity": "sha1-/MC0YeKzvaceMFFVE4I4doJX2d4=", "dev": true, "requires": { - "async": "2.5.0", - "fileset": "2.0.3", - "istanbul-lib-coverage": "1.1.1", - "istanbul-lib-hook": "1.0.7", - "istanbul-lib-instrument": "1.7.4", - "istanbul-lib-report": "1.1.1", - "istanbul-lib-source-maps": "1.2.1", - "istanbul-reports": "1.1.1", - "js-yaml": "3.7.0", - "mkdirp": "0.5.1", - "once": "1.4.0" + "async": "^2.1.4", + "fileset": "^2.0.2", + "istanbul-lib-coverage": "^1.1.1", + "istanbul-lib-hook": "^1.0.7", + "istanbul-lib-instrument": "^1.7.4", + "istanbul-lib-report": "^1.1.1", + "istanbul-lib-source-maps": "^1.2.1", + "istanbul-reports": "^1.1.1", + "js-yaml": "^3.7.0", + "mkdirp": "^0.5.1", + "once": "^1.4.0" } }, "istanbul-instrumenter-loader": { @@ -5441,10 +5442,10 @@ "integrity": "sha1-5UkpAKsLuoNe+oAkywC+mz7qJwA=", "dev": true, "requires": { - "convert-source-map": "1.5.1", - "istanbul-lib-instrument": "1.7.4", - "loader-utils": "0.2.17", - "object-assign": "4.1.1" + "convert-source-map": "^1.3.0", + "istanbul-lib-instrument": "^1.1.3", + "loader-utils": "^0.2.16", + "object-assign": "^4.1.0" }, "dependencies": { "loader-utils": { @@ -5453,10 +5454,10 @@ "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", "dev": true, "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1", - "object-assign": "4.1.1" + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" } } } @@ -5473,7 +5474,7 @@ "integrity": "sha512-3U2HB9y1ZV9UmFlE12Fx+nPtFqIymzrqCksrXujm3NVbAZIJg/RfYgO1XiIa0mbmxTjWpVEVlkIZJ25xVIAfkQ==", "dev": true, "requires": { - "append-transform": "0.4.0" + "append-transform": "^0.4.0" } }, "istanbul-lib-instrument": { @@ -5482,13 +5483,13 @@ "integrity": "sha1-6f2SDkdn89Ge3HZeLWs/XMvQ7qg=", "dev": true, "requires": { - "babel-generator": "6.25.0", - "babel-template": "6.25.0", - "babel-traverse": "6.25.0", - "babel-types": "6.25.0", - "babylon": "6.17.4", - "istanbul-lib-coverage": "1.1.1", - "semver": "5.4.1" + "babel-generator": "^6.18.0", + "babel-template": "^6.16.0", + "babel-traverse": "^6.18.0", + "babel-types": "^6.18.0", + "babylon": "^6.17.4", + "istanbul-lib-coverage": "^1.1.1", + "semver": "^5.3.0" } }, "istanbul-lib-report": { @@ -5497,10 +5498,10 @@ "integrity": "sha512-tvF+YmCmH4thnez6JFX06ujIA19WPa9YUiwjc1uALF2cv5dmE3It8b5I8Ob7FHJ70H9Y5yF+TDkVa/mcADuw1Q==", "dev": true, "requires": { - "istanbul-lib-coverage": "1.1.1", - "mkdirp": "0.5.1", - "path-parse": "1.0.5", - "supports-color": "3.2.3" + "istanbul-lib-coverage": "^1.1.1", + "mkdirp": "^0.5.1", + "path-parse": "^1.0.5", + "supports-color": "^3.1.2" } }, "istanbul-lib-source-maps": { @@ -5509,11 +5510,11 @@ "integrity": "sha512-mukVvSXCn9JQvdJl8wP/iPhqig0MRtuWuD4ZNKo6vB2Ik//AmhAKe3QnPN02dmkRe3lTudFk3rzoHhwU4hb94w==", "dev": true, "requires": { - "debug": "2.6.8", - "istanbul-lib-coverage": "1.1.1", - "mkdirp": "0.5.1", - "rimraf": "2.6.1", - "source-map": "0.5.6" + "debug": "^2.6.3", + "istanbul-lib-coverage": "^1.1.1", + "mkdirp": "^0.5.1", + "rimraf": "^2.6.1", + "source-map": "^0.5.3" } }, "istanbul-reports": { @@ -5522,7 +5523,7 @@ "integrity": "sha512-P8G873A0kW24XRlxHVGhMJBhQ8gWAec+dae7ZxOBzxT4w+a9ATSPvRVK3LB1RAJ9S8bg2tOyWHAGW40Zd2dKfw==", "dev": true, "requires": { - "handlebars": "4.0.10" + "handlebars": "^4.0.3" }, "dependencies": { "async": { @@ -5545,8 +5546,8 @@ "dev": true, "optional": true, "requires": { - "center-align": "0.1.3", - "right-align": "0.1.3", + "center-align": "^0.1.1", + "right-align": "^0.1.1", "wordwrap": "0.0.2" }, "dependencies": { @@ -5565,10 +5566,10 @@ "integrity": "sha1-PTDHGLCaPZbyPqTMH0A8TTup/08=", "dev": true, "requires": { - "async": "1.5.2", - "optimist": "0.6.1", - "source-map": "0.4.4", - "uglify-js": "2.8.29" + "async": "^1.4.0", + "optimist": "^0.6.1", + "source-map": "^0.4.4", + "uglify-js": "^2.6" } }, "optimist": { @@ -5577,8 +5578,8 @@ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { - "minimist": "0.0.8", - "wordwrap": "0.0.3" + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" } }, "source-map": { @@ -5587,7 +5588,7 @@ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { - "amdefine": "1.0.1" + "amdefine": ">=0.0.4" } }, "uglify-js": { @@ -5597,9 +5598,9 @@ "dev": true, "optional": true, "requires": { - "source-map": "0.5.6", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" }, "dependencies": { "source-map": { @@ -5618,9 +5619,9 @@ "dev": true, "optional": true, "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "1.2.0", + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", "window-size": "0.1.0" } } @@ -5632,9 +5633,9 @@ "integrity": "sha1-XPC7TllLRgC7QjVWA2YhKsWuobI=", "dev": true, "requires": { - "exit": "0.1.2", - "glob": "7.1.2", - "jasmine-core": "2.7.0" + "exit": "^0.1.2", + "glob": "^7.0.6", + "jasmine-core": "~2.7.0" }, "dependencies": { "jasmine-core": { @@ -5684,8 +5685,8 @@ "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", "dev": true, "requires": { - "argparse": "1.0.9", - "esprima": "2.7.3" + "argparse": "^1.0.7", + "esprima": "^2.6.0" } }, "jsbn": { @@ -5725,7 +5726,7 @@ "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", "dev": true, "requires": { - "jsonify": "0.0.0" + "jsonify": "~0.0.0" } }, "json-stringify-safe": { @@ -5752,7 +5753,7 @@ "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", "dev": true, "requires": { - "graceful-fs": "4.1.11" + "graceful-fs": "^4.1.6" } }, "jsonify": { @@ -5794,33 +5795,33 @@ "integrity": "sha1-b3oaQGRG+i4YfslTmGmPTO5HYmk=", "dev": true, "requires": { - "bluebird": "3.5.0", - "body-parser": "1.17.2", - "chokidar": "1.7.0", - "colors": "1.1.2", - "combine-lists": "1.0.1", - "connect": "3.6.3", - "core-js": "2.4.1", - "di": "0.0.1", - "dom-serialize": "2.2.1", - "expand-braces": "0.1.2", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "http-proxy": "1.16.2", - "isbinaryfile": "3.0.2", - "lodash": "3.10.1", - "log4js": "0.6.38", - "mime": "1.3.6", - "minimatch": "3.0.4", - "optimist": "0.6.1", - "qjobs": "1.1.5", - "range-parser": "1.2.0", - "rimraf": "2.6.1", - "safe-buffer": "5.1.1", + "bluebird": "^3.3.0", + "body-parser": "^1.16.1", + "chokidar": "^1.4.1", + "colors": "^1.1.0", + "combine-lists": "^1.0.0", + "connect": "^3.6.0", + "core-js": "^2.2.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.0", + "expand-braces": "^0.1.1", + "glob": "^7.1.1", + "graceful-fs": "^4.1.2", + "http-proxy": "^1.13.0", + "isbinaryfile": "^3.0.0", + "lodash": "^3.8.0", + "log4js": "^0.6.31", + "mime": "^1.3.4", + "minimatch": "^3.0.2", + "optimist": "^0.6.1", + "qjobs": "^1.1.4", + "range-parser": "^1.2.0", + "rimraf": "^2.6.0", + "safe-buffer": "^5.0.1", "socket.io": "1.7.3", - "source-map": "0.5.6", + "source-map": "^0.5.3", "tmp": "0.0.31", - "useragent": "2.2.1" + "useragent": "^2.1.12" }, "dependencies": { "lodash": { @@ -5835,8 +5836,8 @@ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { - "minimist": "0.0.8", - "wordwrap": "0.0.3" + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" } } } @@ -5847,8 +5848,8 @@ "integrity": "sha1-IWh5xorATY1RQOmWGboEtZr9Rs8=", "dev": true, "requires": { - "fs-access": "1.0.1", - "which": "1.3.0" + "fs-access": "^1.0.0", + "which": "^1.2.1" } }, "karma-cli": { @@ -5857,7 +5858,7 @@ "integrity": "sha1-rmw8WKMTodALRRZMRVubhs4X+WA=", "dev": true, "requires": { - "resolve": "1.4.0" + "resolve": "^1.1.6" } }, "karma-coverage-istanbul-reporter": { @@ -5866,8 +5867,8 @@ "integrity": "sha1-0ULNnFVzHJ42Pvc3To7xoxvr+ts=", "dev": true, "requires": { - "istanbul-api": "1.1.11", - "minimatch": "3.0.4" + "istanbul-api": "^1.1.8", + "minimatch": "^3.0.4" } }, "karma-jasmine": { @@ -5882,7 +5883,7 @@ "integrity": "sha1-SKjl7xiAdhfuK14zwRlMNbQ5Ukw=", "dev": true, "requires": { - "karma-jasmine": "1.1.0" + "karma-jasmine": "^1.0.2" } }, "kind-of": { @@ -5891,7 +5892,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.5" + "is-buffer": "^1.1.5" } }, "lcid": { @@ -5900,7 +5901,7 @@ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "dev": true, "requires": { - "invert-kv": "1.0.0" + "invert-kv": "^1.0.0" } }, "less": { @@ -5909,14 +5910,14 @@ "integrity": "sha512-KPdIJKWcEAb02TuJtaLrhue0krtRLoRoo7x6BNJIBelO00t/CCdJQUnHW5V34OnHMWzIktSalJxRO+FvytQlCQ==", "dev": true, "requires": { - "errno": "0.1.7", - "graceful-fs": "4.1.11", - "image-size": "0.5.5", - "mime": "1.3.6", - "mkdirp": "0.5.1", - "promise": "7.3.1", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "mime": "^1.2.11", + "mkdirp": "^0.5.0", + "promise": "^7.1.1", "request": "2.81.0", - "source-map": "0.5.6" + "source-map": "^0.5.3" } }, "less-loader": { @@ -5925,9 +5926,9 @@ "integrity": "sha512-KNTsgCE9tMOM70+ddxp9yyt9iHqgmSs0yTZc5XH5Wo+g80RWRIYNqE58QJKm/yMud5wZEvz50ugRDuzVIkyahg==", "dev": true, "requires": { - "clone": "2.1.1", - "loader-utils": "1.1.0", - "pify": "3.0.0" + "clone": "^2.1.1", + "loader-utils": "^1.1.0", + "pify": "^3.0.0" }, "dependencies": { "clone": { @@ -5950,11 +5951,11 @@ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "strip-bom": "2.0.0" + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" } }, "loader-runner": { @@ -5969,9 +5970,9 @@ "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", "dev": true, "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1" + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0" } }, "lodash": { @@ -6036,8 +6037,8 @@ "integrity": "sha1-LElBFmldb7JUgJQ9P8hy5mKlIv0=", "dev": true, "requires": { - "readable-stream": "1.0.34", - "semver": "4.3.6" + "readable-stream": "~1.0.2", + "semver": "~4.3.3" }, "dependencies": { "isarray": { @@ -6052,10 +6053,10 @@ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", "isarray": "0.0.1", - "string_decoder": "0.10.31" + "string_decoder": "~0.10.x" } }, "semver": { @@ -6084,7 +6085,7 @@ "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", "dev": true, "requires": { - "js-tokens": "3.0.2" + "js-tokens": "^3.0.0" } }, "loud-rejection": { @@ -6094,8 +6095,8 @@ "dev": true, "optional": true, "requires": { - "currently-unhandled": "0.4.1", - "signal-exit": "3.0.2" + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" } }, "lower-case": { @@ -6111,8 +6112,8 @@ "dev": true, "optional": true, "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" } }, "magic-string": { @@ -6121,7 +6122,7 @@ "integrity": "sha1-FNdoATyvLsj96hakmvgvw3fnUgE=", "dev": true, "requires": { - "vlq": "0.2.3" + "vlq": "^0.2.1" } }, "make-error": { @@ -6148,7 +6149,7 @@ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, "requires": { - "object-visit": "1.0.1" + "object-visit": "^1.0.0" } }, "matcher-collection": { @@ -6157,7 +6158,7 @@ "integrity": "sha512-nUCmzKipcJEwYsBVAFh5P+d7JBuhJaW1xs85Hara9xuMLqtCVUrW6DSC0JVIkluxEH2W45nPBM/wjHtBXa/tYA==", "dev": true, "requires": { - "minimatch": "3.0.4" + "minimatch": "^3.0.2" } }, "math-expression-evaluator": { @@ -6172,8 +6173,8 @@ "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", "dev": true, "requires": { - "hash-base": "3.0.4", - "inherits": "2.0.3" + "hash-base": "^3.0.0", + "inherits": "^2.0.1" } }, "media-typer": { @@ -6188,8 +6189,8 @@ "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", "dev": true, "requires": { - "errno": "0.1.7", - "readable-stream": "2.3.3" + "errno": "^0.1.3", + "readable-stream": "^2.0.1" } }, "meow": { @@ -6199,16 +6200,16 @@ "dev": true, "optional": true, "requires": { - "camelcase-keys": "2.1.0", - "decamelize": "1.2.0", - "loud-rejection": "1.6.0", - "map-obj": "1.0.1", - "minimist": "1.2.0", - "normalize-package-data": "2.4.0", - "object-assign": "4.1.1", - "read-pkg-up": "1.0.1", - "redent": "1.0.0", - "trim-newlines": "1.0.0" + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" }, "dependencies": { "minimist": { @@ -6238,19 +6239,19 @@ "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.3" + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" } }, "miller-rabin": { @@ -6259,8 +6260,8 @@ "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", "dev": true, "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0" + "bn.js": "^4.0.0", + "brorand": "^1.0.1" } }, "mime": { @@ -6281,7 +6282,7 @@ "integrity": "sha1-K4WKUuXs1RbbiXrCvodIeDBpjiM=", "dev": true, "requires": { - "mime-db": "1.29.0" + "mime-db": "~1.29.0" } }, "mimic-fn": { @@ -6308,7 +6309,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.8" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -6323,8 +6324,8 @@ "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", "dev": true, "requires": { - "for-in": "1.0.2", - "is-extendable": "1.0.1" + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" }, "dependencies": { "is-extendable": { @@ -6333,7 +6334,7 @@ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "2.0.4" + "is-plain-object": "^2.0.4" } } } @@ -6344,8 +6345,8 @@ "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", "dev": true, "requires": { - "for-in": "0.1.8", - "is-extendable": "0.1.1" + "for-in": "^0.1.3", + "is-extendable": "^0.1.1" }, "dependencies": { "for-in": { @@ -6395,18 +6396,18 @@ "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", "dev": true, "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "fragment-cache": "0.2.1", - "is-odd": "2.0.0", - "is-windows": "1.0.2", - "kind-of": "6.0.2", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-odd": "^2.0.0", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "arr-diff": { @@ -6452,7 +6453,7 @@ "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", "dev": true, "requires": { - "lower-case": "1.1.4" + "lower-case": "^1.1.1" } }, "node-gyp": { @@ -6462,18 +6463,18 @@ "dev": true, "optional": true, "requires": { - "fstream": "1.0.11", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "mkdirp": "0.5.1", - "nopt": "3.0.6", - "npmlog": "4.1.2", - "osenv": "0.1.5", - "request": "2.81.0", - "rimraf": "2.6.1", - "semver": "5.3.0", - "tar": "2.2.1", - "which": "1.3.0" + "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.9.0 <2.82.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" }, "dependencies": { "nopt": { @@ -6483,7 +6484,7 @@ "dev": true, "optional": true, "requires": { - "abbrev": "1.1.1" + "abbrev": "1" } }, "semver": { @@ -6501,28 +6502,28 @@ "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", "dev": true, "requires": { - "assert": "1.4.1", - "browserify-zlib": "0.2.0", - "buffer": "4.9.1", - "console-browserify": "1.1.0", - "constants-browserify": "1.0.0", - "crypto-browserify": "3.12.0", - "domain-browser": "1.2.0", - "events": "1.1.1", - "https-browserify": "1.0.0", - "os-browserify": "0.3.0", + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^1.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", "path-browserify": "0.0.0", - "process": "0.11.10", - "punycode": "1.4.1", - "querystring-es3": "0.2.1", - "readable-stream": "2.3.3", - "stream-browserify": "2.0.1", - "stream-http": "2.8.3", - "string_decoder": "1.0.3", - "timers-browserify": "2.0.10", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", "tty-browserify": "0.0.0", - "url": "0.11.0", - "util": "0.10.4", + "url": "^0.11.0", + "util": "^0.10.3", "vm-browserify": "0.0.4" } }, @@ -6539,25 +6540,25 @@ "dev": true, "optional": true, "requires": { - "async-foreach": "0.1.3", - "chalk": "1.1.3", - "cross-spawn": "3.0.1", - "gaze": "1.1.3", - "get-stdin": "4.0.1", - "glob": "7.1.2", - "in-publish": "2.0.0", - "lodash.assign": "4.2.0", - "lodash.clonedeep": "4.5.0", - "lodash.mergewith": "4.6.1", - "meow": "3.7.0", - "mkdirp": "0.5.1", - "nan": "2.10.0", - "node-gyp": "3.7.0", - "npmlog": "4.1.2", - "request": "2.79.0", - "sass-graph": "2.2.4", - "stdout-stream": "1.4.0", - "true-case-path": "1.0.2" + "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.assign": "^4.2.0", + "lodash.clonedeep": "^4.3.2", + "lodash.mergewith": "^4.6.0", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.10.0", + "node-gyp": "^3.3.1", + "npmlog": "^4.0.0", + "request": "~2.79.0", + "sass-graph": "^2.2.4", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" }, "dependencies": { "caseless": { @@ -6574,10 +6575,10 @@ "dev": true, "optional": true, "requires": { - "chalk": "1.1.3", - "commander": "2.11.0", - "is-my-json-valid": "2.17.2", - "pinkie-promise": "2.0.1" + "chalk": "^1.1.1", + "commander": "^2.9.0", + "is-my-json-valid": "^2.12.4", + "pinkie-promise": "^2.0.0" } }, "qs": { @@ -6594,26 +6595,26 @@ "dev": true, "optional": true, "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.11.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "2.0.6", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.16", - "oauth-sign": "0.8.2", - "qs": "6.3.2", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.4.3", - "uuid": "3.1.0" + "aws-sign2": "~0.6.0", + "aws4": "^1.2.1", + "caseless": "~0.11.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.0", + "forever-agent": "~0.6.1", + "form-data": "~2.1.1", + "har-validator": "~2.0.6", + "hawk": "~3.1.3", + "http-signature": "~1.1.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.7", + "oauth-sign": "~0.8.1", + "qs": "~6.3.0", + "stringstream": "~0.0.4", + "tough-cookie": "~2.3.0", + "tunnel-agent": "~0.4.1", + "uuid": "^3.0.0" } }, "tunnel-agent": { @@ -6631,8 +6632,8 @@ "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.5" + "abbrev": "1", + "osenv": "^0.1.4" } }, "normalize-package-data": { @@ -6641,10 +6642,10 @@ "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "dev": true, "requires": { - "hosted-git-info": "2.6.1", - "is-builtin-module": "1.0.0", - "semver": "5.4.1", - "validate-npm-package-license": "3.0.3" + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, "normalize-path": { @@ -6653,7 +6654,7 @@ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "1.0.2" + "remove-trailing-separator": "^1.0.1" } }, "normalize-range": { @@ -6668,10 +6669,10 @@ "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", "dev": true, "requires": { - "object-assign": "4.1.1", - "prepend-http": "1.0.4", - "query-string": "4.3.4", - "sort-keys": "1.1.2" + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" } }, "npmlog": { @@ -6680,10 +6681,10 @@ "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "requires": { - "are-we-there-yet": "1.1.5", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, "nth-check": { @@ -6692,7 +6693,7 @@ "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", "dev": true, "requires": { - "boolbase": "1.0.0" + "boolbase": "~1.0.0" } }, "null-check": { @@ -6737,9 +6738,9 @@ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, "requires": { - "copy-descriptor": "0.1.1", - "define-property": "0.2.5", - "kind-of": "3.2.2" + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" }, "dependencies": { "define-property": { @@ -6748,7 +6749,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } } } @@ -6759,7 +6760,7 @@ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.0" } }, "object.omit": { @@ -6768,8 +6769,8 @@ "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", "dev": true, "requires": { - "for-own": "0.1.5", - "is-extendable": "0.1.1" + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" }, "dependencies": { "for-own": { @@ -6778,7 +6779,7 @@ "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", "dev": true, "requires": { - "for-in": "1.0.2" + "for-in": "^1.0.1" } } } @@ -6789,7 +6790,7 @@ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.1" } }, "obuf": { @@ -6819,7 +6820,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "onetime": { @@ -6828,7 +6829,7 @@ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", "dev": true, "requires": { - "mimic-fn": "1.2.0" + "mimic-fn": "^1.0.0" } }, "opn": { @@ -6837,8 +6838,8 @@ "integrity": "sha1-erwi5kTf9jsKltWrfyeQwPAavJU=", "dev": true, "requires": { - "object-assign": "4.1.1", - "pinkie-promise": "2.0.1" + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" } }, "optimist": { @@ -6847,7 +6848,7 @@ "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=", "dev": true, "requires": { - "wordwrap": "0.0.3" + "wordwrap": "~0.0.2" } }, "options": { @@ -6862,7 +6863,7 @@ "integrity": "sha512-IEvtB5vM5ULvwnqMxWBLxkS13JIEXbakizMSo3yoPNPCIWzg8TG3Usn/UhXoZFM/m+FuEA20KdzPSFq/0rS+UA==", "dev": true, "requires": { - "url-parse": "1.4.1" + "url-parse": "~1.4.0" } }, "os-browserify": { @@ -6883,7 +6884,7 @@ "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { - "lcid": "1.0.0" + "lcid": "^1.0.0" } }, "os-tmpdir": { @@ -6898,8 +6899,8 @@ "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, "pako": { @@ -6914,7 +6915,7 @@ "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", "dev": true, "requires": { - "no-case": "2.3.2" + "no-case": "^2.2.0" } }, "parse-asn1": { @@ -6923,11 +6924,11 @@ "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", "dev": true, "requires": { - "asn1.js": "4.10.1", - "browserify-aes": "1.2.0", - "create-hash": "1.2.0", - "evp_bytestokey": "1.0.3", - "pbkdf2": "3.0.16" + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3" } }, "parse-glob": { @@ -6936,10 +6937,10 @@ "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", "dev": true, "requires": { - "glob-base": "0.3.0", - "is-dotfile": "1.0.3", - "is-extglob": "1.0.0", - "is-glob": "2.0.1" + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" } }, "parse-json": { @@ -6948,7 +6949,7 @@ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { - "error-ex": "1.3.2" + "error-ex": "^1.2.0" } }, "parsejson": { @@ -6957,7 +6958,7 @@ "integrity": "sha1-q343WfIJ7OmUN5c/fQ8fZK4OZKs=", "dev": true, "requires": { - "better-assert": "1.0.2" + "better-assert": "~1.0.0" } }, "parseqs": { @@ -6966,7 +6967,7 @@ "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", "dev": true, "requires": { - "better-assert": "1.0.2" + "better-assert": "~1.0.0" } }, "parseuri": { @@ -6975,7 +6976,7 @@ "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", "dev": true, "requires": { - "better-assert": "1.0.2" + "better-assert": "~1.0.0" } }, "parseurl": { @@ -7008,7 +7009,7 @@ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { - "pinkie-promise": "2.0.1" + "pinkie-promise": "^2.0.0" } }, "path-is-absolute": { @@ -7041,9 +7042,9 @@ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "pbkdf2": { @@ -7052,11 +7053,11 @@ "integrity": "sha512-y4CXP3thSxqf7c0qmOF+9UeOTrifiVTIM+u7NWlq+PRsHbr7r7dpCmvzrZxa96JJUNi0Y5w9VqG5ZNeCVMoDcA==", "dev": true, "requires": { - "create-hash": "1.2.0", - "create-hmac": "1.1.7", - "ripemd160": "2.0.2", - "safe-buffer": "5.1.1", - "sha.js": "2.4.11" + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" } }, "performance-now": { @@ -7083,7 +7084,7 @@ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { - "pinkie": "2.0.4" + "pinkie": "^2.0.0" } }, "portfinder": { @@ -7092,9 +7093,9 @@ "integrity": "sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=", "dev": true, "requires": { - "async": "1.5.2", - "debug": "2.6.8", - "mkdirp": "0.5.1" + "async": "^1.5.2", + "debug": "^2.2.0", + "mkdirp": "0.5.x" }, "dependencies": { "async": { @@ -7117,10 +7118,10 @@ "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "dev": true, "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.5", - "source-map": "0.5.6", - "supports-color": "3.2.3" + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" } }, "postcss-calc": { @@ -7129,9 +7130,9 @@ "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", "dev": true, "requires": { - "postcss": "5.2.18", - "postcss-message-helpers": "2.0.0", - "reduce-css-calc": "1.3.0" + "postcss": "^5.0.2", + "postcss-message-helpers": "^2.0.0", + "reduce-css-calc": "^1.2.6" } }, "postcss-colormin": { @@ -7140,9 +7141,9 @@ "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", "dev": true, "requires": { - "colormin": "1.1.2", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" + "colormin": "^1.0.5", + "postcss": "^5.0.13", + "postcss-value-parser": "^3.2.3" } }, "postcss-convert-values": { @@ -7151,8 +7152,8 @@ "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", "dev": true, "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" + "postcss": "^5.0.11", + "postcss-value-parser": "^3.1.2" } }, "postcss-discard-comments": { @@ -7161,7 +7162,7 @@ "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", "dev": true, "requires": { - "postcss": "5.2.18" + "postcss": "^5.0.14" } }, "postcss-discard-duplicates": { @@ -7170,7 +7171,7 @@ "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", "dev": true, "requires": { - "postcss": "5.2.18" + "postcss": "^5.0.4" } }, "postcss-discard-empty": { @@ -7179,7 +7180,7 @@ "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", "dev": true, "requires": { - "postcss": "5.2.18" + "postcss": "^5.0.14" } }, "postcss-discard-overridden": { @@ -7188,7 +7189,7 @@ "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", "dev": true, "requires": { - "postcss": "5.2.18" + "postcss": "^5.0.16" } }, "postcss-discard-unused": { @@ -7197,8 +7198,8 @@ "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", "dev": true, "requires": { - "postcss": "5.2.18", - "uniqs": "2.0.0" + "postcss": "^5.0.14", + "uniqs": "^2.0.0" } }, "postcss-filter-plugins": { @@ -7207,7 +7208,7 @@ "integrity": "sha512-T53GVFsdinJhgwm7rg1BzbeBRomOg9y5MBVhGcsV0CxurUdVj1UlPdKtn7aqYA/c/QVkzKMjq2bSV5dKG5+AwQ==", "dev": true, "requires": { - "postcss": "5.2.18" + "postcss": "^5.0.4" } }, "postcss-load-config": { @@ -7216,10 +7217,10 @@ "integrity": "sha1-U56a/J3chiASHr+djDZz4M5Q0oo=", "dev": true, "requires": { - "cosmiconfig": "2.2.2", - "object-assign": "4.1.1", - "postcss-load-options": "1.2.0", - "postcss-load-plugins": "2.3.0" + "cosmiconfig": "^2.1.0", + "object-assign": "^4.1.0", + "postcss-load-options": "^1.2.0", + "postcss-load-plugins": "^2.3.0" } }, "postcss-load-options": { @@ -7228,8 +7229,8 @@ "integrity": "sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw=", "dev": true, "requires": { - "cosmiconfig": "2.2.2", - "object-assign": "4.1.1" + "cosmiconfig": "^2.1.0", + "object-assign": "^4.1.0" } }, "postcss-load-plugins": { @@ -7238,8 +7239,8 @@ "integrity": "sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI=", "dev": true, "requires": { - "cosmiconfig": "2.2.2", - "object-assign": "4.1.1" + "cosmiconfig": "^2.1.1", + "object-assign": "^4.1.0" } }, "postcss-loader": { @@ -7248,10 +7249,10 @@ "integrity": "sha1-piHqH6KQYqg5cqRvVEhncTAZFus=", "dev": true, "requires": { - "loader-utils": "1.1.0", - "object-assign": "4.1.1", - "postcss": "5.2.18", - "postcss-load-config": "1.2.0" + "loader-utils": "^1.0.2", + "object-assign": "^4.1.1", + "postcss": "^5.2.15", + "postcss-load-config": "^1.2.0" } }, "postcss-merge-idents": { @@ -7260,9 +7261,9 @@ "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", "dev": true, "requires": { - "has": "1.0.3", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" + "has": "^1.0.1", + "postcss": "^5.0.10", + "postcss-value-parser": "^3.1.1" } }, "postcss-merge-longhand": { @@ -7271,7 +7272,7 @@ "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", "dev": true, "requires": { - "postcss": "5.2.18" + "postcss": "^5.0.4" } }, "postcss-merge-rules": { @@ -7280,11 +7281,11 @@ "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", "dev": true, "requires": { - "browserslist": "1.7.7", - "caniuse-api": "1.6.1", - "postcss": "5.2.18", - "postcss-selector-parser": "2.2.3", - "vendors": "1.0.2" + "browserslist": "^1.5.2", + "caniuse-api": "^1.5.2", + "postcss": "^5.0.4", + "postcss-selector-parser": "^2.2.2", + "vendors": "^1.0.0" } }, "postcss-message-helpers": { @@ -7299,9 +7300,9 @@ "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", "dev": true, "requires": { - "object-assign": "4.1.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" + "object-assign": "^4.0.1", + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.2" } }, "postcss-minify-gradients": { @@ -7310,8 +7311,8 @@ "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", "dev": true, "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" + "postcss": "^5.0.12", + "postcss-value-parser": "^3.3.0" } }, "postcss-minify-params": { @@ -7320,10 +7321,10 @@ "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", "dev": true, "requires": { - "alphanum-sort": "1.0.2", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0", - "uniqs": "2.0.0" + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.2", + "postcss-value-parser": "^3.0.2", + "uniqs": "^2.0.0" } }, "postcss-minify-selectors": { @@ -7332,10 +7333,10 @@ "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", "dev": true, "requires": { - "alphanum-sort": "1.0.2", - "has": "1.0.3", - "postcss": "5.2.18", - "postcss-selector-parser": "2.2.3" + "alphanum-sort": "^1.0.2", + "has": "^1.0.1", + "postcss": "^5.0.14", + "postcss-selector-parser": "^2.0.0" } }, "postcss-modules-extract-imports": { @@ -7344,7 +7345,7 @@ "integrity": "sha1-ZhQOzs447wa/DT41XWm/WdFB6oU=", "dev": true, "requires": { - "postcss": "6.0.23" + "postcss": "^6.0.1" }, "dependencies": { "ansi-styles": { @@ -7353,7 +7354,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.2" + "color-convert": "^1.9.0" } }, "chalk": { @@ -7362,9 +7363,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "has-flag": { @@ -7379,9 +7380,9 @@ "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", "dev": true, "requires": { - "chalk": "2.4.1", - "source-map": "0.6.1", - "supports-color": "5.4.0" + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" } }, "source-map": { @@ -7396,7 +7397,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -7407,8 +7408,8 @@ "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", "dev": true, "requires": { - "css-selector-tokenizer": "0.7.0", - "postcss": "6.0.23" + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" }, "dependencies": { "ansi-styles": { @@ -7417,7 +7418,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.2" + "color-convert": "^1.9.0" } }, "chalk": { @@ -7426,9 +7427,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "has-flag": { @@ -7443,9 +7444,9 @@ "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", "dev": true, "requires": { - "chalk": "2.4.1", - "source-map": "0.6.1", - "supports-color": "5.4.0" + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" } }, "source-map": { @@ -7460,7 +7461,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -7471,8 +7472,8 @@ "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", "dev": true, "requires": { - "css-selector-tokenizer": "0.7.0", - "postcss": "6.0.23" + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" }, "dependencies": { "ansi-styles": { @@ -7481,7 +7482,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.2" + "color-convert": "^1.9.0" } }, "chalk": { @@ -7490,9 +7491,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "has-flag": { @@ -7507,9 +7508,9 @@ "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", "dev": true, "requires": { - "chalk": "2.4.1", - "source-map": "0.6.1", - "supports-color": "5.4.0" + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" } }, "source-map": { @@ -7524,7 +7525,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -7535,8 +7536,8 @@ "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", "dev": true, "requires": { - "icss-replace-symbols": "1.1.0", - "postcss": "6.0.23" + "icss-replace-symbols": "^1.1.0", + "postcss": "^6.0.1" }, "dependencies": { "ansi-styles": { @@ -7545,7 +7546,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.2" + "color-convert": "^1.9.0" } }, "chalk": { @@ -7554,9 +7555,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "has-flag": { @@ -7571,9 +7572,9 @@ "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", "dev": true, "requires": { - "chalk": "2.4.1", - "source-map": "0.6.1", - "supports-color": "5.4.0" + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" } }, "source-map": { @@ -7588,7 +7589,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -7599,7 +7600,7 @@ "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", "dev": true, "requires": { - "postcss": "5.2.18" + "postcss": "^5.0.5" } }, "postcss-normalize-url": { @@ -7608,10 +7609,10 @@ "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", "dev": true, "requires": { - "is-absolute-url": "2.1.0", - "normalize-url": "1.9.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" + "is-absolute-url": "^2.0.0", + "normalize-url": "^1.4.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3" } }, "postcss-ordered-values": { @@ -7620,8 +7621,8 @@ "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", "dev": true, "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.1" } }, "postcss-reduce-idents": { @@ -7630,8 +7631,8 @@ "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", "dev": true, "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.2" } }, "postcss-reduce-initial": { @@ -7640,7 +7641,7 @@ "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", "dev": true, "requires": { - "postcss": "5.2.18" + "postcss": "^5.0.4" } }, "postcss-reduce-transforms": { @@ -7649,9 +7650,9 @@ "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", "dev": true, "requires": { - "has": "1.0.3", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" + "has": "^1.0.1", + "postcss": "^5.0.8", + "postcss-value-parser": "^3.0.1" } }, "postcss-selector-parser": { @@ -7660,9 +7661,9 @@ "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", "dev": true, "requires": { - "flatten": "1.0.2", - "indexes-of": "1.0.1", - "uniq": "1.0.1" + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" } }, "postcss-svgo": { @@ -7671,10 +7672,10 @@ "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", "dev": true, "requires": { - "is-svg": "2.1.0", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0", - "svgo": "0.7.2" + "is-svg": "^2.0.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3", + "svgo": "^0.7.0" } }, "postcss-unique-selectors": { @@ -7683,9 +7684,9 @@ "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", "dev": true, "requires": { - "alphanum-sort": "1.0.2", - "postcss": "5.2.18", - "uniqs": "2.0.0" + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.4", + "uniqs": "^2.0.0" } }, "postcss-url": { @@ -7694,13 +7695,13 @@ "integrity": "sha1-mLMWW+jVkkccsMqt3iwNH4MvEz4=", "dev": true, "requires": { - "directory-encoder": "0.7.2", - "js-base64": "2.4.5", - "mime": "1.3.6", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "path-is-absolute": "1.0.1", - "postcss": "5.2.18" + "directory-encoder": "^0.7.2", + "js-base64": "^2.1.5", + "mime": "^1.2.11", + "minimatch": "^3.0.0", + "mkdirp": "^0.5.0", + "path-is-absolute": "^1.0.0", + "postcss": "^5.0.0" } }, "postcss-value-parser": { @@ -7715,9 +7716,9 @@ "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", "dev": true, "requires": { - "has": "1.0.3", - "postcss": "5.2.18", - "uniqs": "2.0.0" + "has": "^1.0.1", + "postcss": "^5.0.4", + "uniqs": "^2.0.0" } }, "prepend-http": { @@ -7738,8 +7739,8 @@ "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", "dev": true, "requires": { - "renderkid": "2.0.1", - "utila": "0.4.0" + "renderkid": "^2.0.1", + "utila": "~0.4" } }, "process": { @@ -7761,7 +7762,7 @@ "dev": true, "optional": true, "requires": { - "asap": "2.0.6" + "asap": "~2.0.3" } }, "protractor": { @@ -7770,21 +7771,21 @@ "integrity": "sha1-myIXQXCaTGLVzVPGqt1UpxE36V8=", "dev": true, "requires": { - "@types/node": "6.0.85", - "@types/q": "0.0.32", - "@types/selenium-webdriver": "2.53.42", + "@types/node": "^6.0.46", + "@types/q": "^0.0.32", + "@types/selenium-webdriver": "~2.53.39", "blocking-proxy": "0.0.5", - "chalk": "1.1.3", - "glob": "7.1.2", - "jasmine": "2.7.0", - "jasminewd2": "2.1.0", - "optimist": "0.6.1", + "chalk": "^1.1.3", + "glob": "^7.0.3", + "jasmine": "^2.5.3", + "jasminewd2": "^2.1.0", + "optimist": "~0.6.0", "q": "1.4.1", - "saucelabs": "1.3.0", + "saucelabs": "~1.3.0", "selenium-webdriver": "3.0.1", - "source-map-support": "0.4.15", - "webdriver-js-extender": "1.0.0", - "webdriver-manager": "12.0.6" + "source-map-support": "~0.4.0", + "webdriver-js-extender": "^1.0.0", + "webdriver-manager": "^12.0.6" }, "dependencies": { "optimist": { @@ -7793,8 +7794,8 @@ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { - "minimist": "0.0.8", - "wordwrap": "0.0.3" + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" } }, "q": { @@ -7809,17 +7810,17 @@ "integrity": "sha1-PfGkgZdwELTL+MnYXHpXeCjA5ws=", "dev": true, "requires": { - "adm-zip": "0.4.7", - "chalk": "1.1.3", - "del": "2.2.2", - "glob": "7.1.2", - "ini": "1.3.4", - "minimist": "1.2.0", - "q": "1.4.1", - "request": "2.81.0", - "rimraf": "2.6.1", - "semver": "5.4.1", - "xml2js": "0.4.17" + "adm-zip": "^0.4.7", + "chalk": "^1.1.1", + "del": "^2.2.0", + "glob": "^7.0.3", + "ini": "^1.3.4", + "minimist": "^1.2.0", + "q": "^1.4.1", + "request": "^2.78.0", + "rimraf": "^2.5.2", + "semver": "^5.3.0", + "xml2js": "^0.4.17" }, "dependencies": { "minimist": { @@ -7838,7 +7839,7 @@ "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", "dev": true, "requires": { - "forwarded": "0.1.2", + "forwarded": "~0.1.2", "ipaddr.js": "1.6.0" } }, @@ -7861,11 +7862,11 @@ "integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==", "dev": true, "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.2.0", - "parse-asn1": "5.1.1", - "randombytes": "2.0.6" + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1" } }, "punycode": { @@ -7898,8 +7899,8 @@ "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", "dev": true, "requires": { - "object-assign": "4.1.1", - "strict-uri-encode": "1.1.0" + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" } }, "querystring": { @@ -7926,8 +7927,8 @@ "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", "dev": true, "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" + "is-number": "^3.0.0", + "kind-of": "^4.0.0" }, "dependencies": { "is-number": { @@ -7936,7 +7937,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -7945,7 +7946,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.5" + "is-buffer": "^1.1.5" } } } @@ -7956,7 +7957,7 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "1.1.5" + "is-buffer": "^1.1.5" } } } @@ -7967,7 +7968,7 @@ "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", "dev": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "^5.1.0" } }, "randomfill": { @@ -7976,8 +7977,8 @@ "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", "dev": true, "requires": { - "randombytes": "2.0.6", - "safe-buffer": "5.1.1" + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" } }, "range-parser": { @@ -8023,9 +8024,9 @@ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { - "load-json-file": "1.1.0", - "normalize-package-data": "2.4.0", - "path-type": "1.1.0" + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" } }, "read-pkg-up": { @@ -8034,8 +8035,8 @@ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { - "find-up": "1.1.2", - "read-pkg": "1.1.0" + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" } }, "readable-stream": { @@ -8044,13 +8045,13 @@ "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" } }, "readdirp": { @@ -8059,10 +8060,10 @@ "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "minimatch": "3.0.4", - "readable-stream": "2.3.3", - "set-immediate-shim": "1.0.1" + "graceful-fs": "^4.1.2", + "minimatch": "^3.0.2", + "readable-stream": "^2.0.2", + "set-immediate-shim": "^1.0.1" } }, "redent": { @@ -8072,8 +8073,8 @@ "dev": true, "optional": true, "requires": { - "indent-string": "2.1.0", - "strip-indent": "1.0.1" + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" } }, "reduce-css-calc": { @@ -8082,9 +8083,9 @@ "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", "dev": true, "requires": { - "balanced-match": "0.4.2", - "math-expression-evaluator": "1.2.17", - "reduce-function-call": "1.0.2" + "balanced-match": "^0.4.2", + "math-expression-evaluator": "^1.2.14", + "reduce-function-call": "^1.0.1" } }, "reduce-function-call": { @@ -8093,7 +8094,7 @@ "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", "dev": true, "requires": { - "balanced-match": "0.4.2" + "balanced-match": "^0.4.2" } }, "reflect-metadata": { @@ -8120,8 +8121,8 @@ "integrity": "sha1-mxpsNdTQ3871cRrmUejp09cRQUU=", "dev": true, "requires": { - "is-equal-shallow": "0.1.3", - "is-primitive": "2.0.0" + "is-equal-shallow": "^0.1.3", + "is-primitive": "^2.0.0" } }, "regex-not": { @@ -8130,8 +8131,8 @@ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, "requires": { - "extend-shallow": "3.0.2", - "safe-regex": "1.1.0" + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" } }, "regexpu-core": { @@ -8140,9 +8141,9 @@ "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", "dev": true, "requires": { - "regenerate": "1.3.2", - "regjsgen": "0.2.0", - "regjsparser": "0.1.5" + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" } }, "regjsgen": { @@ -8157,7 +8158,7 @@ "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "dev": true, "requires": { - "jsesc": "0.5.0" + "jsesc": "~0.5.0" } }, "relateurl": { @@ -8178,11 +8179,11 @@ "integrity": "sha1-iYyr/Ivt5Le5ETWj/9Mj5YwNsxk=", "dev": true, "requires": { - "css-select": "1.2.0", - "dom-converter": "0.1.4", - "htmlparser2": "3.3.0", - "strip-ansi": "3.0.1", - "utila": "0.3.3" + "css-select": "^1.1.0", + "dom-converter": "~0.1", + "htmlparser2": "~3.3.0", + "strip-ansi": "^3.0.0", + "utila": "~0.3" }, "dependencies": { "utila": { @@ -8211,7 +8212,7 @@ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, "requires": { - "is-finite": "1.0.2" + "is-finite": "^1.0.0" } }, "request": { @@ -8220,28 +8221,28 @@ "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", "dev": true, "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.16", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.6.0", - "uuid": "3.1.0" + "aws-sign2": "~0.6.0", + "aws4": "^1.2.1", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.0", + "forever-agent": "~0.6.1", + "form-data": "~2.1.1", + "har-validator": "~4.2.1", + "hawk": "~3.1.3", + "http-signature": "~1.1.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.7", + "oauth-sign": "~0.8.1", + "performance-now": "^0.2.0", + "qs": "~6.4.0", + "safe-buffer": "^5.0.1", + "stringstream": "~0.0.4", + "tough-cookie": "~2.3.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.0.0" } }, "require-directory": { @@ -8274,7 +8275,7 @@ "integrity": "sha512-aW7sVKPufyHqOmyyLzg/J+8606v5nevBgaliIlV7nUpVMsDnoBGV/cbSLNjZAg9q0Cfd/+easKVKQ8vOu8fn1Q==", "dev": true, "requires": { - "path-parse": "1.0.5" + "path-parse": "^1.0.5" } }, "resolve-url": { @@ -8289,8 +8290,8 @@ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", "dev": true, "requires": { - "onetime": "2.0.1", - "signal-exit": "3.0.2" + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" } }, "ret": { @@ -8305,7 +8306,7 @@ "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", "dev": true, "requires": { - "align-text": "0.1.4" + "align-text": "^0.1.1" } }, "rimraf": { @@ -8314,7 +8315,7 @@ "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", "dev": true, "requires": { - "glob": "7.1.2" + "glob": "^7.0.5" } }, "ripemd160": { @@ -8323,8 +8324,8 @@ "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", "dev": true, "requires": { - "hash-base": "3.0.4", - "inherits": "2.0.3" + "hash-base": "^3.0.0", + "inherits": "^2.0.1" } }, "rsvp": { @@ -8339,7 +8340,7 @@ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", "dev": true, "requires": { - "is-promise": "2.1.0" + "is-promise": "^2.1.0" } }, "rw": { @@ -8359,7 +8360,7 @@ "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", "dev": true, "requires": { - "rx-lite": "4.0.8" + "rx-lite": "*" } }, "rxjs": { @@ -8367,7 +8368,7 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.4.2.tgz", "integrity": "sha1-KjI2/L8D31e64G/Wly/ZnlwI/Pc=", "requires": { - "symbol-observable": "1.0.4" + "symbol-observable": "^1.0.1" } }, "safe-buffer": { @@ -8382,7 +8383,7 @@ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { - "ret": "0.1.15" + "ret": "~0.1.10" } }, "sass-graph": { @@ -8392,10 +8393,10 @@ "dev": true, "optional": true, "requires": { - "glob": "7.1.2", - "lodash": "4.17.4", - "scss-tokenizer": "0.2.3", - "yargs": "7.1.0" + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^7.0.0" } }, "sass-loader": { @@ -8404,11 +8405,11 @@ "integrity": "sha512-JoiyD00Yo1o61OJsoP2s2kb19L1/Y2p3QFcCdWdF6oomBGKVYuZyqHWemRBfQ2uGYsk+CH3eCguXNfpjzlcpaA==", "dev": true, "requires": { - "clone-deep": "2.0.2", - "loader-utils": "1.1.0", - "lodash.tail": "4.1.1", - "neo-async": "2.5.1", - "pify": "3.0.0" + "clone-deep": "^2.0.1", + "loader-utils": "^1.0.1", + "lodash.tail": "^4.1.1", + "neo-async": "^2.5.0", + "pify": "^3.0.0" }, "dependencies": { "pify": { @@ -8425,7 +8426,7 @@ "integrity": "sha1-0kDoAJ33+ocwbsRXimm6O1xCT+4=", "dev": true, "requires": { - "https-proxy-agent": "1.0.0" + "https-proxy-agent": "^1.0.0" } }, "sax": { @@ -8440,7 +8441,7 @@ "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", "dev": true, "requires": { - "ajv": "5.5.2" + "ajv": "^5.0.0" } }, "script-loader": { @@ -8449,7 +8450,7 @@ "integrity": "sha512-UMNLEvgOAQuzK8ji8qIscM3GIrRCWN6MmMXGD4SD5l6cSycgGsCo0tX5xRnfQcoghqct0tjHjcykgI1PyBE2aA==", "dev": true, "requires": { - "raw-loader": "0.5.1" + "raw-loader": "~0.5.1" } }, "scss-tokenizer": { @@ -8459,8 +8460,8 @@ "dev": true, "optional": true, "requires": { - "js-base64": "2.4.5", - "source-map": "0.4.4" + "js-base64": "^2.1.8", + "source-map": "^0.4.2" }, "dependencies": { "source-map": { @@ -8470,7 +8471,7 @@ "dev": true, "optional": true, "requires": { - "amdefine": "1.0.1" + "amdefine": ">=0.0.4" } } } @@ -8487,10 +8488,10 @@ "integrity": "sha1-ot6l2kqX9mcuiefKcnbO+jZRR6c=", "dev": true, "requires": { - "adm-zip": "0.4.7", - "rimraf": "2.6.1", + "adm-zip": "^0.4.7", + "rimraf": "^2.5.4", "tmp": "0.0.30", - "xml2js": "0.4.17" + "xml2js": "^0.4.17" }, "dependencies": { "tmp": { @@ -8499,7 +8500,7 @@ "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", "dev": true, "requires": { - "os-tmpdir": "1.0.2" + "os-tmpdir": "~1.0.1" } } } @@ -8516,7 +8517,7 @@ "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", "dev": true, "requires": { - "semver": "5.4.1" + "semver": "^5.3.0" } }, "send": { @@ -8526,18 +8527,18 @@ "dev": true, "requires": { "debug": "2.6.9", - "depd": "1.1.2", - "destroy": "1.0.4", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "etag": "1.8.1", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "1.6.3", + "http-errors": "~1.6.2", "mime": "1.4.1", "ms": "2.0.0", - "on-finished": "2.3.0", - "range-parser": "1.2.0", - "statuses": "1.4.0" + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" }, "dependencies": { "debug": { @@ -8567,10 +8568,10 @@ "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { - "depd": "1.1.2", + "depd": "~1.1.2", "inherits": "2.0.3", "setprototypeof": "1.1.0", - "statuses": "1.4.0" + "statuses": ">= 1.4.0 < 2" } }, "mime": { @@ -8599,13 +8600,13 @@ "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", "dev": true, "requires": { - "accepts": "1.3.5", + "accepts": "~1.3.4", "batch": "0.6.1", "debug": "2.6.9", - "escape-html": "1.0.3", - "http-errors": "1.6.3", - "mime-types": "2.1.18", - "parseurl": "1.3.2" + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" }, "dependencies": { "accepts": { @@ -8614,7 +8615,7 @@ "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "dev": true, "requires": { - "mime-types": "2.1.18", + "mime-types": "~2.1.18", "negotiator": "0.6.1" } }, @@ -8639,10 +8640,10 @@ "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { - "depd": "1.1.2", + "depd": "~1.1.2", "inherits": "2.0.3", "setprototypeof": "1.1.0", - "statuses": "1.5.0" + "statuses": ">= 1.4.0 < 2" } }, "mime-db": { @@ -8657,7 +8658,7 @@ "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "dev": true, "requires": { - "mime-db": "1.33.0" + "mime-db": "~1.33.0" } }, "parseurl": { @@ -8686,9 +8687,9 @@ "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", "dev": true, "requires": { - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "parseurl": "1.3.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", "send": "0.16.2" }, "dependencies": { @@ -8724,10 +8725,10 @@ "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "split-string": "3.1.0" + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" }, "dependencies": { "extend-shallow": { @@ -8736,7 +8737,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -8759,8 +8760,8 @@ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, "shallow-clone": { @@ -8769,9 +8770,9 @@ "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==", "dev": true, "requires": { - "is-extendable": "0.1.1", - "kind-of": "5.1.0", - "mixin-object": "2.0.1" + "is-extendable": "^0.1.1", + "kind-of": "^5.0.0", + "mixin-object": "^2.0.1" }, "dependencies": { "kind-of": { @@ -8794,7 +8795,7 @@ "integrity": "sha1-IglwbxyFCp8dENDYQJGLRvJuG8k=", "dev": true, "requires": { - "debug": "2.6.8" + "debug": "^2.2.0" } }, "snapdragon": { @@ -8803,14 +8804,14 @@ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, "requires": { - "base": "0.11.2", - "debug": "2.6.8", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "map-cache": "0.2.2", - "source-map": "0.5.6", - "source-map-resolve": "0.5.2", - "use": "3.1.0" + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" }, "dependencies": { "define-property": { @@ -8819,7 +8820,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "extend-shallow": { @@ -8828,7 +8829,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -8839,9 +8840,9 @@ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, "requires": { - "define-property": "1.0.0", - "isobject": "3.0.1", - "snapdragon-util": "3.0.1" + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" }, "dependencies": { "define-property": { @@ -8850,7 +8851,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "is-accessor-descriptor": { @@ -8859,7 +8860,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -8868,7 +8869,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -8877,9 +8878,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "kind-of": { @@ -8896,7 +8897,7 @@ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.2.0" } }, "sntp": { @@ -8905,7 +8906,7 @@ "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", "dev": true, "requires": { - "hoek": "2.16.3" + "hoek": "2.x.x" } }, "socket.io": { @@ -9056,8 +9057,8 @@ "integrity": "sha1-2bKJMWyn33dZXvKZ4HXw+TfrQgc=", "dev": true, "requires": { - "faye-websocket": "0.10.0", - "uuid": "2.0.3" + "faye-websocket": "^0.10.0", + "uuid": "^2.0.2" }, "dependencies": { "uuid": { @@ -9074,12 +9075,12 @@ "integrity": "sha1-8CEqhVDkyUaMjM6u79LjSTwDOtU=", "dev": true, "requires": { - "debug": "2.6.8", + "debug": "^2.2.0", "eventsource": "0.1.6", - "faye-websocket": "0.11.1", - "inherits": "2.0.3", - "json3": "3.3.2", - "url-parse": "1.4.1" + "faye-websocket": "~0.11.0", + "inherits": "^2.0.1", + "json3": "^3.3.2", + "url-parse": "^1.1.1" }, "dependencies": { "faye-websocket": { @@ -9088,7 +9089,7 @@ "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", "dev": true, "requires": { - "websocket-driver": "0.7.0" + "websocket-driver": ">=0.5.1" } } } @@ -9099,7 +9100,7 @@ "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", "dev": true, "requires": { - "is-plain-obj": "1.1.0" + "is-plain-obj": "^1.0.0" } }, "source-list-map": { @@ -9120,9 +9121,9 @@ "integrity": "sha512-MYbFX9DYxmTQFfy2v8FC1XZwpwHKYxg3SK8Wb7VPBKuhDjz8gi9re2819MsG4p49HDyiOSUKlmZ+nQBArW5CGw==", "dev": true, "requires": { - "async": "2.5.0", - "loader-utils": "0.2.17", - "source-map": "0.6.1" + "async": "^2.5.0", + "loader-utils": "~0.2.2", + "source-map": "~0.6.1" }, "dependencies": { "loader-utils": { @@ -9131,10 +9132,10 @@ "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", "dev": true, "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1", - "object-assign": "4.1.1" + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" } }, "source-map": { @@ -9151,11 +9152,11 @@ "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", "dev": true, "requires": { - "atob": "2.1.1", - "decode-uri-component": "0.2.0", - "resolve-url": "0.2.1", - "source-map-url": "0.4.0", - "urix": "0.1.0" + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" } }, "source-map-support": { @@ -9164,7 +9165,7 @@ "integrity": "sha1-AyAt9lwG0r2MfsI2KhkwVv7407E=", "dev": true, "requires": { - "source-map": "0.5.6" + "source-map": "^0.5.6" } }, "source-map-url": { @@ -9179,8 +9180,8 @@ "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", "dev": true, "requires": { - "spdx-expression-parse": "3.0.0", - "spdx-license-ids": "3.0.0" + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" } }, "spdx-exceptions": { @@ -9195,8 +9196,8 @@ "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", "dev": true, "requires": { - "spdx-exceptions": "2.1.0", - "spdx-license-ids": "3.0.0" + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, "spdx-license-ids": { @@ -9211,12 +9212,12 @@ "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=", "dev": true, "requires": { - "debug": "2.6.8", - "handle-thing": "1.2.5", - "http-deceiver": "1.2.7", - "safe-buffer": "5.1.1", - "select-hose": "2.0.0", - "spdy-transport": "2.1.0" + "debug": "^2.6.8", + "handle-thing": "^1.2.5", + "http-deceiver": "^1.2.7", + "safe-buffer": "^5.0.1", + "select-hose": "^2.0.0", + "spdy-transport": "^2.0.18" } }, "spdy-transport": { @@ -9225,13 +9226,13 @@ "integrity": "sha512-bpUeGpZcmZ692rrTiqf9/2EUakI6/kXX1Rpe0ib/DyOzbiexVfXkw6GnvI9hVGvIwVaUhkaBojjCZwLNRGQg1g==", "dev": true, "requires": { - "debug": "2.6.8", - "detect-node": "2.0.3", - "hpack.js": "2.1.6", - "obuf": "1.1.2", - "readable-stream": "2.3.3", - "safe-buffer": "5.1.1", - "wbuf": "1.7.3" + "debug": "^2.6.8", + "detect-node": "^2.0.3", + "hpack.js": "^2.1.6", + "obuf": "^1.1.1", + "readable-stream": "^2.2.9", + "safe-buffer": "^5.0.1", + "wbuf": "^1.7.2" } }, "split-string": { @@ -9240,7 +9241,7 @@ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, "requires": { - "extend-shallow": "3.0.2" + "extend-shallow": "^3.0.0" } }, "sprintf-js": { @@ -9255,14 +9256,14 @@ "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", "dev": true, "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "tweetnacl": "~0.14.0" }, "dependencies": { "assert-plus": { @@ -9279,8 +9280,8 @@ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "requires": { - "define-property": "0.2.5", - "object-copy": "0.1.0" + "define-property": "^0.2.5", + "object-copy": "^0.1.0" }, "dependencies": { "define-property": { @@ -9289,7 +9290,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } } } @@ -9307,7 +9308,7 @@ "dev": true, "optional": true, "requires": { - "readable-stream": "2.3.3" + "readable-stream": "^2.0.1" } }, "stream-browserify": { @@ -9316,8 +9317,8 @@ "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", "dev": true, "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3" + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" } }, "stream-http": { @@ -9326,11 +9327,11 @@ "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", "dev": true, "requires": { - "builtin-status-codes": "3.0.0", - "inherits": "2.0.3", - "readable-stream": "2.3.6", - "to-arraybuffer": "1.0.1", - "xtend": "4.0.1" + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" }, "dependencies": { "process-nextick-args": { @@ -9345,13 +9346,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { - "core-util-is": "1.0.2", - "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.2" + "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": { @@ -9360,7 +9361,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } } } @@ -9377,8 +9378,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" }, "dependencies": { "ansi-regex": { @@ -9393,7 +9394,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } } } @@ -9404,7 +9405,7 @@ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "dev": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } }, "stringstream": { @@ -9419,7 +9420,7 @@ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "strip-bom": { @@ -9428,7 +9429,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "0.2.1" + "is-utf8": "^0.2.0" } }, "strip-indent": { @@ -9438,7 +9439,7 @@ "dev": true, "optional": true, "requires": { - "get-stdin": "4.0.1" + "get-stdin": "^4.0.1" } }, "strip-json-comments": { @@ -9453,7 +9454,7 @@ "integrity": "sha1-dFMzhM9pjHEEx5URULSXF63C87s=", "dev": true, "requires": { - "loader-utils": "1.1.0" + "loader-utils": "^1.0.2" } }, "stylus": { @@ -9462,12 +9463,12 @@ "integrity": "sha1-QrlWCTHKcJDOhRWnmLqeaqPW3Hk=", "dev": true, "requires": { - "css-parse": "1.7.0", - "debug": "2.6.8", - "glob": "7.0.6", - "mkdirp": "0.5.1", - "sax": "0.5.8", - "source-map": "0.1.43" + "css-parse": "1.7.x", + "debug": "*", + "glob": "7.0.x", + "mkdirp": "0.5.x", + "sax": "0.5.x", + "source-map": "0.1.x" }, "dependencies": { "glob": { @@ -9476,12 +9477,12 @@ "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "sax": { @@ -9496,7 +9497,7 @@ "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", "dev": true, "requires": { - "amdefine": "1.0.1" + "amdefine": ">=0.0.4" } } } @@ -9507,9 +9508,9 @@ "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==", "dev": true, "requires": { - "loader-utils": "1.1.0", - "lodash.clonedeep": "4.5.0", - "when": "3.6.4" + "loader-utils": "^1.0.2", + "lodash.clonedeep": "^4.5.0", + "when": "~3.6.x" } }, "supports-color": { @@ -9518,7 +9519,7 @@ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "1.0.0" + "has-flag": "^1.0.0" } }, "svgo": { @@ -9527,13 +9528,13 @@ "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", "dev": true, "requires": { - "coa": "1.0.4", - "colors": "1.1.2", - "csso": "2.3.2", - "js-yaml": "3.7.0", - "mkdirp": "0.5.1", - "sax": "1.2.4", - "whet.extend": "0.9.9" + "coa": "~1.0.1", + "colors": "~1.1.2", + "csso": "~2.3.1", + "js-yaml": "~3.7.0", + "mkdirp": "~0.5.1", + "sax": "~1.2.1", + "whet.extend": "~0.9.9" } }, "symbol-observable": { @@ -9554,9 +9555,9 @@ "dev": true, "optional": true, "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" } }, "temp": { @@ -9565,8 +9566,8 @@ "integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=", "dev": true, "requires": { - "os-tmpdir": "1.0.2", - "rimraf": "2.2.8" + "os-tmpdir": "^1.0.0", + "rimraf": "~2.2.6" }, "dependencies": { "rimraf": { @@ -9595,7 +9596,7 @@ "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", "dev": true, "requires": { - "setimmediate": "1.0.5" + "setimmediate": "^1.0.4" } }, "tmp": { @@ -9604,7 +9605,7 @@ "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", "dev": true, "requires": { - "os-tmpdir": "1.0.2" + "os-tmpdir": "~1.0.1" } }, "to-array": { @@ -9631,7 +9632,7 @@ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } }, "to-regex": { @@ -9640,10 +9641,10 @@ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "dev": true, "requires": { - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "regex-not": "1.0.2", - "safe-regex": "1.1.0" + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" } }, "to-regex-range": { @@ -9652,8 +9653,8 @@ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, "requires": { - "is-number": "3.0.0", - "repeat-string": "1.6.1" + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" }, "dependencies": { "is-number": { @@ -9662,7 +9663,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } } } @@ -9682,7 +9683,7 @@ "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.0.0.tgz", "integrity": "sha1-H5kpOnfvQqRI0DKoGqmCtz82DS8=", "requires": { - "commander": "2.11.0" + "commander": "2" } }, "topojson-server": { @@ -9690,7 +9691,7 @@ "resolved": "https://registry.npmjs.org/topojson-server/-/topojson-server-3.0.0.tgz", "integrity": "sha1-N4546Hw5cqe1vixdYENptrrmnF4=", "requires": { - "commander": "2.11.0" + "commander": "2" } }, "topojson-simplify": { @@ -9698,8 +9699,8 @@ "resolved": "https://registry.npmjs.org/topojson-simplify/-/topojson-simplify-3.0.0.tgz", "integrity": "sha1-WFJ9JsqFvYWJtYAPbBDpOIUkWuU=", "requires": { - "commander": "2.11.0", - "topojson-client": "3.0.0" + "commander": "2", + "topojson-client": "3" } } } @@ -9716,7 +9717,7 @@ "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", "dev": true, "requires": { - "punycode": "1.4.1" + "punycode": "^1.4.1" } }, "trim-newlines": { @@ -9739,7 +9740,7 @@ "dev": true, "optional": true, "requires": { - "glob": "6.0.4" + "glob": "^6.0.4" }, "dependencies": { "glob": { @@ -9749,11 +9750,11 @@ "dev": true, "optional": true, "requires": { - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } } } @@ -9764,16 +9765,16 @@ "integrity": "sha1-VRJ/95DH7r9rpowebd6UsJqqIeA=", "dev": true, "requires": { - "arrify": "1.0.1", - "chalk": "1.1.3", - "diff": "3.3.0", - "make-error": "1.3.0", - "minimist": "1.2.0", - "mkdirp": "0.5.1", - "source-map-support": "0.4.15", - "tsconfig": "6.0.0", - "v8flags": "2.1.1", - "yn": "2.0.0" + "arrify": "^1.0.0", + "chalk": "^1.1.1", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.0", + "tsconfig": "^6.0.0", + "v8flags": "^2.0.11", + "yn": "^2.0.0" }, "dependencies": { "minimist": { @@ -9790,8 +9791,8 @@ "integrity": "sha1-aw6DdgA9evGGT434+J3QBZ/80DI=", "dev": true, "requires": { - "strip-bom": "3.0.0", - "strip-json-comments": "2.0.1" + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" }, "dependencies": { "strip-bom": { @@ -9808,10 +9809,10 @@ "integrity": "sha1-U7Abl5xcE/2xOvs/uVgXflmRWI0=", "dev": true, "requires": { - "minimist": "1.2.0", - "mkdirp": "0.5.1", - "source-map": "0.5.6", - "source-map-support": "0.4.15" + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map": "^0.5.6", + "source-map-support": "^0.4.2" }, "dependencies": { "minimist": { @@ -9833,15 +9834,15 @@ "integrity": "sha1-5WRZ+wlacwfxA7hAUhdPXju+9u0=", "dev": true, "requires": { - "babel-code-frame": "6.22.0", - "colors": "1.1.2", - "diff": "3.3.0", - "glob": "7.1.2", - "optimist": "0.6.1", - "resolve": "1.4.0", - "semver": "5.4.1", - "tslib": "1.7.1", - "tsutils": "2.8.0" + "babel-code-frame": "^6.22.0", + "colors": "^1.1.2", + "diff": "^3.2.0", + "glob": "^7.1.1", + "optimist": "~0.6.0", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.6.0", + "tsutils": "^2.0.0" }, "dependencies": { "optimist": { @@ -9850,8 +9851,8 @@ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { - "minimist": "0.0.8", - "wordwrap": "0.0.3" + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" } } } @@ -9862,7 +9863,7 @@ "integrity": "sha1-AWAXNymzvxOGKN0UoVN+AIUdgUo=", "dev": true, "requires": { - "tslib": "1.7.1" + "tslib": "^1.7.1" } }, "tty-browserify": { @@ -9877,7 +9878,7 @@ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "^5.0.1" } }, "tweetnacl": { @@ -9894,7 +9895,7 @@ "dev": true, "requires": { "media-typer": "0.3.0", - "mime-types": "2.1.16" + "mime-types": "~2.1.15" } }, "typescript": { @@ -9909,8 +9910,8 @@ "integrity": "sha512-/kVQDzwiE9Vy7Y63eMkMozF4jIt0C2+xHctF9YpqNWdE/NLOuMurshkpoYGUlAbeYhACPv0HJPIHJul0Ak4/uw==", "dev": true, "requires": { - "commander": "2.15.1", - "source-map": "0.6.1" + "commander": "~2.15.0", + "source-map": "~0.6.1" }, "dependencies": { "commander": { @@ -9946,10 +9947,10 @@ "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", "dev": true, "requires": { - "arr-union": "3.1.0", - "get-value": "2.0.6", - "is-extendable": "0.1.1", - "set-value": "0.4.3" + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" }, "dependencies": { "extend-shallow": { @@ -9958,7 +9959,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } }, "set-value": { @@ -9967,10 +9968,10 @@ "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "to-object-path": "0.3.0" + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" } } } @@ -9999,8 +10000,8 @@ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, "requires": { - "has-value": "0.3.1", - "isobject": "3.0.1" + "has-value": "^0.3.1", + "isobject": "^3.0.0" }, "dependencies": { "has-value": { @@ -10009,9 +10010,9 @@ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, "requires": { - "get-value": "2.0.6", - "has-values": "0.1.4", - "isobject": "2.1.0" + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" }, "dependencies": { "isobject": { @@ -10075,8 +10076,8 @@ "integrity": "sha512-B7QYFyvv+fOBqBVeefsxv6koWWtjmHaMFT6KZWti4KRw8YUD/hOU+3AECvXuzyVawIBx3z7zQRejXCDSO5kk1Q==", "dev": true, "requires": { - "loader-utils": "1.1.0", - "mime": "1.3.6" + "loader-utils": "^1.0.2", + "mime": "1.3.x" } }, "url-parse": { @@ -10085,8 +10086,8 @@ "integrity": "sha512-x95Td74QcvICAA0+qERaVkRpTGKyBHHYdwL2LXZm5t/gBtCB9KQSO/0zQgSTYEV1p0WcvSg79TLNPSvd5IDJMQ==", "dev": true, "requires": { - "querystringify": "2.0.0", - "requires-port": "1.0.0" + "querystringify": "^2.0.0", + "requires-port": "^1.0.0" } }, "use": { @@ -10095,7 +10096,7 @@ "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.2" }, "dependencies": { "kind-of": { @@ -10118,8 +10119,8 @@ "integrity": "sha1-z1k+9PLRdYdei7ZY6pLhik/QbY4=", "dev": true, "requires": { - "lru-cache": "2.2.4", - "tmp": "0.0.31" + "lru-cache": "2.2.x", + "tmp": "0.0.x" }, "dependencies": { "lru-cache": { @@ -10169,7 +10170,7 @@ "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", "dev": true, "requires": { - "user-home": "1.1.1" + "user-home": "^1.1.1" } }, "validate-npm-package-license": { @@ -10178,8 +10179,8 @@ "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", "dev": true, "requires": { - "spdx-correct": "3.0.0", - "spdx-expression-parse": "3.0.0" + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, "vary": { @@ -10200,9 +10201,9 @@ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "dev": true, "requires": { - "assert-plus": "1.0.0", + "assert-plus": "^1.0.0", "core-util-is": "1.0.2", - "extsprintf": "1.3.0" + "extsprintf": "^1.2.0" }, "dependencies": { "assert-plus": { @@ -10240,8 +10241,8 @@ "integrity": "sha512-FMB5VqpLqOCcqrzA9okZFc0wq0Qbmdm396qJxvQZhDpyu0W95G9JCmp74tx7iyYnyOcBtUuKJsgIKAqjozvmmQ==", "dev": true, "requires": { - "ensure-posix-path": "1.0.2", - "matcher-collection": "1.0.5" + "ensure-posix-path": "^1.0.0", + "matcher-collection": "^1.0.0" } }, "watchpack": { @@ -10250,9 +10251,9 @@ "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", "dev": true, "requires": { - "chokidar": "2.0.4", - "graceful-fs": "4.1.11", - "neo-async": "2.5.1" + "chokidar": "^2.0.2", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" }, "dependencies": { "anymatch": { @@ -10261,8 +10262,8 @@ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, "requires": { - "micromatch": "3.1.10", - "normalize-path": "2.1.1" + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" } }, "arr-diff": { @@ -10283,16 +10284,16 @@ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { - "arr-flatten": "1.1.0", - "array-unique": "0.3.2", - "extend-shallow": "2.0.1", - "fill-range": "4.0.0", - "isobject": "3.0.1", - "repeat-element": "1.1.2", - "snapdragon": "0.8.2", - "snapdragon-node": "2.1.1", - "split-string": "3.1.0", - "to-regex": "3.0.2" + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" }, "dependencies": { "extend-shallow": { @@ -10301,7 +10302,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -10312,19 +10313,19 @@ "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", "dev": true, "requires": { - "anymatch": "2.0.0", - "async-each": "1.0.1", - "braces": "2.3.2", - "fsevents": "1.2.4", - "glob-parent": "3.1.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "4.0.0", - "lodash.debounce": "4.0.8", - "normalize-path": "2.1.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0", - "upath": "1.1.0" + "anymatch": "^2.0.0", + "async-each": "^1.0.0", + "braces": "^2.3.0", + "fsevents": "^1.2.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "lodash.debounce": "^4.0.8", + "normalize-path": "^2.1.1", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0", + "upath": "^1.0.5" } }, "expand-brackets": { @@ -10333,13 +10334,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "debug": "2.6.8", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "define-property": { @@ -10348,7 +10349,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "extend-shallow": { @@ -10357,7 +10358,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } }, "is-accessor-descriptor": { @@ -10366,7 +10367,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -10375,7 +10376,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.5" + "is-buffer": "^1.1.5" } } } @@ -10386,7 +10387,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -10395,7 +10396,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.5" + "is-buffer": "^1.1.5" } } } @@ -10406,9 +10407,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" } }, "kind-of": { @@ -10425,14 +10426,14 @@ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { - "array-unique": "0.3.2", - "define-property": "1.0.0", - "expand-brackets": "2.1.4", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "define-property": { @@ -10441,7 +10442,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "extend-shallow": { @@ -10450,7 +10451,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -10461,10 +10462,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-number": "3.0.0", - "repeat-string": "1.6.1", - "to-regex-range": "2.1.1" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" }, "dependencies": { "extend-shallow": { @@ -10473,7 +10474,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -10485,8 +10486,8 @@ "dev": true, "optional": true, "requires": { - "nan": "2.10.0", - "node-pre-gyp": "0.10.0" + "nan": "^2.9.2", + "node-pre-gyp": "^0.10.0" }, "dependencies": { "abbrev": { @@ -10512,8 +10513,8 @@ "dev": true, "optional": true, "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.6" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, "balanced-match": { @@ -10526,7 +10527,7 @@ "bundled": true, "dev": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -10590,7 +10591,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "2.2.4" + "minipass": "^2.2.1" } }, "fs.realpath": { @@ -10605,14 +10606,14 @@ "dev": true, "optional": true, "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" + "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" } }, "glob": { @@ -10621,12 +10622,12 @@ "dev": true, "optional": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "has-unicode": { @@ -10641,7 +10642,7 @@ "dev": true, "optional": true, "requires": { - "safer-buffer": "2.1.2" + "safer-buffer": "^2.1.0" } }, "ignore-walk": { @@ -10650,7 +10651,7 @@ "dev": true, "optional": true, "requires": { - "minimatch": "3.0.4" + "minimatch": "^3.0.4" } }, "inflight": { @@ -10659,8 +10660,8 @@ "dev": true, "optional": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -10679,7 +10680,7 @@ "bundled": true, "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "isarray": { @@ -10693,7 +10694,7 @@ "bundled": true, "dev": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -10706,8 +10707,8 @@ "bundled": true, "dev": true, "requires": { - "safe-buffer": "5.1.1", - "yallist": "3.0.2" + "safe-buffer": "^5.1.1", + "yallist": "^3.0.0" } }, "minizlib": { @@ -10716,7 +10717,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "2.2.4" + "minipass": "^2.2.1" } }, "mkdirp": { @@ -10739,9 +10740,9 @@ "dev": true, "optional": true, "requires": { - "debug": "2.6.9", - "iconv-lite": "0.4.21", - "sax": "1.2.4" + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" } }, "node-pre-gyp": { @@ -10750,16 +10751,16 @@ "dev": true, "optional": true, "requires": { - "detect-libc": "1.0.3", - "mkdirp": "0.5.1", - "needle": "2.2.0", - "nopt": "4.0.1", - "npm-packlist": "1.1.10", - "npmlog": "4.1.2", - "rc": "1.2.7", - "rimraf": "2.6.2", - "semver": "5.5.0", - "tar": "4.4.1" + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.1.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" } }, "nopt": { @@ -10768,8 +10769,8 @@ "dev": true, "optional": true, "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.5" + "abbrev": "1", + "osenv": "^0.1.4" } }, "npm-bundled": { @@ -10784,8 +10785,8 @@ "dev": true, "optional": true, "requires": { - "ignore-walk": "3.0.1", - "npm-bundled": "1.0.3" + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" } }, "npmlog": { @@ -10794,10 +10795,10 @@ "dev": true, "optional": true, "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, "number-is-nan": { @@ -10816,7 +10817,7 @@ "bundled": true, "dev": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "os-homedir": { @@ -10837,8 +10838,8 @@ "dev": true, "optional": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, "path-is-absolute": { @@ -10859,10 +10860,10 @@ "dev": true, "optional": true, "requires": { - "deep-extend": "0.5.1", - "ini": "1.3.5", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" + "deep-extend": "^0.5.1", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { @@ -10879,13 +10880,13 @@ "dev": true, "optional": true, "requires": { - "core-util-is": "1.0.2", - "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.2" + "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" } }, "rimraf": { @@ -10894,7 +10895,7 @@ "dev": true, "optional": true, "requires": { - "glob": "7.1.2" + "glob": "^7.0.5" } }, "safe-buffer": { @@ -10937,9 +10938,9 @@ "bundled": true, "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "string_decoder": { @@ -10948,7 +10949,7 @@ "dev": true, "optional": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } }, "strip-ansi": { @@ -10956,7 +10957,7 @@ "bundled": true, "dev": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "strip-json-comments": { @@ -10971,13 +10972,13 @@ "dev": true, "optional": true, "requires": { - "chownr": "1.0.1", - "fs-minipass": "1.2.5", - "minipass": "2.2.4", - "minizlib": "1.1.0", - "mkdirp": "0.5.1", - "safe-buffer": "5.1.1", - "yallist": "3.0.2" + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.2.4", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.1", + "yallist": "^3.0.2" } }, "util-deprecate": { @@ -10992,7 +10993,7 @@ "dev": true, "optional": true, "requires": { - "string-width": "1.0.2" + "string-width": "^1.0.2" } }, "wrappy": { @@ -11013,8 +11014,8 @@ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "is-glob": "3.1.0", - "path-dirname": "1.0.2" + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" }, "dependencies": { "is-glob": { @@ -11023,7 +11024,7 @@ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "is-extglob": "2.1.1" + "is-extglob": "^2.1.0" } } } @@ -11034,7 +11035,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -11043,7 +11044,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -11052,9 +11053,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "is-extglob": { @@ -11069,7 +11070,7 @@ "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", "dev": true, "requires": { - "is-extglob": "2.1.1" + "is-extglob": "^2.1.1" } }, "is-number": { @@ -11078,7 +11079,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -11087,7 +11088,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.5" + "is-buffer": "^1.1.5" } } } @@ -11104,19 +11105,19 @@ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "extglob": "2.0.4", - "fragment-cache": "0.2.1", - "kind-of": "6.0.2", - "nanomatch": "1.2.9", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" } } } @@ -11127,7 +11128,7 @@ "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", "dev": true, "requires": { - "minimalistic-assert": "1.0.1" + "minimalistic-assert": "^1.0.0" } }, "webdriver-js-extender": { @@ -11136,8 +11137,8 @@ "integrity": "sha1-gcUzqeM9W/tZe05j4s2yW1R3dRU=", "dev": true, "requires": { - "@types/selenium-webdriver": "2.53.42", - "selenium-webdriver": "2.53.3" + "@types/selenium-webdriver": "^2.53.35", + "selenium-webdriver": "^2.53.2" }, "dependencies": { "adm-zip": { @@ -11159,9 +11160,9 @@ "dev": true, "requires": { "adm-zip": "0.4.4", - "rimraf": "2.6.1", + "rimraf": "^2.2.8", "tmp": "0.0.24", - "ws": "1.1.2", + "ws": "^1.0.1", "xml2js": "0.4.4" } }, @@ -11177,8 +11178,8 @@ "integrity": "sha1-MREBAAMAiuGSQOuhdJe1fHKcVV0=", "dev": true, "requires": { - "sax": "0.6.1", - "xmlbuilder": "4.2.1" + "sax": "0.6.x", + "xmlbuilder": ">=1.0.0" } } } @@ -11189,27 +11190,27 @@ "integrity": "sha1-FakdvjSWbYpLmcfWVu/ZKi5ab2o=", "dev": true, "requires": { - "acorn": "5.7.1", - "acorn-dynamic-import": "2.0.2", - "ajv": "4.11.8", - "ajv-keywords": "1.5.1", - "async": "2.5.0", - "enhanced-resolve": "3.4.1", - "interpret": "1.1.0", - "json-loader": "0.5.7", - "json5": "0.5.1", - "loader-runner": "2.3.0", - "loader-utils": "0.2.17", - "memory-fs": "0.4.1", - "mkdirp": "0.5.1", - "node-libs-browser": "2.1.0", - "source-map": "0.5.6", - "supports-color": "3.2.3", - "tapable": "0.2.8", - "uglify-js": "2.8.29", - "watchpack": "1.6.0", - "webpack-sources": "0.2.3", - "yargs": "6.6.0" + "acorn": "^5.0.0", + "acorn-dynamic-import": "^2.0.0", + "ajv": "^4.7.0", + "ajv-keywords": "^1.1.1", + "async": "^2.1.2", + "enhanced-resolve": "^3.0.0", + "interpret": "^1.0.0", + "json-loader": "^0.5.4", + "json5": "^0.5.1", + "loader-runner": "^2.3.0", + "loader-utils": "^0.2.16", + "memory-fs": "~0.4.1", + "mkdirp": "~0.5.0", + "node-libs-browser": "^2.0.0", + "source-map": "^0.5.3", + "supports-color": "^3.1.0", + "tapable": "~0.2.5", + "uglify-js": "^2.8.5", + "watchpack": "^1.3.1", + "webpack-sources": "^0.2.3", + "yargs": "^6.0.0" }, "dependencies": { "ajv": { @@ -11218,8 +11219,8 @@ "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", "dev": true, "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" } }, "camelcase": { @@ -11234,8 +11235,8 @@ "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", "dev": true, "requires": { - "center-align": "0.1.3", - "right-align": "0.1.3", + "center-align": "^0.1.1", + "right-align": "^0.1.1", "wordwrap": "0.0.2" } }, @@ -11245,7 +11246,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "loader-utils": { @@ -11254,10 +11255,10 @@ "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", "dev": true, "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1", - "object-assign": "4.1.1" + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" } }, "source-list-map": { @@ -11272,9 +11273,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "uglify-js": { @@ -11283,9 +11284,9 @@ "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", "dev": true, "requires": { - "source-map": "0.5.6", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" }, "dependencies": { "yargs": { @@ -11294,9 +11295,9 @@ "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "dev": true, "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "1.2.0", + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", "window-size": "0.1.0" } } @@ -11308,8 +11309,8 @@ "integrity": "sha1-F8Yr+vE8cH+dAsR54Nzd6DgGl/s=", "dev": true, "requires": { - "source-list-map": "1.1.2", - "source-map": "0.5.6" + "source-list-map": "^1.1.1", + "source-map": "~0.5.3" } }, "wordwrap": { @@ -11324,19 +11325,19 @@ "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", "dev": true, "requires": { - "camelcase": "3.0.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "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": "4.2.1" + "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": "^4.2.0" }, "dependencies": { "camelcase": { @@ -11351,9 +11352,9 @@ "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "dev": true, "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" } } } @@ -11364,7 +11365,7 @@ "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", "dev": true, "requires": { - "camelcase": "3.0.0" + "camelcase": "^3.0.0" }, "dependencies": { "camelcase": { @@ -11383,11 +11384,11 @@ "integrity": "sha512-FCrqPy1yy/sN6U/SaEZcHKRXGlqU0DUaEBL45jkUYoB8foVb6wCnbIJ1HKIx+qUFTW+3JpVcCJCxZ8VATL4e+A==", "dev": true, "requires": { - "memory-fs": "0.4.1", - "mime": "1.6.0", - "path-is-absolute": "1.0.1", - "range-parser": "1.2.0", - "time-stamp": "2.0.0" + "memory-fs": "~0.4.1", + "mime": "^1.5.0", + "path-is-absolute": "^1.0.0", + "range-parser": "^1.0.3", + "time-stamp": "^2.0.0" }, "dependencies": { "mime": { @@ -11405,22 +11406,22 @@ "dev": true, "requires": { "ansi-html": "0.0.7", - "chokidar": "1.7.0", - "compression": "1.7.2", - "connect-history-api-fallback": "1.5.0", - "express": "4.16.3", - "html-entities": "1.2.1", - "http-proxy-middleware": "0.17.4", + "chokidar": "^1.6.0", + "compression": "^1.5.2", + "connect-history-api-fallback": "^1.3.0", + "express": "^4.13.3", + "html-entities": "^1.2.0", + "http-proxy-middleware": "~0.17.4", "opn": "4.0.2", - "portfinder": "1.0.13", - "serve-index": "1.9.1", + "portfinder": "^1.0.9", + "serve-index": "^1.7.2", "sockjs": "0.3.18", "sockjs-client": "1.1.2", - "spdy": "3.4.7", - "strip-ansi": "3.0.1", - "supports-color": "3.2.3", - "webpack-dev-middleware": "1.12.2", - "yargs": "6.6.0" + "spdy": "^3.4.1", + "strip-ansi": "^3.0.0", + "supports-color": "^3.1.1", + "webpack-dev-middleware": "^1.10.2", + "yargs": "^6.0.0" }, "dependencies": { "camelcase": { @@ -11435,7 +11436,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "string-width": { @@ -11444,9 +11445,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "yargs": { @@ -11455,19 +11456,19 @@ "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", "dev": true, "requires": { - "camelcase": "3.0.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "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": "4.2.1" + "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": "^4.2.0" } }, "yargs-parser": { @@ -11476,7 +11477,7 @@ "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", "dev": true, "requires": { - "camelcase": "3.0.0" + "camelcase": "^3.0.0" } } } @@ -11487,7 +11488,7 @@ "integrity": "sha1-8dgB0sXTn4P/7J8RkkCz476ZShw=", "dev": true, "requires": { - "lodash": "4.17.4" + "lodash": "^4.17.4" } }, "webpack-sources": { @@ -11496,8 +11497,8 @@ "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==", "dev": true, "requires": { - "source-list-map": "2.0.0", - "source-map": "0.6.1" + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" }, "dependencies": { "source-map": { @@ -11514,8 +11515,8 @@ "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", "dev": true, "requires": { - "http-parser-js": "0.4.13", - "websocket-extensions": "0.1.3" + "http-parser-js": ">=0.4.0", + "websocket-extensions": ">=0.1.1" } }, "websocket-extensions": { @@ -11542,7 +11543,7 @@ "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", "dev": true, "requires": { - "isexe": "2.0.0" + "isexe": "^2.0.0" } }, "which-module": { @@ -11557,7 +11558,7 @@ "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "requires": { - "string-width": "2.1.1" + "string-width": "^1.0.2 || 2" } }, "window-size": { @@ -11578,8 +11579,8 @@ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" }, "dependencies": { "is-fullwidth-code-point": { @@ -11588,7 +11589,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "string-width": { @@ -11597,9 +11598,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } } } @@ -11616,8 +11617,8 @@ "integrity": "sha1-iiRPoFJAHgjJiGz0SoUYnh/UBn8=", "dev": true, "requires": { - "options": "0.0.6", - "ultron": "1.0.2" + "options": ">=0.0.5", + "ultron": "1.0.x" } }, "wtf-8": { @@ -11632,8 +11633,8 @@ "integrity": "sha1-F76T6q4/O3eTWceVtBlwWogX6Gg=", "dev": true, "requires": { - "sax": "1.2.4", - "xmlbuilder": "4.2.1" + "sax": ">=0.6.0", + "xmlbuilder": "^4.1.0" } }, "xmlbuilder": { @@ -11642,7 +11643,7 @@ "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", "dev": true, "requires": { - "lodash": "4.17.4" + "lodash": "^4.0.0" } }, "xmldom": { @@ -11688,19 +11689,19 @@ "dev": true, "optional": true, "requires": { - "camelcase": "3.0.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "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" + "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" }, "dependencies": { "camelcase": { @@ -11717,7 +11718,7 @@ "dev": true, "optional": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "string-width": { @@ -11727,9 +11728,9 @@ "dev": true, "optional": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } } } @@ -11741,7 +11742,7 @@ "dev": true, "optional": true, "requires": { - "camelcase": "3.0.0" + "camelcase": "^3.0.0" }, "dependencies": { "camelcase": { From ddbeeb7f51cf0030adcd25342468d3ecf8f4aada Mon Sep 17 00:00:00 2001 From: electronic Max Date: Mon, 24 Dec 2018 19:48:48 -0700 Subject: [PATCH 05/55] fixed a concurrency related bug pertaining to impacts cache writing over itself --- scripts/api.py | 54 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/scripts/api.py b/scripts/api.py index ede02d6..bb4f476 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -6,6 +6,8 @@ import re import sys import os +import traceback +import copy from datetime import datetime sys.path.append(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "db")) import databaseBursts @@ -14,7 +16,7 @@ app = Flask(__name__) #initialise the flask server api = Api(app) #initialise the flask server ID_POINTER = 0 #so we know which packets we've seen (for caching) -impacts = dict() #for building and caching impacts +_impact_cache = dict() #for building and caching impacts geos = dict() #for building and caching geo data lastDays = 0 #timespan of the last request (for caching) @@ -24,9 +26,14 @@ #return aggregated data for the given time period (in days, called by refine) class Refine(Resource): def get(self, days): - response = make_response(jsonify({"bursts": GetBursts(days), "macMan": MacMan(), "manDev": ManDev(), "impacts": GetImpacts(days), "usage": GenerateUsage()})) - response.headers['Access-Control-Allow-Origin'] = '*' - return response + try: + response = make_response(jsonify({"bursts": GetBursts(days), "macMan": MacMan(), "manDev": ManDev(), "impacts": GetImpacts(days), "usage": GenerateUsage()})) + response.headers['Access-Control-Allow-Origin'] = '*' + return response + except: + print("Unexpected error:", sys.exc_info()) + traceback.print_exc() + sys.exit(-1) #get the mac address, manufacturer, and custom name of every device class Devices(Resource): @@ -99,17 +106,20 @@ def GetBursts(days): #get impact (traffic) of every device/external ip combination for the given time period (in days) def GetImpacts(days): - - global geos, ID_POINTER, lastDays + global geos, ID_POINTER, lastDays, _impact_cache + print("GetImpacts: days::", days, " ID>::", ID_POINTER, " lastDays::", lastDays) #we can only keep the cache if we're looking at the same packets as the previous request if days is not lastDays: print("ResetImpactCache()") - ResetImpactCache() + ResetImpactCache() + + impacts = copy.deepcopy(_impact_cache) # shallow copy + idp = ID_POINTER #get all packets from the database (if we have cached impacts from before, then only get new packets) - packets = DB_MANAGER.execute("SELECT * FROM packets WHERE id > %s AND time > (NOW() - INTERVAL %s) ORDER BY id", (str(ID_POINTER), "'" + str(days) + " DAY'")) + packets = DB_MANAGER.execute("SELECT * FROM packets WHERE id > %s AND time > (NOW() - INTERVAL %s) ORDER BY id", (str(idp), "'" + str(days) + " DAY'")) result = [] local_ip_mask = re.compile('^(192\.168|10\.|255\.255\.255\.255).*') #so we can filter for local ip addresses @@ -135,28 +145,32 @@ def GetImpacts(days): geos[ext_ip] = GetGeo(ext_ip) print("UpdateImpact ", pkt_mac, ext_ip, pkt_len) - UpdateImpact(pkt_mac, ext_ip, pkt_len) + UpdateImpact(impacts, pkt_mac, ext_ip, pkt_len) - #fast forward the id pointer so we know this packet is cached - if ID_POINTER < pkt_id: - ID_POINTER = pkt_id + if idp < pkt_id: + idp = pkt_id #build a list of all device/ip impacts and geo data for ip,geo in geos.items(): for mac,_ in ManDev().items(): item = geo.copy() # emax added .copy here() this is so gross - item['impact'] = GetImpact(mac, ip) + item['impact'] = GetImpact(mac, ip, impacts) print("Calling getimpact mac::", mac, " ip::", ip, 'impact result ', item['impact']); item['companyid'] = ip item['appid'] = mac if item['impact'] > 0: result.append(item) + + # commit these all at once to the globals # + _impact_cache = impacts lastDays = days - print("result ", json.dumps(result)) + ID_POINTER = idp + + # print("result ", json.dumps(result)) return result #shipit #setter method for impacts -def UpdateImpact(mac, ip, impact): +def UpdateImpact(impacts, mac, ip, impact): if mac in impacts: print("updateimpact existing mac ", mac) if ip in impacts[mac]: @@ -171,20 +185,18 @@ def UpdateImpact(mac, ip, impact): impacts[mac][ip] = impact #impact did not exist #getter method for impacts -def GetImpact(mac, ip): - global impacts +def GetImpact(mac, ip, impacts=_impact_cache): if mac in impacts: if ip in impacts[mac]: return impacts[mac][ip] - else: - return 0 #impact does not exist + else: return 0 #impact does not exist else: return 0 #impact does not exist #clear impact dictionary and packet id pointer def ResetImpactCache(): - global impacts, ID_POINTER - impacts = dict() + global _impacts_cache, ID_POINTER + _impacts_cache = dict() ID_POINTER = 0 #generate fake usage for devices (a hack so they show up in refine) From 227be364dc8618c73c538cf6e0e097ffb587987d Mon Sep 17 00:00:00 2001 From: electronic Max Date: Tue, 25 Dec 2018 10:23:19 -0700 Subject: [PATCH 06/55] major cleanup of UI --- categorisation/predictions.py | 9 +++--- scripts/api.py | 4 +-- ui/src/app/app.module.ts | 14 +++++---- ui/src/app/refinebar/refinebar.component.ts | 31 ++++++++++--------- ui/src/app/refinecat/refinecat.component.ts | 22 ++++++------- ui/src/app/tiled-all/tiled-all.component.html | 4 +-- .../tiled-display.component.html | 4 +-- 7 files changed, 45 insertions(+), 43 deletions(-) diff --git a/categorisation/predictions.py b/categorisation/predictions.py index 48ad814..6585ec6 100644 --- a/categorisation/predictions.py +++ b/categorisation/predictions.py @@ -241,8 +241,9 @@ def predictOther(self, rows, printing=False): for externalIP in ext.keys(): if ext[externalIP]*1.0 / total*1.0 > percentCutoff: c_name = DB_MANAGER.execute("SELECT c_name FROM geodata WHERE ip=%s LIMIT 1", (externalIP,), False) - if ext[externalIP] == total: - return "Exclusively " + c_name[0] - else: - return "Mostly " + c_name[0] + if c_name and len(c_name) > 0: + if ext[externalIP] == total: + return "Exclusively " + c_name[0] + else : + return "Mostly " + c_name[0] return "Unknown" diff --git a/scripts/api.py b/scripts/api.py index bb4f476..679e15e 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -144,7 +144,7 @@ def GetImpacts(days): if ext_ip not in geos: geos[ext_ip] = GetGeo(ext_ip) - print("UpdateImpact ", pkt_mac, ext_ip, pkt_len) + # print("UpdateImpact ", pkt_mac, ext_ip, pkt_len) UpdateImpact(impacts, pkt_mac, ext_ip, pkt_len) if idp < pkt_id: @@ -155,7 +155,7 @@ def GetImpacts(days): for mac,_ in ManDev().items(): item = geo.copy() # emax added .copy here() this is so gross item['impact'] = GetImpact(mac, ip, impacts) - print("Calling getimpact mac::", mac, " ip::", ip, 'impact result ', item['impact']); + # print("Calling getimpact mac::", mac, " ip::", ip, 'impact result ', item['impact']); item['companyid'] = ip item['appid'] = mac if item['impact'] > 0: diff --git a/ui/src/app/app.module.ts b/ui/src/app/app.module.ts index 0daccca..1678dba 100644 --- a/ui/src/app/app.module.ts +++ b/ui/src/app/app.module.ts @@ -4,7 +4,7 @@ import { LoaderService } from './loader.service'; import { HttpModule } from '@angular/http'; import { AppComponent } from './app.component'; import { RefinebarComponent } from './refinebar/refinebar.component'; -import { UsageListenerComponent } from './usage-listener/usage-listener.component'; +// import { UsageListenerComponent } from './usage-listener/usage-listener.component'; import { UsagetableComponent } from './usagetable/usagetable.component'; import { FormsModule } from '@angular/forms'; import { RouterModule, Routes } from '@angular/router'; @@ -16,9 +16,11 @@ import { Ng2CompleterModule } from 'ng2-completer'; import { SingleDisplayComponent } from './single-display/single-display.component'; import { TiledDisplayComponent } from './tiled-display/tiled-display.component'; -import { CompareComponent } from './compare/compare.component'; +// no longer used with IoTRefine => +// import { CompareComponent } from './compare/compare.component'; +// import { CompareContainerComponent } from './compare-container/compare-container.component'; +// import { CompanyListComponent } from './company-list/company-list.component'; -import { CompareContainerComponent } from './compare-container/compare-container.component'; import { AutocompleteComponent } from './autocomplete/autocomplete.component'; import { HostUtilsService } from "app/host-utils.service"; import { AppinfoComponent } from './appinfo/appinfo.component'; @@ -54,10 +56,10 @@ const appRoutes: Routes = [ FoobarComponent, ErrorComponent, CompanybarComponent, - CompareComponent, + // CompareComponent, CompanyListComponent, - UsageListenerComponent, - CompareContainerComponent, + // UsageListenerComponent, + // CompareContainerComponent, AutocompleteComponent, AppinfoComponent, CompanyinfoComponent, diff --git a/ui/src/app/refinebar/refinebar.component.ts b/ui/src/app/refinebar/refinebar.component.ts index 2d80efe..b14c780 100644 --- a/ui/src/app/refinebar/refinebar.component.ts +++ b/ui/src/app/refinebar/refinebar.component.ts @@ -46,7 +46,7 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { // @ViewChild('thing') svg: ElementRef; // this gets a direct el reference to the svg element // incoming attribute - @Input('appusage') usage_in: AppUsage[]; + // @Input('appusage') usage_in: AppUsage[]; @Input() showModes = true; @Input() highlightApp: APIAppInfo; @Input() showLegend = true; @@ -101,18 +101,18 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { return Array.from(nE.getElementsByTagName('svg'))[0]; } + // todo; move this out to loader getIoTData(): void { this.http.get('http://localhost:4201/api/refine/15').toPromise().then(response2 => { - this.usage = response2.json()["usage"]; - this.impacts = response2.json()["impacts"]; - var manDev = response2.json()["manDev"]; - - this.impacts.forEach(function(impact){ - if (manDev[impact.appid] != "unknown") { - impact.appid = manDev[impact.appid]; - } - }); + this.usage = response2.json()["usage"]; // ah ha! + this.impacts = response2.json()["impacts"]; + var manDev = response2.json()["manDev"]; + this.impacts.forEach(function(impact){ + if (manDev[impact.appid] != "unknown") { + impact.appid = manDev[impact.appid]; + } + }); this.render() }); } @@ -157,13 +157,14 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { } + // TODO // this gets called when this.usage_in changes ngOnChanges(changes: SimpleChanges): void { - if (!this.usage_in) { return; } + if (!this.usage) { return; } this.init.then(() => { - if (!this.usage_in || !this.usage || !this.apps || this.apps.length !== this.usage_in.length) { - delete this.apps; - } + // if (!this.usage_in || !this.usage || !this.apps || this.apps.length !== this.usage_in.length) { + // delete this.apps; + // } this.render(); }); } @@ -194,7 +195,7 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { return this._getApp(usg.appid).then(app => { const hosts = app && app.hosts; - if (!hosts) { console.warn('No hosts found for app ', usg.appid); return Promise.resolve([]); } + if (!hosts) { console.warn('No hosts found for app ', usg.appid); return Promise.resolve([]); } return Promise.all(hosts.map(host => this.hostutils.findCompany(host, app))) .then((companies: CompanyInfo[]) => _.uniq(companies.filter((company) => company !== undefined && company.typetag !== 'ignore'))) diff --git a/ui/src/app/refinecat/refinecat.component.ts b/ui/src/app/refinecat/refinecat.component.ts index 9a930b7..d4cb910 100644 --- a/ui/src/app/refinecat/refinecat.component.ts +++ b/ui/src/app/refinecat/refinecat.component.ts @@ -45,7 +45,7 @@ export class RefinecatComponent { // @ViewChild('thing') svg: ElementRef; // this gets a direct el reference to the svg element // incoming attribute - @Input('appusage') usage_in: AppUsage[]; + // @Input('appusage') usage_in: AppUsage[]; @Input() showModes = true; @Input() highlightApp: APIAppInfo; @Input() showLegend = true; @@ -111,7 +111,7 @@ getIoTData(): void { var manDev = response2.json()["manDev"]; this.data.forEach(function(burst){ - if (manDev[burst.device] != "unknown") { + if (manDev[burst.device] !== "unknown") { burst.device = manDev[burst.device]; } }); @@ -209,17 +209,15 @@ selectOnlyDay(d): void{ } render(classd, spaced, inputData: BurstData[], enableBrush) { - if (inputData == undefined) {return;} + if (inputData === undefined) {return;} - var data = inputData.filter( d => {if (d.device == this.lastHovering || this.lastHovering == undefined) {return d;}}); + var data = inputData.filter( d => {if (d.device === this.lastHovering || this.lastHovering === undefined) {return d;}}); - if (this.lastHovering == undefined) - { - data = data.filter(obj => this._ignoredApps.indexOf(obj.device) == -1 ) + if (this.lastHovering === undefined) { + data = data.filter(obj => this._ignoredApps.indexOf(obj.device) === -1 ) } - if (this.firstDay !== undefined) - { + if (this.firstDay !== undefined) { data = data.filter(obj => obj.value > this.firstDay && obj.value < this.lastDay ) } @@ -234,7 +232,7 @@ render(classd, spaced, inputData: BurstData[], enableBrush) { left: 30 } var svgel = this.getSVGElement(); - //console.log(svgel); + // console.log(svgel); if (!svgel) { return; } var rect = svgel.getBoundingClientRect(), @@ -248,8 +246,8 @@ render(classd, spaced, inputData: BurstData[], enableBrush) { var height = (this.lessThanDay(padding.pad)) ? (height_svgel - margin.top - margin.bottom - oneDayDisplacement) : (height_svgel - margin.top - margin.bottom); - //console.log(width); - //console.log(height); + // console.log(width); + // console.log(height); var x = d3.scaleLinear().range([0 + margin.right, width - margin.left]), y = d3.scaleLinear().range([margin.top, height - margin.bottom - margin.top]); diff --git a/ui/src/app/tiled-all/tiled-all.component.html b/ui/src/app/tiled-all/tiled-all.component.html index dc17838..d497347 100644 --- a/ui/src/app/tiled-all/tiled-all.component.html +++ b/ui/src/app/tiled-all/tiled-all.component.html @@ -1,9 +1,9 @@
- +
- +
diff --git a/ui/src/app/tiled-display/tiled-display.component.html b/ui/src/app/tiled-display/tiled-display.component.html index 40b2699..fa8d919 100644 --- a/ui/src/app/tiled-display/tiled-display.component.html +++ b/ui/src/app/tiled-display/tiled-display.component.html @@ -21,8 +21,8 @@
- - + +
From 8456ec1f67e54838198527c0e955c3417765e507 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Wed, 26 Dec 2018 12:12:24 -0700 Subject: [PATCH 07/55] got rid of components no longer in use that were causing spurious loads --- ui/src/app/app.module.ts | 16 +++++++++------- .../single-display/single-display.component.html | 2 +- .../tiled-display/tiled-display.component.html | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/ui/src/app/app.module.ts b/ui/src/app/app.module.ts index 1678dba..75a2c72 100644 --- a/ui/src/app/app.module.ts +++ b/ui/src/app/app.module.ts @@ -5,10 +5,10 @@ import { HttpModule } from '@angular/http'; import { AppComponent } from './app.component'; import { RefinebarComponent } from './refinebar/refinebar.component'; // import { UsageListenerComponent } from './usage-listener/usage-listener.component'; -import { UsagetableComponent } from './usagetable/usagetable.component'; +// import { UsagetableComponent } from './usagetable/usagetable.component'; import { FormsModule } from '@angular/forms'; import { RouterModule, Routes } from '@angular/router'; -import { FoobarComponent } from './foobar/foobar.component'; +// import { FoobarComponent } from './foobar/foobar.component'; import { ErrorComponent } from './error/error.component'; import { UsageConnectorService } from './usage-connector.service'; import { CompanybarComponent } from './companybar/companybar.component'; @@ -21,7 +21,7 @@ import { TiledDisplayComponent } from './tiled-display/tiled-display.component'; // import { CompareContainerComponent } from './compare-container/compare-container.component'; // import { CompanyListComponent } from './company-list/company-list.component'; -import { AutocompleteComponent } from './autocomplete/autocomplete.component'; +// import { AutocompleteComponent } from './autocomplete/autocomplete.component'; import { HostUtilsService } from "app/host-utils.service"; import { AppinfoComponent } from './appinfo/appinfo.component'; import { CompanyinfoComponent } from './companyinfo/companyinfo.component'; @@ -50,17 +50,19 @@ const appRoutes: Routes = [ declarations: [ AppComponent, RefinebarComponent, - UsagetableComponent, SingleDisplayComponent, TiledDisplayComponent, - FoobarComponent, ErrorComponent, CompanybarComponent, - // CompareComponent, CompanyListComponent, + // not used with IoT Refine >> + // UsagetableComponent, + // CompareComponent, // UsageListenerComponent, // CompareContainerComponent, - AutocompleteComponent, + // AutocompleteComponent, + // not ever used: + // FoobarComponent, AppinfoComponent, CompanyinfoComponent, RefinecatComponent, diff --git a/ui/src/app/single-display/single-display.component.html b/ui/src/app/single-display/single-display.component.html index a8201db..71b37ca 100644 --- a/ui/src/app/single-display/single-display.component.html +++ b/ui/src/app/single-display/single-display.component.html @@ -1,4 +1,4 @@
- +
\ No newline at end of file diff --git a/ui/src/app/tiled-display/tiled-display.component.html b/ui/src/app/tiled-display/tiled-display.component.html index fa8d919..000479d 100644 --- a/ui/src/app/tiled-display/tiled-display.component.html +++ b/ui/src/app/tiled-display/tiled-display.component.html @@ -1,6 +1,6 @@
- +
From 8533ff741345247896350a6bb133fcd64a016716 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Wed, 26 Dec 2018 17:38:10 -0700 Subject: [PATCH 08/55] added an initial notify trigger --- db/notify_trigger.sql | 58 +++++++++++++++++++++++++++++++++++++++++++ scripts/capture.py | 15 ++++++++--- 2 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 db/notify_trigger.sql diff --git a/db/notify_trigger.sql b/db/notify_trigger.sql new file mode 100644 index 0000000..84ae9cb --- /dev/null +++ b/db/notify_trigger.sql @@ -0,0 +1,58 @@ +CREATE FUNCTION notify_trigger() RETURNS trigger AS $trigger$ +DECLARE + rec RECORD; + payload TEXT; + column_name TEXT; + column_value TEXT; + payload_items JSONB; +BEGIN + -- Set record row depending on operation + CASE TG_OP + WHEN 'INSERT', 'UPDATE' THEN + rec := NEW; + WHEN 'DELETE' THEN + rec := OLD; + ELSE + RAISE EXCEPTION 'Unknown TG_OP: "%". Should not occur!', TG_OP; + END CASE; + + -- Get required fields + FOREACH column_name IN ARRAY TG_ARGV LOOP + EXECUTE format('SELECT $1.%I::TEXT', column_name) + INTO column_value + USING rec; + payload_items := coalesce(payload_items,'{}')::jsonb || json_build_object(column_name,column_value)::jsonb; + END LOOP; + + -- Build the payload + payload := json_build_object( + 'timestamp',CURRENT_TIMESTAMP, + 'operation',TG_OP, + 'schema',TG_TABLE_SCHEMA, + 'table',TG_TABLE_NAME, + 'data',payload_items + ); + + -- Notify the channel + PERFORM pg_notify('db_notifications', payload); + + RETURN rec; +END; +$trigger$ LANGUAGE plpgsql; + +CREATE TRIGGER packets_notify AFTER INSERT OR UPDATE OR DELETE ON packets +FOR EACH ROW EXECUTE PROCEDURE notify_trigger( + 'id', + 'mac', + 'src', + 'dst', + 'len', + 'burst' +); + +CREATE TRIGGER packets_notify AFTER INSERT OR UPDATE OR DELETE ON devices +FOR EACH ROW EXECUTE PROCEDURE notify_trigger( + 'mac', + 'manufacturer', + 'name' +); diff --git a/scripts/capture.py b/scripts/capture.py index de582af..1e2e381 100755 --- a/scripts/capture.py +++ b/scripts/capture.py @@ -1,6 +1,6 @@ #! /usr/bin/env python3 -import pyshark, datetime, psycopg2, re, argparse, sys +import pyshark, datetime, psycopg2, re, argparse, sys, traceback #constants COMMIT_INTERVAL = 5 @@ -12,6 +12,7 @@ def DatabaseInsert(packets): global timestamp + print("packets ", len(packets)) local_ip_mask = re.compile('^(192\.168|10\.|255\.255\.255\.255).*') #so we can filter for local ip addresses #open db connection @@ -29,9 +30,12 @@ def DatabaseInsert(packets): try: src = packet['ip'].src dst = packet['ip'].dst - except KeyError: + except KeyError as ke: src = '' dst = '' + print("error", ke) + # print(packet) + continue srcLocal = local_ip_mask.match(src) dstLocal = local_ip_mask.match(dst) @@ -49,7 +53,12 @@ def DatabaseInsert(packets): proto = packet.highest_layer #insert packets into table - cur.execute("INSERT INTO packets (time, src, dst, mac, len, proto) VALUES (%s, %s, %s, %s, %s, %s)", (packet.sniff_time, src, dst, mac, packet.length, proto)) + try: + cur.execute("INSERT INTO packets (time, src, dst, mac, len, proto) VALUES (%s, %s, %s, %s, %s, %s)", (packet.sniff_time, src, dst, mac, packet.length, proto)) + except: + print("Unexpected error on insert:", sys.exc_info()) + traceback.print_exc() + sys.exit(-1) counter += 1 #commit the new records and close db connection From 2b292410f42d0ec6a1aedc0e411fc741a3c2abf4 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Thu, 27 Dec 2018 15:48:28 -0700 Subject: [PATCH 09/55] patching in events ~ updates to browser works --- db/databaseBursts.py | 25 ++++++++++++++++++++ scripts/api.py | 25 ++++++++++++++++++-- scripts/capture.py | 13 +++++++++-- ui/package-lock.json | 14 +++++++++--- ui/package.json | 4 ++-- ui/src/app/app.component.ts | 10 +++++++- ui/src/app/loader.service.ts | 44 +++++++++++++++++++++++++++++++++++- 7 files changed, 124 insertions(+), 11 deletions(-) diff --git a/db/databaseBursts.py b/db/databaseBursts.py index 99ec1eb..1129de5 100644 --- a/db/databaseBursts.py +++ b/db/databaseBursts.py @@ -2,6 +2,9 @@ Handles all interaction with the database for burstification and categorisation """ import psycopg2 +import psycopg2.extensions +import select +import threading class dbManager(): @@ -11,6 +14,28 @@ def __init__(self): except: print("Connection error") + + def listen(self, channel, cb=None): + try: + conn = self.connection + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + curs = conn.cursor() + curs.execute("LISTEN %s ;" % channel) + def subp(): + while 1: + if select.select([conn],[],[],5) == ([],[],[]): + print("Timeout") + else: + conn.poll() + while conn.notifies: + notify = conn.notifies.pop(0) + print("Got NOTIFY:", notify.pid, notify.channel, notify.payload) + if cb is not None: + cb(notify.payload) + thread = threading.Thread(target=subp) + thread.start() + except: + print("listen error") def execute(self, query, data, all=True): diff --git a/scripts/api.py b/scripts/api.py index 679e15e..542a6f6 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -1,6 +1,6 @@ #! /usr/bin/env python3 -from flask import Flask, request, jsonify, make_response +from flask import Flask, request, jsonify, make_response, Response from flask_restful import Resource, Api import json import re @@ -27,7 +27,9 @@ class Refine(Resource): def get(self, days): try: - response = make_response(jsonify({"bursts": GetBursts(days), "macMan": MacMan(), "manDev": ManDev(), "impacts": GetImpacts(days), "usage": GenerateUsage()})) + response = make_response(jsonify({"bursts": GetBursts(1), "macMan": MacMan(), "manDev": ManDev(), "impacts": GetImpacts(1), "usage": GenerateUsage()})) + # response = make_response(jsonify({"bursts": GetBursts(days), "macMan": MacMan(), "manDev": ManDev(), "impacts": GetImpacts(days), "usage": GenerateUsage()})) + response.headers['Access-Control-Allow-Origin'] = '*' return response except: @@ -208,6 +210,21 @@ def GenerateUsage(): counter += 1 return usage +_events = [] + +def event_stream(): + import time + while True: + time.sleep(0.5) + while len(_events) > 0: + yield "data: %s\n\n" % _events.pop(0) + +@app.route('/stream') +def stream(): + response = Response(event_stream(), mimetype="text/event-stream") + response.headers['Access-Control-Allow-Origin'] = '*' + return response + #======================= #main part of the script if __name__ == '__main__': @@ -218,5 +235,9 @@ def GenerateUsage(): api.add_resource(Impacts, '/api/impacts/') api.add_resource(SetDevice, '/api/setdevice//') + # watch for listen events -- not sure if this has to be on its own connection + listenManager = databaseBursts.dbManager() + listenManager.listen('db_notifications', lambda payload:_events.append(payload)) + #Start the flask server app.run(port=4201, threaded=True, host='0.0.0.0') diff --git a/scripts/capture.py b/scripts/capture.py index 1e2e381..34671dd 100755 --- a/scripts/capture.py +++ b/scripts/capture.py @@ -5,6 +5,7 @@ #constants COMMIT_INTERVAL = 5 DEBUG = False +MANUAL_LOCAL_IP = None #initialise vars timestamp = 0 @@ -13,8 +14,12 @@ def DatabaseInsert(packets): global timestamp print("packets ", len(packets)) - local_ip_mask = re.compile('^(192\.168|10\.|255\.255\.255\.255).*') #so we can filter for local ip addresses - + if MANUAL_LOCAL_IP is None: + local_ip_mask = re.compile('^(192\.168|10\.|255\.255\.255\.255).*') #so we can filter for local ip addresses + else: + print('Using local IP mask:', '^(192\.168|10\.|255\.255\.255\.255|%s).*' % MANUAL_LOCAL_IP.replace('.','\.')) + local_ip_mask = re.compile('^(192\.168|10\.|255\.255\.255\.255|%s).*' % MANUAL_LOCAL_IP.replace('.','\.')) #so we can filter for local ip addresses + #open db connection conn = psycopg2.connect("dbname=testdb user=postgres password=password") cur = conn.cursor() @@ -96,6 +101,7 @@ def log(*args): parser = argparse.ArgumentParser() parser.add_argument('--interface', dest="interface", type=str, help="Interface to listen to") parser.add_argument('--cinterval', dest="cinterval", type=int, help="Commit interval in seconds", default=5) + parser.add_argument('--localip', dest="localip", type=str, help="Specify local IP addr (if not 192.168.x.x/10.x.x.x)") parser.add_argument('--debug', dest='debug', action='store_true') args = parser.parse_args() @@ -105,6 +111,9 @@ def log(*args): print(parser.print_help()) sys.exit(-1) + if args.localip is not None: + MANUAL_LOCAL_IP = args.localip + log("Configuring capture on ", args.interface) if args.cinterval is not None: diff --git a/ui/package-lock.json b/ui/package-lock.json index 6b59842..dbcb6b1 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -76,6 +76,14 @@ "webpack-dev-server": "~2.4.5", "webpack-merge": "^2.4.0", "zone.js": "^0.8.4" + }, + "dependencies": { + "typescript": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.3.4.tgz", + "integrity": "sha1-PTgyGCgjHkNPKHUUlZw3qCtin0I=", + "dev": true + } } }, "@angular/common": { @@ -9899,9 +9907,9 @@ } }, "typescript": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.3.4.tgz", - "integrity": "sha1-PTgyGCgjHkNPKHUUlZw3qCtin0I=", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", + "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", "dev": true }, "uglify-js": { diff --git a/ui/package.json b/ui/package.json index 9b29f3d..ac730ab 100644 --- a/ui/package.json +++ b/ui/package.json @@ -45,10 +45,10 @@ "karma-coverage-istanbul-reporter": "^1.2.1", "karma-jasmine": "~1.1.0", "karma-jasmine-html-reporter": "^0.2.2", + "moment": "^2.22.2", "protractor": "~5.1.2", "ts-node": "~3.0.4", "tslint": "~5.3.2", - "typescript": "~2.3.3", - "moment": "^2.22.2" + "typescript": "^2.9.2" } } diff --git a/ui/src/app/app.component.ts b/ui/src/app/app.component.ts index 48c72e7..7d4d528 100644 --- a/ui/src/app/app.component.ts +++ b/ui/src/app/app.component.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; import { Router, NavigationEnd } from "@angular/router"; import { ActivityLogService } from "app/activity-log.service"; +import { LoaderService } from './loader.service'; @Component({ selector: 'app-root', @@ -10,7 +11,7 @@ import { ActivityLogService } from "app/activity-log.service"; export class AppComponent { title = 'app'; - constructor(private router: Router, private actlog: ActivityLogService) { + constructor(private router: Router, private actlog: ActivityLogService, private loader: LoaderService) { this.router.events.subscribe(routeEvent => { // console.info('routeEvent ', routeEvent); if (routeEvent instanceof NavigationEnd) { @@ -18,6 +19,13 @@ export class AppComponent { try { this.actlog.log('routeChange', routeEvent.url); } catch(e) { } } }); + let updates = this.loader.listenToUpdates().subscribe({ + next(x) { console.log("Listen next! ", x); }, + error(err) { + console.log("Listen error! ", err, err.message); + }, + complete() { console.log("Listen complete"); } + }); } // isActive(instruction: any[]): boolean { diff --git a/ui/src/app/loader.service.ts b/ui/src/app/loader.service.ts index d7e77ac..912956c 100644 --- a/ui/src/app/loader.service.ts +++ b/ui/src/app/loader.service.ts @@ -1,10 +1,11 @@ -import { Injectable } from '@angular/core'; +import { Injectable, NgZone } from '@angular/core'; import { Http, HttpModule, Headers, URLSearchParams } from '@angular/http'; import 'rxjs/add/operator/toPromise'; import { mapValues, keys, mapKeys, values, trim, uniq, toPairs } from 'lodash'; import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; import * as _ from 'lodash'; +import { Observable } from '../../node_modules/rxjs/Observable'; enum PI_TYPES { DEVICE_SOFT, USER_LOCATION, USER_LOCATION_COARSE, DEVICE_ID, USER_PERSONAL_DETAILS } @@ -12,6 +13,7 @@ enum PI_TYPES { DEVICE_SOFT, USER_LOCATION, USER_LOCATION_COARSE, DEVICE_ID, USE // export const API_ENDPOINT = 'http://localhost:8118/api'; export const API_ENDPOINT = 'https://negi.io/api'; export const CB_SERVICE_ENDPOINT = 'http://localhost:3333'; +export const IOT_API_ENDPOINT='http://localhost:4201'; export interface App2Hosts { [app: string]: string[] } export interface Host2PITypes { [host: string]: PI_TYPES[] } @@ -19,6 +21,8 @@ export interface String2String { [host: string]: string } export interface AppSubstitutions { [app: string]: string[] }; +let zone = new NgZone({ enableLongStackTrace: false }); + export class GeoIPInfo { host?: string; ip: string; @@ -34,6 +38,23 @@ export class GeoIPInfo { metro_code?: number; }; +export class PacketUpdateInfo { + id: string; + dst: string; + src: string; + burst?: string; + mac: string; + len: string; +} + +export class DBUpdate { + timestamp: string; + operation: string; + schema: string; + table: string; + data: PacketUpdateInfo +} + export let cache = (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor) => { // console.log('@cache:: ~~ ', target, propertyKey, descriptor); let retval: { [method: string]: any } = {}, @@ -432,6 +453,27 @@ export class LoaderService { // returns a previously seen appid return this.apps[appid]; } + + listenToUpdates() : Observable { + return Observable.create(observer => { + const eventSource = new EventSource(IOT_API_ENDPOINT+`/stream`); + eventSource.onopen = thing => { + console.info('EventSource Open', thing); + }; + eventSource.onmessage = score => { + console.info("EventSource onMessage", score, score.data); + zone.run(() => { + observer.next(JSON.parse(score.data)); + }); + }; + eventSource.onerror = error => { + console.error("eventSource onerror", error); + zone.run(() => observer.error(error)); + }; + (window)._eS = eventSource; + return () => eventSource.close(); + }); + } @memoize((appid: string): string => appid) getFullAppInfo(appid: string): Promise { From 00b0e3685729e2fad81308cc86eec5c5ca2c69b1 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Thu, 27 Dec 2018 18:38:43 -0700 Subject: [PATCH 10/55] patched getIoTData into loader, eliminiating redundancy --- scripts/api.py | 2 +- ui/src/app/app.component.ts | 11 +++++-- ui/src/app/geobar/geobar.component.ts | 27 ++++++++-------- ui/src/app/geomap/geomap.component.ts | 29 ++++++++--------- ui/src/app/loader.service.ts | 35 +++++++++++++++++++-- ui/src/app/refinebar/refinebar.component.ts | 32 +++++++++++-------- ui/src/app/refinecat/refinecat.component.ts | 28 ++++++++++------- 7 files changed, 106 insertions(+), 58 deletions(-) diff --git a/scripts/api.py b/scripts/api.py index 542a6f6..e372636 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -27,7 +27,7 @@ class Refine(Resource): def get(self, days): try: - response = make_response(jsonify({"bursts": GetBursts(1), "macMan": MacMan(), "manDev": ManDev(), "impacts": GetImpacts(1), "usage": GenerateUsage()})) + response = make_response(jsonify({"bursts": GetBursts(2), "macMan": MacMan(), "manDev": ManDev(), "impacts": GetImpacts(2), "usage": GenerateUsage()})) # response = make_response(jsonify({"bursts": GetBursts(days), "macMan": MacMan(), "manDev": ManDev(), "impacts": GetImpacts(days), "usage": GenerateUsage()})) response.headers['Access-Control-Allow-Origin'] = '*' diff --git a/ui/src/app/app.component.ts b/ui/src/app/app.component.ts index 7d4d528..9f63c93 100644 --- a/ui/src/app/app.component.ts +++ b/ui/src/app/app.component.ts @@ -3,6 +3,8 @@ import { Router, NavigationEnd } from "@angular/router"; import { ActivityLogService } from "app/activity-log.service"; import { LoaderService } from './loader.service'; +(window)._listen_count = 0; + @Component({ selector: 'app-root', templateUrl: './app.component.html', @@ -20,11 +22,16 @@ export class AppComponent { } }); let updates = this.loader.listenToUpdates().subscribe({ - next(x) { console.log("Listen next! ", x); }, + next(x) { + // console.log("Listen next! ", x); + (window)._listen_count++ + }, error(err) { console.log("Listen error! ", err, err.message); }, - complete() { console.log("Listen complete"); } + complete() { + console.log("Listen complete"); + } }); } diff --git a/ui/src/app/geobar/geobar.component.ts b/ui/src/app/geobar/geobar.component.ts index 9e6df8f..28b34cb 100644 --- a/ui/src/app/geobar/geobar.component.ts +++ b/ui/src/app/geobar/geobar.component.ts @@ -86,20 +86,19 @@ export class GeobarComponent implements AfterViewInit, OnChanges { (window)._rb = this; } getIoTData(): void { - this.http.get('http://localhost:4201/api/refine/15').toPromise().then(response2 => { - this.usage = response2.json()["usage"]; - var impacts = response2.json()["impacts"]; - - var manDev = response2.json()["manDev"]; - - impacts.forEach(function(impact){ - if (manDev[impact.appid] != "unknown") { - impact.appid = manDev[impact.appid]; - } - }); - - this.impacts = impacts.map(impact => ({ appid: impact.appid, country: impact.country_code !== 'XX' ? impact.country_code : 'Unknown', country_code: impact.country_code, impact: impact.impact })) - //console.log(this.impacts) + this.loader.getIoTData().then(bundle => { + this.usage = bundle.usage; + // this.http.get('http://localhost:4201/api/refine/15').toPromise().then(response2 => { + // this.usage = response2.json()["usage"]; + // var impacts = response2.json()["impacts"]; + // var manDev = response2.json()["manDev"]; + // impacts.forEach(function(impact){ + // if (manDev[impact.appid] != "unknown") { + // impact.appid = manDev[impact.appid]; + // } + // }); + this.impacts = bundle.impacts.map(impact => ({ appid: impact.appid, country: impact.country_code !== 'XX' ? impact.country_code : 'Unknown', country_code: impact.country_code, impact: impact.impact })) + // console.log(this.impacts) this.render() }); } diff --git a/ui/src/app/geomap/geomap.component.ts b/ui/src/app/geomap/geomap.component.ts index 50ea0a5..d8da4a6 100644 --- a/ui/src/app/geomap/geomap.component.ts +++ b/ui/src/app/geomap/geomap.component.ts @@ -89,25 +89,26 @@ export class GeomapComponent implements AfterViewInit, OnChanges { (window)._rb = this; } getIoTData(): void { - this.http.get('http://localhost:4201/api/refine/15').toPromise().then(response2 => { - this.usage = response2.json()["usage"]; - var impacts = response2.json()["impacts"]; - var manDev = response2.json()["manDev"]; - impacts.forEach(function(impact){ - if (manDev[impact.appid] != "unknown") { - impact.appid = manDev[impact.appid]; - } - }); - - - var minMax = impacts.reduce((acc, val) => { + // this.http.get('http://localhost:4201/api/refine/15').toPromise().then(response2 => { + // this.usage = response2.json()["usage"]; + // var impacts = response2.json()["impacts"]; + // var manDev = response2.json()["manDev"]; + // impacts.forEach(function(impact){ + // if (manDev[impact.appid] != "unknown") { + // impact.appid = manDev[impact.appid]; + // } + // }); + + this.loader.getIoTData().then(bundle => { + this.usage = bundle.usage; + + let minMax = bundle.impacts.reduce((acc, val) => { acc[0] = ( acc[0] === undefined || val.impact < acc[0] ) ? val.impact : acc[0] acc[1] = ( acc[1] === undefined || val.impact > acc[1] ) ? val.impact : acc[1] return acc; }, []); - this.impacts = impacts.map(impact => ({impact: impact.impact/minMax[1], geo: impact, appid: impact.appid })) - + this.impacts = bundle.impacts.map(impact => ({impact: impact.impact/minMax[1], geo: impact, appid: impact.appid })) this.render() }); } diff --git a/ui/src/app/loader.service.ts b/ui/src/app/loader.service.ts index 912956c..04d7743 100644 --- a/ui/src/app/loader.service.ts +++ b/ui/src/app/loader.service.ts @@ -176,6 +176,13 @@ export class CompanyInfo { } } +export class IoTDataBundle { + usage: any; + impacts: any; + manDev: any; + bursts: any; +} + export class APIAppInfo { app: string; title: string; @@ -461,19 +468,41 @@ export class LoaderService { console.info('EventSource Open', thing); }; eventSource.onmessage = score => { - console.info("EventSource onMessage", score, score.data); + // console.info("EventSource onMessage", score, score.data); zone.run(() => { observer.next(JSON.parse(score.data)); }); }; eventSource.onerror = error => { - console.error("eventSource onerror", error); + // console.error("eventSource onerror", error); zone.run(() => observer.error(error)); }; - (window)._eS = eventSource; return () => eventSource.close(); }); } + + // todo; move this out to loader + @memoize(x => 'iotdata') + getIoTData(): Promise { + return this.http.get(IOT_API_ENDPOINT + '/api/refine/15').toPromise().then(response2 => { + let resp = response2.json(), + impacts = resp.impacts, + manDev = resp.manDev, + bursts = resp.bursts; + + impacts.forEach(function(impact){ + if (manDev[impact.appid] !== "unknown") { + impact.appid = manDev[impact.appid]; + } + }); + bursts.forEach(function(burst){ + if (manDev[burst.device] !== "unknown") { + burst.device = manDev[burst.device]; + } + }); + return resp; + }); + } @memoize((appid: string): string => appid) getFullAppInfo(appid: string): Promise { diff --git a/ui/src/app/refinebar/refinebar.component.ts b/ui/src/app/refinebar/refinebar.component.ts index b14c780..f48995b 100644 --- a/ui/src/app/refinebar/refinebar.component.ts +++ b/ui/src/app/refinebar/refinebar.component.ts @@ -103,19 +103,25 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { // todo; move this out to loader getIoTData(): void { - this.http.get('http://localhost:4201/api/refine/15').toPromise().then(response2 => { - this.usage = response2.json()["usage"]; // ah ha! - this.impacts = response2.json()["impacts"]; - var manDev = response2.json()["manDev"]; - - this.impacts.forEach(function(impact){ - if (manDev[impact.appid] != "unknown") { - impact.appid = manDev[impact.appid]; - } - }); - this.render() + this.loader.getIoTData().then( bundle => { + console.log('!@#ILJ!@#L@!J# got bundle ', bundle); + this.usage = bundle.usage; + this.impacts = bundle.impacts; + this.render(); }); - } + // this.http.get('http://localhost:4201/api/refine/15').toPromise().then(response2 => { + // this.usage = response2.json()["usage"]; // ah ha! + // this.impacts = response2.json()["impacts"]; + // var manDev = response2.json()["manDev"]; + + // this.impacts.forEach(function(impact){ + // if (manDev[impact.appid] !== "unknown") { + // impact.appid = manDev[impact.appid]; + // } + // }); + // this.render() + // }); +} addOrRemove(newClick: string): string[] { //console.log(this._ignoredApps); @@ -210,7 +216,7 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { this.lastMax = 0; this._byTime = val; this.init.then(x => this.compileImpacts(this.usage).then(impacts => { - this.render(); + this.render(); })); } diff --git a/ui/src/app/refinecat/refinecat.component.ts b/ui/src/app/refinecat/refinecat.component.ts index d4cb910..3b6caf9 100644 --- a/ui/src/app/refinecat/refinecat.component.ts +++ b/ui/src/app/refinecat/refinecat.component.ts @@ -106,17 +106,23 @@ export class RefinecatComponent { } getIoTData(): void { - this.http.get('http://localhost:4201/api/refine/15').toPromise().then(response2 => { - this.data = response2.json()["bursts"]; - var manDev = response2.json()["manDev"]; - - this.data.forEach(function(burst){ - if (manDev[burst.device] !== "unknown") { - burst.device = manDev[burst.device]; - } - }); - this.render('','timeseries'.toString(),this.data,false); - }); + this.loader.getIoTData().then( bundle => { + this.data = bundle.bursts; + console.log('refinecat.component data is ', this.data); + this.render('','timeseries'.toString(),this.data,false); + }); + // + // this.http.get('http://localhost:4201/api/refine/15').toPromise().then(response2 => { + // this.data = response2.json()["bursts"]; + // var manDev = response2.json()["manDev"]; + + // this.data.forEach(function(burst){ + // if (manDev[burst.device] !== "unknown") { + // burst.device = manDev[burst.device]; + // } + // }); + // this.render('','timeseries'.toString(),this.data,false); + // }); } getSVGElement() { From 356a742b19c51dd503d1ee41f4bb93aea0d3cdb6 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Fri, 28 Dec 2018 20:12:53 -0700 Subject: [PATCH 11/55] steady cleanup --- ui/src/app/app.component.ts | 28 ++-- ui/src/app/geobar/geobar.component.ts | 14 +- ui/src/app/geomap/geomap.component.ts | 18 +-- ui/src/app/loader.service.ts | 33 ++++- ui/src/app/refinebar/refinebar.component.ts | 134 +++++++++++--------- ui/src/app/refinecat/refinecat.component.ts | 62 ++++----- 6 files changed, 162 insertions(+), 127 deletions(-) diff --git a/ui/src/app/app.component.ts b/ui/src/app/app.component.ts index 9f63c93..0221a17 100644 --- a/ui/src/app/app.component.ts +++ b/ui/src/app/app.component.ts @@ -21,18 +21,22 @@ export class AppComponent { try { this.actlog.log('routeChange', routeEvent.url); } catch(e) { } } }); - let updates = this.loader.listenToUpdates().subscribe({ - next(x) { - // console.log("Listen next! ", x); - (window)._listen_count++ - }, - error(err) { - console.log("Listen error! ", err, err.message); - }, - complete() { - console.log("Listen complete"); - } - }); + + // startup loader listener + this.loader.connectToAsyncDBUpdates(); + + // let updates = this.loader.listenToUpdates().subscribe({ + // next(x) { + // // console.log("Listen next! ", x); + // (window)._listen_count++ + // }, + // error(err) { + // console.log("Listen error! ", err, err.message); + // }, + // complete() { + // console.log("Listen complete"); + // } + // }); } // isActive(instruction: any[]): boolean { diff --git a/ui/src/app/geobar/geobar.component.ts b/ui/src/app/geobar/geobar.component.ts index 28b34cb..a498c72 100644 --- a/ui/src/app/geobar/geobar.component.ts +++ b/ui/src/app/geobar/geobar.component.ts @@ -75,7 +75,7 @@ export class GeobarComponent implements AfterViewInit, OnChanges { this._ignoredApps = new Array(); focus.focusChanged$.subscribe((target) => { - //console.log('hover changed > ', target); + // console.log('hover changed > ', target); if (target !== this._ignoredApps) { this._ignoredApps = target ? target as string[] : []; this.render(); @@ -83,7 +83,7 @@ export class GeobarComponent implements AfterViewInit, OnChanges { }); this.getIoTData(); - (window)._rb = this; + // (window)._rb = this; } getIoTData(): void { this.loader.getIoTData().then(bundle => { @@ -191,8 +191,8 @@ export class GeobarComponent implements AfterViewInit, OnChanges { svg.selectAll('*').remove(); - const usage = this.usage.filter(obj => this._ignoredApps.indexOf(obj.appid) == -1 ), - impacts = this.impacts.filter(obj => this._ignoredApps.indexOf(obj.appid) == -1 ); + const usage = this.usage.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ), + impacts = this.impacts.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ); let apps = _.uniq(impacts.map((x) => x.appid)), countries = _.uniq(impacts.map((x) => x.country)), @@ -219,7 +219,7 @@ export class GeobarComponent implements AfterViewInit, OnChanges { // re-order companies countries = by_country.map((bc) => bc.country); - //console.log(by_country) + // console.log(by_country) let margin = { top: 20, right: 20, bottom: this.showXAxis ? 120 : 0, left: 40 }, width = width_svgel - margin.left - margin.right, // +svg.attr('width') - margin.left - margin.right, @@ -356,12 +356,12 @@ export class GeobarComponent implements AfterViewInit, OnChanges { } @HostListener('mouseenter') mouseEnter() { - //this.actlog.log('mouseenter', 'geobar'); + // this.actlog.log('mouseenter', 'geobar'); } @HostListener('mouseleave') mouseLv() { - //this.actlog.log('mouseleave', 'geobar'); + // this.actlog.log('mouseleave', 'geobar'); } } diff --git a/ui/src/app/geomap/geomap.component.ts b/ui/src/app/geomap/geomap.component.ts index d8da4a6..bbe7720 100644 --- a/ui/src/app/geomap/geomap.component.ts +++ b/ui/src/app/geomap/geomap.component.ts @@ -78,7 +78,7 @@ export class GeomapComponent implements AfterViewInit, OnChanges { this._ignoredApps = new Array(); focus.focusChanged$.subscribe((target) => { - //console.log('hover changed > ', target); + // console.log('hover changed > ', target); if (target !== this._ignoredApps) { this._ignoredApps = target ? target as string[] : []; this.render(); @@ -123,7 +123,7 @@ export class GeomapComponent implements AfterViewInit, OnChanges { if (!this.usage_in || !this.usage || !this.apps || this.apps.length !== this.usage_in.length) { delete this.apps; } - //this.usage = this.usage_in; + // this.usage = this.usage_in; this.compileImpacts(this.usage_in).then(impacts => { this.render(); }); @@ -193,9 +193,9 @@ export class GeomapComponent implements AfterViewInit, OnChanges { svg.selectAll('*').remove(); - const usage = this.usage.filter(obj => this._ignoredApps.indexOf(obj.appid) == -1 ), - impacts = this.impacts.filter(obj => this._ignoredApps.indexOf(obj.appid) == -1 ); - //console.log(impacts); + const usage = this.usage.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ), + impacts = this.impacts.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ); + // console.log(impacts); let apps = _.uniq(impacts.map((x) => x.appid)); if (this.apps === undefined) { @@ -266,9 +266,9 @@ export class GeomapComponent implements AfterViewInit, OnChanges { .attr('transform', function (d, i) { return 'translate(0,' + i * leading + ')'; }) .on('mouseenter', (d) => this.hover.hoverChanged(undefined)) .on('mouseout', (d) => this.hover.hoverChanged(undefined)) - //.on('click', (d) => { + // .on('click', (d) => { // this.focus.focusChanged(this.loader.getCachedAppInfo(d)); - //}) + // }) .attr('opacity', (d) => { let highApp = this.highlightApp || this._hoveringApp; if (highApp) { @@ -300,12 +300,12 @@ export class GeomapComponent implements AfterViewInit, OnChanges { } @HostListener('mouseenter') mouseEnter() { - //this.actlog.log('mouseenter', 'geomap'); + // this.actlog.log('mouseenter', 'geomap'); } @HostListener('mouseleave') mouseLv() { - //this.actlog.log('mouseleave', 'geomap'); + // this.actlog.log('mouseleave', 'geomap'); } } diff --git a/ui/src/app/loader.service.ts b/ui/src/app/loader.service.ts index 04d7743..49bcb60 100644 --- a/ui/src/app/loader.service.ts +++ b/ui/src/app/loader.service.ts @@ -6,6 +6,7 @@ import { mapValues, keys, mapKeys, values, trim, uniq, toPairs } from 'lodash'; import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; import * as _ from 'lodash'; import { Observable } from '../../node_modules/rxjs/Observable'; +import { AppImpact } from './refinebar/refinebar.component'; enum PI_TYPES { DEVICE_SOFT, USER_LOCATION, USER_LOCATION_COARSE, DEVICE_ID, USER_PERSONAL_DETAILS } @@ -243,6 +244,8 @@ export class LoaderService { _host_blacklist : {[key:string]:boolean};n apps: { [id: string]: APIAppInfo } = {}; + updateObservable: Observable; + constructor(private httpM: HttpModule, private http: Http, private sanitiser: DomSanitizer) { this._host_blacklist = host_blacklist.reduce((obj, a) => obj[a]=true && obj, {}); @@ -461,17 +464,16 @@ export class LoaderService { return this.apps[appid]; } - listenToUpdates() : Observable { - return Observable.create(observer => { + connectToAsyncDBUpdates() : void { + this.updateObservable = Observable.create(observer => { const eventSource = new EventSource(IOT_API_ENDPOINT+`/stream`); eventSource.onopen = thing => { console.info('EventSource Open', thing); }; eventSource.onmessage = score => { // console.info("EventSource onMessage", score, score.data); - zone.run(() => { - observer.next(JSON.parse(score.data)); - }); + let incoming = JSON.parse(score.data); + zone.run(() => observer.next(incoming)); }; eventSource.onerror = error => { // console.error("eventSource onerror", error); @@ -481,6 +483,25 @@ export class LoaderService { }); } + asyncAppImpactChanges(): Observable { + return Observable.create(observer => { + this.updateObservable.subscribe({ + next(x) { + if (['INSERT','UPDATE'].indexOf(x.operation) >= 0 && x.table === 'packets') { + // this is an impact operation + observer.next({ + appid: x.data.mac, + companyid:x.data.src, + companyName:undefined, + impact:x.data.len + }); + } + }, + error(e) { observer.error(e); } + }); + }); + } + // todo; move this out to loader @memoize(x => 'iotdata') getIoTData(): Promise { @@ -519,7 +540,7 @@ export class LoaderService { } else { console.warn('null appinfo'); } - //console.log(appinfo); + // console.log(appinfo); return appinfo; }); } diff --git a/ui/src/app/refinebar/refinebar.component.ts b/ui/src/app/refinebar/refinebar.component.ts index f48995b..5bb198e 100644 --- a/ui/src/app/refinebar/refinebar.component.ts +++ b/ui/src/app/refinebar/refinebar.component.ts @@ -11,7 +11,7 @@ import { Http, HttpModule, Headers, URLSearchParams } from '@angular/http'; const LOCAL_IP_MASK_16 = "192.168."; const LOCAL_IP_MASK_24 = "10."; -interface AppImpact { +export interface AppImpact { appid: string; companyid: string; impact: number; @@ -37,8 +37,8 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { private impacts: AppImpact[]; private init: Promise; lastMax = 0; - _timeSpan = 'd'; - _byTime = 'yes'; + // _timeSpan = 'd'; + // _byTime = 'yes'; normaliseImpacts = false; apps: string[]; // keeps app ordering between renders @@ -77,7 +77,7 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { this.getIoTData(); hover.HoverChanged$.subscribe((target) => { - //console.log('hover changed > ', target); + // console.log('hover changed > ', target); if (target !== this._hoveringApp) { this._hoveringApp = target ? target as string : undefined; this.render(); @@ -87,14 +87,14 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { this._ignoredApps = new Array(); focus.focusChanged$.subscribe((target) => { - //console.log('hover changed > ', target); + // console.log('hover changed > ', target); if (target !== this._ignoredApps) { this._ignoredApps = target ? target as string[] : []; this.render(); } }); - (window)._rb = this; + // (window)._rb = this; } getSVGElement() { const nE: HTMLElement = this.el.nativeElement; @@ -104,11 +104,26 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { // todo; move this out to loader getIoTData(): void { this.loader.getIoTData().then( bundle => { - console.log('!@#ILJ!@#L@!J# got bundle ', bundle); + // console.log('!@#ILJ!@#L@!J# got bundle ', bundle); this.usage = bundle.usage; this.impacts = bundle.impacts; this.render(); }); + + var throttledRender = _.throttle(() => this.render(), 1000), + this_ = this; + + this.loader.asyncAppImpactChanges().subscribe({ + next(x: AppImpact) { + console.log('AppImpact CHANGE!', x); + // this_.impacts.push(x); + // throttledRender(); + }, + error(err) { console.log("Listen error! ", err, err.message); }, + complete() { console.log("Listen complete"); } + }); + + // this.http.get('http://localhost:4201/api/refine/15').toPromise().then(response2 => { // this.usage = response2.json()["usage"]; // ah ha! // this.impacts = response2.json()["impacts"]; @@ -124,14 +139,13 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { } addOrRemove(newClick: string): string[] { - //console.log(this._ignoredApps); + // console.log(this._ignoredApps); if (this._ignoredApps.indexOf(newClick) > -1) { this._ignoredApps = this._ignoredApps.filter(obj => obj !== newClick); - } - else { + } else { this._ignoredApps.push(newClick); } - //console.log(this._ignoredApps); + // console.log(this._ignoredApps); this.render() return this._ignoredApps } @@ -186,49 +200,49 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { return this.linear ? v : Math.max(0, 5000*Math.log(v) + 10); } - compileImpacts(usage: AppUsage[]): Promise { - // folds privacy impact in simply by doing a weighted sum over hosts - // usage has to be in a standard unit: days, minutes - // first, normalise usage + // compileImpacts(usage: AppUsage[]): Promise { + // // folds privacy impact in simply by doing a weighted sum over hosts + // // usage has to be in a standard unit: days, minutes + // // first, normalise usage - const timebased = this.byTime === 'yes', - total = _.reduce(usage, (tot, appusage): number => tot + (timebased ? appusage.mins : 1.0), 0), - impacts = usage.map((u) => ({ ...u, impact: - this.nonLinearity((timebased ? u.mins : 1.0) / (1.0 * (this.normaliseImpacts ? total / 1000000 : 1.0))) - })); + // const timebased = this.byTime === 'yes', + // total = _.reduce(usage, (tot, appusage): number => tot + (timebased ? appusage.mins : 1.0), 0), + // impacts = usage.map((u) => ({ ...u, impact: + // this.nonLinearity((timebased ? u.mins : 1.0) / (1.0 * (this.normaliseImpacts ? total / 1000000 : 1.0))) + // })); - return Promise.all(impacts.map((usg): Promise => { + // return Promise.all(impacts.map((usg): Promise => { - return this._getApp(usg.appid).then(app => { - const hosts = app && app.hosts; - if (!hosts) { console.warn('No hosts found for app ', usg.appid); return Promise.resolve([]); } + // return this._getApp(usg.appid).then(app => { + // const hosts = app && app.hosts; + // if (!hosts) { console.warn('No hosts found for app ', usg.appid); return Promise.resolve([]); } - return Promise.all(hosts.map(host => this.hostutils.findCompany(host, app))) - .then((companies: CompanyInfo[]) => _.uniq(companies.filter((company) => company !== undefined && company.typetag !== 'ignore'))) - .then((companies: CompanyInfo[]) => companies.map((company) => ({ appid: usg.appid, companyid: company.id, impact: usg.impact }))); - }); - })).then((nested_impacts: AppImpact[][]): AppImpact[] => _.flatten(nested_impacts)); - } + // return Promise.all(hosts.map(host => this.hostutils.findCompany(host, app))) + // .then((companies: CompanyInfo[]) => _.uniq(companies.filter((company) => company !== undefined && company.typetag !== 'ignore'))) + // .then((companies: CompanyInfo[]) => companies.map((company) => ({ appid: usg.appid, companyid: company.id, impact: usg.impact }))); + // }); + // })).then((nested_impacts: AppImpact[][]): AppImpact[] => _.flatten(nested_impacts)); + // } - // accessors for .byTime - set byTime(val) { - this.lastMax = 0; - this._byTime = val; - this.init.then(x => this.compileImpacts(this.usage).then(impacts => { - this.render(); - })); - } + // // accessors for .byTime + // set byTime(val) { + // this.lastMax = 0; + // this._byTime = val; + // this.init.then(x => this.compileImpacts(this.usage).then(impacts => { + // this.render(); + // })); + // } - get byTime() { return this._byTime; } + // get byTime() { return this._byTime; } - // accessors for timeSpan - set timeSpan(val) { - this._timeSpan = val; - this.render(); - } + // // accessors for timeSpan + // set timeSpan(val) { + // this._timeSpan = val; + // this.render(); + // } - get timeSpan() {return this._timeSpan; } + // get timeSpan() {return this._timeSpan; } setHoveringTypeHighlight(ctype: string) { let svg = this.getSVGElement(); @@ -249,16 +263,16 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { @HostListener('mouseenter') mouseEnter() { - //this.actlog.log('mouseenter', 'refinebar'); + // this.actlog.log('mouseenter', 'refinebar'); } @HostListener('mouseleave') mouseLv() { - //this.actlog.log('mouseleave', 'refinebar'); + // this.actlog.log('mouseleave', 'refinebar'); } setHoveringApp(s: string) { - if (this._hoveringApp != s) { + if (this._hoveringApp !== s) { this._hoveringApp = s; this.render(); } @@ -271,8 +285,6 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { if (!svgel || this.usage === undefined || this.impacts === undefined || this.usage.length === 0) { return; } - //console.log(this._timeSpan); - let rect = svgel.getBoundingClientRect(), width_svgel = Math.round(rect.width - 5), height_svgel = Math.round(rect.height - 5), @@ -292,14 +304,12 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { svg.selectAll('*').remove(); - - let usage = this.usage.filter(obj => this._ignoredApps.indexOf(obj.appid) == -1 ), - impacts = this.impacts.filter(obj => this._ignoredApps.indexOf(obj.appid) == -1 ), + let usage = this.usage.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ), + impacts = this.impacts.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ), apps = _.uniq(impacts.map((x) => x.appid)), companies = _.uniq(impacts.map((x) => x.companyName)), get_impact = (cid, aid) => { const t = impacts.filter((imp) => imp.companyName === cid && imp.appid === aid); - //console.log(t); const reducer = (accumulator, currentValue) => accumulator + currentValue.impact; return t !== undefined ? t.reduce(reducer, 0) : 0; }, @@ -308,12 +318,10 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { total: apps.reduce((total, aid) => total += get_impact(c, aid), 0), ..._.fromPairs(apps.map((aid) => [aid, get_impact(c, aid)])) })); - //console.log(by_company); if (this.apps === undefined) { // sort apps apps.sort((a, b) => _.filter(usage, { appid: b })[0].mins - _.filter(usage, { appid: a })[0].mins); - //console.log(apps) this.apps = apps; } else { apps = this.apps; @@ -385,15 +393,15 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { self.focus.focusChanged(self.addOrRemove(this.parentElement.__data__.key)) }) .on('mouseleave', function (d) { - //console.log("Leave" + d ); - //self.setHoveringApp(undefined) + // console.log("Leave" + d ); + // self.setHoveringApp(undefined) this_.hover.hoverChanged(undefined); }) .on('mouseenter', function (d) { if (this.parentElement && this.parentElement.__data__) { // unsure why this is dying - //console.log("Enter" + this.parentElement.__data__.key ); - //self.setHoveringApp(this.parentElement.__data__.key); + // console.log("Enter" + this.parentElement.__data__.key ); + // self.setHoveringApp(this.parentElement.__data__.key); this_.hover.hoverChanged(this.parentElement.__data__.key); } }); @@ -483,7 +491,7 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { .append('g') .attr('transform', function (d, i) { return 'translate(0,' + i * leading + ')'; }) .on('mouseenter', (d) => { - //console.log(d); + // console.log(d); this.hover.hoverChanged(d) }) .on('mouseout', (d) => this.hover.hoverChanged(undefined)) @@ -506,8 +514,8 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { .attr('x', this.showTypesLegend ? width - 140 - 24 : width - 24) .attr('y', 9.5) .attr('dy', '0.32em') - .text((d) => this._ignoredApps.indexOf(d) == -1 ? d : "Removed: " + d) - .style("fill", d => this._ignoredApps.indexOf(d) == -1 ? 'rgba(0,0,0,1)' : 'rgba(200,0,0,1)') + .text((d) => this._ignoredApps.indexOf(d) === -1 ? d : "Removed: " + d) + .style("fill", d => this._ignoredApps.indexOf(d) === -1 ? 'rgba(0,0,0,1)' : 'rgba(200,0,0,1)') .attr('opacity', (d) => { let highApp = this.highlightApp || this._hoveringApp; if (highApp) { diff --git a/ui/src/app/refinecat/refinecat.component.ts b/ui/src/app/refinecat/refinecat.component.ts index 3b6caf9..9e1be41 100644 --- a/ui/src/app/refinecat/refinecat.component.ts +++ b/ui/src/app/refinecat/refinecat.component.ts @@ -28,7 +28,7 @@ interface BurstData { styleUrls: ['./refinecat.component.scss'], encapsulation: ViewEncapsulation.None }) -export class RefinecatComponent { +export class RefinecatComponent implements AfterViewInit, OnChanges { impacts: AppImpactCat[]; // still in use! @@ -81,8 +81,11 @@ export class RefinecatComponent { this.firstDay = undefined; this.lastDay = undefined; - if (this._hoveringApp == this.lastHovering) {this.lastHovering = undefined;} - else if (this._hoveringApp != undefined) {this.lastHovering = this._hoveringApp;} + if (this._hoveringApp === this.lastHovering) { + this.lastHovering = undefined; + } else if (this._hoveringApp !== undefined) { + this.lastHovering = this._hoveringApp; + } this.getIoTData() } @@ -91,7 +94,7 @@ export class RefinecatComponent { this._ignoredApps = new Array(); focus.focusChanged$.subscribe((target) => { - //console.log('hover changed > ', target); + // console.log('hover changed > ', target); if (target !== this._ignoredApps) { this._ignoredApps = target ? target as string[] : []; this.render('', 'timeseries'.toString(), this.data, false); @@ -134,9 +137,11 @@ lessThanDay(d) { return (d === "hours" || d === "minutes" || d === "seconds") ? true : false; }; -ngAfterViewInit(): void { this.init.then(() => { - this.getIoTData() -}) ; } +ngAfterViewInit(): void { + this.init.then(() => { + this.getIoTData() + }); +} ngOnChanges(changes: SimpleChanges): void { this.getIoTData() @@ -183,21 +188,22 @@ timeRangePad(dates) { }; getDatePadding(minDate, maxDate) { - if (maxDate.diff(minDate, 'years') > 0) + if (maxDate.diff(minDate, 'years') > 0) { return 'months'; - else if (maxDate.diff(minDate, 'months') > 0) + } else if (maxDate.diff(minDate, 'months') > 0) { return 'days'; - else if (maxDate.diff(minDate, 'days') > 0) + } else if (maxDate.diff(minDate, 'days') > 0) { return 'days'; - else if (maxDate.diff(minDate, 'hours') > 0) + } else if (maxDate.diff(minDate, 'hours') > 0) { return 'hours'; - else if (maxDate.diff(minDate, 'minutes') > 0) + } else if (maxDate.diff(minDate, 'minutes') > 0) { return 'minutes'; - else + } else { return 'seconds'; + } }; -selectOnlyDay(d): void{ +selectOnlyDay(d): void { var date = moment(d); date.hour(0) date.minute(0) @@ -285,9 +291,9 @@ render(classd, spaced, inputData: BurstData[], enableBrush) { .tickSize(-width + margin.right, 0) .tickFormat(d3.timeFormat(yFormat)); - //svg.attr("width", width + margin.left + margin.right) + // svg.attr("width", width + margin.left + margin.right) // .attr("height", height + margin.top + margin.bottom); - //console.log(data) + // console.log(data) var context = svg.append("g") .attr("class", "context") @@ -315,7 +321,7 @@ render(classd, spaced, inputData: BurstData[], enableBrush) { catDict[e] = catNumbers[i]; }); - //console.log(catDict) + // console.log(catDict) const catDisplacement = 25; var self = this; @@ -336,12 +342,12 @@ render(classd, spaced, inputData: BurstData[], enableBrush) { }) .attr("r", 9) .on("click", function(d) { - //console.log(new Date(d.value)); + // console.log(new Date(d.value)); self.selectOnlyDay(d.value); }) .append("svg:title") .text((d) => { - var ans = this.lessThanDay(padding.pad) && this.lastDay == undefined ? d.category : d.device + ": " + d.category ; + var ans = this.lessThanDay(padding.pad) && this.lastDay === undefined ? d.category : d.device + ": " + d.category ; return ans; }); @@ -354,20 +360,16 @@ render(classd, spaced, inputData: BurstData[], enableBrush) { .attr("y", 0 + (this.lessThanDay(padding.pad) ? margin.top / 2 : margin.top * 1.5 )) .attr("text-anchor", "middle") .style("font-size", "14px") - .text((d) => - { var ans = this.lastHovering + " Traffic Bursts"; - if (padding.minDate.format('DD/MM/YYYY') == padding.maxDate.format('DD/MM/YYYY')) { + .text((d) => { + var ans = this.lastHovering + " Traffic Bursts"; + if (padding.minDate.format('DD/MM/YYYY') === padding.maxDate.format('DD/MM/YYYY')) { var date = this.lessThanDay(padding.pad) ? " on: " + padding.minDate.format('DD/MM/YYYY') : ""; } else { var date = this.lessThanDay(padding.pad) ? " on: " + padding.minDate.format('DD/MM/YYYY') + " to " + padding.maxDate.format('DD/MM/YYYY') : ""; - } - - - if (this.lastHovering == undefined) {ans = "All Traffic Bursts"} - return ans + date;}); - - - + } + if (this.lastHovering === undefined) {ans = "All Traffic Bursts"} + return ans + date; + }); // ----------------------------------------- Legend --------------------------------------------- /* From ae855cbbb534ac7d1cda582dbeb7aad14a4ee0ee Mon Sep 17 00:00:00 2001 From: electronic Max Date: Fri, 28 Dec 2018 22:09:36 -0700 Subject: [PATCH 12/55] significant progress refactoring api --- db/databaseBursts.py | 5 +- scripts/api.py | 164 ++++++++++++-------- ui/src/app/loader.service.ts | 34 ++-- ui/src/app/refinebar/refinebar.component.ts | 6 +- 4 files changed, 124 insertions(+), 85 deletions(-) diff --git a/db/databaseBursts.py b/db/databaseBursts.py index 1129de5..10a755c 100644 --- a/db/databaseBursts.py +++ b/db/databaseBursts.py @@ -24,12 +24,13 @@ def listen(self, channel, cb=None): def subp(): while 1: if select.select([conn],[],[],5) == ([],[],[]): - print("Timeout") + # print("Timeout") + pass else: conn.poll() while conn.notifies: notify = conn.notifies.pop(0) - print("Got NOTIFY:", notify.pid, notify.channel, notify.payload) + # print("Got NOTIFY:", notify.pid, notify.channel, notify.payload) if cb is not None: cb(notify.payload) thread = threading.Thread(target=subp) diff --git a/scripts/api.py b/scripts/api.py index e372636..a1a1641 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -2,16 +2,12 @@ from flask import Flask, request, jsonify, make_response, Response from flask_restful import Resource, Api -import json -import re -import sys -import os -import traceback -import copy +import json, re, sys, os, traceback, copy, argparse from datetime import datetime sys.path.append(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "db")) import databaseBursts +LOCAL_IP_MASK = re.compile('^(192\.168|10\.|255\.255\.255\.255).*') #so we can filter for local ip addresses DB_MANAGER = databaseBursts.dbManager() #for running database queries app = Flask(__name__) #initialise the flask server api = Api(app) #initialise the flask server @@ -106,53 +102,53 @@ def GetBursts(days): result.append({"value": unixTime, "category": category, "device": device }) return result + #get impact (traffic) of every device/external ip combination for the given time period (in days) -def GetImpacts(days): - global geos, ID_POINTER, lastDays, _impact_cache - - print("GetImpacts: days::", days, " ID>::", ID_POINTER, " lastDays::", lastDays) - #we can only keep the cache if we're looking at the same packets as the previous request - if days is not lastDays: - print("ResetImpactCache()") - ResetImpactCache() +#setter method for impacts +def _update_impact(impacts, mac, ip, impact): + if mac in impacts: + # print("updateimpact existing mac ", mac) + if ip in impacts[mac]: + # print("updateimpact existing ip, updating impact for mac ", mac, " ip ", ip, " impact: ", impacts[mac][ip]) + impacts[mac][ip] += impact + else: + # print("updateimpact no existing ip for mac ", mac, " ip ", ip, " impact: ", impact) + impacts[mac][ip] = impact #impact did not exist + else: + # print("updateimpact unknown mac, creating new entry for ", mac, ip) + impacts[mac] = dict() + impacts[mac][ip] = impact #impact did not exist - impacts = copy.deepcopy(_impact_cache) # shallow copy - idp = ID_POINTER - #get all packets from the database (if we have cached impacts from before, then only get new packets) - packets = DB_MANAGER.execute("SELECT * FROM packets WHERE id > %s AND time > (NOW() - INTERVAL %s) ORDER BY id", (str(idp), "'" + str(days) + " DAY'")) - result = [] - local_ip_mask = re.compile('^(192\.168|10\.|255\.255\.255\.255).*') #so we can filter for local ip addresses - - for packet in packets: - #determine if the src or dst is the external ip address - pkt_id, pkt_time, pkt_src, pkt_dst, pkt_mac, pkt_len, pkt_proto, pkt_burst = packet - - ip_src = local_ip_mask.match(pkt_src) is not None - ip_dst = local_ip_mask.match(pkt_dst) is not None - ext_ip = None - - if (ip_src and ip_dst) or (not ip_src and not ip_dst): - continue #shouldn't happen, either 0 or 2 internal hosts - - #remember which ip address was external - elif ip_src: - ext_ip = pkt_dst - else: - ext_ip = pkt_src - - #make sure we have geo data, then update the impact - if ext_ip not in geos: - geos[ext_ip] = GetGeo(ext_ip) +def packet_to_impact(impacts, packet): + #determine if the src or dst is the external ip address + pkt_id, pkt_time, pkt_src, pkt_dst, pkt_mac, pkt_len, pkt_proto, pkt_burst = packet["id"], packet.get('time'), packet["src"], packet["dst"], packet["mac"], packet["len"], packet.get("proto"), packet.get("burst") + + ip_src = LOCAL_IP_MASK.match(pkt_src) is not None + ip_dst = LOCAL_IP_MASK.match(pkt_dst) is not None + ext_ip = None + + if (ip_src and ip_dst) or (not ip_src and not ip_dst): + return #shouldn't happen, either 0 or 2 internal hosts + + #remember which ip address was external + elif ip_src: + ext_ip = pkt_dst + else: + ext_ip = pkt_src + + #make sure we have geo data, then update the impact + if ext_ip not in geos: + geos[ext_ip] = GetGeo(ext_ip) - # print("UpdateImpact ", pkt_mac, ext_ip, pkt_len) - UpdateImpact(impacts, pkt_mac, ext_ip, pkt_len) + _update_impact(impacts, pkt_mac, ext_ip, pkt_len) - if idp < pkt_id: - idp = pkt_id +def CompileImpacts(impacts, packets): + # first run packet_to_impact + [packet_to_impact(impacts, packet) for packet in packets] - #build a list of all device/ip impacts and geo data + result = [] for ip,geo in geos.items(): for mac,_ in ManDev().items(): item = geo.copy() # emax added .copy here() this is so gross @@ -162,36 +158,44 @@ def GetImpacts(days): item['appid'] = mac if item['impact'] > 0: result.append(item) + pass + pass + return result + + +def GetImpacts(days): + global geos, ID_POINTER, lastDays, _impact_cache + print("GetImpacts: days::", days, " ID>::", ID_POINTER, " lastDays::", lastDays) + #we can only keep the cache if we're looking at the same packets as the previous request + if days is not lastDays: + print("ResetImpactCache()") + ResetImpactCache() + + impacts = copy.deepcopy(_impact_cache) # shallow copy + # get all packets from the database (if we have cached impacts from before, then only get new packets) + packetrows = DB_MANAGER.execute("SELECT * FROM packets WHERE id > %s AND time > (NOW() - INTERVAL %s) ORDER BY id", (str(ID_POINTER), "'" + str(days) + " DAY'")) + packets = [dict(zip(['id', 'time', 'src', 'dst', 'mac', 'len', 'proto', 'burst'], packet)) for packet in packetrows] + # pkt_id, pkt_time, pkt_src, pkt_dst, pkt_mac, pkt_len, pkt_proto, pkt_burst = packet + + # {'id': '212950', 'dst': '224.0.0.251', 'len': '101', 'mac': '78:4f:43:64:62:01', 'src': '192.168.0.24', 'burst': None} + + result = CompileImpacts(impacts, packets) + + max_pktid = max([pkt["id"] for pkt in packets]) + if ID_POINTER < max_pktid: + ID_POINTER = max_pktid - # commit these all at once to the globals # _impact_cache = impacts lastDays = days - ID_POINTER = idp - - # print("result ", json.dumps(result)) return result #shipit -#setter method for impacts -def UpdateImpact(impacts, mac, ip, impact): - if mac in impacts: - print("updateimpact existing mac ", mac) - if ip in impacts[mac]: - print("updateimpact existing ip, updating impact for mac ", mac, " ip ", ip, " impact: ", impacts[mac][ip]) - impacts[mac][ip] += impact - else: - print("updateimpact no existing ip for mac ", mac, " ip ", ip, " impact: ", impact) - impacts[mac][ip] = impact #impact did not exist - else: - print("updateimpact unknown mac, creating new entry for ", mac, ip) - impacts[mac] = dict() - impacts[mac][ip] = impact #impact did not exist - #getter method for impacts def GetImpact(mac, ip, impacts=_impact_cache): if mac in impacts: if ip in impacts[mac]: return impacts[mac][ip] - else: return 0 #impact does not exist + else: + return 0 #impact does not exist else: return 0 #impact does not exist @@ -212,12 +216,29 @@ def GenerateUsage(): _events = [] +def packets_insert_to_impact(packet): + return CompileImpacts(dict(),[packet]) + def event_stream(): import time while True: time.sleep(0.5) while len(_events) > 0: - yield "data: %s\n\n" % _events.pop(0) + try: + event_str = _events.pop(0) + # print("parsing event_str %s" % event_str) + event = json.loads(event_str) + if event["operation"] in ['UPDATE','INSERT'] and event["table"] == 'packets': + # print("gettingp packets insert to impact", event["data"]) + event['data']['len'] = int(event['data'].get('len')) + yield "data: %s\n\n" % json.dumps({"type":'impact', "data": packets_insert_to_impact(event["data"])}) + except GeneratorExit: + return; + except: + print("Unexpected error:", sys.exc_info()) + traceback.print_exc() + return + # sys.exit(-1) @app.route('/stream') def stream(): @@ -228,6 +249,15 @@ def stream(): #======================= #main part of the script if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--localip', dest="localip", type=str, help="Specify local IP addr (if not 192.168.x.x/10.x.x.x)") + args = parser.parse_args() + + if args.localip is not None: + localipmask = '^(192\.168|10\.|255\.255\.255\.255|%s).*' % args.localip.replace('.','\.') + LOCAL_IP_MASK = re.compile(localipmask) #so we can filter for local ip addresses + print("Using local IP mask %s" % localipmask) + #Register the API endpoints with flask api.add_resource(Refine, '/api/refine/') api.add_resource(Devices, '/api/devices') diff --git a/ui/src/app/loader.service.ts b/ui/src/app/loader.service.ts index 49bcb60..80ba8cf 100644 --- a/ui/src/app/loader.service.ts +++ b/ui/src/app/loader.service.ts @@ -49,11 +49,13 @@ export class PacketUpdateInfo { } export class DBUpdate { - timestamp: string; - operation: string; - schema: string; - table: string; - data: PacketUpdateInfo + type: string; + data: any; + // timestamp: string; + // operation: string; + // schema: string; + // table: string; + // data: PacketUpdateInfo } export let cache = (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor) => { @@ -486,16 +488,20 @@ export class LoaderService { asyncAppImpactChanges(): Observable { return Observable.create(observer => { this.updateObservable.subscribe({ - next(x) { - if (['INSERT','UPDATE'].indexOf(x.operation) >= 0 && x.table === 'packets') { - // this is an impact operation - observer.next({ - appid: x.data.mac, - companyid:x.data.src, - companyName:undefined, - impact:x.data.len - }); + next(x) { + if (x.type === 'impact') { + // console.info('incoming is an ', x); + observer.next(x.data); } + // if (['INSERT','UPDATE'].indexOf(x.operation) >= 0 && x.table === 'packets') { + // // this is an impact operation + // observer.next({ + // appid: x.data.mac, + // companyid:x.data.src, + // companyName:undefined, + // impact:x.data.len + // }); + // } }, error(e) { observer.error(e); } }); diff --git a/ui/src/app/refinebar/refinebar.component.ts b/ui/src/app/refinebar/refinebar.component.ts index 5bb198e..54354c2 100644 --- a/ui/src/app/refinebar/refinebar.component.ts +++ b/ui/src/app/refinebar/refinebar.component.ts @@ -116,8 +116,10 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { this.loader.asyncAppImpactChanges().subscribe({ next(x: AppImpact) { console.log('AppImpact CHANGE!', x); - // this_.impacts.push(x); - // throttledRender(); + if (this_.impacts) { + this_.impacts = this_.impacts.concat(x); + } + throttledRender(); }, error(err) { console.log("Listen error! ", err, err.message); }, complete() { console.log("Listen complete"); } From 15874e0bb0c06bad79b65a8e76143f6dac6c2b29 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Thu, 10 Jan 2019 20:09:33 +0000 Subject: [PATCH 13/55] fixed bug --- scripts/api.py | 46 +++++++++++++++---- ui/src/app/app.component.ts | 15 +----- ui/src/app/app.module.ts | 8 ++-- ui/src/app/tiled-all/tiled-all.component.html | 2 +- 4 files changed, 42 insertions(+), 29 deletions(-) diff --git a/scripts/api.py b/scripts/api.py index a1a1641..b01de49 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -148,18 +148,42 @@ def CompileImpacts(impacts, packets): # first run packet_to_impact [packet_to_impact(impacts, packet) for packet in packets] + # compute the updated impacts resulting from these packets + + # old way + # geos -> { ip -> { geo }, ip2 -> { { } + # impacts { mac -> { ip1: xx, ip2: xx, ip3: xx }, mac2 -> { } } + + # iterate over ip in geos whereas now what we want to do is return + # impacts generated by these packets across all of the macs that we see + # macs are gonna be unique in impacts + + # iterating over impacts [should be] safe at this point result = [] - for ip,geo in geos.items(): - for mac,_ in ManDev().items(): - item = geo.copy() # emax added .copy here() this is so gross - item['impact'] = GetImpact(mac, ip, impacts) - # print("Calling getimpact mac::", mac, " ip::", ip, 'impact result ', item['impact']); + for mmac, ipimpacts in impacts.items(): + for ip, impact in ipimpacts.items(): + item = geos[ip].copy() # hard crash here if geos[ip] is none, we should be careful about this + # note: geos[ip] should never be none because the invariant is that packet_to_impact has been + # called BEFORE this point, and that populates the geos. Yeah, ugly huh. I didn't write this + # code, don't blame me! + item['impact'] = impact item['companyid'] = ip - item['appid'] = mac + item['appid'] = mmac if item['impact'] > 0: result.append(item) pass - pass + + # for ip,geo in geos.items(): + # for mac,_ in ManDev().items(): + # item = geo.copy() # emax added .copy here() this is so gross + # item['impact'] = GetImpact(mac, ip, impacts) + # # print("Calling getimpact mac::", mac, " ip::", ip, 'impact result ', item['impact']); + # item['companyid'] = ip + # item['appid'] = mac + # if item['impact'] > 0: + # result.append(item) + # pass + # pass return result @@ -181,9 +205,11 @@ def GetImpacts(days): result = CompileImpacts(impacts, packets) - max_pktid = max([pkt["id"] for pkt in packets]) - if ID_POINTER < max_pktid: - ID_POINTER = max_pktid + if len(packets) > 0: + # we didn't get any packets so we'll start next time with what we get. + max_pktid = max([pkt["id"] for pkt in packets]) + if ID_POINTER < max_pktid: + ID_POINTER = max_pktid _impact_cache = impacts lastDays = days diff --git a/ui/src/app/app.component.ts b/ui/src/app/app.component.ts index 0221a17..10c464b 100644 --- a/ui/src/app/app.component.ts +++ b/ui/src/app/app.component.ts @@ -21,22 +21,9 @@ export class AppComponent { try { this.actlog.log('routeChange', routeEvent.url); } catch(e) { } } }); - + // startup loader listener this.loader.connectToAsyncDBUpdates(); - - // let updates = this.loader.listenToUpdates().subscribe({ - // next(x) { - // // console.log("Listen next! ", x); - // (window)._listen_count++ - // }, - // error(err) { - // console.log("Listen error! ", err, err.message); - // }, - // complete() { - // console.log("Listen complete"); - // } - // }); } // isActive(instruction: any[]): boolean { diff --git a/ui/src/app/app.module.ts b/ui/src/app/app.module.ts index 75a2c72..4c771a1 100644 --- a/ui/src/app/app.module.ts +++ b/ui/src/app/app.module.ts @@ -14,9 +14,9 @@ import { UsageConnectorService } from './usage-connector.service'; import { CompanybarComponent } from './companybar/companybar.component'; import { Ng2CompleterModule } from 'ng2-completer'; -import { SingleDisplayComponent } from './single-display/single-display.component'; -import { TiledDisplayComponent } from './tiled-display/tiled-display.component'; // no longer used with IoTRefine => +// import { SingleDisplayComponent } from './single-display/single-display.component'; +// import { TiledDisplayComponent } from './tiled-display/tiled-display.component'; // import { CompareComponent } from './compare/compare.component'; // import { CompareContainerComponent } from './compare-container/compare-container.component'; // @@ -50,8 +50,8 @@ const appRoutes: Routes = [ declarations: [ AppComponent, RefinebarComponent, - SingleDisplayComponent, - TiledDisplayComponent, + // SingleDisplayComponent, + // TiledDisplayComponent, ErrorComponent, CompanybarComponent, CompanyListComponent, diff --git a/ui/src/app/tiled-all/tiled-all.component.html b/ui/src/app/tiled-all/tiled-all.component.html index d497347..8fd5ec4 100644 --- a/ui/src/app/tiled-all/tiled-all.component.html +++ b/ui/src/app/tiled-all/tiled-all.component.html @@ -3,7 +3,7 @@
- +
From 30168c9ac8484bb4f4f7d3675eb3f012af99b301 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Thu, 10 Jan 2019 21:50:50 +0000 Subject: [PATCH 14/55] small changes, right before trying to refactor db change --- scripts/api.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/api.py b/scripts/api.py index b01de49..61562eb 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -159,6 +159,7 @@ def CompileImpacts(impacts, packets): # macs are gonna be unique in impacts # iterating over impacts [should be] safe at this point + result = [] for mmac, ipimpacts in impacts.items(): for ip, impact in ipimpacts.items(): @@ -243,7 +244,10 @@ def GenerateUsage(): _events = [] def packets_insert_to_impact(packet): - return CompileImpacts(dict(),[packet]) + print("packets insert to packet ", packet) + impacts = CompileImpacts(dict(),[packet]) + print("resulting impacts length ", len(impacts)) + return impacts def event_stream(): import time From 8d9e786570fdb7ba992c485e6f930e91c8850528 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Thu, 10 Jan 2019 22:08:06 +0000 Subject: [PATCH 15/55] now processes batches form db --- scripts/api.py | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/scripts/api.py b/scripts/api.py index 61562eb..a524c60 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -243,32 +243,33 @@ def GenerateUsage(): _events = [] -def packets_insert_to_impact(packet): - print("packets insert to packet ", packet) - impacts = CompileImpacts(dict(),[packet]) - print("resulting impacts length ", len(impacts)) - return impacts - def event_stream(): import time - while True: - time.sleep(0.5) - while len(_events) > 0: - try: + + def packets_insert_to_impact(packets): + print("packets insert to pitt ", packets) + impacts = CompileImpacts(dict(),packets) + print("resulting impacts length ", len(impacts)) + return impacts + + try: + while True: + time.sleep(0.5) + insert_buf = [] + while len(_events) > 0: event_str = _events.pop(0) - # print("parsing event_str %s" % event_str) event = json.loads(event_str) if event["operation"] in ['UPDATE','INSERT'] and event["table"] == 'packets': - # print("gettingp packets insert to impact", event["data"]) event['data']['len'] = int(event['data'].get('len')) - yield "data: %s\n\n" % json.dumps({"type":'impact', "data": packets_insert_to_impact(event["data"])}) - except GeneratorExit: - return; - except: - print("Unexpected error:", sys.exc_info()) - traceback.print_exc() - return - # sys.exit(-1) + insert_buf.append(event["data"]) + yield "data: %s\n\n" % json.dumps({"type":'impact', "data": packets_insert_to_impact(insert_buf)}) + except GeneratorExit: + return; + except: + print("Unexpected error:", sys.exc_info()) + traceback.print_exc() + return + # sys.exit(-1) @app.route('/stream') def stream(): From 091d9d34f48f25848a5ae03d4d94ab6b054b8c22 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Thu, 10 Jan 2019 22:38:22 +0000 Subject: [PATCH 16/55] updated console --- scripts/api.py | 11 +++++++---- ui/src/app/loader.service.ts | 4 ++-- ui/src/app/refinebar/refinebar.component.ts | 6 +++--- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/scripts/api.py b/scripts/api.py index a524c60..cb2dc19 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -246,23 +246,26 @@ def GenerateUsage(): def event_stream(): import time - def packets_insert_to_impact(packets): - print("packets insert to pitt ", packets) + def packets_insert_to_impact(packets): impacts = CompileImpacts(dict(),packets) - print("resulting impacts length ", len(impacts)) + print("packets insert to pitt ", len(packets), " resulting impacts len ~ ", len(impacts)) return impacts try: while True: time.sleep(0.5) insert_buf = [] + while len(_events) > 0: event_str = _events.pop(0) event = json.loads(event_str) if event["operation"] in ['UPDATE','INSERT'] and event["table"] == 'packets': event['data']['len'] = int(event['data'].get('len')) insert_buf.append(event["data"]) - yield "data: %s\n\n" % json.dumps({"type":'impact', "data": packets_insert_to_impact(insert_buf)}) + + if len(insert_buf) > 0: + yield "data: %s\n\n" % json.dumps({"type":'impact', "data": packets_insert_to_impact(insert_buf)}) + except GeneratorExit: return; except: diff --git a/ui/src/app/loader.service.ts b/ui/src/app/loader.service.ts index 80ba8cf..4a6b2b5 100644 --- a/ui/src/app/loader.service.ts +++ b/ui/src/app/loader.service.ts @@ -485,13 +485,13 @@ export class LoaderService { }); } - asyncAppImpactChanges(): Observable { + asyncAppImpactChanges(): Observable { return Observable.create(observer => { this.updateObservable.subscribe({ next(x) { if (x.type === 'impact') { // console.info('incoming is an ', x); - observer.next(x.data); + observer.next(x.data); } // if (['INSERT','UPDATE'].indexOf(x.operation) >= 0 && x.table === 'packets') { // // this is an impact operation diff --git a/ui/src/app/refinebar/refinebar.component.ts b/ui/src/app/refinebar/refinebar.component.ts index 54354c2..59387e3 100644 --- a/ui/src/app/refinebar/refinebar.component.ts +++ b/ui/src/app/refinebar/refinebar.component.ts @@ -114,10 +114,10 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { this_ = this; this.loader.asyncAppImpactChanges().subscribe({ - next(x: AppImpact) { - console.log('AppImpact CHANGE!', x); + next(i: AppImpact[]) { + console.log('AppImpact CHANGE!', i.map(x => ''+[x.companyName, x.companyid, ''+x.impact].join('_')).join(' - ')) if (this_.impacts) { - this_.impacts = this_.impacts.concat(x); + this_.impacts = this_.impacts.concat(i); } throttledRender(); }, From 73eed4b66159fbd38691de79733d22557fa37321 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Thu, 10 Jan 2019 23:19:16 +0000 Subject: [PATCH 17/55] changed database script to now have more triggers on geo and devices --- db/notify_trigger.sql | 11 ++++++++++- scripts/api.py | 14 ++++++++++++++ ui/src/app/loader.service.ts | 2 ++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/db/notify_trigger.sql b/db/notify_trigger.sql index 84ae9cb..762945a 100644 --- a/db/notify_trigger.sql +++ b/db/notify_trigger.sql @@ -50,9 +50,18 @@ FOR EACH ROW EXECUTE PROCEDURE notify_trigger( 'burst' ); -CREATE TRIGGER packets_notify AFTER INSERT OR UPDATE OR DELETE ON devices +CREATE TRIGGER device_notify AFTER INSERT OR UPDATE OR DELETE ON devices FOR EACH ROW EXECUTE PROCEDURE notify_trigger( 'mac', 'manufacturer', 'name' ); + +CREATE TRIGGER geodata_notify AFTER INSERT OR UPDATE OR DELETE ON geodata +FOR EACH ROW EXECUTE PROCEDURE notify_trigger( + 'ip', + 'lat', + 'lon', + 'c_code', + 'c_name' +); diff --git a/scripts/api.py b/scripts/api.py index cb2dc19..cd04566 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -255,6 +255,8 @@ def packets_insert_to_impact(packets): while True: time.sleep(0.5) insert_buf = [] + geo_updates = [] + device_updates = [] while len(_events) > 0: event_str = _events.pop(0) @@ -262,9 +264,21 @@ def packets_insert_to_impact(packets): if event["operation"] in ['UPDATE','INSERT'] and event["table"] == 'packets': event['data']['len'] = int(event['data'].get('len')) insert_buf.append(event["data"]) + if event["operation"] in ['UPDATE','INSERT'] and event["table"] == 'geodata': + print("Geodata update", event["data"]) + geo_updates.append(event["data"]) + if event["operation"] in ['UPDATE','INSERT'] and event["table"] == 'devices': + print("Device update", event["data"]) + device_updates.append(event["data"]) if len(insert_buf) > 0: yield "data: %s\n\n" % json.dumps({"type":'impact', "data": packets_insert_to_impact(insert_buf)}) + if len(geo_updates) > 0: + print("Got a geo update, must reset impact cache.") + ResetImpactCache() + yield "data: %s\n\n" % json.dumps({"type":'geo_update_flush_impacts'}) + if len(device_updates) > 0: + yield "data: %s\n\n" % json.dumps({"type":'device_updates', "data": packets_insert_to_impact(insert_buf)}) except GeneratorExit: return; diff --git a/ui/src/app/loader.service.ts b/ui/src/app/loader.service.ts index 4a6b2b5..73a2e2b 100644 --- a/ui/src/app/loader.service.ts +++ b/ui/src/app/loader.service.ts @@ -492,6 +492,8 @@ export class LoaderService { if (x.type === 'impact') { // console.info('incoming is an ', x); observer.next(x.data); + } else { + console.info("Got an event of type ", x.type, "skipping > ", x); } // if (['INSERT','UPDATE'].indexOf(x.operation) >= 0 && x.table === 'packets') { // // this is an impact operation From 0211e33ad1203efed845f6b96c1463f415247613 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Fri, 11 Jan 2019 00:14:17 +0000 Subject: [PATCH 18/55] things are starting to look good --- scripts/api.py | 6 +- ui/src/app/loader.service.ts | 89 +++++++++++++++------ ui/src/app/refinebar/refinebar.component.ts | 35 +++++--- 3 files changed, 95 insertions(+), 35 deletions(-) diff --git a/scripts/api.py b/scripts/api.py index cd04566..16aa40e 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -198,7 +198,7 @@ def GetImpacts(days): impacts = copy.deepcopy(_impact_cache) # shallow copy # get all packets from the database (if we have cached impacts from before, then only get new packets) - packetrows = DB_MANAGER.execute("SELECT * FROM packets WHERE id > %s AND time > (NOW() - INTERVAL %s) ORDER BY id", (str(ID_POINTER), "'" + str(days) + " DAY'")) + packetrows = DB_MANAGER.execute("SELECT * FROM packets WHERE id > %s AND time > (NOW() - INTERVAL %s) ORDER BY id", (str(ID_POINTER), "'" + str(days) + " DAYS'")) packets = [dict(zip(['id', 'time', 'src', 'dst', 'mac', 'len', 'proto', 'burst'], packet)) for packet in packetrows] # pkt_id, pkt_time, pkt_src, pkt_dst, pkt_mac, pkt_len, pkt_proto, pkt_burst = packet @@ -276,9 +276,9 @@ def packets_insert_to_impact(packets): if len(geo_updates) > 0: print("Got a geo update, must reset impact cache.") ResetImpactCache() - yield "data: %s\n\n" % json.dumps({"type":'geo_update_flush_impacts'}) + yield "data: %s\n\n" % json.dumps({"type":'geodata'}) if len(device_updates) > 0: - yield "data: %s\n\n" % json.dumps({"type":'device_updates', "data": packets_insert_to_impact(insert_buf)}) + yield "data: %s\n\n" % json.dumps({"type":'device', "data": packets_insert_to_impact(insert_buf)}) except GeneratorExit: return; diff --git a/ui/src/app/loader.service.ts b/ui/src/app/loader.service.ts index 73a2e2b..3cecb05 100644 --- a/ui/src/app/loader.service.ts +++ b/ui/src/app/loader.service.ts @@ -6,7 +6,7 @@ import { mapValues, keys, mapKeys, values, trim, uniq, toPairs } from 'lodash'; import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; import * as _ from 'lodash'; import { Observable } from '../../node_modules/rxjs/Observable'; -import { AppImpact } from './refinebar/refinebar.component'; +import { AppImpact, AppDevice } from './refinebar/refinebar.component'; enum PI_TYPES { DEVICE_SOFT, USER_LOCATION, USER_LOCATION_COARSE, DEVICE_ID, USER_PERSONAL_DETAILS } @@ -466,52 +466,95 @@ export class LoaderService { return this.apps[appid]; } + // connectToAsyncDBUpdates() : void { + // this.updateObservable = Observable.create(observer => { + // const eventSource = new EventSource(IOT_API_ENDPOINT+`/stream`); + // eventSource.onopen = thing => { + // console.info('EventSource Open', thing); + // }; + // eventSource.onmessage = score => { + // // console.info("EventSource onMessage", score, score.data); + // let incoming = JSON.parse(score.data); + // zone.run(() => observer.next(incoming)); + // }; + // eventSource.onerror = error => { + // // console.error("eventSource onerror", error); + // zone.run(() => observer.error(error)); + // }; + // return () => eventSource.close(); + // }); + // } connectToAsyncDBUpdates() : void { - this.updateObservable = Observable.create(observer => { - const eventSource = new EventSource(IOT_API_ENDPOINT+`/stream`); + let observers = [],eventSource; + + this.updateObservable = Observable.create(observer => { + observers.push(observer); + if (observers.length === 1 && eventSource === undefined) { + eventSource = new EventSource(IOT_API_ENDPOINT+`/stream`); eventSource.onopen = thing => { console.info('EventSource Open', thing); }; eventSource.onmessage = score => { // console.info("EventSource onMessage", score, score.data); let incoming = JSON.parse(score.data); - zone.run(() => observer.next(incoming)); + zone.run(() => observers.map(obs => obs.next(incoming))) }; eventSource.onerror = error => { // console.error("eventSource onerror", error); - zone.run(() => observer.error(error)); - }; - return () => eventSource.close(); - }); - } + zone.run(() => observers.map(obs => obs.error(error))); + }; + } + return () => { if (eventSource) { eventSource.close(); } } + }); +} + asyncAppImpactChanges(): Observable { return Observable.create(observer => { this.updateObservable.subscribe({ next(x) { if (x.type === 'impact') { - // console.info('incoming is an ', x); observer.next(x.data); - } else { - console.info("Got an event of type ", x.type, "skipping > ", x); - } - // if (['INSERT','UPDATE'].indexOf(x.operation) >= 0 && x.table === 'packets') { - // // this is an impact operation - // observer.next({ - // appid: x.data.mac, - // companyid:x.data.src, - // companyName:undefined, - // impact:x.data.len - // }); - // } + return true; + } + return false; + }, + error(e) { observer.error(e); } + }); + }); + } + asyncGeoUpdateChanges(): Observable { + return Observable.create(observer => { + this.updateObservable.subscribe({ + next(x) { + if (x.type === 'geodata') { + observer.next(x.data); + return true; + } + return false; + }, + error(e) { observer.error(e); } + }); + }); + } + asyncDeviceChanges(): Observable { + return Observable.create(observer => { + this.updateObservable.subscribe({ + next(x) { + if (x.type === 'device') { + observer.next(x.data); + return true; + } + return false; }, error(e) { observer.error(e); } }); }); } + // todo; move this out to loader - @memoize(x => 'iotdata') + // @memoize(x => 'iotdata') getIoTData(): Promise { return this.http.get(IOT_API_ENDPOINT + '/api/refine/15').toPromise().then(response2 => { let resp = response2.json(), diff --git a/ui/src/app/refinebar/refinebar.component.ts b/ui/src/app/refinebar/refinebar.component.ts index 59387e3..afe919a 100644 --- a/ui/src/app/refinebar/refinebar.component.ts +++ b/ui/src/app/refinebar/refinebar.component.ts @@ -17,6 +17,11 @@ export interface AppImpact { impact: number; companyName: string; }; +export interface AppDevice { + mac : string; + manufacturer: string; + name: string; +} @Component({ selector: 'app-refinebar', @@ -103,16 +108,20 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { // todo; move this out to loader getIoTData(): void { - this.loader.getIoTData().then( bundle => { - // console.log('!@#ILJ!@#L@!J# got bundle ', bundle); - this.usage = bundle.usage; - this.impacts = bundle.impacts; - this.render(); - }); - var throttledRender = _.throttle(() => this.render(), 1000), - this_ = this; + let this_ = this, + refresh = () => { + this_.loader.getIoTData().then( bundle => { + // console.log('!@#ILJ!@#L@!J# got bundle ', bundle); + this_.usage = bundle.usage; + this_.impacts = bundle.impacts; + this_.render(); + }); + }, + throttledRender = _.throttle(() => this.render(), 500), + throttledRefresh = _.throttle(refresh, 1000); + refresh(); this.loader.asyncAppImpactChanges().subscribe({ next(i: AppImpact[]) { console.log('AppImpact CHANGE!', i.map(x => ''+[x.companyName, x.companyid, ''+x.impact].join('_')).join(' - ')) @@ -125,8 +134,16 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { complete() { console.log("Listen complete"); } }); + this.loader.asyncGeoUpdateChanges().subscribe({ + next(a: any[]) { + console.info("GOT GEO UPDATE, NOW FLUSHING AND STARTING OVER"); + if (this_.impacts) { + throttledRefresh(); + } + } + }); - // this.http.get('http://localhost:4201/api/refine/15').toPromise().then(response2 => { + // this.http.get('http://localhost:4201/api/refine/15').toPromise().then(response2 => { // this.usage = response2.json()["usage"]; // ah ha! // this.impacts = response2.json()["impacts"]; // var manDev = response2.json()["manDev"]; From 0e816c4ad19b9871341272e52f6064bf820d3d60 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Fri, 11 Jan 2019 00:36:36 +0000 Subject: [PATCH 19/55] changed to minutes, more testing --- scripts/api.py | 37 +++++++++++++++++++----------------- ui/src/app/loader.service.ts | 4 ++-- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/scripts/api.py b/scripts/api.py index 16aa40e..e2c2e15 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -14,16 +14,17 @@ ID_POINTER = 0 #so we know which packets we've seen (for caching) _impact_cache = dict() #for building and caching impacts geos = dict() #for building and caching geo data -lastDays = 0 #timespan of the last request (for caching) +lastN = None +lastUnits = None #============= #api endpoints #return aggregated data for the given time period (in days, called by refine) class Refine(Resource): - def get(self, days): + def get(self, n): try: - response = make_response(jsonify({"bursts": GetBursts(2), "macMan": MacMan(), "manDev": ManDev(), "impacts": GetImpacts(2), "usage": GenerateUsage()})) + response = make_response(jsonify({"bursts": GetBursts(n), "macMan": MacMan(), "manDev": ManDev(), "impacts": GetImpacts(n), "usage": GenerateUsage()})) # response = make_response(jsonify({"bursts": GetBursts(days), "macMan": MacMan(), "manDev": ManDev(), "impacts": GetImpacts(days), "usage": GenerateUsage()})) response.headers['Access-Control-Allow-Origin'] = '*' @@ -50,13 +51,13 @@ def get(self, mac, name): #return all traffic bursts for the given time period (in days) class Bursts(Resource): - def get(self, days): - return jsonify(GetBursts(days)) + def get(self, n): + return jsonify(GetBursts(n)) #return all impacts for the given time period (in days) class Impacts(Resource): - def get(self, days): - return jsonify(GetImpacts(days)) + def get(self, n): + return jsonify(GetImpacts(n)) #================ #internal methods @@ -91,8 +92,8 @@ def GetGeo(ip): return geo #get bursts for the given time period (in days) -def GetBursts(days): - bursts = DB_MANAGER.execute("SELECT MIN(time), MIN(mac), burst, MIN(categories.name) FROM packets JOIN bursts ON bursts.id = packets.burst JOIN categories ON categories.id = bursts.category WHERE time > (NOW() - INTERVAL %s) GROUP BY burst ORDER BY burst", ("'" + str(days) + " DAY'",)) +def GetBursts(n, units="MINUTES"): + bursts = DB_MANAGER.execute("SELECT MIN(time), MIN(mac), burst, MIN(categories.name) FROM packets JOIN bursts ON bursts.id = packets.burst JOIN categories ON categories.id = bursts.category WHERE time > (NOW() - INTERVAL %s) GROUP BY burst ORDER BY burst", ("'" + str(n) + " " + units + "'",)) # " DAY'",)) result = [] epoch = datetime(1970, 1, 1, 0, 0) for burst in bursts: @@ -188,17 +189,18 @@ def CompileImpacts(impacts, packets): return result -def GetImpacts(days): - global geos, ID_POINTER, lastDays, _impact_cache - print("GetImpacts: days::", days, " ID>::", ID_POINTER, " lastDays::", lastDays) + +def GetImpacts(n, units="MINUTES"): + global geos, ID_POINTER, lastN, lastUnits, _impact_cache + print("GetImpacts: ::", n, ' ', units, " ID>::", ID_POINTER, " lastN::", lastN, " lastUnits", lastUnits) #we can only keep the cache if we're looking at the same packets as the previous request - if days is not lastDays: + if n != lastN or units != lastUnits: print("ResetImpactCache()") ResetImpactCache() impacts = copy.deepcopy(_impact_cache) # shallow copy # get all packets from the database (if we have cached impacts from before, then only get new packets) - packetrows = DB_MANAGER.execute("SELECT * FROM packets WHERE id > %s AND time > (NOW() - INTERVAL %s) ORDER BY id", (str(ID_POINTER), "'" + str(days) + " DAYS'")) + packetrows = DB_MANAGER.execute("SELECT * FROM packets WHERE id > %s AND time > (NOW() - INTERVAL %s) ORDER BY id", (str(ID_POINTER), "'" + str(n) + " " + units + "'")) packets = [dict(zip(['id', 'time', 'src', 'dst', 'mac', 'len', 'proto', 'burst'], packet)) for packet in packetrows] # pkt_id, pkt_time, pkt_src, pkt_dst, pkt_mac, pkt_len, pkt_proto, pkt_burst = packet @@ -213,7 +215,8 @@ def GetImpacts(days): ID_POINTER = max_pktid _impact_cache = impacts - lastDays = days + lastN = n + lastUnits = units return result #shipit #getter method for impacts @@ -307,10 +310,10 @@ def stream(): print("Using local IP mask %s" % localipmask) #Register the API endpoints with flask - api.add_resource(Refine, '/api/refine/') + api.add_resource(Refine, '/api/refine/') api.add_resource(Devices, '/api/devices') api.add_resource(Bursts, '/api/bursts/') - api.add_resource(Impacts, '/api/impacts/') + api.add_resource(Impacts, '/api/impacts/') api.add_resource(SetDevice, '/api/setdevice//') # watch for listen events -- not sure if this has to be on its own connection diff --git a/ui/src/app/loader.service.ts b/ui/src/app/loader.service.ts index 3cecb05..01e0778 100644 --- a/ui/src/app/loader.service.ts +++ b/ui/src/app/loader.service.ts @@ -485,7 +485,7 @@ export class LoaderService { // }); // } connectToAsyncDBUpdates() : void { - let observers = [],eventSource; + let observers = [], eventSource; this.updateObservable = Observable.create(observer => { observers.push(observer); @@ -556,7 +556,7 @@ export class LoaderService { // todo; move this out to loader // @memoize(x => 'iotdata') getIoTData(): Promise { - return this.http.get(IOT_API_ENDPOINT + '/api/refine/15').toPromise().then(response2 => { + return this.http.get(IOT_API_ENDPOINT + '/api/refine/5').toPromise().then(response2 => { let resp = response2.json(), impacts = resp.impacts, manDev = resp.manDev, From 0efa0751ecee7778de51dd641a81ef9f7ed2f58f Mon Sep 17 00:00:00 2001 From: electronic Max Date: Fri, 11 Jan 2019 03:38:06 +0000 Subject: [PATCH 20/55] unknown is still not refreshing --- scripts/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/api.py b/scripts/api.py index e2c2e15..8c30aee 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -231,8 +231,8 @@ def GetImpact(mac, ip, impacts=_impact_cache): #clear impact dictionary and packet id pointer def ResetImpactCache(): - global _impacts_cache, ID_POINTER - _impacts_cache = dict() + global _impact_cache, ID_POINTER + _impact_cache = dict() ID_POINTER = 0 #generate fake usage for devices (a hack so they show up in refine) From aa00e3490d49f3dc116c05f9ca3c5fb0978b878f Mon Sep 17 00:00:00 2001 From: electronic Max Date: Sat, 12 Jan 2019 15:38:44 +0000 Subject: [PATCH 21/55] geo now updates but quite slow - what gives --- scripts/api.py | 46 +++++++++++++--------------------------------- scripts/loop.py | 2 +- 2 files changed, 14 insertions(+), 34 deletions(-) diff --git a/scripts/api.py b/scripts/api.py index 8c30aee..9385fec 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -11,11 +11,7 @@ DB_MANAGER = databaseBursts.dbManager() #for running database queries app = Flask(__name__) #initialise the flask server api = Api(app) #initialise the flask server -ID_POINTER = 0 #so we know which packets we've seen (for caching) -_impact_cache = dict() #for building and caching impacts geos = dict() #for building and caching geo data -lastN = None -lastUnits = None #============= #api endpoints @@ -191,36 +187,23 @@ def CompileImpacts(impacts, packets): def GetImpacts(n, units="MINUTES"): - global geos, ID_POINTER, lastN, lastUnits, _impact_cache - print("GetImpacts: ::", n, ' ', units, " ID>::", ID_POINTER, " lastN::", lastN, " lastUnits", lastUnits) + global geos + print("GetImpacts: ::", n, ' ', units) #we can only keep the cache if we're looking at the same packets as the previous request - if n != lastN or units != lastUnits: - print("ResetImpactCache()") - ResetImpactCache() - impacts = copy.deepcopy(_impact_cache) # shallow copy + impacts = dict() # copy.deepcopy(_impact_cache) # get all packets from the database (if we have cached impacts from before, then only get new packets) - packetrows = DB_MANAGER.execute("SELECT * FROM packets WHERE id > %s AND time > (NOW() - INTERVAL %s) ORDER BY id", (str(ID_POINTER), "'" + str(n) + " " + units + "'")) + packetrows = DB_MANAGER.execute("SELECT * FROM packets WHERE time > (NOW() - INTERVAL %s) ORDER BY id", ("'" + str(n) + " " + units + "'",)) packets = [dict(zip(['id', 'time', 'src', 'dst', 'mac', 'len', 'proto', 'burst'], packet)) for packet in packetrows] - # pkt_id, pkt_time, pkt_src, pkt_dst, pkt_mac, pkt_len, pkt_proto, pkt_burst = packet + # pkt_id, pkt_time, pkt_src, pkt_dst, pkt_mac, pkt_len, pkt_proto, pkt_burst = packet # {'id': '212950', 'dst': '224.0.0.251', 'len': '101', 'mac': '78:4f:43:64:62:01', 'src': '192.168.0.24', 'burst': None} result = CompileImpacts(impacts, packets) - - if len(packets) > 0: - # we didn't get any packets so we'll start next time with what we get. - max_pktid = max([pkt["id"] for pkt in packets]) - if ID_POINTER < max_pktid: - ID_POINTER = max_pktid - - _impact_cache = impacts - lastN = n - lastUnits = units return result #shipit -#getter method for impacts -def GetImpact(mac, ip, impacts=_impact_cache): +# Getter method for impacts - nb: i think this is no longer used +def GetImpact(mac, ip, impacts): if mac in impacts: if ip in impacts[mac]: return impacts[mac][ip] @@ -229,13 +212,7 @@ def GetImpact(mac, ip, impacts=_impact_cache): else: return 0 #impact does not exist -#clear impact dictionary and packet id pointer -def ResetImpactCache(): - global _impact_cache, ID_POINTER - _impact_cache = dict() - ID_POINTER = 0 - -#generate fake usage for devices (a hack so they show up in refine) +# Generate fake usage for devices (a hack so they show up in refine) def GenerateUsage(): usage = [] counter = 1 @@ -277,8 +254,11 @@ def packets_insert_to_impact(packets): if len(insert_buf) > 0: yield "data: %s\n\n" % json.dumps({"type":'impact', "data": packets_insert_to_impact(insert_buf)}) if len(geo_updates) > 0: - print("Got a geo update, must reset impact cache.") - ResetImpactCache() + # ResetImpactCache() + # updated ip should be + for update in geo_updates: + print("Got a geo update for %s, must reset GEO cache." % update["ip"]) + geos.pop(update["ip"], None) yield "data: %s\n\n" % json.dumps({"type":'geodata'}) if len(device_updates) > 0: yield "data: %s\n\n" % json.dumps({"type":'device', "data": packets_insert_to_impact(insert_buf)}) diff --git a/scripts/loop.py b/scripts/loop.py index 30253f4..f3fd6e0 100755 --- a/scripts/loop.py +++ b/scripts/loop.py @@ -13,7 +13,7 @@ FILE_PATH = os.path.dirname(os.path.abspath(__file__)) DB_MANAGER = databaseBursts.dbManager() -INTERVAL = 5 +INTERVAL = 1 config = {"EchoFlowNumberCutoff":10,"burstNumberCutoffs":{"Echo":20,"Google Home":60,"Philips Hue Bridge":2,"Unknown":10},"burstTimeIntervals":{"Echo":1,"Google Home":1,"Philips Hue Bridge":1,"Unknown":1}} #handler for signals (don't want to stop processing packets halfway through) From 5fb79c58ac9264cea252c00641dc50016d3a2215 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Sat, 12 Jan 2019 15:43:37 +0000 Subject: [PATCH 22/55] slight refactor of api now staging refactor of loop --- scripts/api.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/api.py b/scripts/api.py index 9385fec..6bdcad8 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -256,9 +256,8 @@ def packets_insert_to_impact(packets): if len(geo_updates) > 0: # ResetImpactCache() # updated ip should be - for update in geo_updates: - print("Got a geo update for %s, must reset GEO cache." % update["ip"]) - geos.pop(update["ip"], None) + print("Got a geo updates for %s, must reset GEO cache." % [u["ip"] for u in geo_updates]) + [geos.pop(u["ip"], None) for u in geo_updates] yield "data: %s\n\n" % json.dumps({"type":'geodata'}) if len(device_updates) > 0: yield "data: %s\n\n" % json.dumps({"type":'device', "data": packets_insert_to_impact(insert_buf)}) From bf0e6f30f13c0388d053ac9ddcae031a401ce2a6 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Sat, 12 Jan 2019 16:27:37 +0000 Subject: [PATCH 23/55] loop is now doing two distincts --- db/schema.sql | 4 +++ scripts/api.py | 6 +++- scripts/capture.py | 2 +- scripts/loop.py | 69 +++++++++++++++++++++++++++++++--------------- 4 files changed, 57 insertions(+), 24 deletions(-) diff --git a/db/schema.sql b/db/schema.sql index 9afd07a..e04e2f9 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -26,6 +26,10 @@ create table packets ( --company integer references companies --optional, assumes table of companies (stolen from refine) ); +-- create two indexes on src and dst to speed up lookups by these cols by loop.py +create index on packets (src); +create index on packets (dst); + drop table if exists devices cascade; create table devices( mac varchar(17) primary key, diff --git a/scripts/api.py b/scripts/api.py index 6bdcad8..6d3ac03 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -160,7 +160,10 @@ def CompileImpacts(impacts, packets): result = [] for mmac, ipimpacts in impacts.items(): for ip, impact in ipimpacts.items(): - item = geos[ip].copy() # hard crash here if geos[ip] is none, we should be careful about this + item = geos.get(ip, None) + if item is None: # we might have just killed the key + continue + item = item.copy() # note: geos[ip] should never be none because the invariant is that packet_to_impact has been # called BEFORE this point, and that populates the geos. Yeah, ugly huh. I didn't write this # code, don't blame me! @@ -280,6 +283,7 @@ def stream(): #main part of the script if __name__ == '__main__': parser = argparse.ArgumentParser() + parser.add_argument('--localip', dest="localip", type=str, help="Specify local IP addr (if not 192.168.x.x/10.x.x.x)") args = parser.parse_args() diff --git a/scripts/capture.py b/scripts/capture.py index 34671dd..1b31f4f 100755 --- a/scripts/capture.py +++ b/scripts/capture.py @@ -100,7 +100,7 @@ def log(*args): parser = argparse.ArgumentParser() parser.add_argument('--interface', dest="interface", type=str, help="Interface to listen to") - parser.add_argument('--cinterval', dest="cinterval", type=int, help="Commit interval in seconds", default=5) + parser.add_argument('--cinterval', dest="cinterval", type=float, help="Commit interval in seconds", default=5) parser.add_argument('--localip', dest="localip", type=str, help="Specify local IP addr (if not 192.168.x.x/10.x.x.x)") parser.add_argument('--debug', dest='debug', action='store_true') args = parser.parse_args() diff --git a/scripts/loop.py b/scripts/loop.py index f3fd6e0..a77ebd3 100755 --- a/scripts/loop.py +++ b/scripts/loop.py @@ -1,11 +1,6 @@ #! /usr/bin/env python3 -import sys -import time -import os -import signal -import requests -import re +import sys, time, os, signal, requests, re, argparse sys.path.append(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "db")) sys.path.append(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "categorisation")) import databaseBursts @@ -14,6 +9,9 @@ FILE_PATH = os.path.dirname(os.path.abspath(__file__)) DB_MANAGER = databaseBursts.dbManager() INTERVAL = 1 +LOCAL_IP_MASK = re.compile('^(192\.168|10\.|255\.255\.255\.255).*') #so we can filter for local ip addresses +DEBUG = False + config = {"EchoFlowNumberCutoff":10,"burstNumberCutoffs":{"Echo":20,"Google Home":60,"Philips Hue Bridge":2,"Unknown":10},"burstTimeIntervals":{"Echo":1,"Google Home":1,"Philips Hue Bridge":1,"Unknown":1}} #handler for signals (don't want to stop processing packets halfway through) @@ -117,29 +115,31 @@ def burstPrediction(devices): DB_MANAGER.updateBurstCategory(burst[0], newCategoryId) def processGeos(): - raw_ips = DB_MANAGER.execute("SELECT DISTINCT src, dst FROM packets", ()) + # raw_ips = DB_MANAGER.execute("SELECT DISTINCT src, dst FROM packets", ()) + raw_ips = set( [r[0] for r in DB_MANAGER.execute("SELECT DISTINCT src FROM packets", ())]).union([r[0] for r in DB_MANAGER.execute("SELECT DISTINCT dst FROM packets", ())]) + + # print("raw_ips", raw_ips) + raw_geos = DB_MANAGER.execute("SELECT ip FROM geodata", ()) - local_ip_mask = re.compile('^(192\.168|10\.|255\.255\.255\.255).*') #so we can filter for local ip addresses known_ips = [] for row in raw_geos: known_ips.append(row[0]) - for dst,src in raw_ips: - external_ip = None - if local_ip_mask.match(dst) is not None: - external_ip = src - else: - external_ip = dst - - if external_ip not in known_ips: - data = requests.get('https://api.ipdata.co/' + external_ip + '?api-key=***REMOVED***') + for ip in raw_ips: + if LOCAL_IP_MASK.match(ip) is not None: + # local ip, so skip + continue + if ip not in known_ips: + data = requests.get('https://api.ipdata.co/' + ip + '?api-key=***REMOVED***') if data.status_code==200 and data.json()['latitude'] is not '': data = data.json() - DB_MANAGER.execute("INSERT INTO geodata VALUES(%s, %s, %s, %s, %s)", (external_ip, data['latitude'], data['longitude'], data['country_code'], data['organisation'][:20])) + DB_MANAGER.execute("INSERT INTO geodata VALUES(%s, %s, %s, %s, %s)", (ip, data['latitude'], data['longitude'], data['country_code'], data['organisation'][:20])) else: - DB_MANAGER.execute("INSERT INTO geodata VALUES(%s, %s, %s, %s, %s)", (external_ip, "0", "0", "XX", "unknown")) - known_ips.append(external_ip) + DB_MANAGER.execute("INSERT INTO geodata VALUES(%s, %s, %s, %s, %s)", (ip, "0", "0", "XX", "unknown")) + known_ips.append(ip) + + if DEBUG: print("Adding to known IPs ", ip) def processMacs(): raw_macs = DB_MANAGER.execute("SELECT DISTINCT mac FROM packets", ()) @@ -155,6 +155,27 @@ def processMacs(): #============ #loop control if __name__ == '__main__': + + parser = argparse.ArgumentParser() + + parser.add_argument('--localip', dest="localip", type=str, help="Specify local IP addr (if not 192.168.x.x/10.x.x.x)") + parser.add_argument('--sleep', dest="sleep", type=float, help="Specify sleep in sec (can be fractions)") + parser.add_argument('--burstify', dest='burst', action="store_true", help='Do packet burstification (Default off)') + parser.add_argument('--predict', dest='predict', action="store_true", help='Do burst prediction (Default off)') + parser.add_argument('--debug', dest='debug', action="store_true", help='Turn debug output on (Default off)') + args = parser.parse_args() + + if args.localip is not None: + localipmask = '^(192\.168|10\.|255\.255\.255\.255|%s).*' % args.localip.replace('.','\.') + print("Using local IP mask %s" % localipmask) + LOCAL_IP_MASK = re.compile(localipmask) #so we can filter for local ip addresses + + if args.sleep is not None: + print("Setting sleep interval %s seconds." % args.sleep) + INTERVAL = args.sleep + + DEBUG = args.debug + #register the signal handler handler = sigTermHandler() @@ -163,8 +184,12 @@ def processMacs(): processGeos() processMacs() devices = requests.get(url='http://localhost:4201/api/devices').json()["manDev"] - packetBurstification(devices) - burstPrediction(devices) + if args.burst: + print("Doing burstification") + packetBurstification(devices) + if args.predict: + print("Doing prediction") + burstPrediction(devices) #exit gracefully if we were asked to shutdown if handler.exit: From 6d83d369d56409ca27db2da63c35e2e561968313 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Sat, 12 Jan 2019 16:45:08 +0000 Subject: [PATCH 24/55] happy little caching of raw_ips --- scripts/loop.py | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/scripts/loop.py b/scripts/loop.py index a77ebd3..811efb1 100755 --- a/scripts/loop.py +++ b/scripts/loop.py @@ -1,6 +1,6 @@ #! /usr/bin/env python3 -import sys, time, os, signal, requests, re, argparse +import sys, time, os, signal, requests, re, argparse, json sys.path.append(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "db")) sys.path.append(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "categorisation")) import databaseBursts @@ -11,6 +11,8 @@ INTERVAL = 1 LOCAL_IP_MASK = re.compile('^(192\.168|10\.|255\.255\.255\.255).*') #so we can filter for local ip addresses DEBUG = False +RAW_IPS = None +_events = [] # async db events config = {"EchoFlowNumberCutoff":10,"burstNumberCutoffs":{"Echo":20,"Google Home":60,"Philips Hue Bridge":2,"Unknown":10},"burstTimeIntervals":{"Echo":1,"Google Home":1,"Philips Hue Bridge":1,"Unknown":1}} @@ -116,7 +118,12 @@ def burstPrediction(devices): def processGeos(): # raw_ips = DB_MANAGER.execute("SELECT DISTINCT src, dst FROM packets", ()) - raw_ips = set( [r[0] for r in DB_MANAGER.execute("SELECT DISTINCT src FROM packets", ())]).union([r[0] for r in DB_MANAGER.execute("SELECT DISTINCT dst FROM packets", ())]) + global RAW_IPS + + if not RAW_IPS: + print("Preloading RAW_IPS") + RAW_IPS = set( [r[0] for r in DB_MANAGER.execute("SELECT DISTINCT src FROM packets", ())]).union([r[0] for r in DB_MANAGER.execute("SELECT DISTINCT dst FROM packets", ())]) + print(" Done ", len(RAW_IPS), " known ips ") # print("raw_ips", raw_ips) @@ -126,7 +133,7 @@ def processGeos(): for row in raw_geos: known_ips.append(row[0]) - for ip in raw_ips: + for ip in RAW_IPS: if LOCAL_IP_MASK.match(ip) is not None: # local ip, so skip continue @@ -152,6 +159,22 @@ def processMacs(): else: DB_MANAGER.execute("INSERT INTO devices VALUES(%s, 'unknown', 'unknown')", (mac[0],)) + +# + +def processEvents(): + global _events + cur_events = _events.copy() + _events.clear() + for evt in cur_events: + evt = json.loads(evt) + if RAW_IPS and evt["operation"] in ['UPDATE','INSERT'] and evt["table"] == 'packets': + # print("adding to raw ips %s %s" % (evt["data"]["src"],evt["data"]["dst"])) + RAW_IPS.add(evt["data"]["src"]) + RAW_IPS.add(evt["data"]["dst"]) + pass + + #============ #loop control if __name__ == '__main__': @@ -179,8 +202,12 @@ def processMacs(): #register the signal handler handler = sigTermHandler() + # watch for listen events -- not sure if this has to be on its own connection + DB_MANAGER.listen('db_notifications', lambda payload:_events.append(payload)) + #loop through categorisation tasks while(True): + processEvents() processGeos() processMacs() devices = requests.get(url='http://localhost:4201/api/devices').json()["manDev"] From 0ac8d82eb031afacee6ac82e49b52be5fe5e9680 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Sat, 12 Jan 2019 18:34:52 +0000 Subject: [PATCH 25/55] added utils which starts to automatically determine local ip --- scripts/rutils.py | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 scripts/rutils.py diff --git a/scripts/rutils.py b/scripts/rutils.py new file mode 100644 index 0000000..046ff98 --- /dev/null +++ b/scripts/rutils.py @@ -0,0 +1,7 @@ + + +def get_local_ip(): + import socket + x = [ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")] + y = [[(s.connect(("8.8.8.8", 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]] + return x or y From 5fdc7f168907afc207dd94f2fac236afc16c8f56 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Sat, 12 Jan 2019 18:54:06 +0000 Subject: [PATCH 26/55] added cool new feature to autoamtically detect local ip address and add it for debugging --- scripts/api.py | 14 +++++++------- scripts/capture.py | 22 +++++++++++----------- scripts/loop.py | 4 ++-- scripts/rutils.py | 18 ++++++++++++++++++ 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/scripts/api.py b/scripts/api.py index 6d3ac03..cda91b7 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -5,9 +5,9 @@ import json, re, sys, os, traceback, copy, argparse from datetime import datetime sys.path.append(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "db")) -import databaseBursts +import databaseBursts, rutils -LOCAL_IP_MASK = re.compile('^(192\.168|10\.|255\.255\.255\.255).*') #so we can filter for local ip addresses +LOCAL_IP_MASK = rutils.make_localip_mask() # re.compile('^(192\.168|10\.|255\.255\.255\.255).*') #so we can filter for local ip addresses DB_MANAGER = databaseBursts.dbManager() #for running database queries app = Flask(__name__) #initialise the flask server api = Api(app) #initialise the flask server @@ -284,13 +284,13 @@ def stream(): if __name__ == '__main__': parser = argparse.ArgumentParser() - parser.add_argument('--localip', dest="localip", type=str, help="Specify local IP addr (if not 192.168.x.x/10.x.x.x)") + # parser.add_argument('--localip', dest="localip", type=str, help="Specify local IP addr (if not 192.168.x.x/10.x.x.x)") args = parser.parse_args() - if args.localip is not None: - localipmask = '^(192\.168|10\.|255\.255\.255\.255|%s).*' % args.localip.replace('.','\.') - LOCAL_IP_MASK = re.compile(localipmask) #so we can filter for local ip addresses - print("Using local IP mask %s" % localipmask) + # if args.localip is not None: + # localipmask = '^(192\.168|10\.|255\.255\.255\.255|%s).*' % args.localip.replace('.','\.') + # LOCAL_IP_MASK = re.compile(localipmask) #so we can filter for local ip addresses + # print("Using local IP mask %s" % localipmask) #Register the API endpoints with flask api.add_resource(Refine, '/api/refine/') diff --git a/scripts/capture.py b/scripts/capture.py index 1b31f4f..b541ff0 100755 --- a/scripts/capture.py +++ b/scripts/capture.py @@ -1,11 +1,11 @@ #! /usr/bin/env python3 -import pyshark, datetime, psycopg2, re, argparse, sys, traceback +import pyshark, datetime, psycopg2, re, argparse, sys, traceback, rutils #constants COMMIT_INTERVAL = 5 DEBUG = False -MANUAL_LOCAL_IP = None +local_ip_mask = rutils.make_localip_mask() #initialise vars timestamp = 0 @@ -14,11 +14,11 @@ def DatabaseInsert(packets): global timestamp print("packets ", len(packets)) - if MANUAL_LOCAL_IP is None: - local_ip_mask = re.compile('^(192\.168|10\.|255\.255\.255\.255).*') #so we can filter for local ip addresses - else: - print('Using local IP mask:', '^(192\.168|10\.|255\.255\.255\.255|%s).*' % MANUAL_LOCAL_IP.replace('.','\.')) - local_ip_mask = re.compile('^(192\.168|10\.|255\.255\.255\.255|%s).*' % MANUAL_LOCAL_IP.replace('.','\.')) #so we can filter for local ip addresses + # if MANUAL_LOCAL_IP is None: + # local_ip_mask = re.compile('^(192\.168|10\.|255\.255\.255\.255).*') #so we can filter for local ip addresses + # else: + # print('Using local IP mask:', '^(192\.168|10\.|255\.255\.255\.255|%s).*' % MANUAL_LOCAL_IP.replace('.','\.')) + # local_ip_mask = re.compile('^(192\.168|10\.|255\.255\.255\.255|%s).*' % MANUAL_LOCAL_IP.replace('.','\.')) #so we can filter for local ip addresses #open db connection conn = psycopg2.connect("dbname=testdb user=postgres password=password") @@ -101,7 +101,7 @@ def log(*args): parser = argparse.ArgumentParser() parser.add_argument('--interface', dest="interface", type=str, help="Interface to listen to") parser.add_argument('--cinterval', dest="cinterval", type=float, help="Commit interval in seconds", default=5) - parser.add_argument('--localip', dest="localip", type=str, help="Specify local IP addr (if not 192.168.x.x/10.x.x.x)") + # parser.add_argument('--localip', dest="localip", type=str, help="Specify local IP addr (if not 192.168.x.x/10.x.x.x)") parser.add_argument('--debug', dest='debug', action='store_true') args = parser.parse_args() @@ -111,8 +111,8 @@ def log(*args): print(parser.print_help()) sys.exit(-1) - if args.localip is not None: - MANUAL_LOCAL_IP = args.localip + # if args.localip is not None: + # MANUAL_LOCAL_IP = args.localip log("Configuring capture on ", args.interface) @@ -128,6 +128,6 @@ def log(*args): log("Starting capture") capture.apply_on_packets(QueuedCommit) #, timeout=30) - capture.close(); + capture.close() diff --git a/scripts/loop.py b/scripts/loop.py index 811efb1..5d4a604 100755 --- a/scripts/loop.py +++ b/scripts/loop.py @@ -3,13 +3,13 @@ import sys, time, os, signal, requests, re, argparse, json sys.path.append(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "db")) sys.path.append(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "categorisation")) -import databaseBursts +import databaseBursts, rutils import predictions FILE_PATH = os.path.dirname(os.path.abspath(__file__)) DB_MANAGER = databaseBursts.dbManager() INTERVAL = 1 -LOCAL_IP_MASK = re.compile('^(192\.168|10\.|255\.255\.255\.255).*') #so we can filter for local ip addresses +LOCAL_IP_MASK = rutils.make_localip_mask() # re.compile('^(192\.168|10\.|255\.255\.255\.255).*') #so we can filter for local ip addresses DEBUG = False RAW_IPS = None _events = [] # async db events diff --git a/scripts/rutils.py b/scripts/rutils.py index 046ff98..9525e9b 100644 --- a/scripts/rutils.py +++ b/scripts/rutils.py @@ -1,4 +1,22 @@ +import re + +def make_localip_mask(override=None): + + if not override: + initial_mask = re.compile('^(192\.168|10\.|255\.255\.255\.255).*') #so we can filter for local ip addresses + else : + pattern = '^(192\.168|10\.|255\.255\.255\.255|%s).*' % override.replace('.','\.') + initial_mask = re.compile(pattern) #so we can filter for local ip addresses + + lip = get_local_ip() + + if lip is not None and initial_mask.match(lip[0]) is None: + print("Fail on initial mask, so adding local interface", lip[0]) + initial_mask = re.compile('^(192\.168|10\.|255\.255\.255\.255|%s).*' % lip[0].replace('.','\.')) + + return initial_mask + def get_local_ip(): import socket From 8b30662303d74523e5070408574ed16374d23f11 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Sat, 12 Jan 2019 19:00:33 +0000 Subject: [PATCH 27/55] updating removing stale code in capture --- scripts/loop.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/loop.py b/scripts/loop.py index 5d4a604..0beedcc 100755 --- a/scripts/loop.py +++ b/scripts/loop.py @@ -181,17 +181,17 @@ def processEvents(): parser = argparse.ArgumentParser() - parser.add_argument('--localip', dest="localip", type=str, help="Specify local IP addr (if not 192.168.x.x/10.x.x.x)") + # parser.add_argument('--localip', dest="localip", type=str, help="Specify local IP addr (if not 192.168.x.x/10.x.x.x)") parser.add_argument('--sleep', dest="sleep", type=float, help="Specify sleep in sec (can be fractions)") parser.add_argument('--burstify', dest='burst', action="store_true", help='Do packet burstification (Default off)') parser.add_argument('--predict', dest='predict', action="store_true", help='Do burst prediction (Default off)') parser.add_argument('--debug', dest='debug', action="store_true", help='Turn debug output on (Default off)') args = parser.parse_args() - if args.localip is not None: - localipmask = '^(192\.168|10\.|255\.255\.255\.255|%s).*' % args.localip.replace('.','\.') - print("Using local IP mask %s" % localipmask) - LOCAL_IP_MASK = re.compile(localipmask) #so we can filter for local ip addresses + # if args.localip is not None: + # localipmask = '^(192\.168|10\.|255\.255\.255\.255|%s).*' % args.localip.replace('.','\.') + # print("Using local IP mask %s" % localipmask) + # LOCAL_IP_MASK = re.compile(localipmask) #so we can filter for local ip addresses if args.sleep is not None: print("Setting sleep interval %s seconds." % args.sleep) From 20328442ca7518ed0b14d2a10ead2e6e7eb7e920 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Sun, 13 Jan 2019 01:00:39 +0000 Subject: [PATCH 28/55] unified impacts stash apply --- ui/src/app/geomap/geomap.component.ts | 192 ++++++++++-------- ui/src/app/refinebar/refinebar.component.ts | 163 ++++----------- ui/src/app/tiled-all/tiled-all.component.html | 6 +- ui/src/app/tiled-all/tiled-all.component.ts | 66 +++++- 4 files changed, 211 insertions(+), 216 deletions(-) diff --git a/ui/src/app/geomap/geomap.component.ts b/ui/src/app/geomap/geomap.component.ts index bbe7720..a027fad 100644 --- a/ui/src/app/geomap/geomap.component.ts +++ b/ui/src/app/geomap/geomap.component.ts @@ -1,5 +1,5 @@ -import { Component, Input, OnInit, OnChanges, SimpleChanges, ViewChild, ElementRef, AfterViewInit, ViewEncapsulation, EventEmitter, Output, HostListener } from '@angular/core'; +import { Component, Input, OnInit, OnChanges, SimpleChanges, ViewChild, ElementRef, AfterViewInit, ViewEncapsulation, EventEmitter, Output, HostListener, NgZone } from '@angular/core'; import { LoaderService, App2Hosts, String2String, CompanyInfo, CompanyDB, APIAppInfo, GeoIPInfo } from '../loader.service'; import { AppUsage } from '../usagetable/usagetable.component'; import * as d3 from 'd3'; @@ -10,6 +10,9 @@ import { FocusService } from 'app/focus.service'; import { HoverService, HoverTarget } from "app/hover.service"; import { Http, HttpModule, Headers, URLSearchParams } from '@angular/http'; import * as colorbrewer from 'colorbrewer'; +import { Observable } from '../../../node_modules/rxjs/Observable'; +import { AppImpact } from '../refinebar/refinebar.component'; +import { Subscription } from '../../../node_modules/rxjs/Subscription'; interface AppImpactGeo { @@ -30,8 +33,18 @@ export class GeomapComponent implements AfterViewInit, OnChanges { // still in use! companyid2info: CompanyDB; - private usage: AppUsage[]; + + + @Input() impactChanges : Observable; + private _impact_listener : Subscription; + + @Input('impacts') impacts_in: AppImpact[]; private impacts: AppImpactGeo[]; + + // these two will die + @Input('appusage') usage_in: AppUsage[]; + private usage: AppUsage[]; + private init: Promise; lastMax = 0; _byTime = 'yes'; @@ -40,12 +53,10 @@ export class GeomapComponent implements AfterViewInit, OnChanges { apps: string[]; // keeps app ordering between renders // @ViewChild('thing') svg: ElementRef; // this gets a direct el reference to the svg element - // incoming attribute - @Input('appusage') usage_in: AppUsage[]; @Input() showModes = true; @Input() highlightApp: APIAppInfo; - @Input() showLegend = true; + @Input() showLegend = false; @Input() showTypesLegend = true; @Input() showXAxis = true; @@ -63,7 +74,9 @@ export class GeomapComponent implements AfterViewInit, OnChanges { private loader: LoaderService, private hostutils: HostUtilsService, private focus: FocusService, - private hover: HoverService) { + private hover: HoverService, + private zone : NgZone) { + this.init = Promise.all([ this.loader.getCompanyInfo().then((ci) => this.companyid2info = ci), ]); @@ -84,80 +97,82 @@ export class GeomapComponent implements AfterViewInit, OnChanges { this.render(); } }); - this.getIoTData(); - - (window)._rb = this; - } - getIoTData(): void { - // this.http.get('http://localhost:4201/api/refine/15').toPromise().then(response2 => { - // this.usage = response2.json()["usage"]; - // var impacts = response2.json()["impacts"]; - // var manDev = response2.json()["manDev"]; - // impacts.forEach(function(impact){ - // if (manDev[impact.appid] != "unknown") { - // impact.appid = manDev[impact.appid]; - // } - // }); - - this.loader.getIoTData().then(bundle => { - this.usage = bundle.usage; - - let minMax = bundle.impacts.reduce((acc, val) => { - acc[0] = ( acc[0] === undefined || val.impact < acc[0] ) ? val.impact : acc[0] - acc[1] = ( acc[1] === undefined || val.impact > acc[1] ) ? val.impact : acc[1] - return acc; - }, []); - - this.impacts = bundle.impacts.map(impact => ({impact: impact.impact/minMax[1], geo: impact, appid: impact.appid })) - this.render() - }); - } - getSVGElement() { - const nE: HTMLElement = this.el.nativeElement; - return Array.from(nE.getElementsByTagName('svg'))[0]; } + // getIoTData(): void { + // this.loader.getIoTData().then(bundle => { + // this.usage = bundle.usage; + + // let minMax = bundle.impacts.reduce((acc, val) => { + // acc[0] = ( acc[0] === undefined || val.impact < acc[0] ) ? val.impact : acc[0] + // acc[1] = ( acc[1] === undefined || val.impact > acc[1] ) ? val.impact : acc[1] + // return acc; + // }, []); + + // this.impacts = bundle.impacts.map(impact => ({impact: impact.impact/minMax[1], geo: impact, appid: impact.appid })) + // this.render() + // }); + // } // this gets called when this.usage_in changes ngOnChanges(changes: SimpleChanges): void { - if (!this.usage_in) { return; } - this.init.then(() => { - if (!this.usage_in || !this.usage || !this.apps || this.apps.length !== this.usage_in.length) { - delete this.apps; + // subscribing to the changes impact + let convert_in = () => { + if (this.impacts_in) { + let minMax = this.impacts_in.reduce((acc, val) => { + acc[0] = ( acc[0] === undefined || val.impact < acc[0] ) ? val.impact : acc[0] + acc[1] = ( acc[1] === undefined || val.impact > acc[1] ) ? val.impact : acc[1] + return acc; + }, []); + this.impacts = this.impacts_in.map(impact => ({impact: impact.impact, /* impact.impact/minMax[1],*/ geo: impact, appid: impact.appid })) + this.zone.run(() => this.render()); } - // this.usage = this.usage_in; - this.compileImpacts(this.usage_in).then(impacts => { - this.render(); - }); - }); + }; + + if (this.usage_in) { this.usage = this.usage_in; } + + if (this.impactChanges && this._impact_listener === undefined) { + this._impact_listener = this.impactChanges.subscribe(target => { + console.info('geomap : change notification coming in.'); + convert_in(); + }); + } + // if (!this.usage_in) { return; } + // this.init.then(() => { convert_in(); }); } + ngAfterViewInit(): void { this.init.then(() => this.render()); } + getSVGElement() { + const nE: HTMLElement = this.el.nativeElement; + return Array.from(nE.getElementsByTagName('svg'))[0]; + } + private _getApp(appid: string): Promise { return this.loader.getCachedAppInfo(appid) && Promise.resolve(this.loader.getCachedAppInfo(appid)) || this.loader.getFullAppInfo(appid); } - compileImpacts(usage: AppUsage[]): Promise { - // folds privacy impact in simply by doing a weighted sum over hosts - // usage has to be in a standard unit: days, minutes - // first, normalise usage - - const timebased = this.byTime === 'yes', - total = _.reduce(usage, (tot, appusage): number => tot + (timebased ? appusage.mins : 1.0), 0), - impacts = usage.map((u) => ({ ...u, impact: (timebased ? u.mins : 1.0) / (1.0 * (this.normaliseImpacts ? total : 1.0)) })); - - return Promise.all(impacts.map((usg): Promise => { - - return this._getApp(usg.appid).then(app => { - const hosts = app.hosts, geos = app.host_locations; - if (!hosts || !geos) { console.warn('No hosts found for app ', usg.appid); return []; } - return geos.map(geo => ({ - appid: usg.appid, - geo: geo, - impact: usg.impact, - })); - }); - })).then((nested_impacts: AppImpactGeo[][]): AppImpactGeo[] => _.flatten(_.flatten(nested_impacts))); - } + // compileImpacts(usage: AppUsage[]): Promise { + // // folds privacy impact in simply by doing a weighted sum over hosts + // // usage has to be in a standard unit: days, minutes + // // first, normalise usage + + // const timebased = this.byTime === 'yes', + // total = _.reduce(usage, (tot, appusage): number => tot + (timebased ? appusage.mins : 1.0), 0), + // impacts = usage.map((u) => ({ ...u, impact: (timebased ? u.mins : 1.0) / (1.0 * (this.normaliseImpacts ? total : 1.0)) })); + + // return Promise.all(impacts.map((usg): Promise => { + + // return this._getApp(usg.appid).then(app => { + // const hosts = app.hosts, geos = app.host_locations; + // if (!hosts || !geos) { console.warn('No hosts found for app ', usg.appid); return []; } + // return geos.map(geo => ({ + // appid: usg.appid, + // geo: geo, + // impact: usg.impact, + // })); + // }); + // })).then((nested_impacts: AppImpactGeo[][]): AppImpactGeo[] => _.flatten(_.flatten(nested_impacts))); + // } // accessors for .byTime @@ -171,7 +186,7 @@ export class GeomapComponent implements AfterViewInit, OnChanges { render() { // console.log(':: render usage:', this.usage && this.usage.length); const svgel = this.getSVGElement(); - if (!svgel || !this.usage || !this.impacts || this.usage.length === 0) { return; } + if (!svgel || !this.usage || !this.impacts) { return; } // console.log('refinebar render! getSVGElement > ', svgel); let rect = svgel.getBoundingClientRect(), @@ -194,17 +209,20 @@ export class GeomapComponent implements AfterViewInit, OnChanges { svg.selectAll('*').remove(); const usage = this.usage.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ), - impacts = this.impacts.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ); + impacts = this.impacts.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ), + minmax = d3.extent(impacts.map( i => i.impact )); + // console.log(impacts); let apps = _.uniq(impacts.map((x) => x.appid)); - if (this.apps === undefined) { - // sort apps - apps.sort((a, b) => _.filter(usage, { appid: b })[0].mins - _.filter(usage, { appid: a })[0].mins); - this.apps = apps; - } else { - apps = this.apps; - } + apps.sort(); + // if (this.apps === undefined) { + // // sort apps + // apps.sort((a, b) => _.filter(usage, { appid: b })[0].mins - _.filter(usage, { appid: a })[0].mins); + // this.apps = apps; + // } else { + // apps = this.apps; + // } let margin = { top: 20, right: 20, bottom: -200, left: 40 }, width = width_svgel - margin.left - margin.right, // +svg.attr('width') - margin.left - margin.right, height = height_svgel - margin.top - margin.bottom, // +svg.attr('height') - margin.top - margin.bottom, @@ -218,7 +236,7 @@ export class GeomapComponent implements AfterViewInit, OnChanges { path = d3.geoPath().projection(projection); this.loader.getWorldMesh().then((mesh) => { - svg.append('path').attr("d", path(topojson.mesh(mesh))).attr('opacity', 0.2); + svg.append('path').attr("d", path(topojson.mesh(mesh))).attr('opacity', 0.2).attr("stroke", '#000').attr("fill", "none"); }); // add circles to svg @@ -241,20 +259,22 @@ export class GeomapComponent implements AfterViewInit, OnChanges { return d.appid === highApp ? 0.75 : 0.01; } return 0.8; - }).attr("r", (d) => Math.max(4, Math.floor(d.impact / 100))) - .attr("fill", (d) => z(d.appid)) + }).attr("r", (d) => { + // console.log('d impact ', d.impact, Math.floor(200*(d.impact / minmax[1])), minmax[1]); + return Math.min(40, Math.floor(120*d.impact / minmax[1])); + }).attr("fill", (d) => z(d.appid)) .on('mouseenter', (d) => this.hover.hoverChanged(undefined)) .on('mouseleave', (d) => this.hover.hoverChanged(undefined)); - datas.enter().append('text') - .attr('x', (d) => projection([d.geo.longitude, d.geo.latitude])[0] + 5) - .attr('y', (d) => projection([d.geo.longitude, d.geo.latitude])[1] + 5) - .attr('opacity', d => this._hoveringApp && d.appid === this._hoveringApp ? 1 : 0) - .text((d) => d.geo.region_name || d.geo.country); + // datas.enter().append('text') + // .attr('x', (d) => projection([d.geo.longitude, d.geo.latitude])[0] + 5) + // .attr('y', (d) => projection([d.geo.longitude, d.geo.latitude])[1] + 5) + // .attr('opacity', d => this._hoveringApp && d.appid === this._hoveringApp ? 1 : 0) + // .text((d) => d.geo.region_name || d.geo.country); const leading = 26; - if (this.showLegend) { + if (this.showLegend) { let g = svg.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top / 2 + ')'); const legend = g.append('g') .attr('class', 'legend') diff --git a/ui/src/app/refinebar/refinebar.component.ts b/ui/src/app/refinebar/refinebar.component.ts index afe919a..348d220 100644 --- a/ui/src/app/refinebar/refinebar.component.ts +++ b/ui/src/app/refinebar/refinebar.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit, OnChanges, SimpleChanges, ViewChild, ElementRef, AfterViewInit, ViewEncapsulation, EventEmitter, Output, HostListener } from '@angular/core'; +import { Component, Input, OnInit, OnChanges, SimpleChanges, ViewChild, ElementRef, AfterViewInit, ViewEncapsulation, EventEmitter, Output, HostListener, NgZone } from '@angular/core'; import { LoaderService, App2Hosts, String2String, CompanyInfo, CompanyDB, APIAppInfo } from '../loader.service'; import { AppUsage } from '../usagetable/usagetable.component'; import * as d3 from 'd3'; @@ -7,6 +7,9 @@ import { HostUtilsService } from 'app/host-utils.service'; import { FocusService } from 'app/focus.service'; import { HoverService, HoverTarget } from "app/hover.service"; import { Http, HttpModule, Headers, URLSearchParams } from '@angular/http'; +import { Observable } from '../../../node_modules/rxjs/Observable'; +import { Observer } from '../../../node_modules/rxjs/Observer'; +import { Subscription } from '../../../node_modules/rxjs/Subscription'; const LOCAL_IP_MASK_16 = "192.168."; const LOCAL_IP_MASK_24 = "10."; @@ -38,8 +41,10 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { // still in use! companyid2info: CompanyDB; - private usage: AppUsage[]; - private impacts: AppImpact[]; + @Input() impacts: AppImpact[]; + @Input() impactChanges : Observable; + // private impacts: AppImpact[]; + private init: Promise; lastMax = 0; // _timeSpan = 'd'; @@ -67,6 +72,8 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { _companyHovering: CompanyInfo; _hoveringApp: string; _ignoredApps: string[]; + _impact_listener : Subscription; + constructor(private httpM: HttpModule, private http: Http, @@ -74,13 +81,13 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { private loader: LoaderService, private hostutils: HostUtilsService, private focus: FocusService, - private hover: HoverService) { + private hover: HoverService, + private zone:NgZone) { + this.init = Promise.all([ this.loader.getCompanyInfo().then((ci) => this.companyid2info = ci), ]); - this.getIoTData(); - hover.HoverChanged$.subscribe((target) => { // console.log('hover changed > ', target); if (target !== this._hoveringApp) { @@ -106,57 +113,6 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { return Array.from(nE.getElementsByTagName('svg'))[0]; } - // todo; move this out to loader - getIoTData(): void { - - let this_ = this, - refresh = () => { - this_.loader.getIoTData().then( bundle => { - // console.log('!@#ILJ!@#L@!J# got bundle ', bundle); - this_.usage = bundle.usage; - this_.impacts = bundle.impacts; - this_.render(); - }); - }, - throttledRender = _.throttle(() => this.render(), 500), - throttledRefresh = _.throttle(refresh, 1000); - - refresh(); - this.loader.asyncAppImpactChanges().subscribe({ - next(i: AppImpact[]) { - console.log('AppImpact CHANGE!', i.map(x => ''+[x.companyName, x.companyid, ''+x.impact].join('_')).join(' - ')) - if (this_.impacts) { - this_.impacts = this_.impacts.concat(i); - } - throttledRender(); - }, - error(err) { console.log("Listen error! ", err, err.message); }, - complete() { console.log("Listen complete"); } - }); - - this.loader.asyncGeoUpdateChanges().subscribe({ - next(a: any[]) { - console.info("GOT GEO UPDATE, NOW FLUSHING AND STARTING OVER"); - if (this_.impacts) { - throttledRefresh(); - } - } - }); - - // this.http.get('http://localhost:4201/api/refine/15').toPromise().then(response2 => { - // this.usage = response2.json()["usage"]; // ah ha! - // this.impacts = response2.json()["impacts"]; - // var manDev = response2.json()["manDev"]; - - // this.impacts.forEach(function(impact){ - // if (manDev[impact.appid] !== "unknown") { - // impact.appid = manDev[impact.appid]; - // } - // }); - // this.render() - // }); -} - addOrRemove(newClick: string): string[] { // console.log(this._ignoredApps); if (this._ignoredApps.indexOf(newClick) > -1) { @@ -194,18 +150,22 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { } }); } - - // TODO // this gets called when this.usage_in changes ngOnChanges(changes: SimpleChanges): void { - if (!this.usage) { return; } - this.init.then(() => { - // if (!this.usage_in || !this.usage || !this.apps || this.apps.length !== this.usage_in.length) { - // delete this.apps; - // } - this.render(); - }); + // console.info("refinebar - ngOnChanges ", this.impacts, this.impactChanges); + var this_ = this; + if (this.impactChanges && this._impact_listener === undefined) { + this._impact_listener = this.impactChanges.subscribe(target => { + // console.info("Got notification of impact changes! re-rendering"); + this.zone.run(() => this_.render()); + }); + if (this.impacts) { + this_.render(); + } + } + // this.render(); + this.init.then(() => { this.render(); }); } ngAfterViewInit(): void { this.init.then(() => this.render()); } @@ -219,50 +179,6 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { return this.linear ? v : Math.max(0, 5000*Math.log(v) + 10); } - // compileImpacts(usage: AppUsage[]): Promise { - // // folds privacy impact in simply by doing a weighted sum over hosts - // // usage has to be in a standard unit: days, minutes - // // first, normalise usage - - // const timebased = this.byTime === 'yes', - // total = _.reduce(usage, (tot, appusage): number => tot + (timebased ? appusage.mins : 1.0), 0), - // impacts = usage.map((u) => ({ ...u, impact: - // this.nonLinearity((timebased ? u.mins : 1.0) / (1.0 * (this.normaliseImpacts ? total / 1000000 : 1.0))) - // })); - - // return Promise.all(impacts.map((usg): Promise => { - - // return this._getApp(usg.appid).then(app => { - // const hosts = app && app.hosts; - // if (!hosts) { console.warn('No hosts found for app ', usg.appid); return Promise.resolve([]); } - - // return Promise.all(hosts.map(host => this.hostutils.findCompany(host, app))) - // .then((companies: CompanyInfo[]) => _.uniq(companies.filter((company) => company !== undefined && company.typetag !== 'ignore'))) - // .then((companies: CompanyInfo[]) => companies.map((company) => ({ appid: usg.appid, companyid: company.id, impact: usg.impact }))); - // }); - // })).then((nested_impacts: AppImpact[][]): AppImpact[] => _.flatten(nested_impacts)); - // } - - - // // accessors for .byTime - // set byTime(val) { - // this.lastMax = 0; - // this._byTime = val; - // this.init.then(x => this.compileImpacts(this.usage).then(impacts => { - // this.render(); - // })); - // } - - // get byTime() { return this._byTime; } - - // // accessors for timeSpan - // set timeSpan(val) { - // this._timeSpan = val; - // this.render(); - // } - - // get timeSpan() {return this._timeSpan; } - setHoveringTypeHighlight(ctype: string) { let svg = this.getSVGElement(); this._hoveringType = ctype; @@ -273,7 +189,6 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { d3.select(svg).selectAll('.ctypelegend g.' + ctype).classed('selected', true) }; } - // this is for displaying what company you're hovering on based // on back rects _companyHover(company: CompanyInfo, hovering: boolean) { @@ -296,13 +211,17 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { this.render(); } } - // render() { // console.log(':: render usage:', this.usage && this.usage.length); + const svgel = this.getSVGElement(); - if (!svgel || this.usage === undefined || this.impacts === undefined || this.usage.length === 0) { return; } + if (!svgel || this.impacts === undefined ) { + console.info('render(): impacts undefined, chilling'); + return; + } + // console.info("render :: ", this.impacts); let rect = svgel.getBoundingClientRect(), width_svgel = Math.round(rect.width - 5), @@ -323,8 +242,7 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { svg.selectAll('*').remove(); - let usage = this.usage.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ), - impacts = this.impacts.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ), + let impacts = this.impacts.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ), apps = _.uniq(impacts.map((x) => x.appid)), companies = _.uniq(impacts.map((x) => x.companyName)), get_impact = (cid, aid) => { @@ -338,13 +256,14 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { ..._.fromPairs(apps.map((aid) => [aid, get_impact(c, aid)])) })); - if (this.apps === undefined) { - // sort apps - apps.sort((a, b) => _.filter(usage, { appid: b })[0].mins - _.filter(usage, { appid: a })[0].mins); - this.apps = apps; - } else { - apps = this.apps; - } + apps.sort(); + // if (this.apps === undefined) { + // // sort apps + // apps.sort((a, b) => _.filter(usage, { appid: b })[0].mins - _.filter(usage, { appid: a })[0].mins); + // this.apps = apps; + // } else { + // apps = this.apps; + // } by_company.sort((c1, c2) => c2.total - c1.total); // apps.reduce((total, app) => total += c2[app], 0) - apps.reduce((total, app) => total += c1[app], 0)); diff --git a/ui/src/app/tiled-all/tiled-all.component.html b/ui/src/app/tiled-all/tiled-all.component.html index 8fd5ec4..5d8f5b3 100644 --- a/ui/src/app/tiled-all/tiled-all.component.html +++ b/ui/src/app/tiled-all/tiled-all.component.html @@ -1,10 +1,10 @@
- +
- - + +
\ No newline at end of file diff --git a/ui/src/app/tiled-all/tiled-all.component.ts b/ui/src/app/tiled-all/tiled-all.component.ts index a6b5c1d..54773bc 100644 --- a/ui/src/app/tiled-all/tiled-all.component.ts +++ b/ui/src/app/tiled-all/tiled-all.component.ts @@ -1,9 +1,15 @@ import { Component, OnInit } from '@angular/core'; -import { CompanyInfo, APIAppInfo } from "app/loader.service"; +import { CompanyInfo, APIAppInfo, LoaderService } from "app/loader.service"; import { FocusTarget, FocusService } from "app/focus.service"; import { UsageListener } from "app/usage-listener/usage-listener.component"; import { UsageConnectorService } from "app/usage-connector.service"; import { ActivatedRoute, Router } from "@angular/router"; +import { AppImpact } from '../refinebar/refinebar.component'; +import * as _ from 'lodash'; +import { Observable } from '../../../node_modules/rxjs/Observable'; +import { Observer } from '../../../node_modules/rxjs/Observer'; +import { AppUsage } from '../usagetable/usagetable.component'; + // target watcher watches for clicks on apps and companies export class TargetWatcher extends UsageListener { @@ -45,8 +51,12 @@ export class TiledAllComponent extends TargetWatcher implements OnInit { showUsageTable = false; mode: string; - - constructor(focus: FocusService, connector: UsageConnectorService, private route: ActivatedRoute) { + impacts: AppImpact[]; + usage : AppUsage[]; + impactChanges: Observable; + private impactObservers: Observer[] = []; + + constructor(focus: FocusService, connector: UsageConnectorService, private route: ActivatedRoute, private loader: LoaderService) { super(focus, connector); this.route.params.subscribe(params => { console.log("SETTING MODE", params.mode); @@ -56,9 +66,55 @@ export class TiledAllComponent extends TargetWatcher implements OnInit { console.log("SETTING QUERY PARAMS MODE", params.mode); this.mode = params.mode; }); - //console.log("Here"); + this.impactChanges = this._make_impact_observable(); + } + + // handling propagating + _make_impact_observable(): Observable { + return Observable.create( observer => { + this.impactObservers.push(observer); + }); } + triggerImpactsChange(): any { + this.impactObservers.map(obs => obs.next({})); + } + // todo; move this out to loader + getIoTData(): void { + let this_ = this, + reload = () => { + this_.loader.getIoTData().then( bundle => { + // console.log('!@#ILJ!@#L@!J# got bundle ', bundle); + this_.usage = bundle.usage; + this_.impacts = bundle.impacts; + console.log("yooo assigning impacts ", this_.impacts); + this_.triggerImpactsChange(); + // this_.render(); + }); + }, + throttledReload = _.throttle(reload, 1000); - ngOnInit() {} + this.loader.asyncAppImpactChanges().subscribe({ + next(i: AppImpact[]) { + console.log('AppImpact CHANGE!', i.map(x => ''+[x.companyName, x.companyid, ''+x.impact].join('_')).join(' - ')) + if (this_.impacts) { + this_.impacts = this_.impacts.concat(i); + this_.triggerImpactsChange(); + } + }, + error(err) { console.log("Listen error! ", err, err.message); }, + complete() { console.log("Listen complete"); } + }); + this.loader.asyncGeoUpdateChanges().subscribe({ + next(a: any[]) { + console.info(" ~ got GEO UPDATE, NOW FLUSHING AND STARTING OVER"); + if (this_.impacts) { throttledReload(); } + } + }); + reload(); + } + + ngOnInit() { + this.getIoTData(); + } } From b83d758b5efc6c9688576e348da624807df13f45 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Sun, 13 Jan 2019 02:36:49 +0000 Subject: [PATCH 29/55] unified impacts stash apply --- ui/src/app/geobar/geobar.component.ts | 120 +++++++++--------- ui/src/app/tiled-all/tiled-all.component.html | 2 +- 2 files changed, 59 insertions(+), 63 deletions(-) diff --git a/ui/src/app/geobar/geobar.component.ts b/ui/src/app/geobar/geobar.component.ts index a498c72..ff897a6 100644 --- a/ui/src/app/geobar/geobar.component.ts +++ b/ui/src/app/geobar/geobar.component.ts @@ -1,5 +1,5 @@ -import { Component, Input, OnInit, OnChanges, SimpleChanges, ViewChild, ElementRef, AfterViewInit, ViewEncapsulation, EventEmitter, Output, HostListener } from '@angular/core'; +import { Component, Input, OnInit, OnChanges, SimpleChanges, ViewChild, ElementRef, AfterViewInit, ViewEncapsulation, EventEmitter, Output, HostListener, NgZone } from '@angular/core'; import { LoaderService, App2Hosts, String2String, CompanyInfo, CompanyDB, APIAppInfo, GeoIPInfo } from '../loader.service'; import { AppUsage } from '../usagetable/usagetable.component'; import * as d3 from 'd3'; @@ -8,6 +8,9 @@ import { HostUtilsService } from 'app/host-utils.service'; import { FocusService } from 'app/focus.service'; import { HoverService, HoverTarget } from "app/hover.service"; import { Http, HttpModule, Headers, URLSearchParams } from '@angular/http'; +import { Observable } from '../../../node_modules/rxjs/Observable'; +import { Subscription } from '../../../node_modules/rxjs/Subscription'; +import { AppImpact } from '../refinebar/refinebar.component'; interface AppImpactGeo { appid: string; @@ -28,11 +31,16 @@ export class GeobarComponent implements AfterViewInit, OnChanges { // still in use! companyid2info: CompanyDB; + @Input() impactChanges : Observable; + private _impact_listener : Subscription; + @Input('impacts') impacts_in : AppImpact[]; + private usage: AppUsage[]; private impacts: AppImpactGeo[]; private init: Promise; + lastMax = 0; - _byTime = 'yes'; + // _byTime = 'yes'; normaliseImpacts = false; apps: string[]; // keeps app ordering between renders @@ -61,7 +69,8 @@ export class GeobarComponent implements AfterViewInit, OnChanges { private loader: LoaderService, private hostutils: HostUtilsService, private focus: FocusService, - private hover: HoverService) { + private hover: HoverService, + private zone :NgZone) { this.init = Promise.all([ this.loader.getCompanyInfo().then((ci) => this.companyid2info = ci), ]); @@ -82,50 +91,37 @@ export class GeobarComponent implements AfterViewInit, OnChanges { } }); - this.getIoTData(); + // this.getIoTData(); // (window)._rb = this; } - getIoTData(): void { - this.loader.getIoTData().then(bundle => { - this.usage = bundle.usage; - // this.http.get('http://localhost:4201/api/refine/15').toPromise().then(response2 => { - // this.usage = response2.json()["usage"]; - // var impacts = response2.json()["impacts"]; - // var manDev = response2.json()["manDev"]; - // impacts.forEach(function(impact){ - // if (manDev[impact.appid] != "unknown") { - // impact.appid = manDev[impact.appid]; - // } - // }); - this.impacts = bundle.impacts.map(impact => ({ appid: impact.appid, country: impact.country_code !== 'XX' ? impact.country_code : 'Unknown', country_code: impact.country_code, impact: impact.impact })) - // console.log(this.impacts) - this.render() - }); - } + // getIoTData(): void { + // this.loader.getIoTData().then(bundle => { + // this.usage = bundle.usage; + // this.impacts = bundle.impacts.map(impact => ({ appid: impact.appid, country: impact.country_code !== 'XX' ? impact.country_code : 'Unknown', country_code: impact.country_code, impact: impact.impact })) + // this.render() + // }); + // } + getSVGElement() { const nE: HTMLElement = this.el.nativeElement; return Array.from(nE.getElementsByTagName('svg'))[0]; } // this gets called when this.usage_in changes ngOnChanges(changes: SimpleChanges): void { - if (!this.usage_in) { return; } - this.init.then(() => { - if (!this.usage_in || !this.usage || !this.apps || this.apps.length !== this.usage_in.length) { - delete this.apps; + + let convert_in = () => { + if (this.impacts_in) { + this.impacts = this.impacts_in.map(impact => ({ appid: impact.appid, country: (impact).country_code !== 'XX' ? (impact).country_code : 'Unknown', country_code: (impact).country_code, impact: impact.impact })); + this.zone.run(() => this.render()); } - this.compileImpacts(this.usage_in).then(impacts => { - /* this.usage = this.usage_in; - let red_impacts = impacts.reduce((perapp, impact) => { - let appcat = (perapp[impact.appid] || {}); - appcat[impact.country] = (appcat[impact.country] || 0) + impact.impact; - perapp[impact.appid] = appcat; - return perapp; - }, {}); - this.impacts = _.flatten(_.map(red_impacts, (country, appid) => _.map(country, (impact, cat) => ({ appid: appid, country: cat, impact: impact } as AppImpactGeo)))); - // console.log('country geo impacts after comp > ', impacts); */ - this.render(); - }); - }); + }; + + if (!this.usage_in) { return; } + + if (this.impactChanges && this._impact_listener === undefined) { + this._impact_listener = this.impactChanges.subscribe(convert_in); + } + this.init.then(convert_in); } ngAfterViewInit(): void { this.init.then(() => this.render()); } @@ -135,36 +131,36 @@ export class GeobarComponent implements AfterViewInit, OnChanges { || this.loader.getFullAppInfo(appid); } - compileImpacts(usage: AppUsage[]): Promise { - // folds privacy impact in simply by doing a weighted sum over hosts - // usage has to be in a standard unit: days, minutes - // first, normalise usage + // compileImpacts(usage: AppUsage[]): Promise { + // // folds privacy impact in simply by doing a weighted sum over hosts + // // usage has to be in a standard unit: days, minutes + // // first, normalise usage - const timebased = this.byTime === 'yes', - total = _.reduce(usage, (tot, appusage): number => tot + (timebased ? appusage.mins : 1.0), 0), - impacts = usage.map((u) => ({ ...u, impact: (timebased ? u.mins : 1.0) / (1.0 * (this.normaliseImpacts ? total : 1.0)) })); + // const timebased = this.byTime === 'yes', + // total = _.reduce(usage, (tot, appusage): number => tot + (timebased ? appusage.mins : 1.0), 0), + // impacts = usage.map((u) => ({ ...u, impact: (timebased ? u.mins : 1.0) / (1.0 * (this.normaliseImpacts ? total : 1.0)) })); - return Promise.all(impacts.map((usg): Promise => { + // return Promise.all(impacts.map((usg): Promise => { - return this._getApp(usg.appid).then(app => { - const hosts = app.hosts, geos = app.host_locations; - if (!hosts || !geos) { console.warn('No hosts found for app ', usg.appid); return []; } - return geos.map(geo => ({ appid: usg.appid, country: geo.country_name !== '' ? geo.country_name : 'Unknown', country_code: geo.country_code, impact: usg.impact })); - }); - })).then((nested_impacts: AppImpactGeo[][]): AppImpactGeo[] => _.flatten(_.flatten(nested_impacts))); - } + // return this._getApp(usg.appid).then(app => { + // const hosts = app.hosts, geos = app.host_locations; + // if (!hosts || !geos) { console.warn('No hosts found for app ', usg.appid); return []; } + // return geos.map(geo => ({ appid: usg.appid, country: geo.country_name !== '' ? geo.country_name : 'Unknown', country_code: geo.country_code, impact: usg.impact })); + // }); + // })).then((nested_impacts: AppImpactGeo[][]): AppImpactGeo[] => _.flatten(_.flatten(nested_impacts))); + // } // accessors for .byTime - set byTime(val) { - this.lastMax = 0; - this._byTime = val; - this.init.then(x => this.compileImpacts(this.usage).then(impacts => { - this.impacts = impacts; - this.render(); - })); - } - get byTime() { return this._byTime; } + // set byTime(val) { + // this.lastMax = 0; + // this._byTime = val; + // this.init.then(x => this.compileImpacts(this.usage).then(impacts => { + // this.impacts = impacts; + // this.render(); + // })); + // } + // get byTime() { return this._byTime; } render() { // console.log(':: render usage:', this.usage && this.usage.length); diff --git a/ui/src/app/tiled-all/tiled-all.component.html b/ui/src/app/tiled-all/tiled-all.component.html index 5d8f5b3..9084040 100644 --- a/ui/src/app/tiled-all/tiled-all.component.html +++ b/ui/src/app/tiled-all/tiled-all.component.html @@ -5,6 +5,6 @@
- +
\ No newline at end of file From c1e570a1b51d865fe9d73e9e30a79320cb1e634e Mon Sep 17 00:00:00 2001 From: electronic Max Date: Sun, 13 Jan 2019 03:18:37 +0000 Subject: [PATCH 30/55] geobar is better --- scripts/loop.py | 2 +- ui/src/app/geobar/geobar.component.ts | 12 ++++++------ ui/src/app/geomap/geomap.component.ts | 21 ++++++++++----------- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/scripts/loop.py b/scripts/loop.py index 0beedcc..6aa1582 100755 --- a/scripts/loop.py +++ b/scripts/loop.py @@ -141,7 +141,7 @@ def processGeos(): data = requests.get('https://api.ipdata.co/' + ip + '?api-key=***REMOVED***') if data.status_code==200 and data.json()['latitude'] is not '': data = data.json() - DB_MANAGER.execute("INSERT INTO geodata VALUES(%s, %s, %s, %s, %s)", (ip, data['latitude'], data['longitude'], data['country_code'], data['organisation'][:20])) + DB_MANAGER.execute("INSERT INTO geodata VALUES(%s, %s, %s, %s, %s)", (ip, data['latitude'], data['longitude'], data['country_code'] or data['continent_code'], data['organisation'][:20])) else: DB_MANAGER.execute("INSERT INTO geodata VALUES(%s, %s, %s, %s, %s)", (ip, "0", "0", "XX", "unknown")) known_ips.append(ip) diff --git a/ui/src/app/geobar/geobar.component.ts b/ui/src/app/geobar/geobar.component.ts index ff897a6..eb68fdd 100644 --- a/ui/src/app/geobar/geobar.component.ts +++ b/ui/src/app/geobar/geobar.component.ts @@ -35,7 +35,7 @@ export class GeobarComponent implements AfterViewInit, OnChanges { private _impact_listener : Subscription; @Input('impacts') impacts_in : AppImpact[]; - private usage: AppUsage[]; + @Input('appusage') usage: AppUsage[]; private impacts: AppImpactGeo[]; private init: Promise; @@ -48,7 +48,7 @@ export class GeobarComponent implements AfterViewInit, OnChanges { // @ViewChild('thing') svg: ElementRef; // this gets a direct el reference to the svg element // incoming attribute - @Input('appusage') usage_in: AppUsage[]; + // @Input('appusage') usage_in: AppUsage[]; @Input() showModes = true; @Input() highlightApp: APIAppInfo; @Input() showLegend = true; @@ -111,17 +111,17 @@ export class GeobarComponent implements AfterViewInit, OnChanges { let convert_in = () => { if (this.impacts_in) { - this.impacts = this.impacts_in.map(impact => ({ appid: impact.appid, country: (impact).country_code !== 'XX' ? (impact).country_code : 'Unknown', country_code: (impact).country_code, impact: impact.impact })); + this.impacts = this.impacts_in.map(impact => ({ ...impact, appid: impact.appid, country: (impact).country_code !== 'XX' ? (impact).country_code : 'Unknown', country_code: (impact).country_code, impact: impact.impact })); + // console.log('geobar impacts ', this.impacts); this.zone.run(() => this.render()); } }; - - if (!this.usage_in) { return; } + // if (!this.usage_in) { return; } if (this.impactChanges && this._impact_listener === undefined) { this._impact_listener = this.impactChanges.subscribe(convert_in); } - this.init.then(convert_in); + // this.init.then(convert_in); } ngAfterViewInit(): void { this.init.then(() => this.render()); } diff --git a/ui/src/app/geomap/geomap.component.ts b/ui/src/app/geomap/geomap.component.ts index a027fad..e23b78e 100644 --- a/ui/src/app/geomap/geomap.component.ts +++ b/ui/src/app/geomap/geomap.component.ts @@ -42,8 +42,8 @@ export class GeomapComponent implements AfterViewInit, OnChanges { private impacts: AppImpactGeo[]; // these two will die - @Input('appusage') usage_in: AppUsage[]; - private usage: AppUsage[]; + @Input('appusage') usage: AppUsage[]; + // private usage: AppUsage[]; private init: Promise; lastMax = 0; @@ -117,21 +117,19 @@ export class GeomapComponent implements AfterViewInit, OnChanges { // subscribing to the changes impact let convert_in = () => { if (this.impacts_in) { - let minMax = this.impacts_in.reduce((acc, val) => { - acc[0] = ( acc[0] === undefined || val.impact < acc[0] ) ? val.impact : acc[0] - acc[1] = ( acc[1] === undefined || val.impact > acc[1] ) ? val.impact : acc[1] - return acc; - }, []); + // let minMax = this.impacts_in.reduce((acc, val) => { + // acc[0] = ( acc[0] === undefined || val.impact < acc[0] ) ? val.impact : acc[0] + // acc[1] = ( acc[1] === undefined || val.impact > acc[1] ) ? val.impact : acc[1] + // return acc; + // }, []); this.impacts = this.impacts_in.map(impact => ({impact: impact.impact, /* impact.impact/minMax[1],*/ geo: impact, appid: impact.appid })) this.zone.run(() => this.render()); } }; - if (this.usage_in) { this.usage = this.usage_in; } - if (this.impactChanges && this._impact_listener === undefined) { this._impact_listener = this.impactChanges.subscribe(target => { - console.info('geomap : change notification coming in.'); + // console.info('geomap : change notification coming in.'); convert_in(); }); } @@ -261,11 +259,12 @@ export class GeomapComponent implements AfterViewInit, OnChanges { return 0.8; }).attr("r", (d) => { // console.log('d impact ', d.impact, Math.floor(200*(d.impact / minmax[1])), minmax[1]); - return Math.min(40, Math.floor(120*d.impact / minmax[1])); + return Math.floor(80*d.impact / minmax[1]); }).attr("fill", (d) => z(d.appid)) .on('mouseenter', (d) => this.hover.hoverChanged(undefined)) .on('mouseleave', (d) => this.hover.hoverChanged(undefined)); + console.info('map ending render'); // datas.enter().append('text') // .attr('x', (d) => projection([d.geo.longitude, d.geo.latitude])[0] + 5) // .attr('y', (d) => projection([d.geo.longitude, d.geo.latitude])[1] + 5) From 483ca601648086636cca03b54da4f561bea64167 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Sun, 13 Jan 2019 04:24:13 +0000 Subject: [PATCH 31/55] still debugging geomap --- ui/src/app/geomap/geomap.component.ts | 33 +++++++++++++++------------ 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/ui/src/app/geomap/geomap.component.ts b/ui/src/app/geomap/geomap.component.ts index e23b78e..0c70e41 100644 --- a/ui/src/app/geomap/geomap.component.ts +++ b/ui/src/app/geomap/geomap.component.ts @@ -33,8 +33,7 @@ export class GeomapComponent implements AfterViewInit, OnChanges { // still in use! companyid2info: CompanyDB; - - + @Input() impactChanges : Observable; private _impact_listener : Subscription; @@ -115,15 +114,19 @@ export class GeomapComponent implements AfterViewInit, OnChanges { // this gets called when this.usage_in changes ngOnChanges(changes: SimpleChanges): void { // subscribing to the changes impact - let convert_in = () => { - if (this.impacts_in) { + let this_ = this, + convert_in = () => { + if (this_.impacts_in !== undefined) { // let minMax = this.impacts_in.reduce((acc, val) => { // acc[0] = ( acc[0] === undefined || val.impact < acc[0] ) ? val.impact : acc[0] // acc[1] = ( acc[1] === undefined || val.impact > acc[1] ) ? val.impact : acc[1] // return acc; // }, []); - this.impacts = this.impacts_in.map(impact => ({impact: impact.impact, /* impact.impact/minMax[1],*/ geo: impact, appid: impact.appid })) - this.zone.run(() => this.render()); + this.zone.run(() => { + console.info('geomap updating impacts'); + this_.impacts = this_.impacts_in.map(impact => ({ impact: impact.impact, /* impact.impact/minMax[1],*/ geo: impact, appid: impact.appid })); + this_.render(); + }); } }; @@ -133,6 +136,7 @@ export class GeomapComponent implements AfterViewInit, OnChanges { convert_in(); }); } + // this.render(); // if (!this.usage_in) { return; } // this.init.then(() => { convert_in(); }); } @@ -206,11 +210,12 @@ export class GeomapComponent implements AfterViewInit, OnChanges { svg.selectAll('*').remove(); - const usage = this.usage.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ), - impacts = this.impacts.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ), + const usage = this.usage, // this.usage.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ), + impacts = this.impacts, // this.impacts.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ), minmax = d3.extent(impacts.map( i => i.impact )); - // console.log(impacts); + + console.log(' impact extents ', minmax[0], ' - ', minmax[1]); let apps = _.uniq(impacts.map((x) => x.appid)); apps.sort(); @@ -238,11 +243,10 @@ export class GeomapComponent implements AfterViewInit, OnChanges { }); // add circles to svg - var datas = svg.selectAll("circle") - .data(impacts); + var datas = svg.selectAll("circle").data(impacts); + // console.info('selecting on impacts ', impacts); - datas.enter() - .append("circle") + datas.enter().append("circle") .attr("cx", (d) => { const lat = projection([d.geo.longitude, d.geo.latitude])[0]; return lat; @@ -258,7 +262,7 @@ export class GeomapComponent implements AfterViewInit, OnChanges { } return 0.8; }).attr("r", (d) => { - // console.log('d impact ', d.impact, Math.floor(200*(d.impact / minmax[1])), minmax[1]); + // console.log('d impact r=', Math.floor(80*d.impact / minmax[1]), minmax[1]); return Math.floor(80*d.impact / minmax[1]); }).attr("fill", (d) => z(d.appid)) .on('mouseenter', (d) => this.hover.hoverChanged(undefined)) @@ -310,7 +314,6 @@ export class GeomapComponent implements AfterViewInit, OnChanges { .attr('dy', '0.32em') .text((d) => this.loader.getCachedAppInfo(d) && this.loader.getCachedAppInfo(d).storeinfo.title || d); } - } @HostListener('window:resize') onResize() { From e6b5898b9d47c8228f6d42f01dec8b16b79a83f8 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Sun, 13 Jan 2019 04:46:00 +0000 Subject: [PATCH 32/55] geomap now behaving --- ui/src/app/geomap/geomap.component.ts | 28 ++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/ui/src/app/geomap/geomap.component.ts b/ui/src/app/geomap/geomap.component.ts index 0c70e41..0138437 100644 --- a/ui/src/app/geomap/geomap.component.ts +++ b/ui/src/app/geomap/geomap.component.ts @@ -124,7 +124,33 @@ export class GeomapComponent implements AfterViewInit, OnChanges { // }, []); this.zone.run(() => { console.info('geomap updating impacts'); - this_.impacts = this_.impacts_in.map(impact => ({ impact: impact.impact, /* impact.impact/minMax[1],*/ geo: impact, appid: impact.appid })); + let iin = this_.impacts_in; + // this_.impacts = iin.map(impact => ({ impact: impact.impact, /* impact.impact/minMax[1],*/ geo: impact, appid: impact.appid })); + + let apps = _.uniq(iin.map((x) => x.appid)), + companies = _.uniq(iin.map((x) => x.companyName)), + get_impact = (cname, aid) => { + const t = iin.filter((imp) => imp.companyName === cname && imp.appid === aid); + const reducer = (accumulator, currentValue) => accumulator + currentValue.impact; + return t !== undefined ? t.reduce(reducer, 0) : 0; + }, + by_company = apps.map(app => { + return companies.map((c) => { + return { + company: c, + impact: get_impact(c, app), + geo: iin.filter((imp) => imp.companyName === c && imp.appid === app)[0], + appid: app + }; + }); + }); + this_.impacts = _.flatten(by_company).filter(x => x.geo); + console.info('impacts ', this_.impacts); + + // company: c, + // impact: apps.reduce((total, aid) => total += get_impact(c, aid), 0), + // })); + this_.render(); }); } From cc8e17df1e3f57ffab142909f382b6911fd0f783 Mon Sep 17 00:00:00 2001 From: mcnutty Date: Mon, 14 Jan 2019 12:01:29 +0000 Subject: [PATCH 33/55] updated typescript req --- ui/package-lock.json | 7 +++---- ui/package.json | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index dbcb6b1..bfbc6e8 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -9907,10 +9907,9 @@ } }, "typescript": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", - "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", - "dev": true + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz", + "integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==" }, "uglify-js": { "version": "3.4.2", diff --git a/ui/package.json b/ui/package.json index ac730ab..91e7b77 100644 --- a/ui/package.json +++ b/ui/package.json @@ -24,11 +24,12 @@ "colorbrewer": "^1.0.0", "core-js": "^2.4.1", "d3": "^4.10.0", + "moment": "^2.22.2", "ng2-completer": "^1.6.0", "rxjs": "^5.1.0", "topojson": "^3.0.0", - "zone.js": "^0.8.4", - "moment": "^2.22.2" + "typescript": "^3.2.2", + "zone.js": "^0.8.4" }, "devDependencies": { "@angular/cli": "^1.1.3", @@ -48,7 +49,6 @@ "moment": "^2.22.2", "protractor": "~5.1.2", "ts-node": "~3.0.4", - "tslint": "~5.3.2", - "typescript": "^2.9.2" + "tslint": "~5.3.2" } } From 5ea88fac148b36582a92544edd4c881dc2e1f21d Mon Sep 17 00:00:00 2001 From: mcnutty Date: Mon, 14 Jan 2019 13:54:09 +0000 Subject: [PATCH 34/55] moved trigger sql into schema --- db/notify_trigger.sql | 67 ------------------------------------ db/schema.sql | 80 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 76 insertions(+), 71 deletions(-) delete mode 100644 db/notify_trigger.sql diff --git a/db/notify_trigger.sql b/db/notify_trigger.sql deleted file mode 100644 index 762945a..0000000 --- a/db/notify_trigger.sql +++ /dev/null @@ -1,67 +0,0 @@ -CREATE FUNCTION notify_trigger() RETURNS trigger AS $trigger$ -DECLARE - rec RECORD; - payload TEXT; - column_name TEXT; - column_value TEXT; - payload_items JSONB; -BEGIN - -- Set record row depending on operation - CASE TG_OP - WHEN 'INSERT', 'UPDATE' THEN - rec := NEW; - WHEN 'DELETE' THEN - rec := OLD; - ELSE - RAISE EXCEPTION 'Unknown TG_OP: "%". Should not occur!', TG_OP; - END CASE; - - -- Get required fields - FOREACH column_name IN ARRAY TG_ARGV LOOP - EXECUTE format('SELECT $1.%I::TEXT', column_name) - INTO column_value - USING rec; - payload_items := coalesce(payload_items,'{}')::jsonb || json_build_object(column_name,column_value)::jsonb; - END LOOP; - - -- Build the payload - payload := json_build_object( - 'timestamp',CURRENT_TIMESTAMP, - 'operation',TG_OP, - 'schema',TG_TABLE_SCHEMA, - 'table',TG_TABLE_NAME, - 'data',payload_items - ); - - -- Notify the channel - PERFORM pg_notify('db_notifications', payload); - - RETURN rec; -END; -$trigger$ LANGUAGE plpgsql; - -CREATE TRIGGER packets_notify AFTER INSERT OR UPDATE OR DELETE ON packets -FOR EACH ROW EXECUTE PROCEDURE notify_trigger( - 'id', - 'mac', - 'src', - 'dst', - 'len', - 'burst' -); - -CREATE TRIGGER device_notify AFTER INSERT OR UPDATE OR DELETE ON devices -FOR EACH ROW EXECUTE PROCEDURE notify_trigger( - 'mac', - 'manufacturer', - 'name' -); - -CREATE TRIGGER geodata_notify AFTER INSERT OR UPDATE OR DELETE ON geodata -FOR EACH ROW EXECUTE PROCEDURE notify_trigger( - 'ip', - 'lat', - 'lon', - 'c_code', - 'c_name' -); diff --git a/db/schema.sql b/db/schema.sql index e04e2f9..f26aeac 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -1,19 +1,19 @@ --categories table matches readable descriptions to bursts of traffic ("this burst is a weather info request") -drop table if exists categories cascade ; +drop table if exists categories cascade; create table categories ( id SERIAL primary key, name varchar(40) not null -- e.g. "Alexa-time" or "Alexa-joke" ); --collates bursts of traffic and optionally assigns them a category -drop table if exists bursts cascade ; +drop table if exists bursts cascade; create table bursts ( id SERIAL primary key, category integer references categories --primary key assumed when no column given ); --store core packet info, and optionally which burst it is part ofi, and which company it represents -drop table if exists packets cascade ; +drop table if exists packets cascade; create table packets ( id SERIAL primary key, time timestamp not null, @@ -47,7 +47,7 @@ create table geodata( ); --store simplified profiles of devices: Name, time, destination company, traffic -drop table if exists models cascade ; +drop table if exists models cascade; create table models ( id SERIAL primary key, device varchar(17) not null, --device mac address @@ -56,3 +56,75 @@ create table models ( location varchar(2) not null, --country the company is based in impact real not null --amount of traffic in mb ); + +drop function if exists notify_trigger; +CREATE FUNCTION notify_trigger() RETURNS trigger AS $trigger$ +DECLARE + rec RECORD; + payload TEXT; + column_name TEXT; + column_value TEXT; + payload_items JSONB; +BEGIN + -- Set record row depending on operation + CASE TG_OP + WHEN 'INSERT', 'UPDATE' THEN + rec := NEW; + WHEN 'DELETE' THEN + rec := OLD; + ELSE + RAISE EXCEPTION 'Unknown TG_OP: "%". Should not occur!', TG_OP; + END CASE; + + -- Get required fields + FOREACH column_name IN ARRAY TG_ARGV LOOP + EXECUTE format('SELECT $1.%I::TEXT', column_name) + INTO column_value + USING rec; + payload_items := coalesce(payload_items,'{}')::jsonb || json_build_object(column_name,column_value)::jsonb; + END LOOP; + + -- Build the payload + payload := json_build_object( + 'timestamp',CURRENT_TIMESTAMP, + 'operation',TG_OP, + 'schema',TG_TABLE_SCHEMA, + 'table',TG_TABLE_NAME, + 'data',payload_items + ); + + -- Notify the channel + PERFORM pg_notify('db_notifications', payload); + + RETURN rec; +END; +$trigger$ LANGUAGE plpgsql; + +drop trigger if exists packets_notify on packets; +CREATE TRIGGER packets_notify AFTER INSERT OR UPDATE OR DELETE ON packets +FOR EACH ROW EXECUTE PROCEDURE notify_trigger( + 'id', + 'mac', + 'src', + 'dst', + 'len', + 'burst' +); + +drop trigger if exists device_notify on devices; +CREATE TRIGGER device_notify AFTER INSERT OR UPDATE OR DELETE ON devices +FOR EACH ROW EXECUTE PROCEDURE notify_trigger( + 'mac', + 'manufacturer', + 'name' +); + +drop trigger if exists geodata_notify on geodata; +CREATE TRIGGER geodata_notify AFTER INSERT OR UPDATE OR DELETE ON geodata +FOR EACH ROW EXECUTE PROCEDURE notify_trigger( + 'ip', + 'lat', + 'lon', + 'c_code', + 'c_name' +); From 9596f2f3eb4ad6a3ae633263d951d1e13c5e779b Mon Sep 17 00:00:00 2001 From: mcnutty Date: Mon, 14 Jan 2019 13:55:32 +0000 Subject: [PATCH 35/55] minor tidy up --- scripts/api.py | 1 - scripts/capture.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/api.py b/scripts/api.py index 17584fb..843bbb2 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -290,7 +290,6 @@ def stream(): # print("Using local IP mask %s" % localipmask) #Register the API endpoints with flask -<<<<<<< HEAD api.add_resource(Refine, '/api/refine/') api.add_resource(Devices, '/api/devices') api.add_resource(Bursts, '/api/bursts/') diff --git a/scripts/capture.py b/scripts/capture.py index 9e1418a..09fa2f9 100755 --- a/scripts/capture.py +++ b/scripts/capture.py @@ -67,7 +67,7 @@ def DatabaseInsert(packets): conn.commit() cur.close() conn.close() - print("Captured " + len(packets) + " packets this tick") + print(f"Captured {str(len(packets))} packets this tick") def QueuedCommit(packet): #commit packets to the database in COMMIT_INTERVAL second intervals From 13f41e95b80ceb35f89b4b486e8f6aafc033fd8e Mon Sep 17 00:00:00 2001 From: mcnutty Date: Mon, 14 Jan 2019 14:44:24 +0000 Subject: [PATCH 36/55] fixed bug in reset-database.py --- scripts/api.py | 1 + scripts/loop.py | 6 +----- scripts/reset-database.py | 4 ++-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/scripts/api.py b/scripts/api.py index 843bbb2..e0fbef0 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -253,6 +253,7 @@ def packets_insert_to_impact(packets): if len(insert_buf) > 0: yield "data: %s\n\n" % json.dumps({"type":'impact', "data": packets_insert_to_impact(insert_buf)}) + print("sent new packets") if len(geo_updates) > 0: # ResetImpactCache() # updated ip should be diff --git a/scripts/loop.py b/scripts/loop.py index 301ae94..6c9f176 100755 --- a/scripts/loop.py +++ b/scripts/loop.py @@ -122,16 +122,12 @@ def burstPrediction(devices): DB_MANAGER.updateBurstCategory(burst[0], newCategoryId) def processGeos(): - # raw_ips = DB_MANAGER.execute("SELECT DISTINCT src, dst FROM packets", ()) global RAW_IPS if not RAW_IPS: - print("Preloading RAW_IPS") RAW_IPS = set( [r[0] for r in DB_MANAGER.execute("SELECT DISTINCT src FROM packets", ())]).union([r[0] for r in DB_MANAGER.execute("SELECT DISTINCT dst FROM packets", ())]) - print(" Done ", len(RAW_IPS), " known ips ") + print(f"Loaded {str(len(RAW_IPS))} known IPs") - # print("raw_ips", raw_ips) - raw_geos = DB_MANAGER.execute("SELECT ip FROM geodata", ()) known_ips = [] diff --git a/scripts/reset-database.py b/scripts/reset-database.py index 19056f5..3d30a8d 100755 --- a/scripts/reset-database.py +++ b/scripts/reset-database.py @@ -6,6 +6,6 @@ import databaseBursts FILE_PATH = os.path.dirname(os.path.abspath(__file__)) DB_MANAGER = databaseBursts.dbManager() -print("Resetting database...") -DB_MANAGER.execute(open(os.path.join(os.path.dirname(FILE_PATH), "db", "schema.sql"), "rb").read(), "") +schema = open(os.path.join(os.path.dirname(FILE_PATH), "db", "schema.sql"), "rb").read() +DB_MANAGER.execute(schema, None) print("Database sucessfully reset") From 58ffb8ab79c4d774479780c7fb58f8423b94326a Mon Sep 17 00:00:00 2001 From: electronic Max Date: Mon, 14 Jan 2019 15:02:24 +0000 Subject: [PATCH 37/55] loop now doesnt march into oblivion --- db/databaseBursts.py | 9 +++++++-- scripts/loop.py | 3 ++- ui/src/app/geobar/geobar.component.ts | 4 +++- ui/src/app/geomap/geomap.component.ts | 2 +- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/db/databaseBursts.py b/db/databaseBursts.py index 10a755c..7cc9899 100644 --- a/db/databaseBursts.py +++ b/db/databaseBursts.py @@ -21,22 +21,27 @@ def listen(self, channel, cb=None): conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) curs = conn.cursor() curs.execute("LISTEN %s ;" % channel) + stop = [False] + def stopme(): + stop[0] = True def subp(): - while 1: + while not stop[0]: # kill me with a sharp stick. if select.select([conn],[],[],5) == ([],[],[]): # print("Timeout") pass else: conn.poll() - while conn.notifies: + while not stop[0] and conn.notifies: notify = conn.notifies.pop(0) # print("Got NOTIFY:", notify.pid, notify.channel, notify.payload) if cb is not None: cb(notify.payload) thread = threading.Thread(target=subp) thread.start() + return stopme except: print("listen error") + return lambda: None def execute(self, query, data, all=True): diff --git a/scripts/loop.py b/scripts/loop.py index 6aa1582..ca29a2a 100755 --- a/scripts/loop.py +++ b/scripts/loop.py @@ -203,7 +203,7 @@ def processEvents(): handler = sigTermHandler() # watch for listen events -- not sure if this has to be on its own connection - DB_MANAGER.listen('db_notifications', lambda payload:_events.append(payload)) + listener_thread_stopper = DB_MANAGER.listen('db_notifications', lambda payload:_events.append(payload)) #loop through categorisation tasks while(True): @@ -220,6 +220,7 @@ def processEvents(): #exit gracefully if we were asked to shutdown if handler.exit: + listener_thread_stopper() break time.sleep(INTERVAL) diff --git a/ui/src/app/geobar/geobar.component.ts b/ui/src/app/geobar/geobar.component.ts index eb68fdd..4bff129 100644 --- a/ui/src/app/geobar/geobar.component.ts +++ b/ui/src/app/geobar/geobar.component.ts @@ -205,7 +205,9 @@ export class GeobarComponent implements AfterViewInit, OnChanges { if (this.apps === undefined) { // sort apps - apps.sort((a, b) => _.filter(usage, { appid: b })[0].mins - _.filter(usage, { appid: a })[0].mins); + // TODO: this is crashing: + apps.sort(); + // apps.sort((a, b) => _.filter(usage, { appid: b })[0].mins - _.filter(usage, { appid: a })[0].mins); this.apps = apps; } else { apps = this.apps; diff --git a/ui/src/app/geomap/geomap.component.ts b/ui/src/app/geomap/geomap.component.ts index 0138437..efb817a 100644 --- a/ui/src/app/geomap/geomap.component.ts +++ b/ui/src/app/geomap/geomap.component.ts @@ -145,7 +145,7 @@ export class GeomapComponent implements AfterViewInit, OnChanges { }); }); this_.impacts = _.flatten(by_company).filter(x => x.geo); - console.info('impacts ', this_.impacts); + // console.info('impacts ', this_.impacts); // company: c, // impact: apps.reduce((total, aid) => total += get_impact(c, aid), 0), From d8745afc4bc53cfef3d3f3d13bc5382abe503fbd Mon Sep 17 00:00:00 2001 From: mcnutty Date: Mon, 14 Jan 2019 16:13:59 +0000 Subject: [PATCH 38/55] command line args to capture and loop now override values from config.cfg --- README.md | 2 +- config/config-sample.cfg | 18 +++++++++++++-- config/config.cfg | 7 ++++++ scripts/api.py | 12 +--------- scripts/capture.py | 33 +++++++++++++++++----------- scripts/loop.py | 47 +++++++++++++++++++++++++++------------- 6 files changed, 77 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 6cbde7d..3f2cbd7 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ A static version of IoT Refine is hosted at: https://dkarandikar.github.io/Stati ## Install 1. Ensure postgres is installed, create a db with name testdb, user is postgres and password is password -2. Install python3 dependencies: `pip3 install psycopg2 scapy pandas sklearn ipdata Pyshark` +2. Install python3 dependencies: `pip3 install psycopg2-binary scapy pandas sklearn ipdata Pyshark` 3. Install angular (for Refine web interface): `cd ui/ && npm install && npm install @angular/cli@1.1.3` diff --git a/config/config-sample.cfg b/config/config-sample.cfg index 49b33c1..222f19a 100644 --- a/config/config-sample.cfg +++ b/config/config-sample.cfg @@ -1,11 +1,25 @@ [api] -;insert the value of port using %(port)s +;port the api will run on port=4201 +;url of the api, insert the value of port using %(port)s url=http://localhost:%(port)s/api [macvendors] -<<<<<<< HEAD:config/config-sample.cfg +;API key used to retreive device manufacturers from mac address key=key_goes_here [loop] +;whether to automatically name new deices with a random fruit and number (e.g. Orange#123) autogen-device-names=True +;frequency at which the processing loop is run in seconds (float) +interval=1 +;whether packets will be grouped into bursts +burstify=False +;whether content prediction will be run on bursts +predict=False + +[capture] +;interval to capture packets on +interface=eth0 +;frequency at which new packets are commited to the database in seconds (float) +interval=5 diff --git a/config/config.cfg b/config/config.cfg index 0eab693..f998237 100644 --- a/config/config.cfg +++ b/config/config.cfg @@ -8,3 +8,10 @@ key=***REMOVED*** [loop] autogen-device-names=True +interval=1 +burstify=False +predict=False + +[capture] +interface=en0 +interval=5 diff --git a/scripts/api.py b/scripts/api.py index e0fbef0..4216dc5 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -2,7 +2,7 @@ from flask import Flask, request, jsonify, make_response, Response from flask_restful import Resource, Api -import json, re, sys, os, traceback, copy, argparse +import json, re, sys, os, traceback, copy from datetime import datetime sys.path.append(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "db")) import databaseBursts, rutils, configparser @@ -253,7 +253,6 @@ def packets_insert_to_impact(packets): if len(insert_buf) > 0: yield "data: %s\n\n" % json.dumps({"type":'impact', "data": packets_insert_to_impact(insert_buf)}) - print("sent new packets") if len(geo_updates) > 0: # ResetImpactCache() # updated ip should be @@ -280,15 +279,6 @@ def stream(): #======================= #main part of the script if __name__ == '__main__': - parser = argparse.ArgumentParser() - - # parser.add_argument('--localip', dest="localip", type=str, help="Specify local IP addr (if not 192.168.x.x/10.x.x.x)") - args = parser.parse_args() - - # if args.localip is not None: - # localipmask = '^(192\.168|10\.|255\.255\.255\.255|%s).*' % args.localip.replace('.','\.') - # LOCAL_IP_MASK = re.compile(localipmask) #so we can filter for local ip addresses - # print("Using local IP mask %s" % localipmask) #Register the API endpoints with flask api.add_resource(Refine, '/api/refine/') diff --git a/scripts/capture.py b/scripts/capture.py index 09fa2f9..8cad21a 100755 --- a/scripts/capture.py +++ b/scripts/capture.py @@ -1,15 +1,17 @@ #! /usr/bin/env python3 -import pyshark, datetime, psycopg2, re, argparse, sys, traceback, rutils +import pyshark, datetime, psycopg2, re, argparse, sys, traceback, rutils, configparser, os #constants -COMMIT_INTERVAL = 5 DEBUG = False local_ip_mask = rutils.make_localip_mask() #initialise vars timestamp = 0 queue = [] +COMMIT_INTERVAL = None +config = configparser.ConfigParser() +config.read(os.path.split(os.path.dirname(os.path.abspath(__file__)))[0] + "/config/config.cfg") def DatabaseInsert(packets): global timestamp @@ -97,27 +99,32 @@ def log(*args): parser = argparse.ArgumentParser() parser.add_argument('--interface', dest="interface", type=str, help="Interface to listen to") - parser.add_argument('--cinterval', dest="cinterval", type=float, help="Commit interval in seconds", default=5) - # parser.add_argument('--localip', dest="localip", type=str, help="Specify local IP addr (if not 192.168.x.x/10.x.x.x)") + parser.add_argument('--interval', dest="interval", type=float, help="Commit interval in seconds") parser.add_argument('--debug', dest='debug', action='store_true') args = parser.parse_args() DEBUG = args.debug + INTERFACE = None - if args.interface is None: + if args.interface is not None: + INTERFACE = args.interface + elif "capture" in config and "interface" in config['capture']: + INTERFACE = config['capture']['interface'] + else: + print(parser.print_help()) + sys.exit(-1) + + if args.interval is not None: + COMMIT_INTERVAL = args.interval + elif "capture" in config and "interval" in config['capture']: + COMMIT_INTERVAL = float(config['capture']['interval']) + else: print(parser.print_help()) sys.exit(-1) - - # if args.localip is not None: - # MANUAL_LOCAL_IP = args.localip log("Configuring capture on ", args.interface) - if args.cinterval is not None: - COMMIT_INTERVAL = args.cinterval - log("Setting commit interval as ", COMMIT_INTERVAL) - - capture = pyshark.LiveCapture(interface=args.interface) + capture = pyshark.LiveCapture(interface=INTERFACE) if DEBUG: capture.set_debug() diff --git a/scripts/loop.py b/scripts/loop.py index c5d5d57..e00da16 100755 --- a/scripts/loop.py +++ b/scripts/loop.py @@ -10,8 +10,7 @@ FILE_PATH = os.path.dirname(os.path.abspath(__file__)) DB_MANAGER = databaseBursts.dbManager() -INTERVAL = 1 -LOCAL_IP_MASK = rutils.make_localip_mask() # re.compile('^(192\.168|10\.|255\.255\.255\.255).*') #so we can filter for local ip addresses +LOCAL_IP_MASK = rutils.make_localip_mask() DEBUG = False RAW_IPS = None _events = [] # async db events @@ -20,14 +19,13 @@ config.read(os.path.split(os.path.dirname(os.path.abspath(__file__)))[0] + "/config/config.cfg") fruits = ["Apple", "Orange", "Banana", "Cherry", "Apricot", "Avocado", "Blueberry", "Cherry", "Cranberry", "Grape", "Kiwi", "Lime", "Lemon", "Mango", "Nectarine", "Peach", "Pineapple", "Raspberry", "Strawberry"] -#handler for signals (don't want to stop processing packets halfway through) +#handler for signals (don't want to stop processing packets halfway through a loop) class sigTermHandler: exit = False def __init__(self): signal.signal(signal.SIGINT, self.shutdown) signal.signal(signal.SIGTERM, self.shutdown) def shutdown(self, signum, frame): - print("caught signal") self.exit = True def packetBurstification(devices): @@ -126,7 +124,6 @@ def processGeos(): if not RAW_IPS: RAW_IPS = set( [r[0] for r in DB_MANAGER.execute("SELECT DISTINCT src FROM packets", ())]).union([r[0] for r in DB_MANAGER.execute("SELECT DISTINCT dst FROM packets", ())]) - print(f"Loaded {str(len(RAW_IPS))} known IPs") raw_geos = DB_MANAGER.execute("SELECT ip FROM geodata", ()) known_ips = [] @@ -172,7 +169,6 @@ def processEvents(): for evt in cur_events: evt = json.loads(evt) if RAW_IPS and evt["operation"] in ['UPDATE','INSERT'] and evt["table"] == 'packets': - # print("adding to raw ips %s %s" % (evt["data"]["src"],evt["data"]["dst"])) RAW_IPS.add(evt["data"]["src"]) RAW_IPS.add(evt["data"]["dst"]) pass @@ -182,9 +178,7 @@ def processEvents(): if __name__ == '__main__': parser = argparse.ArgumentParser() - - # parser.add_argument('--localip', dest="localip", type=str, help="Specify local IP addr (if not 192.168.x.x/10.x.x.x)") - parser.add_argument('--sleep', dest="sleep", type=float, help="Specify sleep in sec (can be fractions)") + parser.add_argument('--interval', dest="interval", type=float, help="Specify loop interval in sec (can be fractions)") parser.add_argument('--burstify', dest='burst', action="store_true", help='Do packet burstification (Default off)') parser.add_argument('--predict', dest='predict', action="store_true", help='Do burst prediction (Default off)') parser.add_argument('--debug', dest='debug', action="store_true", help='Turn debug output on (Default off)') @@ -195,11 +189,34 @@ def processEvents(): # print("Using local IP mask %s" % localipmask) # LOCAL_IP_MASK = re.compile(localipmask) #so we can filter for local ip addresses - if args.sleep is not None: - print("Setting sleep interval %s seconds." % args.sleep) - INTERVAL = args.sleep - DEBUG = args.debug + INTERVAL = None + ISBURST = None + ISPREDICT = None + + if args.interval is not None: + INTERVAL = float(args.interval) + elif "loop" in config and "interval" in config['loop']: + INTERVAL = float(config['loop']['interval']) + else: + parser.print_help() + sys.exit(-1) + + if args.burst is not None: + ISBURST = args.burst + elif "loop" in config and "burstify" in config['loop']: + ISBURST = config['loop']['burstify'] + else: + parser.print_help() + sys.exit(-1) + + if args.predict is not None: + ISPREDICT = args.predict + elif "loop" in config and "predict" in config['loop']: + ISPREDICT = config['loop']['predict'] + else: + parser.print_help() + sys.exit(-1) #register the signal handler handler = sigTermHandler() @@ -214,10 +231,10 @@ def processEvents(): processMacs() apiUrl = config['api']['url'] + '/devices' devices = requests.get(url=apiUrl).json()["manDev"] - if args.burst: + if ISBURST: print("Doing burstification") packetBurstification(devices) - if args.predict: + if ISPREDICT: print("Doing prediction") burstPrediction(devices) From baad3d2934e9bbd4d494ef82685f364d5295d4b6 Mon Sep 17 00:00:00 2001 From: mcnutty Date: Mon, 14 Jan 2019 16:48:19 +0000 Subject: [PATCH 39/55] bugfixes --- db/schema.sql | 2 +- scripts/capture.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/db/schema.sql b/db/schema.sql index f26aeac..56e0481 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -57,7 +57,7 @@ create table models ( impact real not null --amount of traffic in mb ); -drop function if exists notify_trigger; +drop function if exists notify_trigger(); CREATE FUNCTION notify_trigger() RETURNS trigger AS $trigger$ DECLARE rec RECORD; diff --git a/scripts/capture.py b/scripts/capture.py index 8cad21a..ea9084f 100755 --- a/scripts/capture.py +++ b/scripts/capture.py @@ -69,7 +69,7 @@ def DatabaseInsert(packets): conn.commit() cur.close() conn.close() - print(f"Captured {str(len(packets))} packets this tick") + print("Captured " + str(len(packets)) + " packets this tick") def QueuedCommit(packet): #commit packets to the database in COMMIT_INTERVAL second intervals From ef96ccced4c877a94154194e613a84cc1a1f24c6 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Mon, 14 Jan 2019 22:21:19 +0000 Subject: [PATCH 40/55] improved halt code to more immediately halt; filterd out multicast crap --- scripts/capture.py | 3 +++ scripts/loop.py | 53 ++++++++++++++++++++++++++++------------------ scripts/rutils.py | 4 ++++ 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/scripts/capture.py b/scripts/capture.py index b541ff0..716f18e 100755 --- a/scripts/capture.py +++ b/scripts/capture.py @@ -42,6 +42,9 @@ def DatabaseInsert(packets): # print(packet) continue + if rutils.is_multicast_v4(src) or rutils.is_multicast_v4(dst): + continue + srcLocal = local_ip_mask.match(src) dstLocal = local_ip_mask.match(dst) diff --git a/scripts/loop.py b/scripts/loop.py index ca29a2a..a20dfed 100755 --- a/scripts/loop.py +++ b/scripts/loop.py @@ -11,6 +11,7 @@ INTERVAL = 1 LOCAL_IP_MASK = rutils.make_localip_mask() # re.compile('^(192\.168|10\.|255\.255\.255\.255).*') #so we can filter for local ip addresses DEBUG = False +dbgprint = lambda *args: print(*args) if DEBUG else '' RAW_IPS = None _events = [] # async db events @@ -121,9 +122,9 @@ def processGeos(): global RAW_IPS if not RAW_IPS: - print("Preloading RAW_IPS") + dbgprint("Preloading RAW_IPS") RAW_IPS = set( [r[0] for r in DB_MANAGER.execute("SELECT DISTINCT src FROM packets", ())]).union([r[0] for r in DB_MANAGER.execute("SELECT DISTINCT dst FROM packets", ())]) - print(" Done ", len(RAW_IPS), " known ips ") + dbgprint(" Done ", len(RAW_IPS), " known ips ") # print("raw_ips", raw_ips) @@ -138,6 +139,7 @@ def processGeos(): # local ip, so skip continue if ip not in known_ips: + dbgprint("Getting ", ip) data = requests.get('https://api.ipdata.co/' + ip + '?api-key=***REMOVED***') if data.status_code==200 and data.json()['latitude'] is not '': data = data.json() @@ -146,7 +148,7 @@ def processGeos(): DB_MANAGER.execute("INSERT INTO geodata VALUES(%s, %s, %s, %s, %s)", (ip, "0", "0", "XX", "unknown")) known_ips.append(ip) - if DEBUG: print("Adding to known IPs ", ip) + dbgprint("Adding to known IPs ", ip) def processMacs(): raw_macs = DB_MANAGER.execute("SELECT DISTINCT mac FROM packets", ()) @@ -159,20 +161,21 @@ def processMacs(): else: DB_MANAGER.execute("INSERT INTO devices VALUES(%s, 'unknown', 'unknown')", (mac[0],)) - # def processEvents(): global _events + dbgprint("processEvents has ", len(_events), " waiting in queue") cur_events = _events.copy() _events.clear() for evt in cur_events: evt = json.loads(evt) if RAW_IPS and evt["operation"] in ['UPDATE','INSERT'] and evt["table"] == 'packets': - # print("adding to raw ips %s %s" % (evt["data"]["src"],evt["data"]["dst"])) RAW_IPS.add(evt["data"]["src"]) RAW_IPS.add(evt["data"]["dst"]) pass + dbgprint("RAW IPS now has ", len(RAW_IPS) if RAW_IPS else 'none') + #============ @@ -199,28 +202,36 @@ def processEvents(): DEBUG = args.debug - #register the signal handler - handler = sigTermHandler() - + running = [True] # watch for listen events -- not sure if this has to be on its own connection listener_thread_stopper = DB_MANAGER.listen('db_notifications', lambda payload:_events.append(payload)) + def shutdown(*sargs): + running[0] = False + listener_thread_stopper() + + signal.signal(signal.SIGINT, shutdown) + signal.signal(signal.SIGTERM, shutdown) + + #register the signal handler + #handler = sigTermHandler() + #loop through categorisation tasks - while(True): + while(running[0]): + dbgprint("Awake!"); processEvents() - processGeos() - processMacs() - devices = requests.get(url='http://localhost:4201/api/devices').json()["manDev"] - if args.burst: + if running[0]: + processGeos() + if running[0]: + processMacs() + if running[0] and args.burst: + devices = requests.get(url='http://localhost:4201/api/devices').json()["manDev"] print("Doing burstification") packetBurstification(devices) - if args.predict: + if running[0] and args.predict: + devices = requests.get(url='http://localhost:4201/api/devices').json()["manDev"] print("Doing prediction") burstPrediction(devices) - - #exit gracefully if we were asked to shutdown - if handler.exit: - listener_thread_stopper() - break - - time.sleep(INTERVAL) + if running[0]: + dbgprint("sleeping zzzz ", INTERVAL); + time.sleep(INTERVAL) diff --git a/scripts/rutils.py b/scripts/rutils.py index 9525e9b..85a98bd 100644 --- a/scripts/rutils.py +++ b/scripts/rutils.py @@ -17,6 +17,10 @@ def make_localip_mask(override=None): return initial_mask +def is_multicast_v4(ipv4): + splits = ipv4 and ipv4.split('.') + # print(' checking ', ipv4, ', ismulti? ', splits and int(splits[0]) >= 224 and int(splits[0]) <= 239) + return splits and int(splits[0]) >= 224 and int(splits[0]) <= 239 def get_local_ip(): import socket From d00e6711ec87b2f6053cd467e1f01afde188ec80 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Tue, 15 Jan 2019 02:59:10 +0000 Subject: [PATCH 41/55] first attempt at fruit --- db/schema.sql | 2 +- ui/src/app/refinebar/refinebar.component.ts | 19 ++++++++++++++++++- ui/src/app/tiled-all/tiled-all.component.html | 2 +- ui/src/app/tiled-all/tiled-all.component.ts | 5 +++-- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/db/schema.sql b/db/schema.sql index 56e0481..d48c705 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -34,7 +34,7 @@ drop table if exists devices cascade; create table devices( mac varchar(17) primary key, manufacturer varchar(40), - name varchar(40) DEFAULT 'unknown' + name varchar(255) DEFAULT 'unknown' ); drop table if exists geodata cascade; diff --git a/ui/src/app/refinebar/refinebar.component.ts b/ui/src/app/refinebar/refinebar.component.ts index 348d220..5090ad3 100644 --- a/ui/src/app/refinebar/refinebar.component.ts +++ b/ui/src/app/refinebar/refinebar.component.ts @@ -62,6 +62,9 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { @Input() showLegend = true; @Input() showTypesLegend = false; @Input() showXAxis = true; + @Input('devices') devices_in :({[mac: string]: string}); + _devices: ({[mac: string]: string}) = {}; + @Input() scale = false; linear = false; @@ -73,6 +76,8 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { _hoveringApp: string; _ignoredApps: string[]; _impact_listener : Subscription; + + constructor(private httpM: HttpModule, @@ -105,6 +110,15 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { this.render(); } }); + + this.loader.asyncDeviceChanges().subscribe(devices => { + console.info(` ~ device name update ${devices.length} devices`); + devices.map( d => { + console.info(`adding mac::name binding ${d.mac} -> ${d.name}`); + this._devices[d.mac] = d.name; + }); + this.render(); + }); // (window)._rb = this; } @@ -164,6 +178,9 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { this_.render(); } } + if (this.devices_in) { + this._devices = Object.assign({}, this.devices_in); + } // this.render(); this.init.then(() => { this.render(); }); } @@ -452,7 +469,7 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { .attr('x', this.showTypesLegend ? width - 140 - 24 : width - 24) .attr('y', 9.5) .attr('dy', '0.32em') - .text((d) => this._ignoredApps.indexOf(d) === -1 ? d : "Removed: " + d) + .text((d) => this._ignoredApps.indexOf(d) === -1 ? this._devices[d] || d : "Removed: " + d) .style("fill", d => this._ignoredApps.indexOf(d) === -1 ? 'rgba(0,0,0,1)' : 'rgba(200,0,0,1)') .attr('opacity', (d) => { let highApp = this.highlightApp || this._hoveringApp; diff --git a/ui/src/app/tiled-all/tiled-all.component.html b/ui/src/app/tiled-all/tiled-all.component.html index 9084040..c93a2e2 100644 --- a/ui/src/app/tiled-all/tiled-all.component.html +++ b/ui/src/app/tiled-all/tiled-all.component.html @@ -1,6 +1,6 @@
- +
diff --git a/ui/src/app/tiled-all/tiled-all.component.ts b/ui/src/app/tiled-all/tiled-all.component.ts index 54773bc..d600432 100644 --- a/ui/src/app/tiled-all/tiled-all.component.ts +++ b/ui/src/app/tiled-all/tiled-all.component.ts @@ -4,7 +4,7 @@ import { FocusTarget, FocusService } from "app/focus.service"; import { UsageListener } from "app/usage-listener/usage-listener.component"; import { UsageConnectorService } from "app/usage-connector.service"; import { ActivatedRoute, Router } from "@angular/router"; -import { AppImpact } from '../refinebar/refinebar.component'; +import { AppImpact, AppDevice } from '../refinebar/refinebar.component'; import * as _ from 'lodash'; import { Observable } from '../../../node_modules/rxjs/Observable'; import { Observer } from '../../../node_modules/rxjs/Observer'; @@ -53,6 +53,7 @@ export class TiledAllComponent extends TargetWatcher implements OnInit { mode: string; impacts: AppImpact[]; usage : AppUsage[]; + devices : {[mac: string]:string}; impactChanges: Observable; private impactObservers: Observer[] = []; @@ -86,7 +87,7 @@ export class TiledAllComponent extends TargetWatcher implements OnInit { // console.log('!@#ILJ!@#L@!J# got bundle ', bundle); this_.usage = bundle.usage; this_.impacts = bundle.impacts; - console.log("yooo assigning impacts ", this_.impacts); + this_.devices = bundle.manDev; this_.triggerImpactsChange(); // this_.render(); }); From 66d2f9f47ed2bfc2ed6988442042442f40b92e14 Mon Sep 17 00:00:00 2001 From: mcnutty Date: Tue, 15 Jan 2019 12:21:36 +0000 Subject: [PATCH 42/55] this should stop unidentified impacts showing up off the coast of Ghana --- ui/src/app/geomap/geomap.component.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ui/src/app/geomap/geomap.component.ts b/ui/src/app/geomap/geomap.component.ts index efb817a..69b6f85 100644 --- a/ui/src/app/geomap/geomap.component.ts +++ b/ui/src/app/geomap/geomap.component.ts @@ -243,8 +243,8 @@ export class GeomapComponent implements AfterViewInit, OnChanges { console.log(' impact extents ', minmax[0], ' - ', minmax[1]); - let apps = _.uniq(impacts.map((x) => x.appid)); - apps.sort(); + let apps = _.uniq(impacts.map((x) => x.appid)); + apps.sort(); // if (this.apps === undefined) { // // sort apps // apps.sort((a, b) => _.filter(usage, { appid: b })[0].mins - _.filter(usage, { appid: a })[0].mins); @@ -269,8 +269,12 @@ export class GeomapComponent implements AfterViewInit, OnChanges { }); // add circles to svg - var datas = svg.selectAll("circle").data(impacts); - // console.info('selecting on impacts ', impacts); + var datas = svg.selectAll("circle").data(impacts); + + //filter out impacts that have lat/lon of 0,0 (i.e. we don't know where they are located) + datas.filter((d) => {(d.geo.longitude == 0 && d.geo.lattitude == 0) ? 0 : 1}); + + // console.info('selecting on impacts ', impacts); datas.enter().append("circle") .attr("cx", (d) => { From c4928a66bd54ffbde81f7e7f886e1ba42b352ecf Mon Sep 17 00:00:00 2001 From: electronic Max Date: Tue, 15 Jan 2019 14:15:24 +0000 Subject: [PATCH 43/55] fixed major problem with loader --- ui/src/app/loader.service.ts | 10 +++++----- ui/src/app/refinebar/refinebar.component.ts | 3 +++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ui/src/app/loader.service.ts b/ui/src/app/loader.service.ts index 01e0778..e280d83 100644 --- a/ui/src/app/loader.service.ts +++ b/ui/src/app/loader.service.ts @@ -562,11 +562,11 @@ export class LoaderService { manDev = resp.manDev, bursts = resp.bursts; - impacts.forEach(function(impact){ - if (manDev[impact.appid] !== "unknown") { - impact.appid = manDev[impact.appid]; - } - }); + // impacts.forEach(function(impact){ + // if (manDev[impact.appid] !== "unknown") { + // impact.appid = manDev[impact.appid]; + // } + // }); bursts.forEach(function(burst){ if (manDev[burst.device] !== "unknown") { burst.device = manDev[burst.device]; diff --git a/ui/src/app/refinebar/refinebar.component.ts b/ui/src/app/refinebar/refinebar.component.ts index 5090ad3..17e029f 100644 --- a/ui/src/app/refinebar/refinebar.component.ts +++ b/ui/src/app/refinebar/refinebar.component.ts @@ -465,6 +465,9 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { return 1.0; }); + console.info("raw impacts ", this.impacts); + console.info("devices list is now > ", this.apps && this.apps.slice(), apps.slice().reverse(), " and devices ", this._devices); + legend.append('text') .attr('x', this.showTypesLegend ? width - 140 - 24 : width - 24) .attr('y', 9.5) From 24769838685908451c85060788c043ed281355c2 Mon Sep 17 00:00:00 2001 From: mcnutty Date: Tue, 15 Jan 2019 14:39:31 +0000 Subject: [PATCH 44/55] systemd + autologin --- README.md | 2 ++ service/daemon-sample.sh | 3 +++ service/logintask-sample.desktop | 8 ++++++++ 3 files changed, 13 insertions(+) create mode 100644 service/logintask-sample.desktop diff --git a/README.md b/README.md index 3f2cbd7..f5ebbd5 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,8 @@ A static version of IoT Refine is hosted at: https://dkarandikar.github.io/Stati 3. Enable or disable IoT Refine on boot by running `sudo systemctl {enable|disable} iotrefine` +4. To have chromium point at iotrefine on login, copy and fill out logintask-sample.desktop and move it to ~/.config/autostart/ + ## Configure Device Names Device names will initially display as MAC addresses. To assign a 'friendly' name to a device, use the `SetDevice` API endpoint: diff --git a/service/daemon-sample.sh b/service/daemon-sample.sh index af25e90..79b3224 100755 --- a/service/daemon-sample.sh +++ b/service/daemon-sample.sh @@ -24,6 +24,9 @@ trap 'term' QUIT cd $LOCATION cd scripts +#start the wifi hotspot +./reset-wifi + #start capturing packets ./capture.py & PID3=$! diff --git a/service/logintask-sample.desktop b/service/logintask-sample.desktop new file mode 100644 index 0000000..caf7dd6 --- /dev/null +++ b/service/logintask-sample.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Name=chromium-autostart +GenericName=chromium autostart +Comment=start chromium and point it at iotrefine +Exec=path_to_iotrefine/scripts/restart-browser.sh +Terminal=true +Type=Application +X-GNOME-Autostart-enabled=true From 1c3149582d16fd2a7d1c7777540e6b798f8b95a4 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Tue, 15 Jan 2019 16:45:03 +0000 Subject: [PATCH 45/55] updated layout so that it's less scrunchied --- ui/src/app/geobar/geobar.component.ts | 2 +- ui/src/app/refinebar/refinebar.component.ts | 4 +-- ui/src/app/tiled-all/tiled-all.component.html | 2 +- ui/src/app/tiled-all/tiled-all.component.scss | 32 ++++++++++++++++--- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/ui/src/app/geobar/geobar.component.ts b/ui/src/app/geobar/geobar.component.ts index 4bff129..8207fae 100644 --- a/ui/src/app/geobar/geobar.component.ts +++ b/ui/src/app/geobar/geobar.component.ts @@ -219,7 +219,7 @@ export class GeobarComponent implements AfterViewInit, OnChanges { // console.log(by_country) - let margin = { top: 20, right: 20, bottom: this.showXAxis ? 120 : 0, left: 40 }, + let margin = { top: 20, right: 20, bottom: this.showXAxis ? 70 : 0, left: 40 }, width = width_svgel - margin.left - margin.right, // +svg.attr('width') - margin.left - margin.right, height = height_svgel - margin.top - margin.bottom; // +svg.attr('height') - margin.top - margin.bottom, diff --git a/ui/src/app/refinebar/refinebar.component.ts b/ui/src/app/refinebar/refinebar.component.ts index 17e029f..fe08c84 100644 --- a/ui/src/app/refinebar/refinebar.component.ts +++ b/ui/src/app/refinebar/refinebar.component.ts @@ -465,8 +465,8 @@ export class RefinebarComponent implements AfterViewInit, OnChanges { return 1.0; }); - console.info("raw impacts ", this.impacts); - console.info("devices list is now > ", this.apps && this.apps.slice(), apps.slice().reverse(), " and devices ", this._devices); + // console.info("raw impacts ", this.impacts); + // console.info("devices list is now > ", this.apps && this.apps.slice(), apps.slice().reverse(), " and devices ", this._devices); legend.append('text') .attr('x', this.showTypesLegend ? width - 140 - 24 : width - 24) diff --git a/ui/src/app/tiled-all/tiled-all.component.html b/ui/src/app/tiled-all/tiled-all.component.html index c93a2e2..dc78ef3 100644 --- a/ui/src/app/tiled-all/tiled-all.component.html +++ b/ui/src/app/tiled-all/tiled-all.component.html @@ -5,6 +5,6 @@
- +
\ No newline at end of file diff --git a/ui/src/app/tiled-all/tiled-all.component.scss b/ui/src/app/tiled-all/tiled-all.component.scss index 6f42c34..e5e110c 100644 --- a/ui/src/app/tiled-all/tiled-all.component.scss +++ b/ui/src/app/tiled-all/tiled-all.component.scss @@ -36,17 +36,41 @@ flex-basis: 30%; } .gridright { + display:flex; + flex-direction:column; height: 100%; // display: flex; // flex-direction: column; flex-grow: 0.5; align-items: stretch; - app-refinecat /deep/ div.main, - app-geomap /deep/ div.main, - app-geobar /deep/ div.main { - height: 30vh; + // app-refinecat /deep/ div.main, + app-geomap { + height: 50vh; width: 100%; + flex-grow:0; + justify-content:flex-start; } + app-geobar { /*/deep/ div.main*/ + flex-grow:1; + justify-content: flex-end; + position: relative; + } + app-geobar /deep/ div.main { /*/deep/ div.main*/ + // height:100%; + position:absolute; + top:0px;right:0px;bottom:0px;left:0px; + } + + // .start { + // justify-content:flex-start; + // flex-grow:0.0; + // } + // .end { + // justify-content: flex-end; + // } + // .grow { + // flex-grow:1.0; + // } } .fullwidth { flex-grow: 0; From 8fcb929f0f1fc15a7443b1ce0680e48f4695e505 Mon Sep 17 00:00:00 2001 From: mcnutty Date: Tue, 15 Jan 2019 17:06:02 +0000 Subject: [PATCH 46/55] gitignore update --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ebd4e45..c071b1b 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,6 @@ tests/__pycache__/ *node_modules* -scripts/config.cfg +config/config.cfg service/daemon.sh service/iotrefine.service From 84b859463c192c8a0ec36782642502431c9d6a7b Mon Sep 17 00:00:00 2001 From: electronic Max Date: Tue, 15 Jan 2019 21:30:57 +0000 Subject: [PATCH 47/55] slight updates and tweaks --- db/databaseBursts.py | 5 +++-- db/schema.sql | 1 + scripts/api.py | 6 ++++-- scripts/loop.py | 5 ++++- ui/src/app/geomap/geomap.component.ts | 12 +++++++----- 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/db/databaseBursts.py b/db/databaseBursts.py index 7cc9899..185f45d 100644 --- a/db/databaseBursts.py +++ b/db/databaseBursts.py @@ -8,9 +8,10 @@ class dbManager(): - def __init__(self): + def __init__(self, dbname='testdb', username='postgres', password='password'): try: - self.connection = psycopg2.connect("dbname=testdb user=postgres password=password") + print("connection string ", "dbname=%(dbname)s user=%(username)s password=%(password)s" % {'dbname':dbname,'username':username,'password':password }) + self.connection = psycopg2.connect("dbname=%(dbname)s user=%(username)s password=%(password)s" % {'dbname':dbname,'username':username,'password':password }) except: print("Connection error") diff --git a/db/schema.sql b/db/schema.sql index d48c705..eb19743 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -29,6 +29,7 @@ create table packets ( -- create two indexes on src and dst to speed up lookups by these cols by loop.py create index on packets (src); create index on packets (dst); +create index on packets (time); drop table if exists devices cascade; create table devices( diff --git a/scripts/api.py b/scripts/api.py index ce95f66..599e5d8 100755 --- a/scripts/api.py +++ b/scripts/api.py @@ -8,7 +8,7 @@ import databaseBursts, rutils, configparser LOCAL_IP_MASK = rutils.make_localip_mask() # re.compile('^(192\.168|10\.|255\.255\.255\.255).*') #so we can filter for local ip addresses -DB_MANAGER = databaseBursts.dbManager() #for running database queries +DB_MANAGER = None #for running database queries app = Flask(__name__) #initialise the flask server api = Api(app) #initialise the flask server geos = dict() #for building and caching geo data @@ -196,8 +196,9 @@ def GetImpacts(n, units="MINUTES"): impacts = dict() # copy.deepcopy(_impact_cache) # get all packets from the database (if we have cached impacts from before, then only get new packets) - packetrows = DB_MANAGER.execute("SELECT * FROM packets WHERE time > (NOW() - INTERVAL %s) ORDER BY id", ("'" + str(n) + " " + units + "'",)) + packetrows = DB_MANAGER.execute("SELECT * FROM packets WHERE time > (NOW() - INTERVAL %s)", ("'" + str(n) + " " + units + "'",)) packets = [dict(zip(['id', 'time', 'src', 'dst', 'mac', 'len', 'proto', 'burst'], packet)) for packet in packetrows] + print("got ", len(packets), "packets") # pkt_id, pkt_time, pkt_src, pkt_dst, pkt_mac, pkt_len, pkt_proto, pkt_burst = packet # {'id': '212950', 'dst': '224.0.0.251', 'len': '101', 'mac': '78:4f:43:64:62:01', 'src': '192.168.0.24', 'burst': None} @@ -302,6 +303,7 @@ def stream(): api.add_resource(SetDevice, '/api/setdevice//') # watch for listen events -- not sure if this has to be on its own connection + DB_MANAGER = databaseBursts.dbManager() listenManager = databaseBursts.dbManager() listenManager.listen('db_notifications', lambda payload:_events.append(payload)) diff --git a/scripts/loop.py b/scripts/loop.py index 2be1376..7be1952 100755 --- a/scripts/loop.py +++ b/scripts/loop.py @@ -6,7 +6,7 @@ import databaseBursts, rutils, predictions FILE_PATH = os.path.dirname(os.path.abspath(__file__)) -DB_MANAGER = databaseBursts.dbManager() +DB_MANAGER = None LOCAL_IP_MASK = rutils.make_localip_mask() DEBUG = False log = lambda *args: print(*args) if DEBUG else '' @@ -194,6 +194,9 @@ def processEvents(): CONFIG = configparser.ConfigParser() CONFIG.read(CONFIG_PATH) + dbconnect = {} + DB_MANAGER = databaseBursts.dbManager() + INTERVAL = None ISBURST = None ISPREDICT = None diff --git a/ui/src/app/geomap/geomap.component.ts b/ui/src/app/geomap/geomap.component.ts index efb817a..4db2933 100644 --- a/ui/src/app/geomap/geomap.component.ts +++ b/ui/src/app/geomap/geomap.component.ts @@ -214,7 +214,7 @@ export class GeomapComponent implements AfterViewInit, OnChanges { render() { // console.log(':: render usage:', this.usage && this.usage.length); const svgel = this.getSVGElement(); - if (!svgel || !this.usage || !this.impacts) { return; } + if (!svgel) { return; } // console.log('refinebar render! getSVGElement > ', svgel); let rect = svgel.getBoundingClientRect(), @@ -236,15 +236,16 @@ export class GeomapComponent implements AfterViewInit, OnChanges { svg.selectAll('*').remove(); - const usage = this.usage, // this.usage.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ), - impacts = this.impacts, // this.impacts.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ), + const usage = this.usage || [], // this.usage.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ), + impacts = this.impacts || [], // this.impacts.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ), minmax = d3.extent(impacts.map( i => i.impact )); - - console.log(' impact extents ', minmax[0], ' - ', minmax[1]); + console.log(' impact extents ', minmax[0], ' - ', minmax[1]); let apps = _.uniq(impacts.map((x) => x.appid)); apps.sort(); + + // old sorting method // if (this.apps === undefined) { // // sort apps // apps.sort((a, b) => _.filter(usage, { appid: b })[0].mins - _.filter(usage, { appid: a })[0].mins); @@ -252,6 +253,7 @@ export class GeomapComponent implements AfterViewInit, OnChanges { // } else { // apps = this.apps; // } + let margin = { top: 20, right: 20, bottom: -200, left: 40 }, width = width_svgel - margin.left - margin.right, // +svg.attr('width') - margin.left - margin.right, height = height_svgel - margin.top - margin.bottom, // +svg.attr('height') - margin.top - margin.bottom, From 1bf110c974ade63381fb59355bb420bf419690ea Mon Sep 17 00:00:00 2001 From: mcnutty Date: Wed, 16 Jan 2019 14:46:42 +0000 Subject: [PATCH 48/55] more linux config for the demo --- scripts/login-delay.sh | 6 ++++++ scripts/reset-browser.sh | 2 +- scripts/soft-reset.sh | 12 ++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100755 scripts/login-delay.sh create mode 100755 scripts/soft-reset.sh diff --git a/scripts/login-delay.sh b/scripts/login-delay.sh new file mode 100755 index 0000000..111a76d --- /dev/null +++ b/scripts/login-delay.sh @@ -0,0 +1,6 @@ +#! /bin/sh + +echo Starting IoT-refine +echo Waiting for service to come up... +sleep 15 +chromium --start-fullscreen --app=http://localhost:4200 http://localhost:4200 diff --git a/scripts/reset-browser.sh b/scripts/reset-browser.sh index e9d4422..0706749 100755 --- a/scripts/reset-browser.sh +++ b/scripts/reset-browser.sh @@ -1,4 +1,4 @@ #! /bin/sh killall chromium #stop currently open browser window(s) -chromium --app=http://localhost:4200 http://localhost:4200 & #open chromium fullscreen on iotrefine +chromium --start-fullscreen --app=http://localhost:4200 http://localhost:4200 & #open chromium fullscreen on iotrefine diff --git a/scripts/soft-reset.sh b/scripts/soft-reset.sh new file mode 100755 index 0000000..2c0ff68 --- /dev/null +++ b/scripts/soft-reset.sh @@ -0,0 +1,12 @@ +#! /bin/bash + +LOCATION=/home/mcnutty/Documents/IoT-refine/scripts/ + +cd $LOCATION +service iotrefine stop +sleep 1 +./reset-database.py +sleep 1 +service iotrefine start +sleep 20 +./reset-browser.sh From c79bd56563cd1c678a08640b26360a8a68caa673 Mon Sep 17 00:00:00 2001 From: mcnutty Date: Wed, 16 Jan 2019 15:23:09 +0000 Subject: [PATCH 49/55] linux config bugfixes --- config/config.cfg | 17 ----------------- scripts/reset-browser.sh | 2 +- scripts/reset-wifi.sh | 2 +- scripts/soft-reset.sh | 5 ++++- 4 files changed, 6 insertions(+), 20 deletions(-) delete mode 100644 config/config.cfg diff --git a/config/config.cfg b/config/config.cfg deleted file mode 100644 index f998237..0000000 --- a/config/config.cfg +++ /dev/null @@ -1,17 +0,0 @@ -[api] -;insert the value of port using %(port)s -port=4201 -url=http://localhost:%(port)s/api - -[macvendors] -key=***REMOVED*** - -[loop] -autogen-device-names=True -interval=1 -burstify=False -predict=False - -[capture] -interface=en0 -interval=5 diff --git a/scripts/reset-browser.sh b/scripts/reset-browser.sh index 0706749..5e475cf 100755 --- a/scripts/reset-browser.sh +++ b/scripts/reset-browser.sh @@ -1,4 +1,4 @@ #! /bin/sh killall chromium #stop currently open browser window(s) -chromium --start-fullscreen --app=http://localhost:4200 http://localhost:4200 & #open chromium fullscreen on iotrefine +nohup chromium --start-fullscreen --app=http://localhost:4200 http://localhost:4200 & #open chromium fullscreen on iotrefine diff --git a/scripts/reset-wifi.sh b/scripts/reset-wifi.sh index bcaded2..f8ca17c 100755 --- a/scripts/reset-wifi.sh +++ b/scripts/reset-wifi.sh @@ -4,5 +4,5 @@ nmcli r wifi off #turn wifi off sleep 1 nmcli r wifi on #turn wifi on sleep 1 -nmcli d wifi hotspot ifname wlp58s0 ssid iotrefine password password123 #start hotspot on built-in wifi card +nmcli d wifi hotspot ifname wlp58s0 ssid "IoT Refine Demo" password "heresmydata" #start hotspot on built-in wifi card sleep 1 diff --git a/scripts/soft-reset.sh b/scripts/soft-reset.sh index 2c0ff68..efe5a3f 100755 --- a/scripts/soft-reset.sh +++ b/scripts/soft-reset.sh @@ -3,10 +3,13 @@ LOCATION=/home/mcnutty/Documents/IoT-refine/scripts/ cd $LOCATION +echo Service down... service iotrefine stop sleep 1 ./reset-database.py sleep 1 +echo Service up... service iotrefine start +echo Waiting for everything to start... sleep 20 -./reset-browser.sh +env DISPLAY=0.0 ./reset-browser.sh From 45b7a0b2ad66edcb6ce365b53aace5f18dbf1d0b Mon Sep 17 00:00:00 2001 From: mcnutty Date: Wed, 16 Jan 2019 16:14:44 +0000 Subject: [PATCH 50/55] more bugz squashed --- scripts/reset-browser.sh | 2 +- scripts/reset-wifi.sh | 2 +- scripts/soft-reset.sh | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/reset-browser.sh b/scripts/reset-browser.sh index 5e475cf..5c6bbdb 100755 --- a/scripts/reset-browser.sh +++ b/scripts/reset-browser.sh @@ -1,4 +1,4 @@ #! /bin/sh killall chromium #stop currently open browser window(s) -nohup chromium --start-fullscreen --app=http://localhost:4200 http://localhost:4200 & #open chromium fullscreen on iotrefine +/usr/bin/chromium --start-fullscreen --app=http://localhost:4200 http://localhost:4200 & #open chromium fullscreen on iotrefine diff --git a/scripts/reset-wifi.sh b/scripts/reset-wifi.sh index f8ca17c..ade5258 100755 --- a/scripts/reset-wifi.sh +++ b/scripts/reset-wifi.sh @@ -4,5 +4,5 @@ nmcli r wifi off #turn wifi off sleep 1 nmcli r wifi on #turn wifi on sleep 1 -nmcli d wifi hotspot ifname wlp58s0 ssid "IoT Refine Demo" password "heresmydata" #start hotspot on built-in wifi card +nmcli d wifi hotspot ifname wlp58s0 ssid "IoT Refine Demo" password "wheresmydata" #start hotspot on built-in wifi card sleep 1 diff --git a/scripts/soft-reset.sh b/scripts/soft-reset.sh index efe5a3f..497f461 100755 --- a/scripts/soft-reset.sh +++ b/scripts/soft-reset.sh @@ -1,15 +1,16 @@ #! /bin/bash LOCATION=/home/mcnutty/Documents/IoT-refine/scripts/ +USER=mcnutty cd $LOCATION echo Service down... service iotrefine stop sleep 1 -./reset-database.py +sudo -u $USER ./reset-database.py sleep 1 echo Service up... service iotrefine start echo Waiting for everything to start... sleep 20 -env DISPLAY=0.0 ./reset-browser.sh +sudo -u $USER nohup ./reset-browser.sh From 347db7946ac382f021ae0ae8ab3a6fc922547da7 Mon Sep 17 00:00:00 2001 From: mcnutty Date: Wed, 16 Jan 2019 17:18:40 +0000 Subject: [PATCH 51/55] made login script wipe database --- scripts/login-delay.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/login-delay.sh b/scripts/login-delay.sh index 111a76d..417778f 100755 --- a/scripts/login-delay.sh +++ b/scripts/login-delay.sh @@ -1,6 +1,12 @@ #! /bin/sh -echo Starting IoT-refine +LOCATION=/home/mcnutty/Documents/IoT-refine/scripts/ + +echo Starting IoT-refine\n +echo Clearing database... +cd $LOCATION +./reset-database.sh + echo Waiting for service to come up... sleep 15 chromium --start-fullscreen --app=http://localhost:4200 http://localhost:4200 From 830a798aa1dfb58d9035da05f75bd8dac2e5e9fc Mon Sep 17 00:00:00 2001 From: mcnutty Date: Wed, 16 Jan 2019 17:20:08 +0000 Subject: [PATCH 52/55] typo --- scripts/login-delay.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/login-delay.sh b/scripts/login-delay.sh index 417778f..58d9aed 100755 --- a/scripts/login-delay.sh +++ b/scripts/login-delay.sh @@ -5,7 +5,7 @@ LOCATION=/home/mcnutty/Documents/IoT-refine/scripts/ echo Starting IoT-refine\n echo Clearing database... cd $LOCATION -./reset-database.sh +./reset-database.py echo Waiting for service to come up... sleep 15 From fa291634f685560c0641da67d0dd0ce5dfe1dad1 Mon Sep 17 00:00:00 2001 From: electronic Max Date: Wed, 16 Jan 2019 17:26:16 +0000 Subject: [PATCH 53/55] filtered out broadcast to macs --- scripts/capture.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/capture.py b/scripts/capture.py index 89e4b51..7496303 100755 --- a/scripts/capture.py +++ b/scripts/capture.py @@ -43,7 +43,7 @@ def DatabaseInsert(packets): # print(packet) continue - if rutils.is_multicast_v4(src) or rutils.is_multicast_v4(dst): + if rutils.is_multicast_v4(src) or rutils.is_multicast_v4(dst) or packet['eth'].src == 'ff:ff:ff:ff:ff:ff' or packet['eth'].dst == 'ff:ff:ff:ff:ff:ff': continue srcLocal = local_ip_mask.match(src) From 482df5f4bfdfe4a32f83266484bf565ea53e493a Mon Sep 17 00:00:00 2001 From: mcnutty Date: Wed, 16 Jan 2019 17:41:36 +0000 Subject: [PATCH 54/55] wording tweak --- scripts/login-delay.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/login-delay.sh b/scripts/login-delay.sh index 58d9aed..8beb70d 100755 --- a/scripts/login-delay.sh +++ b/scripts/login-delay.sh @@ -2,11 +2,8 @@ LOCATION=/home/mcnutty/Documents/IoT-refine/scripts/ -echo Starting IoT-refine\n -echo Clearing database... cd $LOCATION ./reset-database.py - echo Waiting for service to come up... sleep 15 chromium --start-fullscreen --app=http://localhost:4200 http://localhost:4200 From dfad48f0f896bfb4aa1f7301319c567389df56dd Mon Sep 17 00:00:00 2001 From: electronic Max Date: Wed, 16 Jan 2019 17:47:06 +0000 Subject: [PATCH 55/55] small bugfixes in preparation of bre happiness --- scripts/loop.py | 2 +- ui/src/app/geomap/geomap.component.ts | 6 +++--- ui/src/app/tiled-all/tiled-all.component.ts | 13 ++++++++++++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/scripts/loop.py b/scripts/loop.py index 7be1952..f2b7946 100755 --- a/scripts/loop.py +++ b/scripts/loop.py @@ -133,7 +133,7 @@ def processGeos(): data = requests.get('https://api.ipdata.co/' + ip + '?api-key=' + CONFIG['macvendors']['key']) if data.status_code==200 and data.json()['latitude'] is not '': data = data.json() - DB_MANAGER.execute("INSERT INTO geodata VALUES(%s, %s, %s, %s, %s)", (ip, data['latitude'], data['longitude'], data['country_code'] or data['continent_code'], data['organisation'][:20])) + DB_MANAGER.execute("INSERT INTO geodata VALUES(%s, %s, %s, %s, %s)", (ip, data['latitude'], data['longitude'], data['country_code'] or data['continent_code'], data['organisation'][:20] or 'unknown')) else: DB_MANAGER.execute("INSERT INTO geodata VALUES(%s, %s, %s, %s, %s)", (ip, "0", "0", "XX", "unknown")) known_ips.append(ip) diff --git a/ui/src/app/geomap/geomap.component.ts b/ui/src/app/geomap/geomap.component.ts index 4db2933..420ae3d 100644 --- a/ui/src/app/geomap/geomap.component.ts +++ b/ui/src/app/geomap/geomap.component.ts @@ -123,7 +123,7 @@ export class GeomapComponent implements AfterViewInit, OnChanges { // return acc; // }, []); this.zone.run(() => { - console.info('geomap updating impacts'); + // console.info('geomap updating impacts'); let iin = this_.impacts_in; // this_.impacts = iin.map(impact => ({ impact: impact.impact, /* impact.impact/minMax[1],*/ geo: impact, appid: impact.appid })); @@ -240,7 +240,7 @@ export class GeomapComponent implements AfterViewInit, OnChanges { impacts = this.impacts || [], // this.impacts.filter(obj => this._ignoredApps.indexOf(obj.appid) === -1 ), minmax = d3.extent(impacts.map( i => i.impact )); - console.log(' impact extents ', minmax[0], ' - ', minmax[1]); + // console.log(' impact extents ', minmax[0], ' - ', minmax[1]); let apps = _.uniq(impacts.map((x) => x.appid)); apps.sort(); @@ -296,7 +296,7 @@ export class GeomapComponent implements AfterViewInit, OnChanges { .on('mouseenter', (d) => this.hover.hoverChanged(undefined)) .on('mouseleave', (d) => this.hover.hoverChanged(undefined)); - console.info('map ending render'); + // console.info('map ending render'); // datas.enter().append('text') // .attr('x', (d) => projection([d.geo.longitude, d.geo.latitude])[0] + 5) // .attr('y', (d) => projection([d.geo.longitude, d.geo.latitude])[1] + 5) diff --git a/ui/src/app/tiled-all/tiled-all.component.ts b/ui/src/app/tiled-all/tiled-all.component.ts index d600432..571324b 100644 --- a/ui/src/app/tiled-all/tiled-all.component.ts +++ b/ui/src/app/tiled-all/tiled-all.component.ts @@ -55,6 +55,7 @@ export class TiledAllComponent extends TargetWatcher implements OnInit { usage : AppUsage[]; devices : {[mac: string]:string}; impactChanges: Observable; + _last_load_time: Date; private impactObservers: Observer[] = []; constructor(focus: FocusService, connector: UsageConnectorService, private route: ActivatedRoute, private loader: LoaderService) { @@ -90,13 +91,14 @@ export class TiledAllComponent extends TargetWatcher implements OnInit { this_.devices = bundle.manDev; this_.triggerImpactsChange(); // this_.render(); + this_._last_load_time = new Date(); }); }, throttledReload = _.throttle(reload, 1000); this.loader.asyncAppImpactChanges().subscribe({ next(i: AppImpact[]) { - console.log('AppImpact CHANGE!', i.map(x => ''+[x.companyName, x.companyid, ''+x.impact].join('_')).join(' - ')) + // console.log('AppImpact CHANGE!', i.map(x => ''+[x.companyName, x.companyid, ''+x.impact].join('_')).join(' - ')) if (this_.impacts) { this_.impacts = this_.impacts.concat(i); this_.triggerImpactsChange(); @@ -112,6 +114,15 @@ export class TiledAllComponent extends TargetWatcher implements OnInit { if (this_.impacts) { throttledReload(); } } }); + + setInterval(() => { + let msec_since_reload = (new Date()).valueOf() - this_._last_load_time.valueOf(); + console.info('WATCHDOG checking ~~~~ ', msec_since_reload, ' msec since last reload'); + if (msec_since_reload > 1000*60) { + console.info('WATCHDOG forcing reload of impacts ~~~~ ', msec_since_reload, 'since last reload'); + reload(); + } + }, 10*1000); reload(); }