From 0820983d81d3317640b04072679858c1a54bcb9e Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Wed, 23 Jan 2019 17:11:23 +0300 Subject: [PATCH 01/88] go.mod -- update dnsproxy to v0.9.11 and it's dependencies --- go.mod | 8 ++++---- go.sum | 26 ++++++++++++-------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index 68a55326..bef5f146 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,13 @@ module github.com/AdguardTeam/AdGuardHome require ( - github.com/AdguardTeam/dnsproxy v0.9.10 + github.com/AdguardTeam/dnsproxy v0.9.11 github.com/StackExchange/wmi v0.0.0-20180725035823-b12b22c5341f // indirect github.com/bluele/gcache v0.0.0-20171010155617-472614239ac7 github.com/go-ole/go-ole v1.2.1 // indirect github.com/go-test/deep v1.0.1 github.com/gobuffalo/packr v1.19.0 - github.com/hmage/golibs v0.0.0-20181229160906-c8491df0bfc4 + github.com/hmage/golibs v0.0.0-20190121112702-20153bd03c24 github.com/joomcode/errorx v0.1.0 github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 // indirect github.com/kardianos/service v0.0.0-20181115005516-4c239ee84e7b @@ -17,8 +17,8 @@ require ( github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect github.com/stretchr/testify v1.2.2 go.uber.org/goleak v0.10.0 - golang.org/x/net v0.0.0-20181220203305-927f97764cc3 - golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb + golang.org/x/net v0.0.0-20190119204137-ed066c81e75e + golang.org/x/sys v0.0.0-20190122071731-054c452bb702 gopkg.in/asaskevich/govalidator.v4 v4.0.0-20160518190739-766470278477 gopkg.in/yaml.v2 v2.2.1 ) diff --git a/go.sum b/go.sum index 29de0319..c2a71787 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,13 @@ -github.com/AdguardTeam/dnsproxy v0.9.10 h1:q364WlTvC+CS8kJbMy7TCyt4Niqixxw584MQJtCGhJU= -github.com/AdguardTeam/dnsproxy v0.9.10/go.mod h1:IqBhopgNpzB168kMurbjXf86dn50geasBIuGVxY63j0= +github.com/AdguardTeam/dnsproxy v0.9.11 h1:1rsXJTHUqwxTLaGMUD2NrzHw2j88Nk9EhTX6EgmdVlM= +github.com/AdguardTeam/dnsproxy v0.9.11/go.mod h1:JAmwpTphfUBBw6TIOFGxL49/oSl+J97kaRW8viqXNe8= github.com/StackExchange/wmi v0.0.0-20180725035823-b12b22c5341f h1:5ZfJxyXo8KyX8DgGXC5B7ILL8y51fci/qYz2B4j8iLY= github.com/StackExchange/wmi v0.0.0-20180725035823-b12b22c5341f/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw= github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us= -github.com/ameshkov/dnscrypt v1.0.4 h1:vtwHm5m4R2dhcCx23wiI+gNBoy7qm4h7+kZ4Pucw/vE= -github.com/ameshkov/dnscrypt v1.0.4/go.mod h1:hVW52S6r0QvUpIwsyfZ1ifYYpfGu5pewD3pl7afMJcQ= +github.com/ameshkov/dnscrypt v1.0.6 h1:55wfnNF8c4E3JXDNlwPl2Pbs7UPPIh+kI6KK3THqYS0= +github.com/ameshkov/dnscrypt v1.0.6/go.mod h1:ZvT9LaNaJfDNXKIbkYFf24HUgHuQR6MNT6nwVvN4jMQ= github.com/ameshkov/dnsstamps v1.0.1 h1:LhGvgWDzhNJh+kBQd/AfUlq1vfVe109huiXw4JhnPug= github.com/ameshkov/dnsstamps v1.0.1/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A= github.com/beefsack/go-rate v0.0.0-20180408011153-efa7637bb9b6 h1:KXlsf+qt/X5ttPGEjR0tPH1xaWWoKBEg9Q1THAj2h3I= @@ -26,8 +26,8 @@ github.com/gobuffalo/packd v0.0.0-20181031195726-c82734870264 h1:roWyi0eEdiFreSq github.com/gobuffalo/packd v0.0.0-20181031195726-c82734870264/go.mod h1:Yf2toFaISlyQrr5TfO3h6DB9pl9mZRmyvBGQb/aQ/pI= github.com/gobuffalo/packr v1.19.0 h1:3UDmBDxesCOPF8iZdMDBBWKfkBoYujIMIZePnobqIUI= github.com/gobuffalo/packr v1.19.0/go.mod h1:MstrNkfCQhd5o+Ct4IJ0skWlxN8emOq8DsoT1G98VIU= -github.com/hmage/golibs v0.0.0-20181229160906-c8491df0bfc4 h1:FMAReGTEDNr4AdbScv/PqzjMQUpkkVHiF/t8sDHQQVQ= -github.com/hmage/golibs v0.0.0-20181229160906-c8491df0bfc4/go.mod h1:H6Ev6svFxUVPFThxLtdnFfcE9e3GWufpfmcVFpqV6HM= +github.com/hmage/golibs v0.0.0-20190121112702-20153bd03c24 h1:yyDtaSMcAZdm1I6uL8YLghpWiJljfBHs8NC/P86PYQk= +github.com/hmage/golibs v0.0.0-20190121112702-20153bd03c24/go.mod h1:H6Ev6svFxUVPFThxLtdnFfcE9e3GWufpfmcVFpqV6HM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -67,21 +67,19 @@ go.uber.org/goleak v0.10.0 h1:G3eWbSNIskeRqtsN/1uI5B+eP73y3JUuBsv9AZjehb4= go.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190122013713-64072686203f h1:u1CmMhe3a44hy8VIgpInORnI01UVaUYheqR7x9BxT3c= +golang.org/x/crypto v0.0.0-20190122013713-64072686203f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181213202711-891ebc4b82d6 h1:gT0Y6H7hbVPUtvtk0YGxMXPgN+p8fYlqWkgJeUCZcaQ= -golang.org/x/net v0.0.0-20181213202711-891ebc4b82d6/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190119204137-ed066c81e75e h1:MDa3fSUp6MdYHouVmCCNz/zaH2a6CRcxY3VhT/K3C5Q= +golang.org/x/net v0.0.0-20190119204137-ed066c81e75e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 h1:0oC8rFnE+74kEmuHZ46F6KHsMr5Gx2gUQPuNz28iQZM= -golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb h1:pf3XwC90UUdNPYWZdFjhGBE7DUFuK3Ct1zWmZ65QN30= -golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190122071731-054c452bb702 h1:Lk4tbZFnlyPgV+sLgTw5yGfzrlOn9kx4vSombi2FFlY= +golang.org/x/sys v0.0.0-20190122071731-054c452bb702/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= gopkg.in/asaskevich/govalidator.v4 v4.0.0-20160518190739-766470278477 h1:5xUJw+lg4zao9W4HIDzlFbMYgSgtvNVHh00MEHvbGpQ= gopkg.in/asaskevich/govalidator.v4 v4.0.0-20160518190739-766470278477/go.mod h1:QDV1vrFSrowdoOba0UM8VJPUZONT7dnfdLsM+GG53Z8= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= From 8725c1df7a95b74a8053aa6832b2b0d467ce8fbb Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Wed, 23 Jan 2019 17:26:15 +0300 Subject: [PATCH 02/88] Add stub OpenAPI methods --- config.go | 11 +++++++++++ control.go | 26 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/config.go b/config.go index 0e680a1b..6d1a723c 100644 --- a/config.go +++ b/config.go @@ -40,6 +40,7 @@ type configuration struct { Filters []filter `yaml:"filters"` UserRules []string `yaml:"user_rules"` DHCP dhcpd.ServerConfig `yaml:"dhcp"` + TLS tlsConfig `yaml:"tls"` logSettings `yaml:",inline"` @@ -60,6 +61,16 @@ type dnsConfig struct { var defaultDNS = []string{"tls://1.1.1.1", "tls://1.0.0.1"} +// field ordering is important -- yaml fields will mirror ordering from here +type tlsConfig struct { + ServerName string `yaml:"server_name" json:"server_name"` + ForceHTTPS bool `yaml:"force_https" json:"force_https"` + PortHTTPS int `yaml:"port_https" json:"port_https"` + PortDNSOverTLS int `yaml:"port_dns_over_tls" json:"port_dns_over_tls"` + CertificateChain string `yaml:"certificate_chain" json:"certificate_chain"` + PrivateKey string `yaml:"private_key" json:"private_key"` +} + // initialize to default values, will be changed later when reading config or parsing command line var config = configuration{ ourConfigFilename: "AdGuardHome.yaml", diff --git a/control.go b/control.go index 35373c2c..71bb3dd5 100644 --- a/control.go +++ b/control.go @@ -1025,6 +1025,29 @@ func handleInstallConfigure(w http.ResponseWriter, r *http.Request) { } } +// --- +// TLS +// --- +func handleTLSStatus(w http.ResponseWriter, r *http.Request) { + err := json.NewEncoder(w).Encode(&config.TLS) + if err != nil { + httpError(w, http.StatusInternalServerError, "Failed to marshal json with TLS status: %s", err) + return + } +} + +func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { + newconfig := tlsConfig{} + err := json.NewDecoder(r.body).Decode(&newconfig) + if err != nil { + httpError(w, http.StatusBadRequest, "Failed to parse new TLS config json: %s", err) + return + } + + // TODO: validate before applying + config.TLS = newconfig +} + func registerInstallHandlers() { http.HandleFunc("/control/install/get_addresses", preInstall(ensureGET(handleInstallGetAddresses))) http.HandleFunc("/control/install/configure", preInstall(ensurePOST(handleInstallConfigure))) @@ -1068,4 +1091,7 @@ func registerControlHandlers() { http.HandleFunc("/control/dhcp/interfaces", postInstall(optionalAuth(ensureGET(handleDHCPInterfaces)))) http.HandleFunc("/control/dhcp/set_config", postInstall(optionalAuth(ensurePOST(handleDHCPSetConfig)))) http.HandleFunc("/control/dhcp/find_active_dhcp", postInstall(optionalAuth(ensurePOST(handleDHCPFindActiveServer)))) + + http.HandleFunc("/control/tls/status", postInstall(optionalAuth(ensureGET(handleTLSStatus)))) + http.HandleFunc("/control/tls/configure", postInstall(optionalAuth(ensurePOST(handleTLSConfigure)))) } From 7451eb1346f4b185a506da7b72542cfc5f6e933a Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Thu, 24 Jan 2019 18:51:50 +0300 Subject: [PATCH 03/88] Initial components for encryption settings --- client/src/__locales/en.json | 22 ++ client/src/actions/index.js | 31 +++ client/src/api/Api.js | 18 ++ client/src/components/Settings/Dhcp/Form.js | 52 +---- .../src/components/Settings/Dhcp/Interface.js | 2 +- .../components/Settings/Encryption/Form.js | 195 ++++++++++++++++++ .../components/Settings/Encryption/index.js | 47 +++++ client/src/components/Settings/Settings.css | 24 ++- client/src/components/Settings/index.js | 6 + client/src/components/ui/Checkbox.css | 5 + client/src/containers/Settings.js | 18 +- client/src/helpers/form.js | 72 +++++++ client/src/reducers/index.js | 28 +++ control.go | 2 +- 14 files changed, 471 insertions(+), 51 deletions(-) create mode 100644 client/src/components/Settings/Encryption/Form.js create mode 100644 client/src/components/Settings/Encryption/index.js create mode 100644 client/src/helpers/form.js diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 2bae1d12..36bd3501 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -210,5 +210,27 @@ "next": "Next", "open_dashboard": "Open Dashboard", "install_saved": "All settings saved", + "encryption_title": "Encryption", + "encryption_desc": "Encryption (HTTPS/TLS) support for both DNS and admin web interface", + "encryption_config_saved": "Encryption config saved", + "encryption_server": "Server name", + "encryption_server_enter": "Enter your domain name", + "encryption_server_desc": "In order to use HTTPS, you need yo enter the server name that matches your SSL certificate.", + "encryption_redirect": "Redirect to HTTPS automatically", + "encryption_redirect_desc": "If checked, AdGuard Home will automatically redirect you from HTTP to HTTPS addresses.", + "encryption_https": "HTTPS port", + "encryption_https_desc": "If HTTPS port is configured, AdGuard Home admin interface will be accessible via HTTPS, and it will also provide DNS-over-HTTPS on '\\dns-query' location.", + "encryption_dot": "DNS-over-TLS port", + "encryption_dot_desc": "If this port is configured, AdGuard Home will run a DNS-over-TLS server on this port.", + "encryption_certificates": "Certificates", + "encryption_certificates_desc": "In order to use encryption, you need to provide a valid SSL certificates chain for your domain. You can get a free certificate on letsencrypt.org or you can buy it from one of the trusted Certificate Authorities.", + "encryption_certificates_input": "Copy/paste your PEM-encoded cerificates here.", + "encryption_status": "Status", + "encryption_certificates_for": "Certificates for {{domains}}", + "encryption_expire": "Expire on {{date}}", + "encryption_key": "Private key", + "encryption_key_input": "Copy/paste your PEM-encoded private key for your cerficate here.", + "form_error_port_range": "Enter port value in the range of 80-65535", + "form_error_equal": "Shouldn't be equal", "form_error_password": "Password mismatched" } \ No newline at end of file diff --git a/client/src/actions/index.js b/client/src/actions/index.js index 1bb99064..da33f0fe 100644 --- a/client/src/actions/index.js +++ b/client/src/actions/index.js @@ -650,3 +650,34 @@ export const toggleDhcp = config => async (dispatch) => { } } }; + +export const getTlsStatusRequest = createAction('GET_TLS_STATUS_REQUEST'); +export const getTlsStatusFailure = createAction('GET_TLS_STATUS_FAILURE'); +export const getTlsStatusSuccess = createAction('GET_TLS_STATUS_SUCCESS'); + +export const getTlsStatus = () => async (dispatch) => { + dispatch(getTlsStatusRequest()); + try { + const status = await apiClient.getTlsStatus(); + dispatch(getTlsStatusSuccess(status)); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(getTlsStatusFailure()); + } +}; + +export const setTlsConfigRequest = createAction('SET_TLS_CONFIG_REQUEST'); +export const setTlsConfigFailure = createAction('SET_TLS_CONFIG_FAILURE'); +export const setTlsConfigSuccess = createAction('SET_TLS_CONFIG_SUCCESS'); + +export const setTlsConfig = config => async (dispatch) => { + dispatch(setTlsConfigRequest()); + try { + await apiClient.setTlsConfig(config); + dispatch(setTlsConfigSuccess(config)); + dispatch(addSuccessToast('encryption_config_saved')); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(setTlsConfigFailure()); + } +}; diff --git a/client/src/api/Api.js b/client/src/api/Api.js index 0dac781a..1971fe4a 100644 --- a/client/src/api/Api.js +++ b/client/src/api/Api.js @@ -354,4 +354,22 @@ export default class Api { }; return this.makeRequest(path, method, parameters); } + + // DNS-over-HTTPS and DNS-over-TLS + TLS_STATUS = { path: 'tls/status', method: 'GET' }; + TLS_CONFIG = { path: 'tls/configure', method: 'POST' }; + + getTlsStatus() { + const { path, method } = this.TLS_STATUS; + return this.makeRequest(path, method); + } + + setTlsConfig(config) { + const { path, method } = this.TLS_CONFIG; + const parameters = { + data: config, + headers: { 'Content-Type': 'application/json' }, + }; + return this.makeRequest(path, method, parameters); + } } diff --git a/client/src/components/Settings/Dhcp/Form.js b/client/src/components/Settings/Dhcp/Form.js index 5b810c7b..af7c1931 100644 --- a/client/src/components/Settings/Dhcp/Form.js +++ b/client/src/components/Settings/Dhcp/Form.js @@ -1,48 +1,10 @@ -import React, { Fragment } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import { Field, reduxForm } from 'redux-form'; -import { withNamespaces, Trans } from 'react-i18next'; +import { withNamespaces } from 'react-i18next'; import flow from 'lodash/flow'; -import { R_IPV4 } from '../../../helpers/constants'; - -const required = (value) => { - if (value || value === 0) { - return false; - } - return form_error_required; -}; - -const ipv4 = (value) => { - if (value && !new RegExp(R_IPV4).test(value)) { - return form_error_ip_format; - } - return false; -}; - -const isPositive = (value) => { - if ((value || value === 0) && (value <= 0)) { - return form_error_positive; - } - return false; -}; - -const toNumber = value => value && parseInt(value, 10); - -const renderField = ({ - input, className, placeholder, type, disabled, meta: { touched, error }, -}) => ( - - - {!disabled && touched && (error && {error})} - -); +import { renderField, required, ipv4, isPositive, toNumber } from '../../../helpers/form'; const Form = (props) => { const { @@ -57,7 +19,7 @@ const Form = (props) => {
-
+
{ validate={[ipv4, required]} />
-
+
{
-
+
@@ -108,7 +70,7 @@ const Form = (props) => {
-
+
{ {!processing && interfaces &&
-
+
{ + const errors = {}; + + if (values.port_dns_over_tls === values.port_https) { + errors.port_dns_over_tls = i18n.t('form_error_equal'); + errors.port_https = i18n.t('form_error_equal'); + } + + return errors; +}; + +const Form = (props) => { + const { + t, + handleSubmit, + invalid, + submitting, + processing, + } = props; + + return ( + +
+
+ +
+
+
+ +
+ encryption_server_desc +
+
+
+
+
+ +
+ encryption_redirect_desc +
+
+
+
+
+
+
+ + +
+ encryption_https_desc +
+
+
+
+
+ + +
+ encryption_dot_desc +
+
+
+
+
+
+
+ +
+ encryption_certificates_desc +
+ +
+
+ encryption_status: +
+
+ encryption_certificates_for + *.example.org, example.org +
+
+ encryption_expire + 2022-01-01 +
+
+
+
+
+
+
+
+ + +
+
+ encryption_status: +
+
Valid RSA private key
+
+
+
+
+ + + + ); +}; + +Form.propTypes = { + handleSubmit: PropTypes.func.isRequired, + submitting: PropTypes.bool.isRequired, + invalid: PropTypes.bool.isRequired, + initialValues: PropTypes.object.isRequired, + processing: PropTypes.bool.isRequired, + t: PropTypes.func.isRequired, +}; + +export default flow([ + withNamespaces(), + reduxForm({ + form: 'encryptionForm', + validate, + }), +])(Form); diff --git a/client/src/components/Settings/Encryption/index.js b/client/src/components/Settings/Encryption/index.js new file mode 100644 index 00000000..b4f876a7 --- /dev/null +++ b/client/src/components/Settings/Encryption/index.js @@ -0,0 +1,47 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { withNamespaces } from 'react-i18next'; + +import Form from './Form'; +import Card from '../../ui/Card'; + +class Encryption extends Component { + handleFormSubmit = (values) => { + this.props.setTlsConfig(values); + }; + + render() { + const { encryption, t } = this.props; + const { + processing, + processingConfig, + ...values + } = encryption; + + return ( +
+ {encryption && !encryption.processing && + +
+ + } +
+ ); + } +} + +Encryption.propTypes = { + setTlsConfig: PropTypes.func.isRequired, + encryption: PropTypes.object.isRequired, + t: PropTypes.func.isRequired, +}; + +export default withNamespaces()(Encryption); diff --git a/client/src/components/Settings/Settings.css b/client/src/components/Settings/Settings.css index 9530ef36..b364be6e 100644 --- a/client/src/components/Settings/Settings.css +++ b/client/src/components/Settings/Settings.css @@ -7,8 +7,8 @@ margin-bottom: 0; } -.form__group--dhcp:last-child { - margin-bottom: 15px; +.form__group--settings:last-child { + margin-bottom: 20px; } .btn-standard { @@ -48,3 +48,23 @@ .dhcp { min-height: 450px; } + +.form__desc { + margin-top: 10px; + font-size: 13px; + color: rgba(74, 74, 74, 0.7); +} + +.form__desc--top { + margin: 0 0 8px; +} + +.form__label--bold { + font-weight: 700; +} + +.form__status { + margin-top: 10px; + font-size: 14px; + line-height: 1.7; +} diff --git a/client/src/components/Settings/index.js b/client/src/components/Settings/index.js index 24e56329..d3d0706b 100644 --- a/client/src/components/Settings/index.js +++ b/client/src/components/Settings/index.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { withNamespaces, Trans } from 'react-i18next'; import Upstream from './Upstream'; import Dhcp from './Dhcp'; +import Encryption from './Encryption'; import Checkbox from '../ui/Checkbox'; import Loading from '../ui/Loading'; import PageTitle from '../ui/PageTitle'; @@ -37,6 +38,7 @@ class Settings extends Component { this.props.initSettings(this.settings); this.props.getDhcpStatus(); this.props.getDhcpInterfaces(); + this.props.getTlsStatus(); } handleUpstreamChange = (value) => { @@ -95,6 +97,10 @@ class Settings extends Component { handleUpstreamSubmit={this.handleUpstreamSubmit} handleUpstreamTest={this.handleUpstreamTest} /> + { - const { settings, dashboard, dhcp } = state; - const props = { settings, dashboard, dhcp }; + const { + settings, + dashboard, + dhcp, + encryption, + } = state; + const props = { + settings, + dashboard, + dhcp, + encryption, + }; return props; }; @@ -32,6 +44,8 @@ const mapDispatchToProps = { getDhcpInterfaces, setDhcpConfig, findActiveDhcp, + getTlsStatus, + setTlsConfig, }; export default connect( diff --git a/client/src/helpers/form.js b/client/src/helpers/form.js new file mode 100644 index 00000000..055d1138 --- /dev/null +++ b/client/src/helpers/form.js @@ -0,0 +1,72 @@ +import React, { Fragment } from 'react'; +import { Trans } from 'react-i18next'; + +import { R_IPV4 } from '../helpers/constants'; + +export const renderField = ({ + input, id, className, placeholder, type, disabled, meta: { touched, error }, +}) => ( + + + {!disabled && touched && (error && {error})} + +); + +export const renderSelectField = ({ + input, placeholder, disabled, meta: { touched, error }, +}) => ( + + + {!disabled && touched && (error && {error})} + +); + +export const required = (value) => { + if (value || value === 0) { + return false; + } + return form_error_required; +}; + +export const ipv4 = (value) => { + if (value && !new RegExp(R_IPV4).test(value)) { + return form_error_ip_format; + } + return false; +}; + +export const isPositive = (value) => { + if ((value || value === 0) && (value <= 0)) { + return form_error_positive; + } + return false; +}; + +export const port = (value) => { + if (value < 80 || value > 65535) { + return form_error_port_range; + } + return false; +}; + +export const toNumber = value => value && parseInt(value, 10); diff --git a/client/src/reducers/index.js b/client/src/reducers/index.js index 19bbbf63..b80f7f37 100644 --- a/client/src/reducers/index.js +++ b/client/src/reducers/index.js @@ -302,6 +302,33 @@ const dhcp = handleActions({ leases: [], }); +const encryption = handleActions({ + [actions.getTlsStatusRequest]: state => ({ ...state, processing: true }), + [actions.getTlsStatusFailure]: state => ({ ...state, processing: false }), + [actions.getTlsStatusSuccess]: (state, { payload }) => { + const newState = { + ...state, + ...payload, + processing: false, + }; + return newState; + }, + + [actions.setTlsConfigRequest]: state => ({ ...state, processingConfig: true }), + [actions.setTlsConfigFailure]: state => ({ ...state, processingConfig: false }), + [actions.setTlsConfigSuccess]: (state, { payload }) => { + const newState = { + ...state, + ...payload, + processingConfig: false, + }; + return newState; + }, +}, { + processing: true, + processingConfig: false, +}); + export default combineReducers({ settings, dashboard, @@ -309,6 +336,7 @@ export default combineReducers({ filtering, toasts, dhcp, + encryption, loadingBar: loadingBarReducer, form: formReducer, }); diff --git a/control.go b/control.go index 71bb3dd5..7199ff11 100644 --- a/control.go +++ b/control.go @@ -1038,7 +1038,7 @@ func handleTLSStatus(w http.ResponseWriter, r *http.Request) { func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { newconfig := tlsConfig{} - err := json.NewDecoder(r.body).Decode(&newconfig) + err := json.NewDecoder(r.Body).Decode(&newconfig) if err != nil { httpError(w, http.StatusBadRequest, "Failed to parse new TLS config json: %s", err) return From ab11c912db1af9a5b714d012851391630701943c Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Fri, 25 Jan 2019 15:18:05 +0300 Subject: [PATCH 04/88] Added topline component and fixed string interpolation --- client/src/__locales/en.json | 3 ++- client/src/actions/index.js | 9 +++++++- client/src/components/App/index.js | 21 +++++++++++++------ .../components/Settings/Encryption/Form.js | 17 ++++++++++----- .../components/ui/{Update.css => Topline.css} | 2 +- client/src/components/ui/Topline.js | 19 +++++++++++++++++ client/src/components/ui/Update.js | 19 ----------------- client/src/containers/App.js | 4 ++-- 8 files changed, 59 insertions(+), 35 deletions(-) rename client/src/components/ui/{Update.css => Topline.css} (89%) create mode 100644 client/src/components/ui/Topline.js delete mode 100644 client/src/components/ui/Update.js diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 36bd3501..25053476 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -223,13 +223,14 @@ "encryption_dot": "DNS-over-TLS port", "encryption_dot_desc": "If this port is configured, AdGuard Home will run a DNS-over-TLS server on this port.", "encryption_certificates": "Certificates", - "encryption_certificates_desc": "In order to use encryption, you need to provide a valid SSL certificates chain for your domain. You can get a free certificate on letsencrypt.org or you can buy it from one of the trusted Certificate Authorities.", + "encryption_certificates_desc": "In order to use encryption, you need to provide a valid SSL certificates chain for your domain. You can get a free certificate on <0>{{link}} or you can buy it from one of the trusted Certificate Authorities.", "encryption_certificates_input": "Copy/paste your PEM-encoded cerificates here.", "encryption_status": "Status", "encryption_certificates_for": "Certificates for {{domains}}", "encryption_expire": "Expire on {{date}}", "encryption_key": "Private key", "encryption_key_input": "Copy/paste your PEM-encoded private key for your cerficate here.", + "topline_expiring_certificate": "Your SSL certificate is about to expire. Update <0>Encryption settings.", "form_error_port_range": "Enter port value in the range of 80-65535", "form_error_equal": "Shouldn't be equal", "form_error_password": "Password mismatched" diff --git a/client/src/actions/index.js b/client/src/actions/index.js index da33f0fe..30527245 100644 --- a/client/src/actions/index.js +++ b/client/src/actions/index.js @@ -659,6 +659,9 @@ export const getTlsStatus = () => async (dispatch) => { dispatch(getTlsStatusRequest()); try { const status = await apiClient.getTlsStatus(); + status.certificate_chain = decodeURIComponent(status.certificate_chain); + status.private_key = decodeURIComponent(status.private_key); + dispatch(getTlsStatusSuccess(status)); } catch (error) { dispatch(addErrorToast({ error })); @@ -673,7 +676,11 @@ export const setTlsConfigSuccess = createAction('SET_TLS_CONFIG_SUCCESS'); export const setTlsConfig = config => async (dispatch) => { dispatch(setTlsConfigRequest()); try { - await apiClient.setTlsConfig(config); + const values = { ...config }; + values.certificate_chain = encodeURIComponent(values.certificate_chain); + values.private_key = encodeURIComponent(values.private_key); + + await apiClient.setTlsConfig(values); dispatch(setTlsConfigSuccess(config)); dispatch(addSuccessToast('encryption_config_saved')); } catch (error) { diff --git a/client/src/components/App/index.js b/client/src/components/App/index.js index 74bc6960..5bf0f925 100644 --- a/client/src/components/App/index.js +++ b/client/src/components/App/index.js @@ -1,6 +1,7 @@ import React, { Component, Fragment } from 'react'; import { HashRouter, Route } from 'react-router-dom'; import PropTypes from 'prop-types'; +import { Trans, withNamespaces } from 'react-i18next'; import LoadingBar from 'react-redux-loading-bar'; import 'react-table/react-table.css'; @@ -16,7 +17,7 @@ import Logs from '../../containers/Logs'; import Footer from '../ui/Footer'; import Toasts from '../Toasts'; import Status from '../ui/Status'; -import Update from '../ui/Update'; +import Topline from '../ui/Topline'; import i18n from '../../i18n'; class App extends Component { @@ -55,15 +56,22 @@ class App extends Component { !dashboard.processingVersions && dashboard.isCoreRunning && dashboard.isUpdateAvailable; + const isExpiringCertificate = false; return ( {updateAvailable && - + + {dashboard.announcement} Click here for more info. + + } + {isExpiringCertificate && + + link]}> + topline_expiring_certificate + + } @@ -100,6 +108,7 @@ App.propTypes = { error: PropTypes.string, getVersion: PropTypes.func, changeLanguage: PropTypes.func, + encryption: PropTypes.object, }; -export default App; +export default withNamespaces()(App); diff --git a/client/src/components/Settings/Encryption/Form.js b/client/src/components/Settings/Encryption/Form.js index 3c814fad..e58aa8f4 100644 --- a/client/src/components/Settings/Encryption/Form.js +++ b/client/src/components/Settings/Encryption/Form.js @@ -114,7 +114,12 @@ const Form = (props) => { encryption_certificates
- encryption_certificates_desc + link]} + > + encryption_certificates_desc +
{ encryption_status:
- encryption_certificates_for - *.example.org, example.org + + encryption_certificates_for +
- encryption_expire - 2022-01-01 + + encryption_expire +
diff --git a/client/src/components/ui/Update.css b/client/src/components/ui/Topline.css similarity index 89% rename from client/src/components/ui/Update.css rename to client/src/components/ui/Topline.css index ec7ec532..33c4e8fd 100644 --- a/client/src/components/ui/Update.css +++ b/client/src/components/ui/Topline.css @@ -1,4 +1,4 @@ -.update { +.topline { position: relative; z-index: 102; margin-bottom: 0; diff --git a/client/src/components/ui/Topline.js b/client/src/components/ui/Topline.js new file mode 100644 index 00000000..13bfd827 --- /dev/null +++ b/client/src/components/ui/Topline.js @@ -0,0 +1,19 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import './Topline.css'; + +const Topline = props => ( +
+
+ {props.children} +
+
+); + +Topline.propTypes = { + children: PropTypes.node.isRequired, + type: PropTypes.string.isRequired, +}; + +export default Topline; diff --git a/client/src/components/ui/Update.js b/client/src/components/ui/Update.js deleted file mode 100644 index 5df9df65..00000000 --- a/client/src/components/ui/Update.js +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import './Update.css'; - -const Update = props => ( -
-
- {props.announcement} Click here for more info. -
-
-); - -Update.propTypes = { - announcement: PropTypes.string.isRequired, - announcementUrl: PropTypes.string.isRequired, -}; - -export default Update; diff --git a/client/src/containers/App.js b/client/src/containers/App.js index 905596c5..b6ce2cde 100644 --- a/client/src/containers/App.js +++ b/client/src/containers/App.js @@ -3,8 +3,8 @@ import * as actionCreators from '../actions'; import App from '../components/App'; const mapStateToProps = (state) => { - const { dashboard } = state; - const props = { dashboard }; + const { dashboard, encryption } = state; + const props = { dashboard, encryption }; return props; }; From 38869b22a62be57dc807408e4cbef43fbbf80089 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Wed, 30 Jan 2019 15:01:00 +0300 Subject: [PATCH 05/88] tls/status -- make stubs add warning and status randomly --- control.go | 18 +++++++++++++++++- helpers.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/control.go b/control.go index 7199ff11..c6e518ea 100644 --- a/control.go +++ b/control.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "math/rand" "net" "net/http" "os" @@ -1029,7 +1030,22 @@ func handleInstallConfigure(w http.ResponseWriter, r *http.Request) { // TLS // --- func handleTLSStatus(w http.ResponseWriter, r *http.Request) { - err := json.NewEncoder(w).Encode(&config.TLS) + data := struct { + tlsConfig `json:",inline"` + + // only for API, no need to be stored in config + Status string `yaml:"-" json:"status,omitempty"` + Warning string `yaml:"-" json:"warning,omitempty"` + }{ + tlsConfig: config.TLS, + } + if rand.Intn(2) == 0 { + data.Status = fmt.Sprintf("Random status #%s", RandStringBytesMaskImpr(6)) + } + if rand.Intn(2) == 0 { + data.Warning = fmt.Sprintf("Random warning #%s", RandStringBytesMaskImpr(6)) + } + err := json.NewEncoder(w).Encode(&data) if err != nil { httpError(w, http.StatusInternalServerError, "Failed to marshal json with TLS status: %s", err) return diff --git a/helpers.go b/helpers.go index a0cf1fd7..14116ab6 100644 --- a/helpers.go +++ b/helpers.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "io/ioutil" + "math/rand" "net" "net/http" "os" @@ -235,6 +236,34 @@ func checkPacketPortAvailable(host string, port int) error { return err } +// ------------------------ +// random string generation +// ------------------------ +const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +const ( + letterIdxBits = 6 // 6 bits to represent a letter index + letterIdxMask = 1<= 0; { + if remain == 0 { + cache, remain = rand.Int63(), letterIdxMax + } + if idx := int(cache & letterIdxMask); idx < len(letterBytes) { + b[i] = letterBytes[idx] + i-- + } + cache >>= letterIdxBits + remain-- + } + + return string(b) +} + // --------------------- // debug logging helpers // --------------------- From c5b1105fc1b6f13a0d155d3bfe4c7d5a1348d765 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Wed, 30 Jan 2019 15:20:58 +0300 Subject: [PATCH 06/88] /tls/status -- Expand random stubs for separate statuses of certificate and key --- control.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/control.go b/control.go index c6e518ea..627007d7 100644 --- a/control.go +++ b/control.go @@ -1034,13 +1034,17 @@ func handleTLSStatus(w http.ResponseWriter, r *http.Request) { tlsConfig `json:",inline"` // only for API, no need to be stored in config - Status string `yaml:"-" json:"status,omitempty"` - Warning string `yaml:"-" json:"warning,omitempty"` + StatusCertificate string `yaml:"-" json:"status_cert,omitempty"` + StatusKey string `yaml:"-" json:"status_key,omitempty"` + Warning string `yaml:"-" json:"warning,omitempty"` }{ tlsConfig: config.TLS, } if rand.Intn(2) == 0 { - data.Status = fmt.Sprintf("Random status #%s", RandStringBytesMaskImpr(6)) + data.StatusCertificate = fmt.Sprintf("Random certificate status #%s", RandStringBytesMaskImpr(6)) + } + if rand.Intn(2) == 0 { + data.StatusKey = fmt.Sprintf("Random key status #%s", RandStringBytesMaskImpr(6)) } if rand.Intn(2) == 0 { data.Warning = fmt.Sprintf("Random warning #%s", RandStringBytesMaskImpr(6)) From c025c845d200be5e6e5a08f86c6512b7740f876e Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Wed, 30 Jan 2019 16:24:17 +0300 Subject: [PATCH 07/88] Show random status and warning --- client/src/components/App/index.js | 4 +- .../components/Settings/Encryption/Form.js | 37 ++++++++++++++----- .../components/Settings/Encryption/index.js | 4 ++ 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/client/src/components/App/index.js b/client/src/components/App/index.js index 5bf0f925..a756eadc 100644 --- a/client/src/components/App/index.js +++ b/client/src/components/App/index.js @@ -51,12 +51,12 @@ class App extends Component { } render() { - const { dashboard } = this.props; + const { dashboard, encryption } = this.props; const updateAvailable = !dashboard.processingVersions && dashboard.isCoreRunning && dashboard.isUpdateAvailable; - const isExpiringCertificate = false; + const isExpiringCertificate = !encryption.processing && encryption.warning; return ( diff --git a/client/src/components/Settings/Encryption/Form.js b/client/src/components/Settings/Encryption/Form.js index e58aa8f4..4e85ea5a 100644 --- a/client/src/components/Settings/Encryption/Form.js +++ b/client/src/components/Settings/Encryption/Form.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import { Field, reduxForm } from 'redux-form'; import { Trans, withNamespaces } from 'react-i18next'; @@ -25,6 +25,8 @@ const Form = (props) => { invalid, submitting, processing, + statusCert, + statusKey, } = props; return ( @@ -131,10 +133,17 @@ const Form = (props) => { validate={[required]} />
-
- encryption_status: -
-
+ {statusCert && + +
+ encryption_status: +
+
+ {statusCert} +
+
+ } + {/*
encryption_certificates_for @@ -143,7 +152,7 @@ const Form = (props) => { encryption_expire -
+
*/}
@@ -164,10 +173,16 @@ const Form = (props) => { validate={[required]} />
-
- encryption_status: -
-
Valid RSA private key
+ {statusKey && + +
+ encryption_status: +
+
+ {statusKey} +
+
+ }
@@ -190,6 +205,8 @@ Form.propTypes = { invalid: PropTypes.bool.isRequired, initialValues: PropTypes.object.isRequired, processing: PropTypes.bool.isRequired, + statusCert: PropTypes.string, + statusKey: PropTypes.string, t: PropTypes.func.isRequired, }; diff --git a/client/src/components/Settings/Encryption/index.js b/client/src/components/Settings/Encryption/index.js index b4f876a7..e554cc6f 100644 --- a/client/src/components/Settings/Encryption/index.js +++ b/client/src/components/Settings/Encryption/index.js @@ -15,6 +15,8 @@ class Encryption extends Component { const { processing, processingConfig, + status_cert: statusCert, + status_key: statusKey, ...values } = encryption; @@ -29,6 +31,8 @@ class Encryption extends Component { From c19416bf8e836ebba204d0e6def424c9bacb645c Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Wed, 30 Jan 2019 19:17:30 +0300 Subject: [PATCH 08/88] Move up tls block in config, don't send json with zero values --- config.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/config.go b/config.go index 6d1a723c..4a35502b 100644 --- a/config.go +++ b/config.go @@ -37,10 +37,10 @@ type configuration struct { AuthPass string `yaml:"auth_pass"` Language string `yaml:"language"` // two-letter ISO 639-1 language code DNS dnsConfig `yaml:"dns"` + TLS tlsConfig `yaml:"tls"` Filters []filter `yaml:"filters"` UserRules []string `yaml:"user_rules"` DHCP dhcpd.ServerConfig `yaml:"dhcp"` - TLS tlsConfig `yaml:"tls"` logSettings `yaml:",inline"` @@ -63,10 +63,10 @@ var defaultDNS = []string{"tls://1.1.1.1", "tls://1.0.0.1"} // field ordering is important -- yaml fields will mirror ordering from here type tlsConfig struct { - ServerName string `yaml:"server_name" json:"server_name"` - ForceHTTPS bool `yaml:"force_https" json:"force_https"` - PortHTTPS int `yaml:"port_https" json:"port_https"` - PortDNSOverTLS int `yaml:"port_dns_over_tls" json:"port_dns_over_tls"` + ServerName string `yaml:"server_name" json:"server_name,omitempty"` + ForceHTTPS bool `yaml:"force_https" json:"force_https,omitempty"` + PortHTTPS int `yaml:"port_https" json:"port_https,omitempty"` + PortDNSOverTLS int `yaml:"port_dns_over_tls" json:"port_dns_over_tls,omitempty"` CertificateChain string `yaml:"certificate_chain" json:"certificate_chain"` PrivateKey string `yaml:"private_key" json:"private_key"` } From 38983097785c3d7f37c99cc2560512c4b342a201 Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Wed, 30 Jan 2019 19:46:26 +0300 Subject: [PATCH 09/88] Request tls status after save --- client/src/actions/index.js | 1 + client/src/components/Settings/Encryption/index.js | 2 +- client/src/reducers/index.js | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/client/src/actions/index.js b/client/src/actions/index.js index 30527245..be4e4d7e 100644 --- a/client/src/actions/index.js +++ b/client/src/actions/index.js @@ -683,6 +683,7 @@ export const setTlsConfig = config => async (dispatch) => { await apiClient.setTlsConfig(values); dispatch(setTlsConfigSuccess(config)); dispatch(addSuccessToast('encryption_config_saved')); + dispatch(getTlsStatus()); } catch (error) { dispatch(addErrorToast({ error })); dispatch(setTlsConfigFailure()); diff --git a/client/src/components/Settings/Encryption/index.js b/client/src/components/Settings/Encryption/index.js index e554cc6f..0b67dfe9 100644 --- a/client/src/components/Settings/Encryption/index.js +++ b/client/src/components/Settings/Encryption/index.js @@ -22,7 +22,7 @@ class Encryption extends Component { return (
- {encryption && !encryption.processing && + {encryption && Date: Wed, 30 Jan 2019 21:09:29 +0300 Subject: [PATCH 10/88] tls/configure -- Backend implementation of parsing user certs --- config.go | 5 +++ control.go | 117 ++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 98 insertions(+), 24 deletions(-) diff --git a/config.go b/config.go index 4a35502b..df87d166 100644 --- a/config.go +++ b/config.go @@ -69,6 +69,11 @@ type tlsConfig struct { PortDNSOverTLS int `yaml:"port_dns_over_tls" json:"port_dns_over_tls,omitempty"` CertificateChain string `yaml:"certificate_chain" json:"certificate_chain"` PrivateKey string `yaml:"private_key" json:"private_key"` + + // only for API, no need to be stored in config + StatusCertificate string `yaml:"status_cert" json:"status_cert,omitempty"` + StatusKey string `yaml:"status_key" json:"status_key,omitempty"` + Warning string `yaml:"warning" json:"warning,omitempty"` } // initialize to default values, will be changed later when reading config or parsing command line diff --git a/control.go b/control.go index 627007d7..a7102373 100644 --- a/control.go +++ b/control.go @@ -3,10 +3,12 @@ package main import ( "bytes" "context" + "crypto/tls" + "crypto/x509" "encoding/json" + "encoding/pem" "fmt" "io/ioutil" - "math/rand" "net" "net/http" "os" @@ -1030,25 +1032,7 @@ func handleInstallConfigure(w http.ResponseWriter, r *http.Request) { // TLS // --- func handleTLSStatus(w http.ResponseWriter, r *http.Request) { - data := struct { - tlsConfig `json:",inline"` - - // only for API, no need to be stored in config - StatusCertificate string `yaml:"-" json:"status_cert,omitempty"` - StatusKey string `yaml:"-" json:"status_key,omitempty"` - Warning string `yaml:"-" json:"warning,omitempty"` - }{ - tlsConfig: config.TLS, - } - if rand.Intn(2) == 0 { - data.StatusCertificate = fmt.Sprintf("Random certificate status #%s", RandStringBytesMaskImpr(6)) - } - if rand.Intn(2) == 0 { - data.StatusKey = fmt.Sprintf("Random key status #%s", RandStringBytesMaskImpr(6)) - } - if rand.Intn(2) == 0 { - data.Warning = fmt.Sprintf("Random warning #%s", RandStringBytesMaskImpr(6)) - } + data := config.TLS err := json.NewEncoder(w).Encode(&data) if err != nil { httpError(w, http.StatusInternalServerError, "Failed to marshal json with TLS status: %s", err) @@ -1057,15 +1041,100 @@ func handleTLSStatus(w http.ResponseWriter, r *http.Request) { } func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { - newconfig := tlsConfig{} - err := json.NewDecoder(r.Body).Decode(&newconfig) + data := tlsConfig{} + err := json.NewDecoder(r.Body).Decode(&data) if err != nil { httpError(w, http.StatusBadRequest, "Failed to parse new TLS config json: %s", err) return } - // TODO: validate before applying - config.TLS = newconfig + _, err = tls.X509KeyPair([]byte(data.CertificateChain), []byte(data.PrivateKey)) + if err != nil { + httpError(w, http.StatusBadRequest, "Invalid certificate or key: %s", err) + return + } + + // now do a more extended validation + var certs []*pem.Block // PEM-encoded certificates + var skippedBytes []string // skipped bytes + + pemblock := []byte(data.CertificateChain) + for { + var decoded *pem.Block + decoded, pemblock = pem.Decode(pemblock) + if decoded == nil { + break + } + if decoded.Type == "CERTIFICATE" { + certs = append(certs, decoded) + } else { + skippedBytes = append(skippedBytes, decoded.Type) + } + } + + var parsedCerts []*x509.Certificate + + for _, cert := range certs { + parsed, err := x509.ParseCertificate(cert.Bytes) + if err != nil { + httpError(w, http.StatusBadRequest, "failed to parse certificate: %s", err) + return + } + parsedCerts = append(parsedCerts, parsed) + } + + if len(parsedCerts) == 0 { + httpError(w, http.StatusBadRequest, "You have specified an empty certificate") + return + } + + // spew.Dump(parsedCerts) + + opts := x509.VerifyOptions{ + DNSName: data.ServerName, + } + + log.Printf("number of certs - %d", len(parsedCerts)) + if len(parsedCerts) > 1 { + // set up an intermediate + pool := x509.NewCertPool() + for _, cert := range parsedCerts[1:] { + log.Printf("got an intermediate cert") + pool.AddCert(cert) + } + opts.Intermediates = pool + } + + // TODO: save it as a warning rather than error it out -- shouldn't be a big problem + mainCert := parsedCerts[0] + chains, err := mainCert.Verify(opts) + if err != nil { + httpError(w, http.StatusBadRequest, "Your certificate does not verify: %s", err) + return + } + // spew.Dump(chains) + + config.TLS = data + + // update status + config.TLS.StatusCertificate = fmt.Sprintf("Certificate expires on %s", mainCert.NotAfter) //, valid for hostname %s", mainCert.NotAfter, mainCert.Subject.CommonName) + if len(mainCert.DNSNames) == 1 { + config.TLS.StatusCertificate += fmt.Sprintf(", valid for hostname %s", mainCert.DNSNames[0]) + } else if len(mainCert.DNSNames) > 1 { + config.TLS.StatusCertificate += ", valid for hostnames " + strings.Join(mainCert.DNSNames, ", ") + } + + // issue a warning if certificate is about to expire + now := time.Now() + if mainCert.NotAfter.AddDate(0, 0, -30).After(now) { + timeLeft := time.Until(mainCert.NotAfter) + if timeLeft > 0 { + config.TLS.Warning = fmt.Sprintf("Your certificate expires in %.0f days, we recommend you update it soon", timeLeft.Hours()/24) + } else { + config.TLS.Warning = fmt.Sprintf("Your certificate has expired on %s, we recommend you update it immediatedly", mainCert.NotAfter) + } + } + httpUpdateConfigReloadDNSReturnOK(w, r) } func registerInstallHandlers() { From 4da55dc2aaaf9c39d97ef076fc3b88327383c907 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Wed, 30 Jan 2019 21:38:01 +0300 Subject: [PATCH 11/88] Fixup of previous commit -- fix build failure --- control.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control.go b/control.go index a7102373..4598c859 100644 --- a/control.go +++ b/control.go @@ -1107,7 +1107,7 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { // TODO: save it as a warning rather than error it out -- shouldn't be a big problem mainCert := parsedCerts[0] - chains, err := mainCert.Verify(opts) + _, err = mainCert.Verify(opts) if err != nil { httpError(w, http.StatusBadRequest, "Your certificate does not verify: %s", err) return From 93847bd309c5556ae05a961c41944594eeb03cea Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Fri, 1 Feb 2019 13:57:39 +0300 Subject: [PATCH 12/88] Convert certificate and key to base64 --- client/src/actions/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/actions/index.js b/client/src/actions/index.js index be4e4d7e..be3a8c65 100644 --- a/client/src/actions/index.js +++ b/client/src/actions/index.js @@ -659,8 +659,8 @@ export const getTlsStatus = () => async (dispatch) => { dispatch(getTlsStatusRequest()); try { const status = await apiClient.getTlsStatus(); - status.certificate_chain = decodeURIComponent(status.certificate_chain); - status.private_key = decodeURIComponent(status.private_key); + status.certificate_chain = atob(status.certificate_chain); + status.private_key = atob(status.private_key); dispatch(getTlsStatusSuccess(status)); } catch (error) { @@ -677,8 +677,8 @@ export const setTlsConfig = config => async (dispatch) => { dispatch(setTlsConfigRequest()); try { const values = { ...config }; - values.certificate_chain = encodeURIComponent(values.certificate_chain); - values.private_key = encodeURIComponent(values.private_key); + values.certificate_chain = btoa(values.certificate_chain); + values.private_key = btoa(values.private_key); await apiClient.setTlsConfig(values); dispatch(setTlsConfigSuccess(config)); From d42718465da849a76569332fc2fe97d2fc47e5cf Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Fri, 1 Feb 2019 14:10:39 +0300 Subject: [PATCH 13/88] /tls/configure -- certificates/keys are now transferred encoded with base64 --- control.go | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/control.go b/control.go index 4598c859..ca699f66 100644 --- a/control.go +++ b/control.go @@ -5,6 +5,7 @@ import ( "context" "crypto/tls" "crypto/x509" + "encoding/base64" "encoding/json" "encoding/pem" "fmt" @@ -1048,7 +1049,21 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { return } - _, err = tls.X509KeyPair([]byte(data.CertificateChain), []byte(data.PrivateKey)) + certPEM, err := base64.StdEncoding.DecodeString(data.CertificateChain) + if err != nil { + httpError(w, http.StatusBadRequest, "Failed to base64-decode certificate chain: %s", err) + return + } + + keyPEM, err := base64.StdEncoding.DecodeString(data.PrivateKey) + if err != nil { + httpError(w, http.StatusBadRequest, "Failed to base64-decode private key: %s", err) + return + } + + log.Printf("got certificate: %s", certPEM) + + _, err = tls.X509KeyPair(certPEM, keyPEM) if err != nil { httpError(w, http.StatusBadRequest, "Invalid certificate or key: %s", err) return @@ -1058,7 +1073,7 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { var certs []*pem.Block // PEM-encoded certificates var skippedBytes []string // skipped bytes - pemblock := []byte(data.CertificateChain) + pemblock := []byte(certPEM) for { var decoded *pem.Block decoded, pemblock = pem.Decode(pemblock) @@ -1109,6 +1124,7 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { mainCert := parsedCerts[0] _, err = mainCert.Verify(opts) if err != nil { + // TODO: let self-signed certs through httpError(w, http.StatusBadRequest, "Your certificate does not verify: %s", err) return } From 1dd548c36c3ef903b59b6547fd1577f8d7cbe743 Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Fri, 1 Feb 2019 16:52:59 +0300 Subject: [PATCH 14/88] Added button to reset encryption settings --- client/src/__locales/en.json | 3 +- .../components/Settings/Encryption/Form.js | 43 ++++++++++++------- client/src/helpers/form.js | 2 +- client/src/reducers/index.js | 5 +++ 4 files changed, 35 insertions(+), 18 deletions(-) diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 25053476..33eb0fa1 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -233,5 +233,6 @@ "topline_expiring_certificate": "Your SSL certificate is about to expire. Update <0>Encryption settings.", "form_error_port_range": "Enter port value in the range of 80-65535", "form_error_equal": "Shouldn't be equal", - "form_error_password": "Password mismatched" + "form_error_password": "Password mismatched", + "reset_settings": "Reset settings", } \ No newline at end of file diff --git a/client/src/components/Settings/Encryption/Form.js b/client/src/components/Settings/Encryption/Form.js index 4e85ea5a..db0f1b41 100644 --- a/client/src/components/Settings/Encryption/Form.js +++ b/client/src/components/Settings/Encryption/Form.js @@ -4,15 +4,17 @@ import { Field, reduxForm } from 'redux-form'; import { Trans, withNamespaces } from 'react-i18next'; import flow from 'lodash/flow'; -import { renderField, renderSelectField, required, toNumber, port } from '../../../helpers/form'; +import { renderField, renderSelectField, toNumber, port } from '../../../helpers/form'; import i18n from '../../../i18n'; const validate = (values) => { const errors = {}; - if (values.port_dns_over_tls === values.port_https) { - errors.port_dns_over_tls = i18n.t('form_error_equal'); - errors.port_https = i18n.t('form_error_equal'); + if (values.port_dns_over_tls && values.port_https) { + if (values.port_dns_over_tls === values.port_https) { + errors.port_dns_over_tls = i18n.t('form_error_equal'); + errors.port_https = i18n.t('form_error_equal'); + } } return errors; @@ -22,6 +24,7 @@ const Form = (props) => { const { t, handleSubmit, + reset, invalid, submitting, processing, @@ -46,7 +49,6 @@ const Form = (props) => { type="text" className="form-control" placeholder={t('encryption_server_enter')} - validate={[required]} />
encryption_server_desc @@ -80,7 +82,7 @@ const Form = (props) => { type="number" className="form-control" placeholder={t('encryption_https')} - validate={[required, port]} + validate={[port]} normalize={toNumber} />
@@ -100,7 +102,7 @@ const Form = (props) => { type="number" className="form-control" placeholder={t('encryption_dot')} - validate={[required, port]} + validate={[port]} normalize={toNumber} />
@@ -130,7 +132,6 @@ const Form = (props) => { type="text" className="form-control form-control--textarea" placeholder={t('encryption_certificates_input')} - validate={[required]} />
{statusCert && @@ -170,7 +171,6 @@ const Form = (props) => { type="text" className="form-control form-control--textarea" placeholder="Copy/paste your PEM-encoded private key for your cerficate here." - validate={[required]} />
{statusKey && @@ -188,19 +188,30 @@ const Form = (props) => {
- +
+ + +
); }; Form.propTypes = { handleSubmit: PropTypes.func.isRequired, + reset: PropTypes.func.isRequired, submitting: PropTypes.bool.isRequired, invalid: PropTypes.bool.isRequired, initialValues: PropTypes.object.isRequired, diff --git a/client/src/helpers/form.js b/client/src/helpers/form.js index 055d1138..39855671 100644 --- a/client/src/helpers/form.js +++ b/client/src/helpers/form.js @@ -63,7 +63,7 @@ export const isPositive = (value) => { }; export const port = (value) => { - if (value < 80 || value > 65535) { + if (value && (value < 80 || value > 65535)) { return form_error_port_range; } return false; diff --git a/client/src/reducers/index.js b/client/src/reducers/index.js index fa86b4af..3210b2c3 100644 --- a/client/src/reducers/index.js +++ b/client/src/reducers/index.js @@ -329,6 +329,11 @@ const encryption = handleActions({ processingConfig: false, status_cert: '', status_key: '', + certificate_chain: '', + private_key: '', + server_name: '', + port_https: '', + port_dns_over_tls: '', }); export default combineReducers({ From 4a14c199d8a2acf64fe7448c1fd8ccd1b77edf14 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Fri, 1 Feb 2019 16:53:10 +0300 Subject: [PATCH 15/88] /tls/configure -- allow submitting empty certificates and keys to clear them out from config --- control.go | 176 ++++++++++++++++++++++++++++------------------------- 1 file changed, 92 insertions(+), 84 deletions(-) diff --git a/control.go b/control.go index ca699f66..344cd6af 100644 --- a/control.go +++ b/control.go @@ -1049,105 +1049,113 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { return } - certPEM, err := base64.StdEncoding.DecodeString(data.CertificateChain) - if err != nil { - httpError(w, http.StatusBadRequest, "Failed to base64-decode certificate chain: %s", err) - return - } + var mainCert *x509.Certificate - keyPEM, err := base64.StdEncoding.DecodeString(data.PrivateKey) - if err != nil { - httpError(w, http.StatusBadRequest, "Failed to base64-decode private key: %s", err) - return - } - - log.Printf("got certificate: %s", certPEM) - - _, err = tls.X509KeyPair(certPEM, keyPEM) - if err != nil { - httpError(w, http.StatusBadRequest, "Invalid certificate or key: %s", err) - return - } - - // now do a more extended validation - var certs []*pem.Block // PEM-encoded certificates - var skippedBytes []string // skipped bytes - - pemblock := []byte(certPEM) - for { - var decoded *pem.Block - decoded, pemblock = pem.Decode(pemblock) - if decoded == nil { - break - } - if decoded.Type == "CERTIFICATE" { - certs = append(certs, decoded) - } else { - skippedBytes = append(skippedBytes, decoded.Type) - } - } - - var parsedCerts []*x509.Certificate - - for _, cert := range certs { - parsed, err := x509.ParseCertificate(cert.Bytes) + if data.CertificateChain != "" { + certPEM, err := base64.StdEncoding.DecodeString(data.CertificateChain) if err != nil { - httpError(w, http.StatusBadRequest, "failed to parse certificate: %s", err) + httpError(w, http.StatusBadRequest, "Failed to base64-decode certificate chain: %s", err) return } - parsedCerts = append(parsedCerts, parsed) - } - if len(parsedCerts) == 0 { - httpError(w, http.StatusBadRequest, "You have specified an empty certificate") - return - } + log.Printf("got certificate: %s", certPEM) - // spew.Dump(parsedCerts) + if data.PrivateKey != "" { + keyPEM, err := base64.StdEncoding.DecodeString(data.PrivateKey) + if err != nil { + httpError(w, http.StatusBadRequest, "Failed to base64-decode private key: %s", err) + return + } - opts := x509.VerifyOptions{ - DNSName: data.ServerName, - } - - log.Printf("number of certs - %d", len(parsedCerts)) - if len(parsedCerts) > 1 { - // set up an intermediate - pool := x509.NewCertPool() - for _, cert := range parsedCerts[1:] { - log.Printf("got an intermediate cert") - pool.AddCert(cert) + _, err = tls.X509KeyPair(certPEM, keyPEM) + if err != nil { + httpError(w, http.StatusBadRequest, "Invalid certificate or key: %s", err) + return + } } - opts.Intermediates = pool - } - // TODO: save it as a warning rather than error it out -- shouldn't be a big problem - mainCert := parsedCerts[0] - _, err = mainCert.Verify(opts) - if err != nil { - // TODO: let self-signed certs through - httpError(w, http.StatusBadRequest, "Your certificate does not verify: %s", err) - return + // now do a more extended validation + var certs []*pem.Block // PEM-encoded certificates + var skippedBytes []string // skipped bytes + + pemblock := []byte(certPEM) + for { + var decoded *pem.Block + decoded, pemblock = pem.Decode(pemblock) + if decoded == nil { + break + } + if decoded.Type == "CERTIFICATE" { + certs = append(certs, decoded) + } else { + skippedBytes = append(skippedBytes, decoded.Type) + } + } + + var parsedCerts []*x509.Certificate + + for _, cert := range certs { + parsed, err := x509.ParseCertificate(cert.Bytes) + if err != nil { + httpError(w, http.StatusBadRequest, "failed to parse certificate: %s", err) + return + } + parsedCerts = append(parsedCerts, parsed) + } + + if len(parsedCerts) == 0 { + httpError(w, http.StatusBadRequest, "You have specified an empty certificate") + return + } + + // spew.Dump(parsedCerts) + + opts := x509.VerifyOptions{ + DNSName: data.ServerName, + } + + log.Printf("number of certs - %d", len(parsedCerts)) + if len(parsedCerts) > 1 { + // set up an intermediate + pool := x509.NewCertPool() + for _, cert := range parsedCerts[1:] { + log.Printf("got an intermediate cert") + pool.AddCert(cert) + } + opts.Intermediates = pool + } + + // TODO: save it as a warning rather than error it out -- shouldn't be a big problem + mainCert := parsedCerts[0] + _, err = mainCert.Verify(opts) + if err != nil { + // TODO: let self-signed certs through + httpError(w, http.StatusBadRequest, "Your certificate does not verify: %s", err) + return + } + // spew.Dump(chains) } - // spew.Dump(chains) config.TLS = data // update status - config.TLS.StatusCertificate = fmt.Sprintf("Certificate expires on %s", mainCert.NotAfter) //, valid for hostname %s", mainCert.NotAfter, mainCert.Subject.CommonName) - if len(mainCert.DNSNames) == 1 { - config.TLS.StatusCertificate += fmt.Sprintf(", valid for hostname %s", mainCert.DNSNames[0]) - } else if len(mainCert.DNSNames) > 1 { - config.TLS.StatusCertificate += ", valid for hostnames " + strings.Join(mainCert.DNSNames, ", ") - } + if mainCert != nil { + config.TLS.StatusCertificate = fmt.Sprintf("Certificate expires on %s", mainCert.NotAfter) //, valid for hostname %s", mainCert.NotAfter, mainCert.Subject.CommonName) + if len(mainCert.DNSNames) == 1 { + config.TLS.StatusCertificate += fmt.Sprintf(", valid for hostname %s", mainCert.DNSNames[0]) + } else if len(mainCert.DNSNames) > 1 { + config.TLS.StatusCertificate += ", valid for hostnames " + strings.Join(mainCert.DNSNames, ", ") + } - // issue a warning if certificate is about to expire - now := time.Now() - if mainCert.NotAfter.AddDate(0, 0, -30).After(now) { - timeLeft := time.Until(mainCert.NotAfter) - if timeLeft > 0 { - config.TLS.Warning = fmt.Sprintf("Your certificate expires in %.0f days, we recommend you update it soon", timeLeft.Hours()/24) - } else { - config.TLS.Warning = fmt.Sprintf("Your certificate has expired on %s, we recommend you update it immediatedly", mainCert.NotAfter) + // issue a warning if certificate is about to expire + now := time.Now() + if mainCert.NotAfter.AddDate(0, 0, -30).After(now) { + timeLeft := time.Until(mainCert.NotAfter) + if timeLeft > 0 { + config.TLS.Warning = fmt.Sprintf("Your certificate expires in %.0f days, we recommend you update it soon", timeLeft.Hours()/24) + } else { + config.TLS.Warning = fmt.Sprintf("Your certificate has expired on %s, we recommend you update it immediatedly", mainCert.NotAfter) + } } } httpUpdateConfigReloadDNSReturnOK(w, r) From 351673c060430a605a55f1c07aac1f51e40858cd Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Fri, 1 Feb 2019 17:17:52 +0300 Subject: [PATCH 16/88] Initial port values --- client/src/reducers/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/reducers/index.js b/client/src/reducers/index.js index 3210b2c3..58ff3149 100644 --- a/client/src/reducers/index.js +++ b/client/src/reducers/index.js @@ -332,8 +332,8 @@ const encryption = handleActions({ certificate_chain: '', private_key: '', server_name: '', - port_https: '', - port_dns_over_tls: '', + port_https: 0, + port_dns_over_tls: 0, }); export default combineReducers({ From cb9ffe4de91b1f4111e6e97e89595183d3bc0bfe Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Fri, 1 Feb 2019 17:32:14 +0300 Subject: [PATCH 17/88] Send 0 on empty port value --- client/src/actions/index.js | 2 ++ client/src/reducers/index.js | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/actions/index.js b/client/src/actions/index.js index be3a8c65..9b06352d 100644 --- a/client/src/actions/index.js +++ b/client/src/actions/index.js @@ -679,6 +679,8 @@ export const setTlsConfig = config => async (dispatch) => { const values = { ...config }; values.certificate_chain = btoa(values.certificate_chain); values.private_key = btoa(values.private_key); + values.port_https = values.port_https || 0; + values.port_dns_over_tls = values.port_dns_over_tls || 0; await apiClient.setTlsConfig(values); dispatch(setTlsConfigSuccess(config)); diff --git a/client/src/reducers/index.js b/client/src/reducers/index.js index 58ff3149..50c0e4fc 100644 --- a/client/src/reducers/index.js +++ b/client/src/reducers/index.js @@ -332,8 +332,6 @@ const encryption = handleActions({ certificate_chain: '', private_key: '', server_name: '', - port_https: 0, - port_dns_over_tls: 0, }); export default combineReducers({ From 0d3aa00956ad386ca4b15b035f4e3778026fb049 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Mon, 11 Feb 2019 21:52:39 +0300 Subject: [PATCH 18/88] Default values for DoH and DoT ports --- config.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config.go b/config.go index df87d166..73d26155 100644 --- a/config.go +++ b/config.go @@ -95,6 +95,10 @@ var config = configuration{ }, UpstreamDNS: defaultDNS, }, + TLS: tlsConfig{ + PortHTTPS: 443, + PortDNSOverTLS: 853, // needs to be passed through to dnsproxy + }, Filters: []filter{ {Filter: dnsfilter.Filter{ID: 1}, Enabled: true, URL: "https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt", Name: "AdGuard Simplified Domain Names filter"}, {Filter: dnsfilter.Filter{ID: 2}, Enabled: false, URL: "https://adaway.org/hosts.txt", Name: "AdAway"}, From 35b5f4b48b53b281e41434988d21bc49234ced12 Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Tue, 12 Feb 2019 10:31:37 +0300 Subject: [PATCH 19/88] Fixed json and updated zh_tw --- client/src/__locales/en.json | 10 ++-- client/src/__locales/zh-tw.json | 82 +++++++++++++++++++++++----- client/src/install/Setup/Greeting.js | 32 +++++------ 3 files changed, 87 insertions(+), 37 deletions(-) diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 33eb0fa1..115bd6cb 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -184,14 +184,14 @@ "install_devices_router": "Router", "install_devices_router_desc": "This setup will automatically cover all the devices connected to your home router and you will not need to configure each of them manually.", "install_devices_address": "AdGuard Home DNS server is listening to the following addresses", - "install_devices_router_list_1": "Open the preferences for your router. Usually, you can access it from your browser via a URL (like http://192.168.0.1/ or http://192.168.1.1/). You may be asked to enter the password. If you don't remember it, you can often reset the password by pressing a button on the router itself. Some routers require a specific application, which in that case should be already installed on your computer/phone.", - "install_devices_router_list_2": "Find the DHCP/DNS settings. Look for the DNS letters next to a field which allows two or three sets of numbers, each broken into four groups of one to three digits.", + "install_devices_router_list_1": "Open the preferences for your router. Usually, you can access it from your browser via a URL (like http:\/\/192.168.0.1\/ or http:\/\/192.168.1.1\/). You may be asked to enter the password. If you don't remember it, you can often reset the password by pressing a button on the router itself. Some routers require a specific application, which in that case should be already installed on your computer\/phone.", + "install_devices_router_list_2": "Find the DHCP\/DNS settings. Look for the DNS letters next to a field which allows two or three sets of numbers, each broken into four groups of one to three digits.", "install_devices_router_list_3": "Enter your AdGuard Home server addresses there.", "install_devices_windows_list_1": "Open Control Panel through Start menu or Windows search.", "install_devices_windows_list_2": "Go to Network and Internet category and then to Network and Sharing Center.", "install_devices_windows_list_3": "On the left side of the screen find Change adapter settings and click on it.", "install_devices_windows_list_4": "Select your active connection, right-click on it and choose Properties.", - "install_devices_windows_list_5": "Find Internet Protocol Version 4 (TCP/IP) in the list, select it and then click on Properties again.", + "install_devices_windows_list_5": "Find Internet Protocol Version 4 (TCP\/IP) in the list, select it and then click on Properties again.", "install_devices_windows_list_6": "Choose Use the following DNS server addresses and enter your AdGuard Home server addresses.", "install_devices_macos_list_1": "Click on Apple icon and go to System Preferences.", "install_devices_macos_list_2": "Click on Network.", @@ -209,7 +209,7 @@ "get_started": "Get Started", "next": "Next", "open_dashboard": "Open Dashboard", - "install_saved": "All settings saved", + "install_saved": "Saved successfully", "encryption_title": "Encryption", "encryption_desc": "Encryption (HTTPS/TLS) support for both DNS and admin web interface", "encryption_config_saved": "Encryption config saved", @@ -234,5 +234,5 @@ "form_error_port_range": "Enter port value in the range of 80-65535", "form_error_equal": "Shouldn't be equal", "form_error_password": "Password mismatched", - "reset_settings": "Reset settings", + "reset_settings": "Reset settings" } \ No newline at end of file diff --git a/client/src/__locales/zh-tw.json b/client/src/__locales/zh-tw.json index 674aa364..695afa9d 100644 --- a/client/src/__locales/zh-tw.json +++ b/client/src/__locales/zh-tw.json @@ -1,15 +1,15 @@ { - "url_added_successfully": "\u7db2\u5740\u5df2\u88ab\u6210\u529f\u5730\u52a0\u5165", + "url_added_successfully": "\u7db2\u5740\u88ab\u6210\u529f\u5730\u52a0\u5165", "check_dhcp_servers": "\u6aa2\u67e5\u52d5\u614b\u4e3b\u6a5f\u8a2d\u5b9a\u5354\u5b9a\uff08DHCP\uff09\u4f3a\u670d\u5668", "save_config": "\u5132\u5b58\u914d\u7f6e", - "enabled_dhcp": "\u52d5\u614b\u4e3b\u6a5f\u8a2d\u5b9a\u5354\u5b9a\uff08DHCP\uff09\u4f3a\u670d\u5668\u5df2\u88ab\u555f\u7528", - "disabled_dhcp": "\u52d5\u614b\u4e3b\u6a5f\u8a2d\u5b9a\u5354\u5b9a\uff08DHCP\uff09\u4f3a\u670d\u5668\u5df2\u88ab\u7981\u7528", + "enabled_dhcp": "\u52d5\u614b\u4e3b\u6a5f\u8a2d\u5b9a\u5354\u5b9a\uff08DHCP\uff09\u4f3a\u670d\u5668\u88ab\u555f\u7528", + "disabled_dhcp": "\u52d5\u614b\u4e3b\u6a5f\u8a2d\u5b9a\u5354\u5b9a\uff08DHCP\uff09\u4f3a\u670d\u5668\u88ab\u7981\u7528", "dhcp_title": "\u52d5\u614b\u4e3b\u6a5f\u8a2d\u5b9a\u5354\u5b9a\uff08DHCP\uff09\u4f3a\u670d\u5668\uff08\u5be6\u9a57\u6027\u7684\uff01\uff09", "dhcp_description": "\u5982\u679c\u60a8\u7684\u8def\u7531\u5668\u672a\u63d0\u4f9b\u52d5\u614b\u4e3b\u6a5f\u8a2d\u5b9a\u5354\u5b9a\uff08DHCP\uff09\u8a2d\u5b9a\uff0c\u60a8\u53ef\u4f7f\u7528AdGuard\u81ea\u8eab\u5167\u5efa\u7684DHCP\u4f3a\u670d\u5668\u3002", "dhcp_enable": "\u555f\u7528\u52d5\u614b\u4e3b\u6a5f\u8a2d\u5b9a\u5354\u5b9a\uff08DHCP\uff09\u4f3a\u670d\u5668", "dhcp_disable": "\u7981\u7528\u52d5\u614b\u4e3b\u6a5f\u8a2d\u5b9a\u5354\u5b9a\uff08DHCP\uff09\u4f3a\u670d\u5668", - "dhcp_not_found": "\u65bc\u8a72\u7db2\u8def\u4e0a\u7121\u6709\u6548\u7684\u52d5\u614b\u4e3b\u6a5f\u8a2d\u5b9a\u5354\u5b9a\uff08DHCP\uff09\u4f3a\u670d\u5668\u88ab\u767c\u73fe\u3002\u555f\u7528\u5167\u5efa\u7684DHCP\u4f3a\u670d\u5668\u70ba\u5b89\u5168\u7684\u3002", - "dhcp_found": "\u65bc\u8a72\u7db2\u8def\u4e0a\u67d0\u4e9b\u6709\u6548\u7684\u52d5\u614b\u4e3b\u6a5f\u8a2d\u5b9a\u5354\u5b9a\uff08DHCP\uff09\u4f3a\u670d\u5668\u88ab\u767c\u73fe\u3002\u555f\u7528\u5167\u5efa\u7684DHCP\u4f3a\u670d\u5668\u70ba\u4e0d\u5b89\u5168\u7684\u3002", + "dhcp_not_found": "\u65bc\u8a72\u7db2\u8def\u4e0a\u7121\u73fe\u884c\u7684\u52d5\u614b\u4e3b\u6a5f\u8a2d\u5b9a\u5354\u5b9a\uff08DHCP\uff09\u4f3a\u670d\u5668\u88ab\u767c\u73fe\u3002\u555f\u7528\u5167\u5efa\u7684DHCP\u4f3a\u670d\u5668\u70ba\u5b89\u5168\u7684\u3002", + "dhcp_found": "\u65bc\u8a72\u7db2\u8def\u4e0a\u67d0\u4e9b\u73fe\u884c\u7684\u52d5\u614b\u4e3b\u6a5f\u8a2d\u5b9a\u5354\u5b9a\uff08DHCP\uff09\u4f3a\u670d\u5668\u88ab\u767c\u73fe\u3002\u555f\u7528\u5167\u5efa\u7684DHCP\u4f3a\u670d\u5668\u70ba\u4e0d\u5b89\u5168\u7684\u3002", "dhcp_leases": "\u52d5\u614b\u4e3b\u6a5f\u8a2d\u5b9a\u5354\u5b9a\uff08DHCP\uff09\u79df\u8cc3", "dhcp_leases_not_found": "\u7121\u5df2\u767c\u73fe\u4e4b\u52d5\u614b\u4e3b\u6a5f\u8a2d\u5b9a\u5354\u5b9a\uff08DHCP\uff09\u79df\u8cc3", "dhcp_config_saved": "\u5df2\u5132\u5b58\u52d5\u614b\u4e3b\u6a5f\u8a2d\u5b9a\u5354\u5b9a\uff08DHCP\uff09\u4f3a\u670d\u5668\u914d\u7f6e", @@ -28,6 +28,7 @@ "dhcp_ip_addresses": "IP \u4f4d\u5740", "dhcp_table_hostname": "\u4e3b\u6a5f\u540d\u7a31", "dhcp_table_expires": "\u5230\u671f", + "dhcp_warning": "\u5982\u679c\u60a8\u60f3\u8981\u555f\u7528\u5167\u5efa\u7684\u52d5\u614b\u4e3b\u6a5f\u8a2d\u5b9a\u5354\u5b9a\uff08DHCP\uff09\u4f3a\u670d\u5668\uff0c\u78ba\u4fdd\u7121\u5176\u5b83\u73fe\u884c\u7684DHCP\u4f3a\u670d\u5668\u3002\u5426\u5247\uff0c\u5b83\u53ef\u80fd\u6703\u7834\u58de\u4f9b\u5df2\u9023\u7dda\u7684\u88dd\u7f6e\u4e4b\u7db2\u969b\u7db2\u8def\uff01", "back": "\u8fd4\u56de", "dashboard": "\u5100\u8868\u677f", "settings": "\u8a2d\u5b9a", @@ -36,8 +37,8 @@ "faq": "\u5e38\u898b\u554f\u7b54\u96c6", "version": "\u7248\u672c", "address": "\u4f4d\u5740", - "on": "\u958b\u555f", - "off": "\u95dc\u9589", + "on": "\u958b\u8457", + "off": "\u95dc\u8457", "copyright": "\u7248\u6b0a", "homepage": "\u9996\u9801", "report_an_issue": "\u5831\u544a\u554f\u984c", @@ -73,7 +74,7 @@ "use_adguard_parental": "\u4f7f\u7528AdGuard\u5bb6\u9577\u76e3\u63a7\u4e4b\u7db2\u8def\u670d\u52d9", "use_adguard_parental_hint": "AdGuard Home\u5c07\u6aa2\u67e5\u7db2\u57df\u662f\u5426\u5305\u542b\u6210\u4eba\u8cc7\u6599\u3002\u5b83\u4f7f\u7528\u5982\u540c\u700f\u89bd\u5b89\u5168\u7db2\u8def\u670d\u52d9\u4e00\u6a23\u4e4b\u53cb\u597d\u7684\u96b1\u79c1\u61c9\u7528\u7a0b\u5f0f\u4ecb\u9762\uff08API\uff09\u3002", "enforce_safe_search": "\u5f37\u5236\u57f7\u884c\u5b89\u5168\u641c\u5c0b", - "enforce_save_search_hint": "AdGuard Home\u53ef\u5728\u4ee5\u4e0b\u7684\u641c\u5c0b\u5f15\u64ce\uff1aGoogle\u3001YouTube\u3001Bing\u548cYandex\u4e2d\u5f37\u5236\u57f7\u884c\u5b89\u5168\u641c\u5c0b\u3002", + "enforce_save_search_hint": "AdGuard Home\u53ef\u5728\u4e0b\u5217\u7684\u641c\u5c0b\u5f15\u64ce\uff1aGoogle\u3001YouTube\u3001Bing\u548cYandex\u4e2d\u5f37\u5236\u57f7\u884c\u5b89\u5168\u641c\u5c0b\u3002", "no_servers_specified": "\u7121\u5df2\u660e\u78ba\u6307\u5b9a\u7684\u4f3a\u670d\u5668", "no_settings": "\u7121\u8a2d\u5b9a", "general_settings": "\u4e00\u822c\u7684\u8a2d\u5b9a", @@ -115,6 +116,7 @@ "example_comment": "! \u770b\uff0c\u4e00\u500b\u8a3b\u89e3", "example_comment_meaning": "\u53ea\u662f\u4e00\u500b\u8a3b\u89e3", "example_comment_hash": "# \u4e5f\u662f\u4e00\u500b\u8a3b\u89e3", + "example_regex_meaning": "\u5c01\u9396\u81f3\u8207\u5df2\u660e\u78ba\u6307\u5b9a\u7684\u898f\u5247\u904b\u7b97\u5f0f\uff08Regular Expression\uff09\u76f8\u914d\u7684\u7db2\u57df\u4e4b\u5b58\u53d6", "example_upstream_regular": "\u4e00\u822c\u7684 DNS\uff08\u900f\u904eUDP\uff09", "example_upstream_dot": "\u52a0\u5bc6\u7684 DNS-over-TLS<\/a>", "example_upstream_doh": "\u52a0\u5bc6\u7684 DNS-over-HTTPS <\/a>", @@ -129,7 +131,7 @@ "time_table_header": "\u6642\u9593", "domain_name_table_header": "\u57df\u540d", "type_table_header": "\u985e\u578b", - "response_table_header": "\u53cd\u61c9", + "response_table_header": "\u56de\u61c9", "client_table_header": "\u7528\u6236\u7aef", "empty_response_status": "\u7a7a\u767d\u7684", "show_all_filter_type": "\u986f\u793a\u5168\u90e8", @@ -147,14 +149,66 @@ "of_table_footer_text": "\u4e4b", "rows_table_footer_text": "\u5217", "updated_custom_filtering_toast": "\u5df2\u66f4\u65b0\u81ea\u8a02\u7684\u904e\u6ffe\u898f\u5247", - "rule_removed_from_custom_filtering_toast": "\u898f\u5247\u5df2\u5f9e\u81ea\u8a02\u7684\u904e\u6ffe\u898f\u5247\u4e2d\u88ab\u79fb\u9664", - "rule_added_to_custom_filtering_toast": "\u898f\u5247\u5df2\u88ab\u52a0\u81f3\u81ea\u8a02\u7684\u904e\u6ffe\u898f\u5247\u4e2d", - "query_log_disabled_toast": "\u67e5\u8a62\u8a18\u9304\u5df2\u88ab\u7981\u7528", - "query_log_enabled_toast": "\u67e5\u8a62\u8a18\u9304\u5df2\u88ab\u555f\u7528", + "rule_removed_from_custom_filtering_toast": "\u898f\u5247\u5f9e\u81ea\u8a02\u7684\u904e\u6ffe\u898f\u5247\u4e2d\u88ab\u79fb\u9664", + "rule_added_to_custom_filtering_toast": "\u898f\u5247\u88ab\u52a0\u81f3\u81ea\u8a02\u7684\u904e\u6ffe\u898f\u5247\u4e2d", + "query_log_disabled_toast": "\u67e5\u8a62\u8a18\u9304\u88ab\u7981\u7528", + "query_log_enabled_toast": "\u67e5\u8a62\u8a18\u9304\u88ab\u555f\u7528", "source_label": "\u4f86\u6e90", "found_in_known_domain_db": "\u5728\u5df2\u77e5\u7684\u57df\u540d\u8cc7\u6599\u5eab\u4e2d\u88ab\u767c\u73fe\u3002", "category_label": "\u985e\u5225", "rule_label": "\u898f\u5247", "filter_label": "\u904e\u6ffe\u5668", - "unknown_filter": "\u672a\u77e5\u7684\u904e\u6ffe\u5668 {{filterId}}" + "unknown_filter": "\u672a\u77e5\u7684\u904e\u6ffe\u5668 {{filterId}}", + "install_welcome_title": "\u6b61\u8fce\u81f3AdGuard Home\uff01", + "install_welcome_desc": "AdGuard Home\u662f\u5168\u7db2\u8def\u7bc4\u570d\u5ee3\u544a\u548c\u8ffd\u8e64\u5668\u5c01\u9396\u7684DNS\u4f3a\u670d\u5668\u3002\u5b83\u7684\u76ee\u7684\u70ba\u8b93\u60a8\u63a7\u5236\u60a8\u6574\u500b\u7684\u7db2\u8def\u548c\u6240\u6709\u60a8\u7684\u88dd\u7f6e\uff0c\u4e14\u4e0d\u9700\u8981\u4f7f\u7528\u7528\u6236\u7aef\u7a0b\u5f0f\u3002", + "install_settings_title": "\u7ba1\u7406\u54e1\u7db2\u9801\u4ecb\u9762", + "install_settings_listen": "\u76e3\u807d\u4ecb\u9762", + "install_settings_port": "\u9023\u63a5\u57e0", + "install_settings_interface_link": "\u60a8\u7684AdGuard Home\u7ba1\u7406\u54e1\u7db2\u9801\u4ecb\u9762\u5c07\u65bc\u4e0b\u5217\u7684\u4f4d\u5740\u4e0a\u70ba\u53ef\u7528\u7684\uff1a", + "form_error_port": "\u8f38\u5165\u6709\u6548\u7684\u9023\u63a5\u57e0\u503c", + "install_settings_dns": "DNS \u4f3a\u670d\u5668", + "install_settings_dns_desc": "\u60a8\u5c07\u9700\u8981\u914d\u7f6e\u60a8\u7684\u88dd\u7f6e\u6216\u8def\u7531\u5668\u4ee5\u4f7f\u7528\u65bc\u4e0b\u5217\u7684\u4f4d\u5740\u4e0a\u4e4bDNS\u4f3a\u670d\u5668\uff1a", + "install_settings_all_interfaces": "\u6240\u6709\u7684\u4ecb\u9762", + "install_auth_title": "\u9a57\u8b49", + "install_auth_desc": "\u88ab\u975e\u5e38\u5efa\u8b70\u914d\u7f6e\u5c6c\u65bc\u60a8\u7684AdGuard Home\u7ba1\u7406\u54e1\u7db2\u9801\u4ecb\u9762\u4e4b\u5bc6\u78bc\u9a57\u8b49\u3002\u5373\u4f7f\u5b83\u50c5\u5728\u60a8\u7684\u5340\u57df\u7db2\u8def\u4e2d\u70ba\u53ef\u5b58\u53d6\u7684\uff0c\u8b93\u5b83\u53d7\u4fdd\u8b77\u514d\u65bc\u4e0d\u53d7\u9650\u5236\u7684\u5b58\u53d6\u70ba\u4ecd\u7136\u91cd\u8981\u7684\u3002", + "install_auth_username": "\u7528\u6236\u540d", + "install_auth_password": "\u5bc6\u78bc", + "install_auth_confirm": "\u78ba\u8a8d\u5bc6\u78bc", + "install_auth_username_enter": "\u8f38\u5165\u7528\u6236\u540d", + "install_auth_password_enter": "\u8f38\u5165\u5bc6\u78bc", + "install_step": "\u6b65\u9a5f", + "install_devices_title": "\u914d\u7f6e\u60a8\u7684\u88dd\u7f6e", + "install_devices_desc": "\u70ba\u4f7fAdGuard Home\u958b\u59cb\u904b\u4f5c\uff0c\u60a8\u9700\u8981\u914d\u7f6e\u60a8\u7684\u88dd\u7f6e\u4ee5\u4f7f\u7528\u5b83\u3002", + "install_submit_title": "\u606d\u559c\uff01", + "install_submit_desc": "\u8a72\u8a2d\u7f6e\u7a0b\u5e8f\u88ab\u5b8c\u6210\uff0c\u4e14\u60a8\u6e96\u5099\u597d\u958b\u59cb\u4f7f\u7528AdGuard Home\u3002", + "install_devices_router": "\u8def\u7531\u5668", + "install_devices_router_desc": "\u8a72\u8a2d\u7f6e\u5c07\u81ea\u52d5\u5730\u6db5\u84cb\u88ab\u9023\u7dda\u81f3\u60a8\u7684\u5bb6\u5ead\u8def\u7531\u5668\u4e4b\u6240\u6709\u7684\u88dd\u7f6e\uff0c\u4e14\u60a8\u5c07\u7121\u9700\u624b\u52d5\u5730\u914d\u7f6e\u5b83\u5011\u6bcf\u500b\u3002", + "install_devices_address": "AdGuard Home DNS\u4f3a\u670d\u5668\u6b63\u5728\u76e3\u807d\u4e0b\u5217\u7684\u4f4d\u5740", + "install_devices_router_list_1": "\u958b\u555f\u95dc\u65bc\u60a8\u7684\u8def\u7531\u5668\u4e4b\u504f\u597d\u8a2d\u5b9a\u3002\u901a\u5e38\u5730\uff0c\u60a8\u53ef\u900f\u904e\u7db2\u5740\uff08\u5982 http:\/\/192.168.0.1\/ \u6216 http:\/\/192.168.1.1\/\uff09\u5f9e\u60a8\u7684\u700f\u89bd\u5668\u4e2d\u5b58\u53d6\u5b83\u3002\u60a8\u53ef\u80fd\u88ab\u8981\u6c42\u8f38\u5165\u8a72\u5bc6\u78bc\u3002\u5982\u679c\u60a8\u4e0d\u8a18\u5f97\u5b83\uff0c\u60a8\u7d93\u5e38\u53ef\u900f\u904e\u6309\u58d3\u65bc\u8a72\u8def\u7531\u5668\u672c\u8eab\u4e0a\u7684\u6309\u9215\u4f86\u91cd\u7f6e\u5bc6\u78bc\u3002\u67d0\u4e9b\u8def\u7531\u5668\u9700\u8981\u7279\u5b9a\u7684\u61c9\u7528\u7a0b\u5f0f\uff0c\u65e2\u7136\u5982\u6b64\u5176\u61c9\u5df2\u88ab\u5b89\u88dd\u65bc\u60a8\u7684\u96fb\u8166\/\u624b\u6a5f\u4e0a\u3002", + "install_devices_router_list_2": "\u627e\u5230DHCP\/DNS\u8a2d\u5b9a\u3002\u5c0b\u627e\u7dca\u9130\u8457\u5141\u8a31\u5169\u7d44\u6216\u4e09\u7d44\u6578\u5b57\u96c6\u7684\u6b04\u4f4d\u4e4bDNS\u5b57\u6bcd\uff0c\u6bcf\u7d44\u88ab\u62c6\u6210\u56db\u500b\u542b\u6709\u4e00\u81f3\u4e09\u500b\u6578\u5b57\u7684\u7fa4\u96c6\u3002", + "install_devices_router_list_3": "\u5728\u90a3\u88e1\u8f38\u5165\u60a8\u7684AdGuard Home\u4f3a\u670d\u5668\u4f4d\u5740\u3002", + "install_devices_windows_list_1": "\u901a\u904e\u958b\u59cb\u529f\u80fd\u8868\u6216Windows \u641c\u5c0b\uff0c\u958b\u555f\u63a7\u5236\u53f0\u3002", + "install_devices_windows_list_2": "\u53bb\u7db2\u8def\u548c\u7db2\u969b\u7db2\u8def\u985e\u5225\uff0c\u7136\u5f8c\u53bb\u7db2\u8def\u548c\u5171\u7528\u4e2d\u5fc3\u3002", + "install_devices_windows_list_3": "\u65bc\u756b\u9762\u4e4b\u5de6\u5074\u4e0a\u627e\u5230\u8b8a\u66f4\u4ecb\u9762\u5361\u8a2d\u5b9a\u4e26\u65bc\u5b83\u4e0a\u9ede\u64ca\u3002", + "install_devices_windows_list_4": "\u9078\u64c7\u60a8\u73fe\u884c\u7684\u9023\u7dda\uff0c\u65bc\u5b83\u4e0a\u9ede\u64ca\u6ed1\u9f20\u53f3\u9375\uff0c\u7136\u5f8c\u9078\u64c7\u5167\u5bb9\u3002", + "install_devices_windows_list_5": "\u5728\u6e05\u55ae\u4e2d\u627e\u5230\u7db2\u969b\u7db2\u8def\u901a\u8a0a\u5354\u5b9a\u7b2c 4 \u7248\uff08TCP\/IPv4\uff09\uff0c\u9078\u64c7\u5b83\uff0c\u7136\u5f8c\u518d\u6b21\u65bc\u5167\u5bb9\u4e0a\u9ede\u64ca\u3002", + "install_devices_windows_list_6": "\u9078\u64c7\u4f7f\u7528\u4e0b\u5217\u7684DNS\u4f3a\u670d\u5668\u4f4d\u5740\uff0c\u7136\u5f8c\u8f38\u5165\u60a8\u7684AdGuard Home\u4f3a\u670d\u5668\u4f4d\u5740\u3002", + "install_devices_macos_list_1": "\u65bcApple\u5716\u50cf\u4e0a\u9ede\u64ca\uff0c\u7136\u5f8c\u53bb\u7cfb\u7d71\u504f\u597d\u8a2d\u5b9a\u3002", + "install_devices_macos_list_2": "\u65bc\u7db2\u8def\u4e0a\u9ede\u64ca\u3002", + "install_devices_macos_list_3": "\u9078\u64c7\u5728\u60a8\u7684\u6e05\u55ae\u4e2d\u4e4b\u9996\u8981\u7684\u9023\u7dda\uff0c\u7136\u5f8c\u9ede\u64ca\u9032\u968e\u7684\u3002", + "install_devices_macos_list_4": "\u9078\u64c7\u8a72DNS\u5206\u9801\uff0c\u7136\u5f8c\u8f38\u5165\u60a8\u7684AdGuard Home\u4f3a\u670d\u5668\u4f4d\u5740\u3002", + "install_devices_android_list_1": "\u5f9eAndroid\u9078\u55ae\u4e3b\u756b\u9762\u4e2d\uff0c\u8f15\u89f8\u8a2d\u5b9a\u3002", + "install_devices_android_list_2": "\u65bc\u8a72\u9078\u55ae\u4e0a\u8f15\u89f8Wi-Fi\u3002\u6b63\u5728\u5217\u51fa\u6240\u6709\u53ef\u7528\u7684\u7db2\u8def\u4e4b\u756b\u9762\u5c07\u88ab\u986f\u793a\uff08\u4e0d\u53ef\u80fd\u70ba\u884c\u52d5\u9023\u7dda\u8a2d\u5b9a\u81ea\u8a02\u7684DNS\uff09\u3002", + "install_devices_android_list_3": "\u9577\u6309\u60a8\u6240\u9023\u7dda\u81f3\u7684\u7db2\u8def\uff0c\u7136\u5f8c\u8f15\u89f8\u4fee\u6539\u7db2\u8def\u3002", + "install_devices_android_list_4": "\u65bc\u67d0\u4e9b\u88dd\u7f6e\u4e0a\uff0c\u60a8\u53ef\u80fd\u9700\u8981\u6aa2\u67e5\u95dc\u65bc\u9032\u968e\u7684\u65b9\u6846\u4ee5\u67e5\u770b\u9032\u4e00\u6b65\u7684\u8a2d\u5b9a\u3002\u70ba\u4e86\u8abf\u6574\u60a8\u7684Android DNS\u8a2d\u5b9a\uff0c\u60a8\u5c07\u9700\u8981\u628aIP \u8a2d\u5b9a\u5f9eDHCP\u8f49\u63db\u6210\u975c\u614b\u3002", + "install_devices_android_list_5": "\u4f7f\u8a2d\u5b9aDNS 1\u548cDNS 2\u503c\u66f4\u6539\u6210\u60a8\u7684AdGuard Home\u4f3a\u670d\u5668\u4f4d\u5740\u3002", + "install_devices_ios_list_1": "\u5f9e\u4e3b\u756b\u9762\u4e2d\uff0c\u8f15\u89f8\u8a2d\u5b9a\u3002", + "install_devices_ios_list_2": "\u5728\u5de6\u5074\u7684\u9078\u55ae\u4e2d\u9078\u64c7Wi-Fi\uff08\u4e0d\u53ef\u80fd\u70ba\u884c\u52d5\u7db2\u8def\u914d\u7f6eDNS\uff09\u3002", + "install_devices_ios_list_3": "\u65bc\u76ee\u524d\u73fe\u884c\u7684\u7db2\u8def\u4e4b\u540d\u7a31\u4e0a\u8f15\u89f8\u3002", + "install_devices_ios_list_4": "\u5728\u8a72DNS\u6b04\u4f4d\u4e2d\uff0c\u8f38\u5165\u60a8\u7684AdGuard Home\u4f3a\u670d\u5668\u4f4d\u5740\u3002", + "get_started": "\u958b\u59cb\u5427", + "next": "\u4e0b\u4e00\u6b65", + "open_dashboard": "\u958b\u555f\u5100\u8868\u677f", + "install_saved": "\u5df2\u6210\u529f\u5730\u5132\u5b58", + "form_error_password": "\u4e0d\u76f8\u7b26\u7684\u5bc6\u78bc" } \ No newline at end of file diff --git a/client/src/install/Setup/Greeting.js b/client/src/install/Setup/Greeting.js index 914d89c8..88c43770 100644 --- a/client/src/install/Setup/Greeting.js +++ b/client/src/install/Setup/Greeting.js @@ -1,23 +1,19 @@ -import React, { Component } from 'react'; +import React from 'react'; import { Trans, withNamespaces } from 'react-i18next'; import Controls from './Controls'; -class Greeting extends Component { - render() { - return ( -
-
-

- install_welcome_title -

-

- install_welcome_desc -

-
- -
- ); - } -} +const Greeting = () => ( +
+
+

+ install_welcome_title +

+

+ install_welcome_desc +

+
+ +
+); export default withNamespaces()(Greeting); From 0aeca6bbf5b08d0f28f969162c0e5475c9ed7469 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Tue, 12 Feb 2019 17:23:03 +0300 Subject: [PATCH 20/88] Don't keep certificates and keys encoded with base64 in yaml config --- control.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/control.go b/control.go index 344cd6af..4ce261ca 100644 --- a/control.go +++ b/control.go @@ -1034,6 +1034,14 @@ func handleInstallConfigure(w http.ResponseWriter, r *http.Request) { // --- func handleTLSStatus(w http.ResponseWriter, r *http.Request) { data := config.TLS + if data.CertificateChain != "" { + encoded := base64.StdEncoding.EncodeToString([]byte(data.CertificateChain)) + data.CertificateChain = string(encoded) + } + if data.PrivateKey != "" { + encoded := base64.StdEncoding.EncodeToString([]byte(data.PrivateKey)) + data.PrivateKey = string(encoded) + } err := json.NewEncoder(w).Encode(&data) if err != nil { httpError(w, http.StatusInternalServerError, "Failed to marshal json with TLS status: %s", err) @@ -1057,6 +1065,7 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { httpError(w, http.StatusBadRequest, "Failed to base64-decode certificate chain: %s", err) return } + data.CertificateChain = string(certPEM) log.Printf("got certificate: %s", certPEM) @@ -1067,6 +1076,8 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { return } + data.PrivateKey = string(keyPEM) + _, err = tls.X509KeyPair(certPEM, keyPEM) if err != nil { httpError(w, http.StatusBadRequest, "Invalid certificate or key: %s", err) From 229ef780857120ca62b2f4ab6249c1529e18d1e7 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Tue, 12 Feb 2019 17:23:38 +0300 Subject: [PATCH 21/88] Activate DNS-over-TLS server when certificates, keys and ports are configured. --- config.go | 12 ++++++------ dns.go | 5 +++++ dnsforward/dnsforward.go | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/config.go b/config.go index 73d26155..21700586 100644 --- a/config.go +++ b/config.go @@ -63,12 +63,12 @@ var defaultDNS = []string{"tls://1.1.1.1", "tls://1.0.0.1"} // field ordering is important -- yaml fields will mirror ordering from here type tlsConfig struct { - ServerName string `yaml:"server_name" json:"server_name,omitempty"` - ForceHTTPS bool `yaml:"force_https" json:"force_https,omitempty"` - PortHTTPS int `yaml:"port_https" json:"port_https,omitempty"` - PortDNSOverTLS int `yaml:"port_dns_over_tls" json:"port_dns_over_tls,omitempty"` - CertificateChain string `yaml:"certificate_chain" json:"certificate_chain"` - PrivateKey string `yaml:"private_key" json:"private_key"` + ServerName string `yaml:"server_name" json:"server_name,omitempty"` + ForceHTTPS bool `yaml:"force_https" json:"force_https,omitempty"` + PortHTTPS int `yaml:"port_https" json:"port_https,omitempty"` + PortDNSOverTLS int `yaml:"port_dns_over_tls" json:"port_dns_over_tls,omitempty"` + + dnsforward.TLSConfig `yaml:",inline" json:",inline"` // only for API, no need to be stored in config StatusCertificate string `yaml:"status_cert" json:"status_cert,omitempty"` diff --git a/dns.go b/dns.go index 3e800892..4a5102d5 100644 --- a/dns.go +++ b/dns.go @@ -51,6 +51,11 @@ func generateServerConfig() dnsforward.ServerConfig { Filters: filters, } + newconfig.TLSConfig = config.TLS.TLSConfig + if config.TLS.PortDNSOverTLS != 0 { + newconfig.TLSListenAddr = &net.TCPAddr{IP: net.ParseIP(config.DNS.BindHost), Port: config.TLS.PortDNSOverTLS} + } + for _, u := range config.DNS.UpstreamDNS { dnsUpstream, err := upstream.AddressToUpstream(u, config.DNS.BootstrapDNS, dnsforward.DefaultTimeout) if err != nil { diff --git a/dnsforward/dnsforward.go b/dnsforward/dnsforward.go index e1006f83..54695c6f 100644 --- a/dnsforward/dnsforward.go +++ b/dnsforward/dnsforward.go @@ -1,6 +1,7 @@ package dnsforward import ( + "crypto/tls" "errors" "fmt" "net" @@ -55,6 +56,7 @@ func NewServer(baseDir string) *Server { } // FilteringConfig represents the DNS filtering configuration of AdGuard Home +// The zero FilteringConfig is empty and ready for use. type FilteringConfig struct { ProtectionEnabled bool `yaml:"protection_enabled"` // whether or not use any of dnsfilter features FilteringEnabled bool `yaml:"filtering_enabled"` // whether or not use filter lists @@ -68,6 +70,12 @@ type FilteringConfig struct { dnsfilter.Config `yaml:",inline"` } +type TLSConfig struct { + TLSListenAddr *net.TCPAddr `yaml:"-" json:"-"` + CertificateChain string `yaml:"certificate_chain" json:"certificate_chain"` + PrivateKey string `yaml:"private_key" json:"private_key"` +} + // ServerConfig represents server configuration. // The zero ServerConfig is empty and ready for use. type ServerConfig struct { @@ -77,6 +85,7 @@ type ServerConfig struct { Filters []dnsfilter.Filter // A list of filters to use FilteringConfig + TLSConfig } // if any of ServerConfig values are zero, then default values from below are used @@ -154,6 +163,15 @@ func (s *Server) startInternal(config *ServerConfig) error { Handler: s.handleDNSRequest, } + if s.TLSListenAddr != nil && s.CertificateChain != "" && s.PrivateKey != "" { + proxyConfig.TLSListenAddr = s.TLSListenAddr + keypair, err := tls.X509KeyPair([]byte(s.CertificateChain), []byte(s.PrivateKey)) + if err != nil { + return errorx.Decorate(err, "Failed to parse TLS keypair") + } + proxyConfig.TLSConfig = &tls.Config{Certificates: []tls.Certificate{keypair}} + } + if proxyConfig.UDPListenAddr == nil { proxyConfig.UDPListenAddr = defaultValues.UDPListenAddr } From c061bec6d806f7b7ba340684319a623580cdc166 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Tue, 12 Feb 2019 19:22:17 +0300 Subject: [PATCH 22/88] Lower down logging noise when idle. --- filter.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/filter.go b/filter.go index dcdd40be..84db3a47 100644 --- a/filter.go +++ b/filter.go @@ -166,7 +166,7 @@ func (filter *filter) update(force bool) (bool, error) { return false, nil } - log.Printf("Downloading update for filter %d from %s", filter.ID, filter.URL) + log.Tracef("Downloading update for filter %d from %s", filter.ID, filter.URL) resp, err := client.Get(filter.URL) if resp != nil && resp.Body != nil { @@ -203,7 +203,7 @@ func (filter *filter) update(force bool) (bool, error) { // Check if the filter has been really changed if reflect.DeepEqual(filter.Rules, rules) { - log.Printf("Filter #%d at URL %s hasn't changed, not updating it", filter.ID, filter.URL) + log.Tracef("Filter #%d at URL %s hasn't changed, not updating it", filter.ID, filter.URL) return false, nil } From 4b4faad9e836116f869756a74b17577761b3d0b1 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Tue, 12 Feb 2019 19:53:53 +0300 Subject: [PATCH 23/88] Fix status for certificates not updating. --- control.go | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/control.go b/control.go index 4ce261ca..49207f69 100644 --- a/control.go +++ b/control.go @@ -1057,8 +1057,6 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { return } - var mainCert *x509.Certificate - if data.CertificateChain != "" { certPEM, err := base64.StdEncoding.DecodeString(data.CertificateChain) if err != nil { @@ -1145,30 +1143,30 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { return } // spew.Dump(chains) - } - config.TLS = data + // update status + if mainCert != nil { + data.StatusCertificate = fmt.Sprintf("Certificate expires on %s", mainCert.NotAfter) //, valid for hostname %s", mainCert.NotAfter, mainCert.Subject.CommonName) + if len(mainCert.DNSNames) == 1 { + data.StatusCertificate += fmt.Sprintf(", valid for hostname %s", mainCert.DNSNames[0]) + } else if len(mainCert.DNSNames) > 1 { + data.StatusCertificate += ", valid for hostnames " + strings.Join(mainCert.DNSNames, ", ") + } - // update status - if mainCert != nil { - config.TLS.StatusCertificate = fmt.Sprintf("Certificate expires on %s", mainCert.NotAfter) //, valid for hostname %s", mainCert.NotAfter, mainCert.Subject.CommonName) - if len(mainCert.DNSNames) == 1 { - config.TLS.StatusCertificate += fmt.Sprintf(", valid for hostname %s", mainCert.DNSNames[0]) - } else if len(mainCert.DNSNames) > 1 { - config.TLS.StatusCertificate += ", valid for hostnames " + strings.Join(mainCert.DNSNames, ", ") - } - - // issue a warning if certificate is about to expire - now := time.Now() - if mainCert.NotAfter.AddDate(0, 0, -30).After(now) { - timeLeft := time.Until(mainCert.NotAfter) - if timeLeft > 0 { - config.TLS.Warning = fmt.Sprintf("Your certificate expires in %.0f days, we recommend you update it soon", timeLeft.Hours()/24) - } else { - config.TLS.Warning = fmt.Sprintf("Your certificate has expired on %s, we recommend you update it immediatedly", mainCert.NotAfter) + // issue a warning if certificate is about to expire + now := time.Now() + if mainCert.NotAfter.AddDate(0, 0, -30).After(now) { + timeLeft := time.Until(mainCert.NotAfter) + if timeLeft > 0 { + data.Warning = fmt.Sprintf("Your certificate expires in %.0f days, we recommend you update it soon", timeLeft.Hours()/24) + } else { + data.Warning = fmt.Sprintf("Your certificate has expired on %s, we recommend you update it immediatedly", mainCert.NotAfter) + } } } } + + config.TLS = data httpUpdateConfigReloadDNSReturnOK(w, r) } From 876bec5a65acfd8eae3aa4c4fcb4692d19efc166 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Tue, 12 Feb 2019 20:02:52 +0300 Subject: [PATCH 24/88] /tls/configure -- introduce unmarshalTLS() that transparently base64-decodes the certificate --- control.go | 58 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/control.go b/control.go index 49207f69..6717acd8 100644 --- a/control.go +++ b/control.go @@ -21,6 +21,7 @@ import ( "github.com/AdguardTeam/AdGuardHome/dnsforward" "github.com/AdguardTeam/dnsproxy/upstream" "github.com/hmage/golibs/log" + "github.com/joomcode/errorx" "github.com/miekg/dns" govalidator "gopkg.in/asaskevich/govalidator.v4" ) @@ -1050,33 +1051,17 @@ func handleTLSStatus(w http.ResponseWriter, r *http.Request) { } func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { - data := tlsConfig{} - err := json.NewDecoder(r.Body).Decode(&data) + data, err := unmarshalTLS(r) if err != nil { - httpError(w, http.StatusBadRequest, "Failed to parse new TLS config json: %s", err) + httpError(w, http.StatusBadRequest, "Failed to unmarshal TLS config: %s", err) return } if data.CertificateChain != "" { - certPEM, err := base64.StdEncoding.DecodeString(data.CertificateChain) - if err != nil { - httpError(w, http.StatusBadRequest, "Failed to base64-decode certificate chain: %s", err) - return - } - data.CertificateChain = string(certPEM) - - log.Printf("got certificate: %s", certPEM) + log.Printf("got certificate: %s", data.CertificateChain) if data.PrivateKey != "" { - keyPEM, err := base64.StdEncoding.DecodeString(data.PrivateKey) - if err != nil { - httpError(w, http.StatusBadRequest, "Failed to base64-decode private key: %s", err) - return - } - - data.PrivateKey = string(keyPEM) - - _, err = tls.X509KeyPair(certPEM, keyPEM) + _, err = tls.X509KeyPair([]byte(data.CertificateChain), []byte(data.PrivateKey)) if err != nil { httpError(w, http.StatusBadRequest, "Invalid certificate or key: %s", err) return @@ -1087,7 +1072,7 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { var certs []*pem.Block // PEM-encoded certificates var skippedBytes []string // skipped bytes - pemblock := []byte(certPEM) + pemblock := []byte(data.CertificateChain) for { var decoded *pem.Block decoded, pemblock = pem.Decode(pemblock) @@ -1170,6 +1155,37 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { httpUpdateConfigReloadDNSReturnOK(w, r) } +// unmarshalTLS handles base64-encoded certificates transparently +func unmarshalTLS(r *http.Request) (tlsConfig, error) { + data := tlsConfig{} + err := json.NewDecoder(r.Body).Decode(&data) + if err != nil { + return data, errorx.Decorate(err, "Failed to parse new TLS config json") + } + + if data.CertificateChain != "" { + certPEM, err := base64.StdEncoding.DecodeString(data.CertificateChain) + if err != nil { + return data, errorx.Decorate(err, "Failed to base64-decode certificate chain") + } + data.CertificateChain = string(certPEM) + } + + if data.PrivateKey != "" { + keyPEM, err := base64.StdEncoding.DecodeString(data.PrivateKey) + if err != nil { + return data, errorx.Decorate(err, "Failed to base64-decode private key") + } + + data.PrivateKey = string(keyPEM) + } + + return data, nil +} + +// ------------------------ +// registration of handlers +// ------------------------ func registerInstallHandlers() { http.HandleFunc("/control/install/get_addresses", preInstall(ensureGET(handleInstallGetAddresses))) http.HandleFunc("/control/install/configure", preInstall(ensurePOST(handleInstallConfigure))) From 5cbaeb82a8b3cabdba1d5d4decc67c3fe0dc7d6f Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Tue, 12 Feb 2019 20:08:11 +0300 Subject: [PATCH 25/88] Introduce /tls/validate and validateCertificates() that will also be used by /tls/configure --- control.go | 44 +++++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/control.go b/control.go index 6717acd8..d89d37d4 100644 --- a/control.go +++ b/control.go @@ -1050,6 +1050,20 @@ func handleTLSStatus(w http.ResponseWriter, r *http.Request) { } } +func handleTLSValidate(w http.ResponseWriter, r *http.Request) { + data, err := unmarshalTLS(r) + if err != nil { + httpError(w, http.StatusBadRequest, "Failed to unmarshal TLS config: %s", err) + return + } + + data, err = validateCertificates(data) + if err != nil { + httpError(w, http.StatusBadRequest, "New TLS configuration does not validate: %s", err) + return + } +} + func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { data, err := unmarshalTLS(r) if err != nil { @@ -1057,14 +1071,25 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { return } + data, err = validateCertificates(data) + if err != nil { + httpError(w, http.StatusBadRequest, "New TLS configuration does not validate: %s", err) + return + } + config.TLS = data + httpUpdateConfigReloadDNSReturnOK(w, r) +} + +func validateCertificates(data tlsConfig) (tlsConfig, error) { + var err error + if data.CertificateChain != "" { log.Printf("got certificate: %s", data.CertificateChain) if data.PrivateKey != "" { _, err = tls.X509KeyPair([]byte(data.CertificateChain), []byte(data.PrivateKey)) if err != nil { - httpError(w, http.StatusBadRequest, "Invalid certificate or key: %s", err) - return + return data, errorx.Decorate(err, "Invalid certificate or key") } } @@ -1091,15 +1116,13 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { for _, cert := range certs { parsed, err := x509.ParseCertificate(cert.Bytes) if err != nil { - httpError(w, http.StatusBadRequest, "failed to parse certificate: %s", err) - return + return data, errorx.Decorate(err, "Failed to parse certificate") } parsedCerts = append(parsedCerts, parsed) } if len(parsedCerts) == 0 { - httpError(w, http.StatusBadRequest, "You have specified an empty certificate") - return + return data, fmt.Errorf("You have specified an empty certificate") } // spew.Dump(parsedCerts) @@ -1121,11 +1144,10 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { // TODO: save it as a warning rather than error it out -- shouldn't be a big problem mainCert := parsedCerts[0] - _, err = mainCert.Verify(opts) + _, err := mainCert.Verify(opts) if err != nil { // TODO: let self-signed certs through - httpError(w, http.StatusBadRequest, "Your certificate does not verify: %s", err) - return + return data, errorx.Decorate(err, "Your certificate does not verify") } // spew.Dump(chains) @@ -1151,8 +1173,7 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { } } - config.TLS = data - httpUpdateConfigReloadDNSReturnOK(w, r) + return data, nil } // unmarshalTLS handles base64-encoded certificates transparently @@ -1232,4 +1253,5 @@ func registerControlHandlers() { http.HandleFunc("/control/tls/status", postInstall(optionalAuth(ensureGET(handleTLSStatus)))) http.HandleFunc("/control/tls/configure", postInstall(optionalAuth(ensurePOST(handleTLSConfigure)))) + http.HandleFunc("/control/tls/validate", postInstall(optionalAuth(ensurePOST(handleTLSValidate)))) } From 30050bf278aafc1b3425f75305631babbf06ae7d Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Tue, 12 Feb 2019 21:14:02 +0300 Subject: [PATCH 26/88] Spin up an HTTPS server when certificates, port and private key are configured. --- app.go | 43 +++++++++++++++++++++++++++++++++++++++++++ control.go | 1 + 2 files changed, 44 insertions(+) diff --git a/app.go b/app.go index 61870582..d50aadb8 100644 --- a/app.go +++ b/app.go @@ -1,6 +1,7 @@ package main import ( + "crypto/tls" "fmt" stdlog "log" "net" @@ -10,6 +11,7 @@ import ( "path/filepath" "runtime" "strconv" + "sync" "syscall" "time" @@ -21,6 +23,11 @@ import ( // VersionString will be set through ldflags, contains current version var VersionString = "undefined" var httpServer *http.Server +var httpsServer struct { + server *http.Server + cond *sync.Cond // reacts to config.TLS.PortHTTPS, CertificateChain and PrivateKey + sync.Mutex // protects config.TLS +} const ( // Used in config to indicate that syslog or eventlog (win) should be used for logger output @@ -159,6 +166,42 @@ func run(args options) { registerInstallHandlers() } + httpsServer.cond = sync.NewCond(&httpsServer.Mutex) + + // for https, we have a separate goroutine loop + go func() { + for { // this is an endless loop + httpsServer.cond.L.Lock() + // this mechanism doesn't let us through until all conditions are ment + for config.TLS.PortHTTPS == 0 || config.TLS.PrivateKey == "" || config.TLS.CertificateChain == "" { // sleep until neccessary data is supplied + httpsServer.cond.Wait() + } + log.Printf("%+v", config.TLS) + address := net.JoinHostPort(config.BindHost, strconv.Itoa(config.TLS.PortHTTPS)) + cert, err := tls.X509KeyPair([]byte(config.TLS.CertificateChain), []byte(config.TLS.PrivateKey)) + if err != nil { + log.Fatal(err) + os.Exit(1) + } + config := &tls.Config{ + Certificates: []tls.Certificate{cert}, + } + httpsServer.server = &http.Server{ + Addr: address, + TLSConfig: config, + } + httpsServer.cond.L.Unlock() + + URL := fmt.Sprintf("https://%s", address) + log.Println("Go to " + URL) + err = httpsServer.server.ListenAndServeTLS("", "") + if err != http.ErrServerClosed { + log.Fatal(err) + os.Exit(1) + } + } + }() + // this loop is used as an ability to change listening host and/or port for { address := net.JoinHostPort(config.BindHost, strconv.Itoa(config.BindPort)) diff --git a/control.go b/control.go index d89d37d4..72996c59 100644 --- a/control.go +++ b/control.go @@ -1077,6 +1077,7 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { return } config.TLS = data + httpsServer.cond.Broadcast() httpUpdateConfigReloadDNSReturnOK(w, r) } From 57a33654f7f48419e27b57cbe2050a278b4506cd Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Tue, 12 Feb 2019 21:14:23 +0300 Subject: [PATCH 27/88] Certificate that doesn't go through the chain is not fatal, just send the warning over json. --- config.go | 1 + control.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/config.go b/config.go index 21700586..6d2cb05a 100644 --- a/config.go +++ b/config.go @@ -74,6 +74,7 @@ type tlsConfig struct { StatusCertificate string `yaml:"status_cert" json:"status_cert,omitempty"` StatusKey string `yaml:"status_key" json:"status_key,omitempty"` Warning string `yaml:"warning" json:"warning,omitempty"` + WarningValidation string `yaml:"warning_validation" json:"warning_validation,omitempty"` } // initialize to default values, will be changed later when reading config or parsing command line diff --git a/control.go b/control.go index 72996c59..d9c56e5b 100644 --- a/control.go +++ b/control.go @@ -1147,8 +1147,8 @@ func validateCertificates(data tlsConfig) (tlsConfig, error) { mainCert := parsedCerts[0] _, err := mainCert.Verify(opts) if err != nil { - // TODO: let self-signed certs through - return data, errorx.Decorate(err, "Your certificate does not verify") + // let self-signed certs through + data.WarningValidation = fmt.Sprintf("Your certificate does not verify: %s", err) } // spew.Dump(chains) From 3e67c8d79a6a4dbe901a9322162c598c821384e3 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Tue, 12 Feb 2019 21:22:35 +0300 Subject: [PATCH 28/88] Older npm rewrote the package-lock.json again --- client/package-lock.json | 41 +++++++++++----------------------------- 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index fc06a0d6..380bd20d 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -5629,8 +5629,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -5651,14 +5650,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5673,20 +5670,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -5803,8 +5797,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -5816,7 +5809,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5831,7 +5823,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5839,14 +5830,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -5865,7 +5854,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -5946,8 +5934,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -5959,7 +5946,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -6045,8 +6031,7 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -6082,7 +6067,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6102,7 +6086,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6146,14 +6129,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, From 1d2958f4aadc32bda26dd77084022dd7ccecb804 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Tue, 12 Feb 2019 21:23:17 +0300 Subject: [PATCH 29/88] add temporary packr output to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9232c782..666e5612 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ /querylog.json.1 /scripts/translations/node_modules /scripts/translations/oneskyapp.json +/a_main-packr.go # Test output dnsfilter/tests/top-1m.csv From f417f6257fe0c1a0961ff6f6dfa483dde890c0cb Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Tue, 12 Feb 2019 21:25:01 +0300 Subject: [PATCH 30/88] release.sh -- there is no need to run make clean --- release.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/release.sh b/release.sh index 94cda10b..233c3285 100755 --- a/release.sh +++ b/release.sh @@ -17,10 +17,6 @@ f() { fi } -# Clean and rebuild both static and binary -make clean -make - # Prepare the dist folder rm -rf dist mkdir -p dist From 110434c2d53567b480c918ff181d2b68879be875 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Tue, 12 Feb 2019 21:31:03 +0300 Subject: [PATCH 31/88] Fix broken tar.gz not having a subdirectory inside. --- release.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/release.sh b/release.sh index 233c3285..189eab8f 100755 --- a/release.sh +++ b/release.sh @@ -9,16 +9,19 @@ version=`git describe --abbrev=4 --dirty --always --tags` f() { make cleanfast; CGO_DISABLED=1 make if [[ $GOOS == darwin ]]; then + rm -f dist/AdGuardHome_"$version"_MacOS.zip zip dist/AdGuardHome_"$version"_MacOS.zip AdGuardHome README.md LICENSE.txt elif [[ $GOOS == windows ]]; then + rm -f dist/AdGuardHome_"$version"_Windows.zip zip dist/AdGuardHome_"$version"_Windows.zip AdGuardHome.exe README.md LICENSE.txt else - tar zcvf dist/AdGuardHome_"$version"_"$GOOS"_"$GOARCH".tar.gz AdGuardHome README.md LICENSE.txt + pushd .. + tar zcvf AdGuardHome_"$version"_"$GOOS"_"$GOARCH".tar.gz AdGuardHome/{AdGuardHome,LICENSE.txt,README.md} + popd fi } # Prepare the dist folder -rm -rf dist mkdir -p dist # Prepare releases From e2956cae821a140cc3eb668bde96af365accaf97 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Tue, 12 Feb 2019 21:31:42 +0300 Subject: [PATCH 32/88] release.sh -- Place the targz into dist subdir --- release.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release.sh b/release.sh index 189eab8f..0f133a4b 100755 --- a/release.sh +++ b/release.sh @@ -16,7 +16,7 @@ f() { zip dist/AdGuardHome_"$version"_Windows.zip AdGuardHome.exe README.md LICENSE.txt else pushd .. - tar zcvf AdGuardHome_"$version"_"$GOOS"_"$GOARCH".tar.gz AdGuardHome/{AdGuardHome,LICENSE.txt,README.md} + tar zcvf AdGuardHome/dist/AdGuardHome_"$version"_"$GOOS"_"$GOARCH".tar.gz AdGuardHome/{AdGuardHome,LICENSE.txt,README.md} popd fi } From f0569af367b31742336ffefcb6df7839f446ffc6 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Wed, 13 Feb 2019 11:07:24 +0300 Subject: [PATCH 33/88] Remove redundant printf --- app.go | 1 - 1 file changed, 1 deletion(-) diff --git a/app.go b/app.go index d50aadb8..b57d052c 100644 --- a/app.go +++ b/app.go @@ -176,7 +176,6 @@ func run(args options) { for config.TLS.PortHTTPS == 0 || config.TLS.PrivateKey == "" || config.TLS.CertificateChain == "" { // sleep until neccessary data is supplied httpsServer.cond.Wait() } - log.Printf("%+v", config.TLS) address := net.JoinHostPort(config.BindHost, strconv.Itoa(config.TLS.PortHTTPS)) cert, err := tls.X509KeyPair([]byte(config.TLS.CertificateChain), []byte(config.TLS.PrivateKey)) if err != nil { From 28df187012ccd456524614a6ee96ce65d396a97f Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Wed, 13 Feb 2019 11:08:07 +0300 Subject: [PATCH 34/88] /tls/configure -- restart HTTPS server if settings changed Fixes not using new HTTPS certificate after submitting it. --- config.go | 14 ++++++++++---- control.go | 15 ++++++++++++++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/config.go b/config.go index 6d2cb05a..51fa5bf8 100644 --- a/config.go +++ b/config.go @@ -61,14 +61,18 @@ type dnsConfig struct { var defaultDNS = []string{"tls://1.1.1.1", "tls://1.0.0.1"} -// field ordering is important -- yaml fields will mirror ordering from here -type tlsConfig struct { +type tlsConfigSettings struct { ServerName string `yaml:"server_name" json:"server_name,omitempty"` ForceHTTPS bool `yaml:"force_https" json:"force_https,omitempty"` PortHTTPS int `yaml:"port_https" json:"port_https,omitempty"` PortDNSOverTLS int `yaml:"port_dns_over_tls" json:"port_dns_over_tls,omitempty"` dnsforward.TLSConfig `yaml:",inline" json:",inline"` +} + +// field ordering is important -- yaml fields will mirror ordering from here +type tlsConfig struct { + tlsConfigSettings `yaml:",inline" json:",inline"` // only for API, no need to be stored in config StatusCertificate string `yaml:"status_cert" json:"status_cert,omitempty"` @@ -97,8 +101,10 @@ var config = configuration{ UpstreamDNS: defaultDNS, }, TLS: tlsConfig{ - PortHTTPS: 443, - PortDNSOverTLS: 853, // needs to be passed through to dnsproxy + tlsConfigSettings: tlsConfigSettings{ + PortHTTPS: 443, + PortDNSOverTLS: 853, // needs to be passed through to dnsproxy + }, }, Filters: []filter{ {Filter: dnsfilter.Filter{ID: 1}, Enabled: true, URL: "https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt", Name: "AdGuard Simplified Domain Names filter"}, diff --git a/control.go b/control.go index d9c56e5b..9a4bc85e 100644 --- a/control.go +++ b/control.go @@ -13,6 +13,7 @@ import ( "net" "net/http" "os" + "reflect" "sort" "strconv" "strings" @@ -1076,9 +1077,21 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { httpError(w, http.StatusBadRequest, "New TLS configuration does not validate: %s", err) return } + restartHTTPS := false + if !reflect.DeepEqual(config.TLS.tlsConfigSettings, data.tlsConfigSettings) { + log.Printf("tls config settings have changed, will restart HTTPS server") + restartHTTPS = true + } config.TLS = data - httpsServer.cond.Broadcast() httpUpdateConfigReloadDNSReturnOK(w, r) + // this needs to be done in a goroutine because Shutdown() is a blocking call, and it will block + // until all requests are finished, and _we_ are inside a request right now, so it will block indefinitely + if restartHTTPS { + go func() { + httpsServer.cond.Broadcast() + httpsServer.server.Shutdown(context.TODO()) + }() + } } func validateCertificates(data tlsConfig) (tlsConfig, error) { From bdec98f18e0c8bf0e12b521b9ed6e11d9e6edb8f Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Wed, 13 Feb 2019 11:08:44 +0300 Subject: [PATCH 35/88] Properly calculate if certificate expires in 30 minutes or not. --- control.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/control.go b/control.go index 9a4bc85e..8b9e8d22 100644 --- a/control.go +++ b/control.go @@ -1167,7 +1167,8 @@ func validateCertificates(data tlsConfig) (tlsConfig, error) { // update status if mainCert != nil { - data.StatusCertificate = fmt.Sprintf("Certificate expires on %s", mainCert.NotAfter) //, valid for hostname %s", mainCert.NotAfter, mainCert.Subject.CommonName) + notAfter := mainCert.NotAfter + data.StatusCertificate = fmt.Sprintf("Certificate expires on %s", notAfter) //, valid for hostname %s", mainCert.NotAfter, mainCert.Subject.CommonName) if len(mainCert.DNSNames) == 1 { data.StatusCertificate += fmt.Sprintf(", valid for hostname %s", mainCert.DNSNames[0]) } else if len(mainCert.DNSNames) > 1 { @@ -1176,8 +1177,8 @@ func validateCertificates(data tlsConfig) (tlsConfig, error) { // issue a warning if certificate is about to expire now := time.Now() - if mainCert.NotAfter.AddDate(0, 0, -30).After(now) { - timeLeft := time.Until(mainCert.NotAfter) + if now.AddDate(0, 0, 30).After(notAfter) { + timeLeft := notAfter.Sub(now) if timeLeft > 0 { data.Warning = fmt.Sprintf("Your certificate expires in %.0f days, we recommend you update it soon", timeLeft.Hours()/24) } else { From 571be687336ad874eef025061112b3dfed0fab5d Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Wed, 13 Feb 2019 11:45:23 +0300 Subject: [PATCH 36/88] Validate certificates and update certificate statuses on launch as well. --- app.go | 24 +++++++++++++++++------- config.go | 15 +++++++++------ control.go | 3 +++ 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/app.go b/app.go index b57d052c..f0f2addf 100644 --- a/app.go +++ b/app.go @@ -177,20 +177,30 @@ func run(args options) { httpsServer.cond.Wait() } address := net.JoinHostPort(config.BindHost, strconv.Itoa(config.TLS.PortHTTPS)) + // validate current TLS config and update warnings (it could have been loaded from file) + data, err := validateCertificates(config.TLS) + if err != nil { + log.Fatal(err) + os.Exit(1) + } + confing.TLS = data // update warnings + + // prepare cert for HTTPS server cert, err := tls.X509KeyPair([]byte(config.TLS.CertificateChain), []byte(config.TLS.PrivateKey)) if err != nil { log.Fatal(err) os.Exit(1) } - config := &tls.Config{ - Certificates: []tls.Certificate{cert}, - } - httpsServer.server = &http.Server{ - Addr: address, - TLSConfig: config, - } httpsServer.cond.L.Unlock() + // prepare HTTPS server + httpsServer.server = &http.Server{ + Addr: address, + TLSConfig: &tls.Config{ + Certificates: []tls.Certificate{cert}, + }, + } + URL := fmt.Sprintf("https://%s", address) log.Println("Go to " + URL) err = httpsServer.server.ListenAndServeTLS("", "") diff --git a/config.go b/config.go index 51fa5bf8..bd026649 100644 --- a/config.go +++ b/config.go @@ -70,15 +70,18 @@ type tlsConfigSettings struct { dnsforward.TLSConfig `yaml:",inline" json:",inline"` } +// field ordering is not important -- these are for API and are recalculated on each run +type tlsConfigStatus struct { + StatusCertificate string `yaml:"-" json:"status_cert,omitempty"` + StatusKey string `yaml:"-" json:"status_key,omitempty"` + Warning string `yaml:"-" json:"warning,omitempty"` + WarningValidation string `yaml:"-" json:"warning_validation,omitempty"` +} + // field ordering is important -- yaml fields will mirror ordering from here type tlsConfig struct { tlsConfigSettings `yaml:",inline" json:",inline"` - - // only for API, no need to be stored in config - StatusCertificate string `yaml:"status_cert" json:"status_cert,omitempty"` - StatusKey string `yaml:"status_key" json:"status_key,omitempty"` - Warning string `yaml:"warning" json:"warning,omitempty"` - WarningValidation string `yaml:"warning_validation" json:"warning_validation,omitempty"` + tlsConfigStatus `yaml:"-" json:",inline"` } // initialize to default values, will be changed later when reading config or parsing command line diff --git a/control.go b/control.go index 8b9e8d22..65a7077e 100644 --- a/control.go +++ b/control.go @@ -1156,6 +1156,9 @@ func validateCertificates(data tlsConfig) (tlsConfig, error) { opts.Intermediates = pool } + // clear out all warnings and statuses + data.tlsConfigStatus = tlsConfigStatus{} + // TODO: save it as a warning rather than error it out -- shouldn't be a big problem mainCert := parsedCerts[0] _, err := mainCert.Verify(opts) From e8280c60d8dfabf8ffa146e84713db60854a46c5 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Wed, 13 Feb 2019 11:46:11 +0300 Subject: [PATCH 37/88] =?UTF-8?q?/tls/status=20=E2=80=94=20Add=20`not=5Faf?= =?UTF-8?q?ter`=20field=20with=20a=20valid=20certificate=20expiration=20da?= =?UTF-8?q?te.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.go | 1 + control.go | 1 + 2 files changed, 2 insertions(+) diff --git a/config.go b/config.go index bd026649..799fbc12 100644 --- a/config.go +++ b/config.go @@ -72,6 +72,7 @@ type tlsConfigSettings struct { // field ordering is not important -- these are for API and are recalculated on each run type tlsConfigStatus struct { + NotAfter string `yaml:"-" json:"not_after,omitempty"` StatusCertificate string `yaml:"-" json:"status_cert,omitempty"` StatusKey string `yaml:"-" json:"status_key,omitempty"` Warning string `yaml:"-" json:"warning,omitempty"` diff --git a/control.go b/control.go index 65a7077e..bd7b673c 100644 --- a/control.go +++ b/control.go @@ -1171,6 +1171,7 @@ func validateCertificates(data tlsConfig) (tlsConfig, error) { // update status if mainCert != nil { notAfter := mainCert.NotAfter + data.NotAfter = notAfter.Format(time.RFC3339) data.StatusCertificate = fmt.Sprintf("Certificate expires on %s", notAfter) //, valid for hostname %s", mainCert.NotAfter, mainCert.Subject.CommonName) if len(mainCert.DNSNames) == 1 { data.StatusCertificate += fmt.Sprintf(", valid for hostname %s", mainCert.DNSNames[0]) From b4b800565c9602b8e3d1690dd98b6e9114bc0889 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Wed, 13 Feb 2019 11:46:52 +0300 Subject: [PATCH 38/88] Fixup for "validate certificates". --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index f0f2addf..0d473a6f 100644 --- a/app.go +++ b/app.go @@ -183,7 +183,7 @@ func run(args options) { log.Fatal(err) os.Exit(1) } - confing.TLS = data // update warnings + config.TLS = data // update warnings // prepare cert for HTTPS server cert, err := tls.X509KeyPair([]byte(config.TLS.CertificateChain), []byte(config.TLS.PrivateKey)) From 8da90a7f4afafbbe67dc4b9aa04dc531a3db1bce Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Thu, 14 Feb 2019 17:33:50 +0300 Subject: [PATCH 39/88] Fix panic when https server is not running --- control.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control.go b/control.go index bd7b673c..cc82abab 100644 --- a/control.go +++ b/control.go @@ -1086,7 +1086,7 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { httpUpdateConfigReloadDNSReturnOK(w, r) // this needs to be done in a goroutine because Shutdown() is a blocking call, and it will block // until all requests are finished, and _we_ are inside a request right now, so it will block indefinitely - if restartHTTPS { + if restartHTTPS && httpsServer.server != nil { go func() { httpsServer.cond.Broadcast() httpsServer.server.Shutdown(context.TODO()) From 81bb4aea787a927f94897fa016127aee753cea25 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Thu, 14 Feb 2019 18:00:23 +0300 Subject: [PATCH 40/88] /tls/configure and /tls/status -- now there's an explicit 'enabled' bool. --- app.go | 4 ++-- config.go | 1 + dns.go | 8 +++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app.go b/app.go index 0d473a6f..ba2fce74 100644 --- a/app.go +++ b/app.go @@ -25,7 +25,7 @@ var VersionString = "undefined" var httpServer *http.Server var httpsServer struct { server *http.Server - cond *sync.Cond // reacts to config.TLS.PortHTTPS, CertificateChain and PrivateKey + cond *sync.Cond // reacts to config.TLS.Enabled, PortHTTPS, CertificateChain and PrivateKey sync.Mutex // protects config.TLS } @@ -173,7 +173,7 @@ func run(args options) { for { // this is an endless loop httpsServer.cond.L.Lock() // this mechanism doesn't let us through until all conditions are ment - for config.TLS.PortHTTPS == 0 || config.TLS.PrivateKey == "" || config.TLS.CertificateChain == "" { // sleep until neccessary data is supplied + for config.TLS.Enabled == false || config.TLS.PortHTTPS == 0 || config.TLS.PrivateKey == "" || config.TLS.CertificateChain == "" { // sleep until neccessary data is supplied httpsServer.cond.Wait() } address := net.JoinHostPort(config.BindHost, strconv.Itoa(config.TLS.PortHTTPS)) diff --git a/config.go b/config.go index 799fbc12..a003d593 100644 --- a/config.go +++ b/config.go @@ -62,6 +62,7 @@ type dnsConfig struct { var defaultDNS = []string{"tls://1.1.1.1", "tls://1.0.0.1"} type tlsConfigSettings struct { + Enabled bool `yaml:"enaled" json:"enabled"` ServerName string `yaml:"server_name" json:"server_name,omitempty"` ForceHTTPS bool `yaml:"force_https" json:"force_https,omitempty"` PortHTTPS int `yaml:"port_https" json:"port_https,omitempty"` diff --git a/dns.go b/dns.go index 4a5102d5..adb0d896 100644 --- a/dns.go +++ b/dns.go @@ -51,9 +51,11 @@ func generateServerConfig() dnsforward.ServerConfig { Filters: filters, } - newconfig.TLSConfig = config.TLS.TLSConfig - if config.TLS.PortDNSOverTLS != 0 { - newconfig.TLSListenAddr = &net.TCPAddr{IP: net.ParseIP(config.DNS.BindHost), Port: config.TLS.PortDNSOverTLS} + if config.TLS.Enabled { + newconfig.TLSConfig = config.TLS.TLSConfig + if config.TLS.PortDNSOverTLS != 0 { + newconfig.TLSListenAddr = &net.TCPAddr{IP: net.ParseIP(config.DNS.BindHost), Port: config.TLS.PortDNSOverTLS} + } } for _, u := range config.DNS.UpstreamDNS { From cb97c221fde947fd3449d66717c232b127322b29 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Fri, 15 Feb 2019 15:16:25 +0300 Subject: [PATCH 41/88] /tls/validate and /tls/configure -- do checks on private key, add more fields to certificate status, do keypair check last. --- config.go | 18 +++++++++-- control.go | 92 +++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 99 insertions(+), 11 deletions(-) diff --git a/config.go b/config.go index a003d593..89e7a75b 100644 --- a/config.go +++ b/config.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" "sync" + "time" "github.com/AdguardTeam/AdGuardHome/dhcpd" "github.com/AdguardTeam/AdGuardHome/dnsfilter" @@ -73,9 +74,20 @@ type tlsConfigSettings struct { // field ordering is not important -- these are for API and are recalculated on each run type tlsConfigStatus struct { - NotAfter string `yaml:"-" json:"not_after,omitempty"` - StatusCertificate string `yaml:"-" json:"status_cert,omitempty"` - StatusKey string `yaml:"-" json:"status_key,omitempty"` + // certificate status + ValidChain bool `yaml:"-" json:"valid_chain"` + Subject string `yaml:"-" json:"subject,omitempty"` + Issuer string `yaml:"-" json:"issuer,omitempty"` + NotBefore time.Time `yaml:"-" json:"not_before,omitempty"` + NotAfter time.Time `yaml:"-" json:"not_after,omitempty"` + DNSNames []string `yaml:"-" json:"dns_names"` + StatusCertificate string `yaml:"-" json:"status_cert,omitempty"` + + // key status + ValidKey bool `yaml:"-" json:"valid_key"` + KeyType string `yaml:"-" json:"key_type,omitempty"` + + // warnings Warning string `yaml:"-" json:"warning,omitempty"` WarningValidation string `yaml:"-" json:"warning_validation,omitempty"` } diff --git a/control.go b/control.go index cc82abab..c507def2 100644 --- a/control.go +++ b/control.go @@ -3,11 +3,15 @@ package main import ( "bytes" "context" + "crypto" + "crypto/ecdsa" + "crypto/rsa" "crypto/tls" "crypto/x509" "encoding/base64" "encoding/json" "encoding/pem" + "errors" "fmt" "io/ioutil" "net" @@ -1097,16 +1101,13 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { func validateCertificates(data tlsConfig) (tlsConfig, error) { var err error + // clear out status for certificates + data.tlsConfigStatus = tlsConfigStatus{} + + // check only public certificate separetely from the key if data.CertificateChain != "" { log.Printf("got certificate: %s", data.CertificateChain) - if data.PrivateKey != "" { - _, err = tls.X509KeyPair([]byte(data.CertificateChain), []byte(data.PrivateKey)) - if err != nil { - return data, errorx.Decorate(err, "Invalid certificate or key") - } - } - // now do a more extended validation var certs []*pem.Block // PEM-encoded certificates var skippedBytes []string // skipped bytes @@ -1165,13 +1166,20 @@ func validateCertificates(data tlsConfig) (tlsConfig, error) { if err != nil { // let self-signed certs through data.WarningValidation = fmt.Sprintf("Your certificate does not verify: %s", err) + } else { + data.ValidChain = true } // spew.Dump(chains) // update status if mainCert != nil { notAfter := mainCert.NotAfter - data.NotAfter = notAfter.Format(time.RFC3339) + data.Subject = mainCert.Subject.String() + data.Issuer = mainCert.Issuer.String() + data.NotAfter = notAfter + data.NotBefore = mainCert.NotBefore + data.DNSNames = mainCert.DNSNames + data.StatusCertificate = fmt.Sprintf("Certificate expires on %s", notAfter) //, valid for hostname %s", mainCert.NotAfter, mainCert.Subject.CommonName) if len(mainCert.DNSNames) == 1 { data.StatusCertificate += fmt.Sprintf(", valid for hostname %s", mainCert.DNSNames[0]) @@ -1192,9 +1200,77 @@ func validateCertificates(data tlsConfig) (tlsConfig, error) { } } + // validate private key (right now the only validation possible is just parsing it) + if data.PrivateKey != "" { + // now do a more extended validation + var key *pem.Block // PEM-encoded certificates + var skippedBytes []string // skipped bytes + + // go through all pem blocks, but take first valid pem block and drop the rest + pemblock := []byte(data.PrivateKey) + for { + var decoded *pem.Block + decoded, pemblock = pem.Decode(pemblock) + if decoded == nil { + break + } + if decoded.Type == "PRIVATE KEY" || strings.HasSuffix(decoded.Type, " PRIVATE KEY") { + key = decoded + break + } else { + skippedBytes = append(skippedBytes, decoded.Type) + } + } + + if key == nil { + return data, fmt.Errorf("No valid keys were found") + } + + // parse the decoded key + _, keytype, err := parsePrivateKey(key.Bytes) + if err != nil { + return data, errorx.Decorate(err, "Failed to parse private key") + } + + data.ValidKey = true + data.KeyType = keytype + } + + // if both are set, validate both in unison + if data.PrivateKey != "" && data.CertificateChain != "" { + _, err = tls.X509KeyPair([]byte(data.CertificateChain), []byte(data.PrivateKey)) + if err != nil { + return data, errorx.Decorate(err, "Invalid certificate or key") + } + } + return data, nil } +// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates +// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys. +// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three. +func parsePrivateKey(der []byte) (crypto.PrivateKey, string, error) { + if key, err := x509.ParsePKCS1PrivateKey(der); err == nil { + return key, "RSA", nil + } + if key, err := x509.ParsePKCS8PrivateKey(der); err == nil { + switch key := key.(type) { + case *rsa.PrivateKey: + return key, "RSA", nil + case *ecdsa.PrivateKey: + return key, "ECDSA", nil + default: + return nil, "", errors.New("tls: found unknown private key type in PKCS#8 wrapping") + } + } + if key, err := x509.ParseECPrivateKey(der); err == nil { + return key, "ECDSA", nil + } + + return nil, "", errors.New("tls: failed to parse private key") +} + // unmarshalTLS handles base64-encoded certificates transparently func unmarshalTLS(r *http.Request) (tlsConfig, error) { data := tlsConfig{} From d44f68e844591efa64f5912d71c20bed366e5510 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Fri, 15 Feb 2019 17:06:55 +0300 Subject: [PATCH 42/88] /tls/configure and /tls/validate -- make validation failures non-fatal --- app.go | 6 ++-- control.go | 83 +++++++++++++++++++++++++++++------------------------- 2 files changed, 48 insertions(+), 41 deletions(-) diff --git a/app.go b/app.go index ba2fce74..800b0a43 100644 --- a/app.go +++ b/app.go @@ -178,9 +178,9 @@ func run(args options) { } address := net.JoinHostPort(config.BindHost, strconv.Itoa(config.TLS.PortHTTPS)) // validate current TLS config and update warnings (it could have been loaded from file) - data, err := validateCertificates(config.TLS) - if err != nil { - log.Fatal(err) + data := validateCertificates(config.TLS) + if data.WarningValidation != "" { + log.Fatal(data.WarningValidation) os.Exit(1) } config.TLS = data // update warnings diff --git a/control.go b/control.go index c507def2..af1a6d4c 100644 --- a/control.go +++ b/control.go @@ -78,9 +78,7 @@ func writeAllConfigsAndReloadDNS() error { func httpUpdateConfigReloadDNSReturnOK(w http.ResponseWriter, r *http.Request) { err := writeAllConfigsAndReloadDNS() if err != nil { - errorText := fmt.Sprintf("Couldn't write config file: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError, "Couldn't write config file: %s", err) return } returnOK(w) @@ -1039,20 +1037,7 @@ func handleInstallConfigure(w http.ResponseWriter, r *http.Request) { // TLS // --- func handleTLSStatus(w http.ResponseWriter, r *http.Request) { - data := config.TLS - if data.CertificateChain != "" { - encoded := base64.StdEncoding.EncodeToString([]byte(data.CertificateChain)) - data.CertificateChain = string(encoded) - } - if data.PrivateKey != "" { - encoded := base64.StdEncoding.EncodeToString([]byte(data.PrivateKey)) - data.PrivateKey = string(encoded) - } - err := json.NewEncoder(w).Encode(&data) - if err != nil { - httpError(w, http.StatusInternalServerError, "Failed to marshal json with TLS status: %s", err) - return - } + marshalTLS(w, config.TLS) } func handleTLSValidate(w http.ResponseWriter, r *http.Request) { @@ -1062,11 +1047,8 @@ func handleTLSValidate(w http.ResponseWriter, r *http.Request) { return } - data, err = validateCertificates(data) - if err != nil { - httpError(w, http.StatusBadRequest, "New TLS configuration does not validate: %s", err) - return - } + data = validateCertificates(data) + marshalTLS(w, data) } func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { @@ -1076,18 +1058,21 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { return } - data, err = validateCertificates(data) + restartHTTPS := false + data = validateCertificates(data) + if data.WarningValidation == "" { + if !reflect.DeepEqual(config.TLS.tlsConfigSettings, data.tlsConfigSettings) { + log.Printf("tls config settings have changed, will restart HTTPS server") + restartHTTPS = true + } + config.TLS = data + } + err = writeAllConfigsAndReloadDNS() if err != nil { - httpError(w, http.StatusBadRequest, "New TLS configuration does not validate: %s", err) + httpError(w, http.StatusInternalServerError, "Couldn't write config file: %s", err) return } - restartHTTPS := false - if !reflect.DeepEqual(config.TLS.tlsConfigSettings, data.tlsConfigSettings) { - log.Printf("tls config settings have changed, will restart HTTPS server") - restartHTTPS = true - } - config.TLS = data - httpUpdateConfigReloadDNSReturnOK(w, r) + marshalTLS(w, data) // this needs to be done in a goroutine because Shutdown() is a blocking call, and it will block // until all requests are finished, and _we_ are inside a request right now, so it will block indefinitely if restartHTTPS && httpsServer.server != nil { @@ -1098,7 +1083,7 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { } } -func validateCertificates(data tlsConfig) (tlsConfig, error) { +func validateCertificates(data tlsConfig) tlsConfig { var err error // clear out status for certificates @@ -1131,13 +1116,15 @@ func validateCertificates(data tlsConfig) (tlsConfig, error) { for _, cert := range certs { parsed, err := x509.ParseCertificate(cert.Bytes) if err != nil { - return data, errorx.Decorate(err, "Failed to parse certificate") + data.WarningValidation = fmt.Sprintf("Failed to parse certificate: %s", err) + return data } parsedCerts = append(parsedCerts, parsed) } if len(parsedCerts) == 0 { - return data, fmt.Errorf("You have specified an empty certificate") + data.WarningValidation = fmt.Sprintf("You have specified an empty certificate") + return data } // spew.Dump(parsedCerts) @@ -1223,13 +1210,15 @@ func validateCertificates(data tlsConfig) (tlsConfig, error) { } if key == nil { - return data, fmt.Errorf("No valid keys were found") + data.WarningValidation = "No valid keys were found" + return data } // parse the decoded key _, keytype, err := parsePrivateKey(key.Bytes) if err != nil { - return data, errorx.Decorate(err, "Failed to parse private key") + data.WarningValidation = fmt.Sprintf("Failed to parse private key: %s", err) + return data } data.ValidKey = true @@ -1240,11 +1229,12 @@ func validateCertificates(data tlsConfig) (tlsConfig, error) { if data.PrivateKey != "" && data.CertificateChain != "" { _, err = tls.X509KeyPair([]byte(data.CertificateChain), []byte(data.PrivateKey)) if err != nil { - return data, errorx.Decorate(err, "Invalid certificate or key") + data.WarningValidation = fmt.Sprintf("Invalid certificate or key: %s", err) + return data } } - return data, nil + return data } // Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates @@ -1299,6 +1289,23 @@ func unmarshalTLS(r *http.Request) (tlsConfig, error) { return data, nil } +func marshalTLS(w http.ResponseWriter, data tlsConfig) { + w.Header().Set("Content-Type", "application/json") + if data.CertificateChain != "" { + encoded := base64.StdEncoding.EncodeToString([]byte(data.CertificateChain)) + data.CertificateChain = string(encoded) + } + if data.PrivateKey != "" { + encoded := base64.StdEncoding.EncodeToString([]byte(data.PrivateKey)) + data.PrivateKey = string(encoded) + } + err := json.NewEncoder(w).Encode(data) + if err != nil { + httpError(w, http.StatusInternalServerError, "Failed to marshal json with TLS status: %s", err) + return + } +} + // ------------------------ // registration of handlers // ------------------------ From 05cce8b1073dd4c005157177bdc129cba813a12c Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Mon, 18 Feb 2019 16:06:27 +0300 Subject: [PATCH 43/88] Added validation on change and enable encryption checkbox --- client/src/__locales/en.json | 15 +- client/src/actions/encryption.js | 70 ++++++++ client/src/actions/index.js | 41 ----- client/src/api/Api.js | 10 ++ client/src/components/App/index.js | 12 +- client/src/components/Header/index.js | 4 +- .../components/Settings/Encryption/Form.js | 149 ++++++++++++++---- .../components/Settings/Encryption/index.js | 32 +++- client/src/components/Settings/index.js | 1 + client/src/components/ui/EncryptionTopline.js | 41 +++++ client/src/containers/Settings.js | 6 +- client/src/helpers/constants.js | 1 + client/src/reducers/encryption.js | 62 ++++++++ client/src/reducers/index.js | 33 +--- config.go | 2 +- 15 files changed, 356 insertions(+), 123 deletions(-) create mode 100644 client/src/actions/encryption.js create mode 100644 client/src/components/ui/EncryptionTopline.js create mode 100644 client/src/reducers/encryption.js diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 115bd6cb..59fa9e75 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -219,18 +219,27 @@ "encryption_redirect": "Redirect to HTTPS automatically", "encryption_redirect_desc": "If checked, AdGuard Home will automatically redirect you from HTTP to HTTPS addresses.", "encryption_https": "HTTPS port", - "encryption_https_desc": "If HTTPS port is configured, AdGuard Home admin interface will be accessible via HTTPS, and it will also provide DNS-over-HTTPS on '\\dns-query' location.", + "encryption_https_desc": "If HTTPS port is configured, AdGuard Home admin interface will be accessible via HTTPS, and it will also provide DNS-over-HTTPS on '\/dns-query' location.", "encryption_dot": "DNS-over-TLS port", "encryption_dot_desc": "If this port is configured, AdGuard Home will run a DNS-over-TLS server on this port.", "encryption_certificates": "Certificates", "encryption_certificates_desc": "In order to use encryption, you need to provide a valid SSL certificates chain for your domain. You can get a free certificate on <0>{{link}} or you can buy it from one of the trusted Certificate Authorities.", "encryption_certificates_input": "Copy/paste your PEM-encoded cerificates here.", "encryption_status": "Status", - "encryption_certificates_for": "Certificates for {{domains}}", - "encryption_expire": "Expire on {{date}}", + "encryption_expire": "Expires", "encryption_key": "Private key", "encryption_key_input": "Copy/paste your PEM-encoded private key for your cerficate here.", + "encryption_enable": "Enable Encryption (HTTPS, DNS-over-HTTPS, and DNS-over-TLS)", + "encryption_enable_desc": "If encryption is enabled, AdGuard Home admin interface will work over HTTPS, and the DNS server will listen for requests over DNS-over-HTTPS and DNS-over-TLS.", + "encryption_chain_valid": "Certificate chain is valid", + "encryption_chain_invalid": "Certificate chain is invalid", + "encryption_key_valid": "This is a valid {{type}} private key", + "encryption_key_invalid": "This is an invalid {{type}} private key", + "encryption_subject": "Subject", + "encryption_issuer": "Issuer", + "encryption_hostnames": "Hostnames", "topline_expiring_certificate": "Your SSL certificate is about to expire. Update <0>Encryption settings.", + "topline_expired_certificate": "Your SSL certificate is expired. Update <0>Encryption settings.", "form_error_port_range": "Enter port value in the range of 80-65535", "form_error_equal": "Shouldn't be equal", "form_error_password": "Password mismatched", diff --git a/client/src/actions/encryption.js b/client/src/actions/encryption.js new file mode 100644 index 00000000..a537f31d --- /dev/null +++ b/client/src/actions/encryption.js @@ -0,0 +1,70 @@ +import { createAction } from 'redux-actions'; +import Api from '../api/Api'; +import { addErrorToast, addSuccessToast } from './index'; + +const apiClient = new Api(); + +export const getTlsStatusRequest = createAction('GET_TLS_STATUS_REQUEST'); +export const getTlsStatusFailure = createAction('GET_TLS_STATUS_FAILURE'); +export const getTlsStatusSuccess = createAction('GET_TLS_STATUS_SUCCESS'); + +export const getTlsStatus = () => async (dispatch) => { + dispatch(getTlsStatusRequest()); + try { + const status = await apiClient.getTlsStatus(); + status.certificate_chain = atob(status.certificate_chain); + status.private_key = atob(status.private_key); + + dispatch(getTlsStatusSuccess(status)); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(getTlsStatusFailure()); + } +}; + +export const setTlsConfigRequest = createAction('SET_TLS_CONFIG_REQUEST'); +export const setTlsConfigFailure = createAction('SET_TLS_CONFIG_FAILURE'); +export const setTlsConfigSuccess = createAction('SET_TLS_CONFIG_SUCCESS'); + +export const setTlsConfig = config => async (dispatch) => { + dispatch(setTlsConfigRequest()); + try { + const values = { ...config }; + values.certificate_chain = btoa(values.certificate_chain); + values.private_key = btoa(values.private_key); + values.port_https = values.port_https || 0; + values.port_dns_over_tls = values.port_dns_over_tls || 0; + + const response = await apiClient.setTlsConfig(values); + response.certificate_chain = atob(response.certificate_chain); + response.private_key = atob(response.private_key); + dispatch(setTlsConfigSuccess(response)); + dispatch(addSuccessToast('encryption_config_saved')); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(setTlsConfigFailure()); + } +}; + +export const validateTlsConfigRequest = createAction('VALIDATE_TLS_CONFIG_REQUEST'); +export const validateTlsConfigFailure = createAction('VALIDATE_TLS_CONFIG_FAILURE'); +export const validateTlsConfigSuccess = createAction('VALIDATE_TLS_CONFIG_SUCCESS'); + +export const validateTlsConfig = config => async (dispatch) => { + dispatch(validateTlsConfigRequest()); + try { + const values = { ...config }; + values.certificate_chain = btoa(values.certificate_chain); + values.private_key = btoa(values.private_key); + values.port_https = values.port_https || 0; + values.port_dns_over_tls = values.port_dns_over_tls || 0; + + const response = await apiClient.validateTlsConfig(values); + response.certificate_chain = atob(response.certificate_chain); + response.private_key = atob(response.private_key); + dispatch(validateTlsConfigSuccess(response)); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(validateTlsConfigFailure()); + } +}; diff --git a/client/src/actions/index.js b/client/src/actions/index.js index 9b06352d..1bb99064 100644 --- a/client/src/actions/index.js +++ b/client/src/actions/index.js @@ -650,44 +650,3 @@ export const toggleDhcp = config => async (dispatch) => { } } }; - -export const getTlsStatusRequest = createAction('GET_TLS_STATUS_REQUEST'); -export const getTlsStatusFailure = createAction('GET_TLS_STATUS_FAILURE'); -export const getTlsStatusSuccess = createAction('GET_TLS_STATUS_SUCCESS'); - -export const getTlsStatus = () => async (dispatch) => { - dispatch(getTlsStatusRequest()); - try { - const status = await apiClient.getTlsStatus(); - status.certificate_chain = atob(status.certificate_chain); - status.private_key = atob(status.private_key); - - dispatch(getTlsStatusSuccess(status)); - } catch (error) { - dispatch(addErrorToast({ error })); - dispatch(getTlsStatusFailure()); - } -}; - -export const setTlsConfigRequest = createAction('SET_TLS_CONFIG_REQUEST'); -export const setTlsConfigFailure = createAction('SET_TLS_CONFIG_FAILURE'); -export const setTlsConfigSuccess = createAction('SET_TLS_CONFIG_SUCCESS'); - -export const setTlsConfig = config => async (dispatch) => { - dispatch(setTlsConfigRequest()); - try { - const values = { ...config }; - values.certificate_chain = btoa(values.certificate_chain); - values.private_key = btoa(values.private_key); - values.port_https = values.port_https || 0; - values.port_dns_over_tls = values.port_dns_over_tls || 0; - - await apiClient.setTlsConfig(values); - dispatch(setTlsConfigSuccess(config)); - dispatch(addSuccessToast('encryption_config_saved')); - dispatch(getTlsStatus()); - } catch (error) { - dispatch(addErrorToast({ error })); - dispatch(setTlsConfigFailure()); - } -}; diff --git a/client/src/api/Api.js b/client/src/api/Api.js index 1971fe4a..e77b083c 100644 --- a/client/src/api/Api.js +++ b/client/src/api/Api.js @@ -358,6 +358,7 @@ export default class Api { // DNS-over-HTTPS and DNS-over-TLS TLS_STATUS = { path: 'tls/status', method: 'GET' }; TLS_CONFIG = { path: 'tls/configure', method: 'POST' }; + TLS_VALIDATE = { path: 'tls/validate', method: 'POST' }; getTlsStatus() { const { path, method } = this.TLS_STATUS; @@ -372,4 +373,13 @@ export default class Api { }; return this.makeRequest(path, method, parameters); } + + validateTlsConfig(config) { + const { path, method } = this.TLS_VALIDATE; + const parameters = { + data: config, + headers: { 'Content-Type': 'application/json' }, + }; + return this.makeRequest(path, method, parameters); + } } diff --git a/client/src/components/App/index.js b/client/src/components/App/index.js index a756eadc..b483f64a 100644 --- a/client/src/components/App/index.js +++ b/client/src/components/App/index.js @@ -1,7 +1,7 @@ import React, { Component, Fragment } from 'react'; import { HashRouter, Route } from 'react-router-dom'; import PropTypes from 'prop-types'; -import { Trans, withNamespaces } from 'react-i18next'; +import { withNamespaces } from 'react-i18next'; import LoadingBar from 'react-redux-loading-bar'; import 'react-table/react-table.css'; @@ -18,6 +18,7 @@ import Footer from '../ui/Footer'; import Toasts from '../Toasts'; import Status from '../ui/Status'; import Topline from '../ui/Topline'; +import EncryptionTopline from '../ui/EncryptionTopline'; import i18n from '../../i18n'; class App extends Component { @@ -56,7 +57,6 @@ class App extends Component { !dashboard.processingVersions && dashboard.isCoreRunning && dashboard.isUpdateAvailable; - const isExpiringCertificate = !encryption.processing && encryption.warning; return ( @@ -66,12 +66,8 @@ class App extends Component { {dashboard.announcement}
Click here for more info. } - {isExpiringCertificate && - - link]}> - topline_expiring_certificate - - + {!encryption.processing && + } diff --git a/client/src/components/Header/index.js b/client/src/components/Header/index.js index 4c21a19a..ecc1a2af 100644 --- a/client/src/components/Header/index.js +++ b/client/src/components/Header/index.js @@ -12,7 +12,6 @@ import './Header.css'; class Header extends Component { state = { isMenuOpen: false, - isDropdownOpen: false, }; toggleMenuOpen = () => { @@ -25,6 +24,7 @@ class Header extends Component { render() { const { dashboard } = this.props; + const { isMenuOpen } = this.state; const badgeClass = classnames({ 'badge dns-status': true, 'badge-success': dashboard.protectionEnabled, @@ -52,7 +52,7 @@ class Header extends Component {
diff --git a/client/src/components/Settings/Encryption/Form.js b/client/src/components/Settings/Encryption/Form.js index db0f1b41..e42766ee 100644 --- a/client/src/components/Settings/Encryption/Form.js +++ b/client/src/components/Settings/Encryption/Form.js @@ -1,10 +1,13 @@ import React, { Fragment } from 'react'; +import { connect } from 'react-redux'; import PropTypes from 'prop-types'; -import { Field, reduxForm } from 'redux-form'; +import { Field, reduxForm, formValueSelector } from 'redux-form'; import { Trans, withNamespaces } from 'react-i18next'; import flow from 'lodash/flow'; +import format from 'date-fns/format'; import { renderField, renderSelectField, toNumber, port } from '../../../helpers/form'; +import { EMPTY_DATE } from '../../../helpers/constants'; import i18n from '../../../i18n'; const validate = (values) => { @@ -20,21 +23,46 @@ const validate = (values) => { return errors; }; -const Form = (props) => { +let Form = (props) => { const { t, handleSubmit, + handleChange, + isEnabled, + certificateChain, + privateKey, reset, invalid, submitting, processing, - statusCert, - statusKey, + not_after, + valid_chain, + valid_key, + dns_names, + key_type, + issuer, + subject, + warning_validation, } = props; return (
+
+
+ +
+
+ encryption_enable_desc +
+
+
-
+
@@ -284,7 +297,7 @@ Form.propTypes = { isEnabled: PropTypes.bool.isRequired, certificateChain: PropTypes.string.isRequired, privateKey: PropTypes.string.isRequired, - reset: PropTypes.func.isRequired, + change: PropTypes.func.isRequired, submitting: PropTypes.bool.isRequired, invalid: PropTypes.bool.isRequired, initialValues: PropTypes.object.isRequired, diff --git a/client/src/components/ui/Checkbox.css b/client/src/components/ui/Checkbox.css index 1778a93c..87e71793 100644 --- a/client/src/components/ui/Checkbox.css +++ b/client/src/components/ui/Checkbox.css @@ -73,14 +73,19 @@ opacity: 0; } -.checkbox__input:checked+.checkbox__label:before { +.checkbox__input:checked + .checkbox__label:before { background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMi4zIDkuMiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiPjxwYXRoIGQ9Ik0xMS44IDAuNUw1LjMgOC41IDAuNSA0LjIiLz48L3N2Zz4=); } -.checkbox__input:focus+.checkbox__label:before { +.checkbox__input:focus + .checkbox__label:before { box-shadow: 0 0 1px 1px rgba(74, 74, 74, 0.32); } +.checkbox__input:disabled + .checkbox__label { + opacity: 0.7; + cursor: default; +} + .checkbox__label-text { max-width: 515px; line-height: 1.5; From 2748d4c889627ecfe350b6d89e879771694c3f5f Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Tue, 19 Feb 2019 15:19:11 +0300 Subject: [PATCH 50/88] /tls/configure -- check if https port is usable before accepting the new config --- control.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/control.go b/control.go index af1a6d4c..13c36d84 100644 --- a/control.go +++ b/control.go @@ -1047,6 +1047,20 @@ func handleTLSValidate(w http.ResponseWriter, r *http.Request) { return } + // check if port is available + // BUT: if we are already using this port, no need + alreadyRunning := false + if httpsServer.server != nil { + alreadyRunning = true + } + if !alreadyRunning { + err = checkPortAvailable(config.BindHost, data.PortHTTPS) + if err != nil { + httpError(w, http.StatusBadRequest, "port %d is not available, cannot enable HTTPS on it", data.PortHTTPS) + return + } + } + data = validateCertificates(data) marshalTLS(w, data) } @@ -1058,6 +1072,20 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { return } + // check if port is available + // BUT: if we are already using this port, no need + alreadyRunning := false + if httpsServer.server != nil { + alreadyRunning = true + } + if !alreadyRunning { + err = checkPortAvailable(config.BindHost, data.PortHTTPS) + if err != nil { + httpError(w, http.StatusBadRequest, "port %d is not available, cannot enable HTTPS on it", data.PortHTTPS) + return + } + } + restartHTTPS := false data = validateCertificates(data) if data.WarningValidation == "" { From ba103f9825ff601eb653e0ad0b0d0cca8966cd66 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Tue, 19 Feb 2019 15:21:19 +0300 Subject: [PATCH 51/88] /tls/ -- add ValidCert, without it being true https is not usable --- config.go | 1 + control.go | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config.go b/config.go index ccf395d8..13df2174 100644 --- a/config.go +++ b/config.go @@ -75,6 +75,7 @@ type tlsConfigSettings struct { // field ordering is not important -- these are for API and are recalculated on each run type tlsConfigStatus struct { // certificate status + ValidCert bool `yaml:"-" json:"valid_cert"` ValidChain bool `yaml:"-" json:"valid_chain"` Subject string `yaml:"-" json:"subject,omitempty"` Issuer string `yaml:"-" json:"issuer,omitempty"` diff --git a/control.go b/control.go index 13c36d84..26d2d68f 100644 --- a/control.go +++ b/control.go @@ -1155,6 +1155,8 @@ func validateCertificates(data tlsConfig) tlsConfig { return data } + data.ValidCert = true + // spew.Dump(parsedCerts) opts := x509.VerifyOptions{ @@ -1172,9 +1174,6 @@ func validateCertificates(data tlsConfig) tlsConfig { opts.Intermediates = pool } - // clear out all warnings and statuses - data.tlsConfigStatus = tlsConfigStatus{} - // TODO: save it as a warning rather than error it out -- shouldn't be a big problem mainCert := parsedCerts[0] _, err := mainCert.Verify(opts) From 3c374b594000a47bef40c2d5d20d7ba677c74b31 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Tue, 19 Feb 2019 15:21:38 +0300 Subject: [PATCH 52/88] /tls/ -- add internal usable flag to simplify logic when https needs to be booted up --- app.go | 2 +- config.go | 3 +++ control.go | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app.go b/app.go index 800b0a43..6fdc8cf7 100644 --- a/app.go +++ b/app.go @@ -179,7 +179,7 @@ func run(args options) { address := net.JoinHostPort(config.BindHost, strconv.Itoa(config.TLS.PortHTTPS)) // validate current TLS config and update warnings (it could have been loaded from file) data := validateCertificates(config.TLS) - if data.WarningValidation != "" { + if !data.usable { log.Fatal(data.WarningValidation) os.Exit(1) } diff --git a/config.go b/config.go index 13df2174..c1aa2f8d 100644 --- a/config.go +++ b/config.go @@ -88,6 +88,9 @@ type tlsConfigStatus struct { ValidKey bool `yaml:"-" json:"valid_key"` KeyType string `yaml:"-" json:"key_type,omitempty"` + // is usable? set by validator + usable bool + // warnings Warning string `yaml:"-" json:"warning,omitempty"` WarningValidation string `yaml:"-" json:"warning_validation,omitempty"` diff --git a/control.go b/control.go index 26d2d68f..564c735e 100644 --- a/control.go +++ b/control.go @@ -1088,7 +1088,7 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { restartHTTPS := false data = validateCertificates(data) - if data.WarningValidation == "" { + if data.usable { if !reflect.DeepEqual(config.TLS.tlsConfigSettings, data.tlsConfigSettings) { log.Printf("tls config settings have changed, will restart HTTPS server") restartHTTPS = true @@ -1259,6 +1259,7 @@ func validateCertificates(data tlsConfig) tlsConfig { data.WarningValidation = fmt.Sprintf("Invalid certificate or key: %s", err) return data } + data.usable = true } return data From cca6998efe41f4f11ab705e3f154be65b5c816af Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Tue, 19 Feb 2019 15:43:36 +0300 Subject: [PATCH 53/88] Added https redirect --- client/src/actions/encryption.js | 2 ++ client/src/components/Settings/Encryption/Form.js | 14 ++++++++++++-- client/src/components/Settings/Encryption/index.js | 1 + client/src/helpers/helpers.js | 12 ++++++++++++ client/src/reducers/encryption.js | 5 +++-- 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/client/src/actions/encryption.js b/client/src/actions/encryption.js index a537f31d..4e259409 100644 --- a/client/src/actions/encryption.js +++ b/client/src/actions/encryption.js @@ -1,6 +1,7 @@ import { createAction } from 'redux-actions'; import Api from '../api/Api'; import { addErrorToast, addSuccessToast } from './index'; +import { redirectToCurrentProtocol } from '../helpers/helpers'; const apiClient = new Api(); @@ -40,6 +41,7 @@ export const setTlsConfig = config => async (dispatch) => { response.private_key = atob(response.private_key); dispatch(setTlsConfigSuccess(response)); dispatch(addSuccessToast('encryption_config_saved')); + redirectToCurrentProtocol(response); } catch (error) { dispatch(addErrorToast({ error })); dispatch(setTlsConfigFailure()); diff --git a/client/src/components/Settings/Encryption/Form.js b/client/src/components/Settings/Encryption/Form.js index f3d83e29..a82dfca0 100644 --- a/client/src/components/Settings/Encryption/Form.js +++ b/client/src/components/Settings/Encryption/Form.js @@ -48,9 +48,11 @@ let Form = (props) => { invalid, submitting, processing, + processingValidate, not_after, valid_chain, valid_key, + valid_cert, dns_names, key_type, issuer, @@ -260,7 +262,11 @@ let Form = (props) => {

- {warning_validation && warning_validation} + { + (certificateChain || privateKey) + && warning_validation + && warning_validation + }

@@ -273,7 +279,9 @@ let Form = (props) => { invalid || submitting || processing - || !valid_key + || processingValidate + || (privateKey && !valid_key) + || (certificateChain && !valid_cert) } > save_config @@ -302,11 +310,13 @@ Form.propTypes = { invalid: PropTypes.bool.isRequired, initialValues: PropTypes.object.isRequired, processing: PropTypes.bool.isRequired, + processingValidate: PropTypes.bool.isRequired, status_key: PropTypes.string, not_after: PropTypes.string, warning_validation: PropTypes.string, valid_chain: PropTypes.bool, valid_key: PropTypes.bool, + valid_cert: PropTypes.bool, dns_names: PropTypes.string, key_type: PropTypes.string, issuer: PropTypes.string, diff --git a/client/src/components/Settings/Encryption/index.js b/client/src/components/Settings/Encryption/index.js index 0d56fc75..9e9e0626 100644 --- a/client/src/components/Settings/Encryption/index.js +++ b/client/src/components/Settings/Encryption/index.js @@ -46,6 +46,7 @@ class Encryption extends Component { private_key, }} processing={encryption.processingConfig} + processingValidate={encryption.processingValidate} onSubmit={this.handleFormSubmit} onChange={this.handleFormChange} {...this.props.encryption} diff --git a/client/src/helpers/helpers.js b/client/src/helpers/helpers.js index 0630416d..def06646 100644 --- a/client/src/helpers/helpers.js +++ b/client/src/helpers/helpers.js @@ -140,3 +140,15 @@ export const getWebAddress = (ip, port = '') => { return address; }; + +export const redirectToCurrentProtocol = (values) => { + const { protocol, hostname, hash } = window.location; + const { enabled, port_https } = values; + + if (protocol !== 'https:' && enabled && port_https) { + const port = port_https !== 443 ? `:${port_https}` : ''; + window.location.replace(`https://${hostname}${port}/${hash}`); + } else if (protocol === 'https:' && (!enabled || !port_https)) { + window.location.replace(`http://${hostname}/${hash}`); + } +}; diff --git a/client/src/reducers/encryption.js b/client/src/reducers/encryption.js index 5986f987..f3f2dd67 100644 --- a/client/src/reducers/encryption.js +++ b/client/src/reducers/encryption.js @@ -46,11 +46,12 @@ const encryption = handleActions({ key_type: '', not_after: '', not_before: '', - port_dns_over_tls: 853, - port_https: 443, + port_dns_over_tls: '', + port_https: '', subject: '', valid_chain: false, valid_key: false, + valid_cert: false, status_cert: '', status_key: '', certificate_chain: '', From 0b7f0396de0f6456a37d0595191e2441bd7479f9 Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Tue, 19 Feb 2019 15:46:29 +0300 Subject: [PATCH 54/88] Fixed processing config --- client/src/components/Settings/Encryption/Form.js | 8 ++++---- client/src/components/Settings/Encryption/index.js | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/client/src/components/Settings/Encryption/Form.js b/client/src/components/Settings/Encryption/Form.js index a82dfca0..0ed6fffb 100644 --- a/client/src/components/Settings/Encryption/Form.js +++ b/client/src/components/Settings/Encryption/Form.js @@ -47,7 +47,7 @@ let Form = (props) => { change, invalid, submitting, - processing, + processingConfig, processingValidate, not_after, valid_chain, @@ -278,7 +278,7 @@ let Form = (props) => { disabled={ invalid || submitting - || processing + || processingConfig || processingValidate || (privateKey && !valid_key) || (certificateChain && !valid_cert) @@ -289,7 +289,7 @@ let Form = (props) => {
diff --git a/client/src/components/Settings/Settings.css b/client/src/components/Settings/Settings.css index b364be6e..2da2ce74 100644 --- a/client/src/components/Settings/Settings.css +++ b/client/src/components/Settings/Settings.css @@ -68,3 +68,11 @@ font-size: 14px; line-height: 1.7; } + +.encryption__list { + padding-left: 0; +} + +.encryption__list li { + list-style: inside; +} From b3f33b4b0bb68c4514ebed94647365b20e183915 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Wed, 20 Feb 2019 12:24:56 +0300 Subject: [PATCH 66/88] /status -- add http_port --- control.go | 1 + 1 file changed, 1 insertion(+) diff --git a/control.go b/control.go index 8761dce3..54ab059f 100644 --- a/control.go +++ b/control.go @@ -87,6 +87,7 @@ func httpUpdateConfigReloadDNSReturnOK(w http.ResponseWriter, r *http.Request) { func handleStatus(w http.ResponseWriter, r *http.Request) { data := map[string]interface{}{ "dns_address": config.BindHost, + "http_port": config.BindPort, "dns_port": config.DNS.Port, "protection_enabled": config.DNS.ProtectionEnabled, "querylog_enabled": config.DNS.QueryLogEnabled, From a2dd7c32d50380acb924644fa91ae2829b86e0e7 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Wed, 20 Feb 2019 12:32:10 +0300 Subject: [PATCH 67/88] /tls/ -- move certificate logging to verbose --- control.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control.go b/control.go index 54ab059f..4d13a9b8 100644 --- a/control.go +++ b/control.go @@ -1120,7 +1120,7 @@ func validateCertificates(data tlsConfig) tlsConfig { // check only public certificate separetely from the key if data.CertificateChain != "" { - log.Printf("got certificate: %s", data.CertificateChain) + log.Tracef("got certificate: %s", data.CertificateChain) // now do a more extended validation var certs []*pem.Block // PEM-encoded certificates From 2c2295c1615abb4ac0d7ec5380db51ca9d6ee424 Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Wed, 20 Feb 2019 12:46:34 +0300 Subject: [PATCH 68/88] Fixed http port and reset with save --- client/src/__locales/en.json | 1 + client/src/actions/encryption.js | 5 +++-- client/src/components/Settings/Encryption/Form.js | 12 +++++++++--- client/src/components/Settings/Encryption/index.js | 5 +++++ client/src/helpers/helpers.js | 4 ++-- client/src/reducers/index.js | 3 +++ 6 files changed, 23 insertions(+), 7 deletions(-) diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 8cd51ec5..3412a12a 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -238,6 +238,7 @@ "encryption_subject": "Subject", "encryption_issuer": "Issuer", "encryption_hostnames": "Hostnames", + "encryption_reset": "Are you sure you want to reset encryption settings?", "topline_expiring_certificate": "Your SSL certificate is about to expire. Update <0>Encryption settings.", "topline_expired_certificate": "Your SSL certificate is expired. Update <0>Encryption settings.", "form_error_port_range": "Enter port value in the range of 80-65535", diff --git a/client/src/actions/encryption.js b/client/src/actions/encryption.js index 4e259409..6d6f3332 100644 --- a/client/src/actions/encryption.js +++ b/client/src/actions/encryption.js @@ -27,9 +27,10 @@ export const setTlsConfigRequest = createAction('SET_TLS_CONFIG_REQUEST'); export const setTlsConfigFailure = createAction('SET_TLS_CONFIG_FAILURE'); export const setTlsConfigSuccess = createAction('SET_TLS_CONFIG_SUCCESS'); -export const setTlsConfig = config => async (dispatch) => { +export const setTlsConfig = config => async (dispatch, getState) => { dispatch(setTlsConfigRequest()); try { + const { httpPort } = getState().dashboard; const values = { ...config }; values.certificate_chain = btoa(values.certificate_chain); values.private_key = btoa(values.private_key); @@ -41,7 +42,7 @@ export const setTlsConfig = config => async (dispatch) => { response.private_key = atob(response.private_key); dispatch(setTlsConfigSuccess(response)); dispatch(addSuccessToast('encryption_config_saved')); - redirectToCurrentProtocol(response); + redirectToCurrentProtocol(response, httpPort); } catch (error) { dispatch(addErrorToast({ error })); dispatch(setTlsConfigFailure()); diff --git a/client/src/components/Settings/Encryption/Form.js b/client/src/components/Settings/Encryption/Form.js index b9eeed34..3587d2bd 100644 --- a/client/src/components/Settings/Encryption/Form.js +++ b/client/src/components/Settings/Encryption/Form.js @@ -23,7 +23,7 @@ const validate = (values) => { return errors; }; -const clearFields = (change) => { +const clearFields = (change, setTlsConfig, t) => { const fields = { private_key: '', certificate_chain: '', @@ -33,7 +33,11 @@ const clearFields = (change) => { force_https: false, enabled: false, }; - Object.keys(fields).forEach(field => change(field, fields[field])); + // eslint-disable-next-line no-alert + if (window.confirm(t('encryption_reset'))) { + Object.keys(fields).forEach(field => change(field, fields[field])); + setTlsConfig(fields); + } }; let Form = (props) => { @@ -58,6 +62,7 @@ let Form = (props) => { issuer, subject, warning_validation, + setTlsConfig, } = props; return ( @@ -303,7 +308,7 @@ let Form = (props) => { type="button" className="btn btn-secondary btn-standart" disabled={submitting || processingConfig} - onClick={() => clearFields(change)} + onClick={() => clearFields(change, setTlsConfig, t)} > reset_settings @@ -335,6 +340,7 @@ Form.propTypes = { issuer: PropTypes.string, subject: PropTypes.string, t: PropTypes.func.isRequired, + setTlsConfig: PropTypes.func.isRequired, }; const selector = formValueSelector('encryptionForm'); diff --git a/client/src/components/Settings/Encryption/index.js b/client/src/components/Settings/Encryption/index.js index 4c5ddf27..09efec6f 100644 --- a/client/src/components/Settings/Encryption/index.js +++ b/client/src/components/Settings/Encryption/index.js @@ -8,6 +8,10 @@ import Form from './Form'; import Card from '../../ui/Card'; class Encryption extends Component { + componentDidMount() { + this.props.validateTlsConfig(this.props.encryption); + } + handleFormSubmit = (values) => { this.props.setTlsConfig(values); }; @@ -48,6 +52,7 @@ class Encryption extends Component { }} onSubmit={this.handleFormSubmit} onChange={this.handleFormChange} + setTlsConfig={this.props.setTlsConfig} {...this.props.encryption} /> diff --git a/client/src/helpers/helpers.js b/client/src/helpers/helpers.js index 81e6cbd6..3c0544e6 100644 --- a/client/src/helpers/helpers.js +++ b/client/src/helpers/helpers.js @@ -171,7 +171,7 @@ export const redirectCheck = (url) => { }, STOP_TIMEOUT); }; -export const redirectToCurrentProtocol = (values) => { +export const redirectToCurrentProtocol = (values, httpPort = 80) => { const { protocol, hostname, hash, port, } = window.location; @@ -183,6 +183,6 @@ export const redirectToCurrentProtocol = (values) => { } else if (protocol === 'https:' && enabled && port_https && port_https !== port) { redirectCheck(`https://${hostname}${httpsPort}/${hash}`); } else if (protocol === 'https:' && (!enabled || !port_https)) { - window.location.replace(`http://${hostname}/${hash}`); + window.location.replace(`http://${hostname}:${httpPort}/${hash}`); } }; diff --git a/client/src/reducers/index.js b/client/src/reducers/index.js index 5463918f..8cd04ac5 100644 --- a/client/src/reducers/index.js +++ b/client/src/reducers/index.js @@ -53,6 +53,7 @@ const dashboard = handleActions({ upstream_dns: upstreamDns, protection_enabled: protectionEnabled, language, + http_port: httpPort, } = payload; const newState = { ...state, @@ -65,6 +66,7 @@ const dashboard = handleActions({ upstreamDns: upstreamDns.join('\n'), protectionEnabled, language, + httpPort, }; return newState; }, @@ -172,6 +174,7 @@ const dashboard = handleActions({ upstreamDns: [], protectionEnabled: false, processingProtection: false, + httpPort: 80, }); const queryLogs = handleActions({ From 9c8e4c64eaece3d246aef1061c2882210ad4a410 Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Wed, 20 Feb 2019 13:33:42 +0300 Subject: [PATCH 69/88] Disable save button if key or certificate is empty --- client/src/components/Settings/Encryption/Form.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/components/Settings/Encryption/Form.js b/client/src/components/Settings/Encryption/Form.js index 3587d2bd..65867986 100644 --- a/client/src/components/Settings/Encryption/Form.js +++ b/client/src/components/Settings/Encryption/Form.js @@ -298,6 +298,7 @@ let Form = (props) => { || submitting || processingConfig || processingValidate + || (isEnabled && (!privateKey || !certificateChain)) || (privateKey && !valid_key) || (certificateChain && !valid_cert) } From 4f45f2c3e3f80a04d6bde0510452bd51337ac0f4 Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Wed, 20 Feb 2019 14:26:56 +0300 Subject: [PATCH 70/88] Fix empty values on validate --- .../components/Settings/Encryption/Form.js | 16 +++++++-------- client/src/reducers/encryption.js | 20 ++++++++++++++++++- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/client/src/components/Settings/Encryption/Form.js b/client/src/components/Settings/Encryption/Form.js index 65867986..b85bcfeb 100644 --- a/client/src/components/Settings/Encryption/Form.js +++ b/client/src/components/Settings/Encryption/Form.js @@ -278,15 +278,13 @@ let Form = (props) => {
-
-

- { - (certificateChain || privateKey) - && warning_validation - && warning_validation - } -

-
+ {warning_validation && +
+

+ {warning_validation} +

+
+ }
diff --git a/client/src/reducers/encryption.js b/client/src/reducers/encryption.js index f3f2dd67..3f51b217 100644 --- a/client/src/reducers/encryption.js +++ b/client/src/reducers/encryption.js @@ -28,9 +28,27 @@ const encryption = handleActions({ [actions.validateTlsConfigRequest]: state => ({ ...state, processingValidate: true }), [actions.validateTlsConfigFailure]: state => ({ ...state, processingValidate: false }), [actions.validateTlsConfigSuccess]: (state, { payload }) => { + const { + issuer = '', + key_type = '', + not_after = '', + not_before = '', + subject = '', + warning_validation = '', + dns_names = '', + ...values + } = payload; + const newState = { ...state, - ...payload, + ...values, + issuer, + key_type, + not_after, + not_before, + subject, + warning_validation, + dns_names, processingValidate: false, }; return newState; From 89446fccd5e26d97ad4271208ef0bcb23e2f29e8 Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Wed, 20 Feb 2019 15:39:36 +0300 Subject: [PATCH 71/88] Fixed npm audit vulnerabilities --- client/package-lock.json | 3973 +++++++++++++------------------------- client/package.json | 6 +- 2 files changed, 1329 insertions(+), 2650 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 380bd20d..5ce9329b 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -13,6 +13,185 @@ "@babel/highlight": "7.0.0-beta.44" } }, + "@babel/core": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.3.3.tgz", + "integrity": "sha512-w445QGI2qd0E0GlSnq6huRZWPMmQGCp5gd5ZWS4hagn0EiwzxD5QMFkpchyusAyVC1n27OKXzQ0/88aVU9n4xQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.3.3", + "@babel/helpers": "^7.2.0", + "@babel/parser": "^7.3.3", + "@babel/template": "^7.2.2", + "@babel/traverse": "^7.2.2", + "@babel/types": "^7.3.3", + "convert-source-map": "^1.1.0", + "debug": "^4.1.0", + "json5": "^2.1.0", + "lodash": "^4.17.11", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/generator": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.3.3.tgz", + "integrity": "sha512-aEADYwRRZjJyMnKN7llGIlircxTCofm3dtV5pmY6ob18MSIuipHpA2yZWkPlycwu5HJcx/pADS3zssd8eY7/6A==", + "dev": true, + "requires": { + "@babel/types": "^7.3.3", + "jsesc": "^2.5.1", + "lodash": "^4.17.11", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + } + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", + "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/template": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz", + "integrity": "sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.2.2", + "@babel/types": "^7.2.2" + } + }, + "@babel/traverse": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.2.3.tgz", + "integrity": "sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.2.2", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.0.0", + "@babel/parser": "^7.2.3", + "@babel/types": "^7.2.2", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.10" + } + }, + "@babel/types": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.3.tgz", + "integrity": "sha512-2tACZ80Wg09UnPg5uGAOUvvInaqLk3l/IAhQzlxLQOIXacr6bMsra5SH6AWw/hIDRCSbCdHP2KzSOD+cT7TzMQ==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.11", + "to-fast-properties": "^2.0.0" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "globals": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", + "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==", + "dev": true + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json5": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", + "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, "@babel/generator": { "version": "7.0.0-beta.44", "resolved": "http://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.44.tgz", @@ -69,6 +248,159 @@ "@babel/types": "7.0.0-beta.44" } }, + "@babel/helpers": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.3.1.tgz", + "integrity": "sha512-Q82R3jKsVpUV99mgX50gOPCWwco9Ec5Iln/8Vyu4osNIOQgSrd9RFrQeUvmvddFNoLwMyOUWU+5ckioEKpDoGA==", + "dev": true, + "requires": { + "@babel/template": "^7.1.2", + "@babel/traverse": "^7.1.5", + "@babel/types": "^7.3.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/generator": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.3.3.tgz", + "integrity": "sha512-aEADYwRRZjJyMnKN7llGIlircxTCofm3dtV5pmY6ob18MSIuipHpA2yZWkPlycwu5HJcx/pADS3zssd8eY7/6A==", + "dev": true, + "requires": { + "@babel/types": "^7.3.3", + "jsesc": "^2.5.1", + "lodash": "^4.17.11", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + } + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", + "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/template": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz", + "integrity": "sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.2.2", + "@babel/types": "^7.2.2" + } + }, + "@babel/traverse": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.2.3.tgz", + "integrity": "sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.2.2", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.0.0", + "@babel/parser": "^7.2.3", + "@babel/types": "^7.2.2", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.10" + } + }, + "@babel/types": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.3.tgz", + "integrity": "sha512-2tACZ80Wg09UnPg5uGAOUvvInaqLk3l/IAhQzlxLQOIXacr6bMsra5SH6AWw/hIDRCSbCdHP2KzSOD+cT7TzMQ==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.11", + "to-fast-properties": "^2.0.0" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "globals": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", + "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==", + "dev": true + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, "@babel/highlight": { "version": "7.0.0-beta.44", "resolved": "http://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.44.tgz", @@ -89,9 +421,9 @@ } }, "@babel/parser": { - "version": "7.0.0-beta.51", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.0.0-beta.51.tgz", - "integrity": "sha1-J87C30Cd9gr1gnDtj2qlVAnqhvY=", + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.3.3.tgz", + "integrity": "sha512-xsH1CJoln2r74hR+y7cg2B5JCPaTh+Hd+EbBRk9nWGSNspuo6krjhX0Om6RnRQuIvFq8wVXCLKH3kwKDYhanSg==", "dev": true }, "@babel/runtime": { @@ -265,17 +597,44 @@ } }, "@nodelib/fs.stat": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.1.tgz", - "integrity": "sha512-KU/VDjC5RwtDUZiz3d+DHXJF2lp5hB9dn552TXIyptj8SH1vXmR40mG0JgGq03IlYsOgGfcv8xrLpSQ0YUMQdA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", "dev": true }, - "abab": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz", - "integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4=", + "@types/node": { + "version": "11.9.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.4.tgz", + "integrity": "sha512-Zl8dGvAcEmadgs1tmSPcvwzO1YRsz38bVJQvH1RvRqSR9/5n61Q1ktcDL0ht3FXWR+ZpVmXVwN1LuH4Ax23NsA==", "dev": true }, + "@types/unist": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", + "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==", + "dev": true + }, + "@types/vfile": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/vfile/-/vfile-3.0.2.tgz", + "integrity": "sha512-b3nLFGaGkJ9rzOcuXRfHkZMdjsawuDD0ENL9fzTophtBg8FJHSGbH7daXkEpcwy3v7Xol3pAvsmlYyFhR4pqJw==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/unist": "*", + "@types/vfile-message": "*" + } + }, + "@types/vfile-message": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/vfile-message/-/vfile-message-1.0.1.tgz", + "integrity": "sha512-mlGER3Aqmq7bqR1tTTIVHq8KSAFFRyGbrxuM8C/H82g6k7r2fS+IMEkIu3D7JHzG10NvPdR8DNx0jr0pwpp4dA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/unist": "*" + } + }, "accepts": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", @@ -309,23 +668,6 @@ } } }, - "acorn-globals": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", - "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", - "dev": true, - "requires": { - "acorn": "^4.0.4" - }, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", - "dev": true - } - } - }, "acorn-jsx": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", @@ -382,12 +724,6 @@ "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", "dev": true }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true - }, "ansi-colors": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", @@ -421,25 +757,6 @@ "color-convert": "^1.9.0" } }, - "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "dev": true, - "requires": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" - } - }, - "append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", - "dev": true, - "requires": { - "default-require-extensions": "^2.0.0" - } - }, "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", @@ -466,13 +783,10 @@ } }, "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "^1.0.1" - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true }, "arr-flatten": { "version": "1.1.0", @@ -486,12 +800,6 @@ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, - "array-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", - "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", - "dev": true - }, "array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", @@ -530,9 +838,9 @@ "dev": true }, "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, "arrify": { @@ -546,15 +854,6 @@ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, "asn1.js": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", @@ -592,12 +891,6 @@ } } }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -610,6 +903,12 @@ "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", "dev": true }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, "async": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", @@ -625,12 +924,6 @@ "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", "dev": true }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -651,18 +944,6 @@ "postcss-value-parser": "^3.2.3" } }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", - "dev": true - }, "axios": { "version": "0.18.0", "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", @@ -986,17 +1267,6 @@ "babel-template": "^6.24.1" } }, - "babel-jest": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-20.0.3.tgz", - "integrity": "sha1-5KA7E9wQOJ4UD8ZF0J/8TO0wFnE=", - "dev": true, - "requires": { - "babel-core": "^6.0.0", - "babel-plugin-istanbul": "^4.0.0", - "babel-preset-jest": "^20.0.3" - } - }, "babel-loader": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.2.tgz", @@ -1026,24 +1296,6 @@ "babel-runtime": "^6.22.0" } }, - "babel-plugin-istanbul": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz", - "integrity": "sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ==", - "dev": true, - "requires": { - "babel-plugin-syntax-object-rest-spread": "^6.13.0", - "find-up": "^2.1.0", - "istanbul-lib-instrument": "^1.10.1", - "test-exclude": "^4.2.1" - } - }, - "babel-plugin-jest-hoist": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-20.0.3.tgz", - "integrity": "sha1-r+3IU70/jcNUjqZx++adA8wsF2c=", - "dev": true - }, "babel-plugin-syntax-async-functions": { "version": "6.13.0", "resolved": "http://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", @@ -1531,15 +1783,6 @@ "babel-plugin-transform-flow-strip-types": "^6.22.0" } }, - "babel-preset-jest": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-20.0.3.tgz", - "integrity": "sha1-y6yq3stdaJyh4d4TYOv8ZoYsF4o=", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^20.0.3" - } - }, "babel-preset-react": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.24.1.tgz", @@ -1770,16 +2013,6 @@ "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", "dev": true }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, "big.js": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", @@ -1872,14 +2105,32 @@ } }, "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.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": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } } }, "brorand": { @@ -1888,23 +2139,6 @@ "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", "dev": true }, - "browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", - "dev": true, - "requires": { - "resolve": "1.1.7" - }, - "dependencies": { - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } - } - }, "browserify-aes": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", @@ -1986,15 +2220,6 @@ "electron-to-chromium": "^1.3.47" } }, - "bser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz", - "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, "buffer": { "version": "4.9.1", "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", @@ -2102,6 +2327,23 @@ "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", "dev": true }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "requires": { + "callsites": "^2.0.0" + }, + "dependencies": { + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + } + } + }, "caller-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", @@ -2194,12 +2436,6 @@ "integrity": "sha512-ovvb0uya4cKJct8Rj9Olstz0LaWmyJhCp3NawRG5fVigka8pEhIIwipF7zyYd2Q58UZb5YfIt52pVF444uj2kQ==", "dev": true }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, "ccount": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.3.tgz", @@ -2620,12 +2856,6 @@ "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", "dev": true }, - "ci-info": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.4.0.tgz", - "integrity": "sha512-Oqmw2pVfCl8sCL+1QgMywPfdxPJPkC51y4usw0iiE2S9qnEOAqXy8bwl1CpMpnoU39g4iKJTz6QZj+28FvOnjQ==", - "dev": true - }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -2869,15 +3099,6 @@ "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true }, - "combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, "commander": { "version": "2.17.1", "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", @@ -2890,12 +3111,6 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, - "compare-versions": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.4.0.tgz", - "integrity": "sha512-tK69D7oNXXqUW3ZNo/z7NXTEz22TCF0pTE+YF9cxvaAM9XnkLo1fV621xCLrRR6aevJlKxExkss0vWqUCUpqdg==", - "dev": true - }, "component-emitter": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", @@ -3007,12 +3222,6 @@ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", "dev": true }, - "content-type-parser": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/content-type-parser/-/content-type-parser-1.0.2.tgz", - "integrity": "sha512-lM4l4CnMEwOLHAHr/P6MEZwZFPJFtAAKgL6pogbXmVZggIqXhdB6RbBtPOTsw2FcXwYhehRGERJmRrjOiIB8pQ==", - "dev": true - }, "convert-source-map": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", @@ -3486,21 +3695,6 @@ } } }, - "cssom": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.4.tgz", - "integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog==", - "dev": true - }, - "cssstyle": { - "version": "0.2.37", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz", - "integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=", - "dev": true, - "requires": { - "cssom": "0.3.x" - } - }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -3612,15 +3806,6 @@ "integrity": "sha1-AxkcQyy27qFou3fzpV/9zLiXhRQ=", "dev": true }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, "date-fns": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.29.0.tgz", @@ -3722,15 +3907,6 @@ } } }, - "default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", - "dev": true, - "requires": { - "strip-bom": "^3.0.0" - } - }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -3822,12 +3998,6 @@ } } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -3865,12 +4035,6 @@ "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", "dev": true }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, "diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", @@ -3883,12 +4047,11 @@ } }, "dir-glob": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", - "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", "dev": true, "requires": { - "arrify": "^1.0.1", "path-type": "^3.0.0" } }, @@ -4018,17 +4181,6 @@ "stream-shift": "^1.0.0" } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "optional": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -4246,27 +4398,6 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, - "escodegen": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz", - "integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==", - "dev": true, - "requires": { - "esprima": "^3.1.3", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true - } - } - }, "escope": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", @@ -4742,15 +4873,6 @@ "safe-buffer": "^5.1.1" } }, - "exec-sh": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.2.tgz", - "integrity": "sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw==", - "dev": true, - "requires": { - "merge": "^1.2.0" - } - }, "execa": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", @@ -4781,21 +4903,47 @@ "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=" }, "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "is-posix-bracket": "^0.1.0" - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true, - "requires": { - "fill-range": "^2.1.0" + "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": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } } }, "express": { @@ -4898,12 +5046,74 @@ } }, "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "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": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "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" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } } }, "extract-text-webpack-plugin": { @@ -4953,350 +5163,23 @@ } } }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" }, "fast-glob": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.2.tgz", - "integrity": "sha512-TR6zxCKftDQnUAPvkrCWdBgDq/gbqx8A3ApnBrR5rMvpp6+KMJI0Igw7fkWPgeVK0uhRXTXdvO3O+YP0CaUX2g==", + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.6.tgz", + "integrity": "sha512-0BvMaZc1k9F+MeWWMe8pL6YltFzZYcJsYU7D4JyDA6PAczaXvxqQQ/z+mDF7/4Mw01DeUc+i3CTKajnkANkV4w==", "dev": true, "requires": { "@mrmlnc/readdir-enhanced": "^2.2.1", - "@nodelib/fs.stat": "^1.0.1", + "@nodelib/fs.stat": "^1.1.2", "glob-parent": "^3.1.0", "is-glob": "^4.0.0", - "merge2": "^1.2.1", + "merge2": "^1.2.3", "micromatch": "^3.1.10" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "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.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "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": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "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.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "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.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "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-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "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" - } - } } }, "fast-json-stable-stringify": { @@ -5325,15 +5208,6 @@ "websocket-driver": ">=0.5.1" } }, - "fb-watchman": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", - "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", - "dev": true, - "requires": { - "bser": "^2.0.0" - } - }, "fbjs": { "version": "0.8.17", "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", @@ -5417,33 +5291,27 @@ "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.8.tgz", "integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg==" }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true - }, - "fileset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", - "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", - "dev": true, - "requires": { - "glob": "^7.0.3", - "minimatch": "^3.0.3" - } - }, "fill-range": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } } }, "finalhandler": { @@ -5504,6 +5372,12 @@ "write": "^0.2.1" } }, + "flatted": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", + "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", + "dev": true + }, "flatten": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", @@ -5534,32 +5408,6 @@ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", "dev": true }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true, - "requires": { - "for-in": "^1.0.1" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", - "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "1.0.6", - "mime-types": "^2.1.12" - } - }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", @@ -5629,7 +5477,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -5650,12 +5499,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5670,17 +5521,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -5797,7 +5651,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -5809,6 +5664,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5823,6 +5679,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5830,12 +5687,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -5854,6 +5713,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -5934,7 +5794,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -5946,6 +5807,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -6031,7 +5893,8 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -6067,6 +5930,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6086,6 +5950,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6129,12 +5994,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -6179,15 +6046,6 @@ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, "glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", @@ -6202,23 +6060,25 @@ "path-is-absolute": "^1.0.0" } }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true, - "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" - } - }, "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "is-glob": "^2.0.0" + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } } }, "glob-to-regexp": { @@ -6227,6 +6087,34 @@ "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", "dev": true }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "requires": { + "global-prefix": "^3.0.0" + } + }, + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, "globals": { "version": "9.18.0", "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", @@ -6272,7 +6160,7 @@ "dependencies": { "minimist": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.1.3.tgz", "integrity": "sha1-O+39kaktOQFvz6ocaB6Pqhoe/ag=", "dev": true } @@ -6284,12 +6172,6 @@ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, - "growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true - }, "gud": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz", @@ -6301,111 +6183,6 @@ "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==", "dev": true }, - "handlebars": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz", - "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", - "dev": true, - "requires": { - "async": "^1.4.0", - "optimist": "^0.6.1", - "source-map": "^0.4.4", - "uglify-js": "^2.6" - }, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - }, - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "requires": { - "amdefine": ">=0.0.4" - } - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "dev": true, - "optional": true, - "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "optional": true - } - } - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "optional": true, - "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } - } - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", - "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", - "dev": true, - "requires": { - "ajv": "^5.3.0", - "har-schema": "^2.0.0" - }, - "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - } - } - }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -6578,15 +6355,6 @@ "integrity": "sha1-ZouTd26q5V696POtRkswekljYl4=", "dev": true }, - "html-encoding-sniffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", - "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.1" - } - }, "html-entities": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", @@ -7039,17 +6807,6 @@ } } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -7116,6 +6873,33 @@ "import-from": "^2.1.0" } }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "dependencies": { + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + } + } + }, "import-from": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", @@ -7243,6 +7027,12 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, "inquirer": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", @@ -7402,15 +7192,6 @@ "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", "dev": true }, - "is-ci": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.0.tgz", - "integrity": "sha512-plgvKjQtalH2P3Gytb7L61Lmz95g2DlpzFiQyRSFew8WoJKxtKRzrZMeyRN2supblm3Psc8OQGy7Xjb6XG11jw==", - "dev": true, - "requires": { - "ci-info": "^1.3.0" - } - }, "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", @@ -7457,21 +7238,6 @@ "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", "dev": true }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true, - "requires": { - "is-primitive": "^2.0.0" - } - }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -7479,9 +7245,9 @@ "dev": true }, "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, "is-finite": { @@ -7500,12 +7266,12 @@ "dev": true }, "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "^2.1.1" } }, "is-hexadecimal": { @@ -7515,9 +7281,9 @@ "dev": true }, "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -7576,18 +7342,6 @@ } } }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true - }, "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", @@ -7640,18 +7394,6 @@ "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", "dev": true }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, "is-whitespace-character": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.2.tgz", @@ -7688,21 +7430,10 @@ "dev": true }, "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - } - } + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true }, "isomorphic-fetch": { "version": "2.2.1", @@ -7713,837 +7444,6 @@ "whatwg-fetch": ">=0.10.0" } }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "istanbul-api": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.3.6.tgz", - "integrity": "sha512-luJDnB1uJ5Qsg/WwusGfNXayQ4598yDgW5S0nUS85T576m1LVJzSqLrCDULkT6sTQXVKHa54093gNuCKumMCjQ==", - "dev": true, - "requires": { - "async": "^2.1.4", - "compare-versions": "^3.1.0", - "fileset": "^2.0.2", - "istanbul-lib-coverage": "^1.2.0", - "istanbul-lib-hook": "^1.2.0", - "istanbul-lib-instrument": "^2.1.0", - "istanbul-lib-report": "^1.1.4", - "istanbul-lib-source-maps": "^1.2.5", - "istanbul-reports": "^1.4.1", - "js-yaml": "^3.7.0", - "mkdirp": "^0.5.1", - "once": "^1.4.0" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.0.0-beta.51", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.51.tgz", - "integrity": "sha1-vXHZsZKvl435FYKdOdQJRFZDmgw=", - "dev": true, - "requires": { - "@babel/highlight": "7.0.0-beta.51" - } - }, - "@babel/generator": { - "version": "7.0.0-beta.51", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.51.tgz", - "integrity": "sha1-bHV1/952HQdIXgS67cA5LG2eMPY=", - "dev": true, - "requires": { - "@babel/types": "7.0.0-beta.51", - "jsesc": "^2.5.1", - "lodash": "^4.17.5", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, - "@babel/helper-function-name": { - "version": "7.0.0-beta.51", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.51.tgz", - "integrity": "sha1-IbSHSiJ8+Z7K/MMKkDAtpaJkBWE=", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "7.0.0-beta.51", - "@babel/template": "7.0.0-beta.51", - "@babel/types": "7.0.0-beta.51" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.0.0-beta.51", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.51.tgz", - "integrity": "sha1-MoGy0EWvlcFyzpGyCCXYXqRnZBE=", - "dev": true, - "requires": { - "@babel/types": "7.0.0-beta.51" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.0.0-beta.51", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.51.tgz", - "integrity": "sha1-imw/ZsTSZTUvwHdIT59ugKUauXg=", - "dev": true, - "requires": { - "@babel/types": "7.0.0-beta.51" - } - }, - "@babel/highlight": { - "version": "7.0.0-beta.51", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.51.tgz", - "integrity": "sha1-6IRK4loVlcz9QriWI7Q3bKBtIl0=", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^3.0.0" - } - }, - "@babel/template": { - "version": "7.0.0-beta.51", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.51.tgz", - "integrity": "sha1-lgKkCuvPNXrpZ34lMu9fyBD1+/8=", - "dev": true, - "requires": { - "@babel/code-frame": "7.0.0-beta.51", - "@babel/parser": "7.0.0-beta.51", - "@babel/types": "7.0.0-beta.51", - "lodash": "^4.17.5" - } - }, - "@babel/traverse": { - "version": "7.0.0-beta.51", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.51.tgz", - "integrity": "sha1-mB2vLOw0emIx06odnhgDsDqqpKg=", - "dev": true, - "requires": { - "@babel/code-frame": "7.0.0-beta.51", - "@babel/generator": "7.0.0-beta.51", - "@babel/helper-function-name": "7.0.0-beta.51", - "@babel/helper-split-export-declaration": "7.0.0-beta.51", - "@babel/parser": "7.0.0-beta.51", - "@babel/types": "7.0.0-beta.51", - "debug": "^3.1.0", - "globals": "^11.1.0", - "invariant": "^2.2.0", - "lodash": "^4.17.5" - } - }, - "@babel/types": { - "version": "7.0.0-beta.51", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.51.tgz", - "integrity": "sha1-2AK3tUO1g2x3iqaReXq/APPZfqk=", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.5", - "to-fast-properties": "^2.0.0" - } - }, - "globals": { - "version": "11.7.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz", - "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-2.3.2.tgz", - "integrity": "sha512-l7TD/VnBsIB2OJvSyxaLW/ab1+92dxZNH9wLH7uHPPioy3JZ8tnx2UXUdKmdkgmP2EFPzg64CToUP6dAS3U32Q==", - "dev": true, - "requires": { - "@babel/generator": "7.0.0-beta.51", - "@babel/parser": "7.0.0-beta.51", - "@babel/template": "7.0.0-beta.51", - "@babel/traverse": "7.0.0-beta.51", - "@babel/types": "7.0.0-beta.51", - "istanbul-lib-coverage": "^2.0.1", - "semver": "^5.5.0" - }, - "dependencies": { - "istanbul-lib-coverage": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", - "integrity": "sha512-nPvSZsVlbG9aLhZYaC3Oi1gT/tpyo3Yt5fNyf6NmcKIayz4VV/txxJFFKAK/gU4dcNn8ehsanBbVHVl0+amOLA==", - "dev": true - } - } - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "jsesc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.1.tgz", - "integrity": "sha1-5CGiqOINawgZ3yiQj3glJrlt0f4=", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - } - } - }, - "istanbul-lib-coverage": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz", - "integrity": "sha512-GvgM/uXRwm+gLlvkWHTjDAvwynZkL9ns15calTrmhGgowlwJBbWMYzWbKqE2DT6JDP1AFXKa+Zi0EkqNCUqY0A==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.2.1.tgz", - "integrity": "sha512-eLAMkPG9FU0v5L02lIkcj/2/Zlz9OuluaXikdr5iStk8FDbSwAixTK9TkYxbF0eNnzAJTwM2fkV2A1tpsIp4Jg==", - "dev": true, - "requires": { - "append-transform": "^1.0.0" - } - }, - "istanbul-lib-instrument": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.1.tgz", - "integrity": "sha512-1dYuzkOCbuR5GRJqySuZdsmsNKPL3PTuyPevQfoCXJePT9C8y1ga75neU+Tuy9+yS3G/dgx8wgOmp2KLpgdoeQ==", - "dev": true, - "requires": { - "babel-generator": "^6.18.0", - "babel-template": "^6.16.0", - "babel-traverse": "^6.18.0", - "babel-types": "^6.18.0", - "babylon": "^6.18.0", - "istanbul-lib-coverage": "^1.2.0", - "semver": "^5.3.0" - } - }, - "istanbul-lib-report": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.4.tgz", - "integrity": "sha512-Azqvq5tT0U09nrncK3q82e/Zjkxa4tkFZv7E6VcqP0QCPn6oNljDPfrZEC/umNXds2t7b8sRJfs6Kmpzt8m2kA==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^1.2.0", - "mkdirp": "^0.5.1", - "path-parse": "^1.0.5", - "supports-color": "^3.1.2" - }, - "dependencies": { - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.5.tgz", - "integrity": "sha512-8O2T/3VhrQHn0XcJbP1/GN7kXMiRAlPi+fj3uEHrjBD8Oz7Py0prSC25C09NuAZS6bgW1NNKAvCSHZXB0irSGA==", - "dev": true, - "requires": { - "debug": "^3.1.0", - "istanbul-lib-coverage": "^1.2.0", - "mkdirp": "^0.5.1", - "rimraf": "^2.6.1", - "source-map": "^0.5.3" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "istanbul-reports": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.5.0.tgz", - "integrity": "sha512-HeZG0WHretI9FXBni5wZ9DOgNziqDCEwetxnme5k1Vv5e81uTqcsy3fMH99gXGDGKr1ea87TyGseDMa2h4HEUA==", - "dev": true, - "requires": { - "handlebars": "^4.0.11" - } - }, - "jest": { - "version": "20.0.4", - "resolved": "https://registry.npmjs.org/jest/-/jest-20.0.4.tgz", - "integrity": "sha1-PdJgwpidba1nix6cxNkZRPbWAqw=", - "dev": true, - "requires": { - "jest-cli": "^20.0.4" - }, - "dependencies": { - "ansi-escapes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", - "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "jest-cli": { - "version": "20.0.4", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-20.0.4.tgz", - "integrity": "sha1-5TKxnYiuW8bEF+iwWTpv6VSx3JM=", - "dev": true, - "requires": { - "ansi-escapes": "^1.4.0", - "callsites": "^2.0.0", - "chalk": "^1.1.3", - "graceful-fs": "^4.1.11", - "is-ci": "^1.0.10", - "istanbul-api": "^1.1.1", - "istanbul-lib-coverage": "^1.0.1", - "istanbul-lib-instrument": "^1.4.2", - "istanbul-lib-source-maps": "^1.1.0", - "jest-changed-files": "^20.0.3", - "jest-config": "^20.0.4", - "jest-docblock": "^20.0.3", - "jest-environment-jsdom": "^20.0.3", - "jest-haste-map": "^20.0.4", - "jest-jasmine2": "^20.0.4", - "jest-message-util": "^20.0.3", - "jest-regex-util": "^20.0.3", - "jest-resolve-dependencies": "^20.0.3", - "jest-runtime": "^20.0.4", - "jest-snapshot": "^20.0.3", - "jest-util": "^20.0.3", - "micromatch": "^2.3.11", - "node-notifier": "^5.0.2", - "pify": "^2.3.0", - "slash": "^1.0.0", - "string-length": "^1.0.1", - "throat": "^3.0.0", - "which": "^1.2.12", - "worker-farm": "^1.3.1", - "yargs": "^7.0.2" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "jest-changed-files": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-20.0.3.tgz", - "integrity": "sha1-k5TVzGXEOEBhSb7xv01Sto4D4/g=", - "dev": true - }, - "jest-config": { - "version": "20.0.4", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-20.0.4.tgz", - "integrity": "sha1-43kwqyIXyRNgXv8T5712PsSPruo=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "glob": "^7.1.1", - "jest-environment-jsdom": "^20.0.3", - "jest-environment-node": "^20.0.3", - "jest-jasmine2": "^20.0.4", - "jest-matcher-utils": "^20.0.3", - "jest-regex-util": "^20.0.3", - "jest-resolve": "^20.0.4", - "jest-validate": "^20.0.3", - "pretty-format": "^20.0.3" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "jest-diff": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-20.0.3.tgz", - "integrity": "sha1-gfKI/Z5nXw+yPHXxwrGURf5YZhc=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "diff": "^3.2.0", - "jest-matcher-utils": "^20.0.3", - "pretty-format": "^20.0.3" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "jest-docblock": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-20.0.3.tgz", - "integrity": "sha1-F76phDQswz2DxQ++FUXqDvqkRxI=", - "dev": true - }, - "jest-environment-jsdom": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-20.0.3.tgz", - "integrity": "sha1-BIqKwS7iJfcZBBdxODS7mZeH3pk=", - "dev": true, - "requires": { - "jest-mock": "^20.0.3", - "jest-util": "^20.0.3", - "jsdom": "^9.12.0" - } - }, - "jest-environment-node": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-20.0.3.tgz", - "integrity": "sha1-1Ii8RhKvLCRumG6K52caCZFj1AM=", - "dev": true, - "requires": { - "jest-mock": "^20.0.3", - "jest-util": "^20.0.3" - } - }, - "jest-haste-map": { - "version": "20.0.5", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-20.0.5.tgz", - "integrity": "sha512-0IKAQjUvuZjMCNi/0VNQQF74/H9KB67hsHJqGiwTWQC6XO5Azs7kLWm+6Q/dwuhvDUvABDOBMFK2/FwZ3sZ07Q==", - "dev": true, - "requires": { - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.1.11", - "jest-docblock": "^20.0.3", - "micromatch": "^2.3.11", - "sane": "~1.6.0", - "worker-farm": "^1.3.1" - } - }, - "jest-jasmine2": { - "version": "20.0.4", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-20.0.4.tgz", - "integrity": "sha1-/MWxQReA2RHQQpAu8YWehS5g1eE=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "graceful-fs": "^4.1.11", - "jest-diff": "^20.0.3", - "jest-matcher-utils": "^20.0.3", - "jest-matchers": "^20.0.3", - "jest-message-util": "^20.0.3", - "jest-snapshot": "^20.0.3", - "once": "^1.4.0", - "p-map": "^1.1.1" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "jest-matcher-utils": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-20.0.3.tgz", - "integrity": "sha1-s6a443yld4A7CDKpixZPRLeBVhI=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "pretty-format": "^20.0.3" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "jest-matchers": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jest-matchers/-/jest-matchers-20.0.3.tgz", - "integrity": "sha1-ymnbHDLbWm9wf6XgQBq7VXAN/WA=", - "dev": true, - "requires": { - "jest-diff": "^20.0.3", - "jest-matcher-utils": "^20.0.3", - "jest-message-util": "^20.0.3", - "jest-regex-util": "^20.0.3" - } - }, - "jest-message-util": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-20.0.3.tgz", - "integrity": "sha1-auwoRDBvyw5udNV5bBAG2W/dgxw=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "micromatch": "^2.3.11", - "slash": "^1.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "jest-mock": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-20.0.3.tgz", - "integrity": "sha1-i8Bw6QQUqhVcEajWTIaaDVxx2lk=", - "dev": true - }, - "jest-regex-util": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-20.0.3.tgz", - "integrity": "sha1-hburXRM+RGJbGfr4xqpRItCF12I=", - "dev": true - }, - "jest-resolve": { - "version": "20.0.4", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-20.0.4.tgz", - "integrity": "sha1-lEiz6La6/BVHlETGSZBFt//ll6U=", - "dev": true, - "requires": { - "browser-resolve": "^1.11.2", - "is-builtin-module": "^1.0.0", - "resolve": "^1.3.2" - } - }, - "jest-resolve-dependencies": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-20.0.3.tgz", - "integrity": "sha1-bhSntxevDyyzZnxUneQK8Bexcjo=", - "dev": true, - "requires": { - "jest-regex-util": "^20.0.3" - } - }, - "jest-runtime": { - "version": "20.0.4", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-20.0.4.tgz", - "integrity": "sha1-osgCIZxCA/dU3xQE5JAYYWnRJNg=", - "dev": true, - "requires": { - "babel-core": "^6.0.0", - "babel-jest": "^20.0.3", - "babel-plugin-istanbul": "^4.0.0", - "chalk": "^1.1.3", - "convert-source-map": "^1.4.0", - "graceful-fs": "^4.1.11", - "jest-config": "^20.0.4", - "jest-haste-map": "^20.0.4", - "jest-regex-util": "^20.0.3", - "jest-resolve": "^20.0.4", - "jest-util": "^20.0.3", - "json-stable-stringify": "^1.0.1", - "micromatch": "^2.3.11", - "strip-bom": "3.0.0", - "yargs": "^7.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "jest-snapshot": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-20.0.3.tgz", - "integrity": "sha1-W4R+GtsaTZCFKn+fElCG4YfHZWY=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "jest-diff": "^20.0.3", - "jest-matcher-utils": "^20.0.3", - "jest-util": "^20.0.3", - "natural-compare": "^1.4.0", - "pretty-format": "^20.0.3" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "jest-util": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-20.0.3.tgz", - "integrity": "sha1-DAf32A2C9OWmfG+LnD/n9lz9Mq0=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "graceful-fs": "^4.1.11", - "jest-message-util": "^20.0.3", - "jest-mock": "^20.0.3", - "jest-validate": "^20.0.3", - "leven": "^2.1.0", - "mkdirp": "^0.5.1" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "jest-validate": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-20.0.3.tgz", - "integrity": "sha1-0M/R3k9XnymEhJJcKA+PHZTsPKs=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "jest-matcher-utils": "^20.0.3", - "leven": "^2.1.0", - "pretty-format": "^20.0.3" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, "js-base64": { "version": "2.4.9", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.9.tgz", @@ -8565,48 +7465,6 @@ "esprima": "^2.6.0" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true, - "optional": true - }, - "jsdom": { - "version": "9.12.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-9.12.0.tgz", - "integrity": "sha1-6MVG//ywbADUgzyoRBD+1/igl9Q=", - "dev": true, - "requires": { - "abab": "^1.0.3", - "acorn": "^4.0.4", - "acorn-globals": "^3.1.0", - "array-equal": "^1.0.0", - "content-type-parser": "^1.0.1", - "cssom": ">= 0.3.2 < 0.4.0", - "cssstyle": ">= 0.2.37 < 0.3.0", - "escodegen": "^1.6.1", - "html-encoding-sniffer": "^1.0.1", - "nwmatcher": ">= 1.3.9 < 2.0.0", - "parse5": "^1.5.1", - "request": "^2.79.0", - "sax": "^1.2.1", - "symbol-tree": "^3.2.1", - "tough-cookie": "^2.3.2", - "webidl-conversions": "^4.0.0", - "whatwg-encoding": "^1.0.1", - "whatwg-url": "^4.3.0", - "xml-name-validator": "^2.0.1" - }, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", - "dev": true - } - } - }, "jsesc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", @@ -8625,38 +7483,17 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true, - "requires": { - "jsonify": "~0.0.0" - } - }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, "json3": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", @@ -8668,24 +7505,6 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, "jsx-ast-utils": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz", @@ -8708,9 +7527,9 @@ } }, "known-css-properties": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.6.1.tgz", - "integrity": "sha512-nQRpMcHm1cQ6gmztdvLcIvxocznSMqH/y6XtERrWrHaymOYdDGroRqetJvJycxGEr1aakXiigDgn7JnzuXlk6A==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.11.0.tgz", + "integrity": "sha512-bEZlJzXo5V/ApNNa5z375mJC6Nrz4vG43UgcSCrg2OHC+yuB6j0iDSrY7RQ/+PRofFB03wNIIt9iXIVLr4wc7w==", "dev": true }, "lazy-cache": { @@ -8834,9 +7653,9 @@ } }, "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, "lodash-es": { "version": "4.17.10", @@ -8865,6 +7684,12 @@ "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", "dev": true }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -8966,15 +7791,6 @@ "pify": "^3.0.0" } }, - "makeerror": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", - "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", - "dev": true, - "requires": { - "tmpl": "1.0.x" - } - }, "map-age-cleaner": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", @@ -9023,12 +7839,6 @@ "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=", "dev": true }, - "math-random": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", - "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=", - "dev": true - }, "mathml-tag-names": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.0.tgz", @@ -9100,31 +7910,8 @@ "redent": "^2.0.0", "trim-newlines": "^2.0.0", "yargs-parser": "^10.0.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "yargs-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", - "dev": true, - "requires": { - "camelcase": "^4.1.0" - } - } } }, - "merge": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", - "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==", - "dev": true - }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -9132,9 +7919,9 @@ "dev": true }, "merge2": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.2.tgz", - "integrity": "sha512-bgM8twH86rWni21thii6WCMQMRMmwqqdW3sGWi9IipnVAszdLXRjwDwAnyrVXo6DuP3AjRMMttZKUB48QWIFGg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.3.tgz", + "integrity": "sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA==", "dev": true }, "methods": { @@ -9144,24 +7931,32 @@ "dev": true }, "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "requires": { - "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" + "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" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } } }, "miller-rabin": { @@ -9432,12 +8227,6 @@ "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==", "dev": true }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", - "dev": true - }, "node-libs-browser": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", @@ -9477,16 +8266,13 @@ } } }, - "node-notifier": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.2.1.tgz", - "integrity": "sha512-MIBs+AAd6dJ2SklbbE8RUDRlIVhU8MaNLh1A9SUZDUHPiZkWLFde6UNwG41yQHZEToHgJMXqyVZ9UcS/ReOVTg==", + "node-releases": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.8.tgz", + "integrity": "sha512-gQm+K9mGCiT/NXHy+V/ZZS1N/LOaGGqRAAJJs3X9Ah1g+CIbRcBgNyoNYQ+SEtcyAtB9KqDruu+fF7nWjsqRaA==", "dev": true, "requires": { - "growly": "^1.3.0", - "semver": "^5.4.1", - "shellwords": "^0.1.1", - "which": "^1.3.0" + "semver": "^5.3.0" } }, "normalize-package-data": { @@ -9564,18 +8350,6 @@ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, - "nwmatcher": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.4.tgz", - "integrity": "sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ==", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -9642,16 +8416,6 @@ "es-abstract": "^1.5.1" } }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true, - "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" - } - }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -9729,24 +8493,6 @@ "is-wsl": "^1.1.0" } }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true - } - } - }, "optionator": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", @@ -9782,15 +8528,6 @@ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, - "requires": { - "lcid": "^1.0.0" - } - }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -9885,9 +8622,9 @@ } }, "parse-entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.1.2.tgz", - "integrity": "sha512-5N9lmQ7tmxfXf+hO3X6KRG6w7uYO/HL9fHalSySTdyn63C3WNvTM/1R8tn1u1larNcEbo3Slcy2bsVDQqvEpUg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.0.tgz", + "integrity": "sha512-XXtDdOPLSB0sHecbEapQi6/58U/ODj/KWfIXmmMCJF/eRn8laX6LZbOyioMoETOOJoWRW8/qTSl5VQkUIfKM5g==", "dev": true, "requires": { "character-entities": "^1.0.0", @@ -9898,18 +8635,6 @@ "is-hexadecimal": "^1.0.0" } }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dev": true, - "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" - } - }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -9920,12 +8645,6 @@ "json-parse-better-errors": "^1.0.1" } }, - "parse5": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-1.5.1.tgz", - "integrity": "sha1-m387DeMr543CQBsXVzzK8Pb1nZQ=", - "dev": true - }, "parseurl": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", @@ -10957,14 +9676,20 @@ } }, "postcss-html": { - "version": "0.23.7", - "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-0.23.7.tgz", - "integrity": "sha1-RxRsFeIbnAB0bEARXc/4JwxDnzI=", + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-0.36.0.tgz", + "integrity": "sha512-HeiOxGcuwID0AFsNAL0ox3mW6MHH5cstWN1Z3Y+n6H+g12ih7LHdYxWwEA/QmrebctLjo79xz9ouK3MroHwOJw==", "dev": true, "requires": { - "htmlparser2": "^3.9.2" + "htmlparser2": "^3.10.0" }, "dependencies": { + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, "domhandler": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", @@ -10975,17 +9700,28 @@ } }, "htmlparser2": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", - "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", "dev": true, "requires": { - "domelementtype": "^1.3.0", + "domelementtype": "^1.3.1", "domhandler": "^2.3.0", "domutils": "^1.5.1", "entities": "^1.1.1", "inherits": "^2.0.1", - "readable-stream": "^2.0.2" + "readable-stream": "^3.1.1" + } + }, + "readable-stream": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz", + "integrity": "sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } } } @@ -11032,6 +9768,15 @@ "postcss": "^6.0.11" } }, + "postcss-jsx": { + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/postcss-jsx/-/postcss-jsx-0.36.0.tgz", + "integrity": "sha512-/lWOSXSX5jlITCKFkuYU2WLFdrncZmjSVyNpHAunEgirZXLwI8RjU556e3Uz4mv0WVHnJA9d3JWb36lK9Yx99g==", + "dev": true, + "requires": { + "@babel/core": ">=7.1.0" + } + }, "postcss-lab-function": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-1.1.0.tgz", @@ -11044,72 +9789,54 @@ } }, "postcss-less": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/postcss-less/-/postcss-less-1.1.5.tgz", - "integrity": "sha512-QQIiIqgEjNnquc0d4b6HDOSFZxbFQoy4MPpli2lSLpKhMyBkKwwca2HFqu4xzxlKID/F2fxSOowwtKpgczhF7A==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-less/-/postcss-less-3.1.2.tgz", + "integrity": "sha512-66ZBVo1JGkQ7r13M97xcHcyarWpgg21RaqIZWZXHE3XOtb5+ywK1uZWeY1DYkYRkIX/l8Hvxnx9iSKB68nFr+w==", "dev": true, "requires": { - "postcss": "^5.2.16" + "postcss": "^7.0.14" }, "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "dependencies": { "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } } } }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz", + "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==", "dev": true, "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" } }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { - "has-flag": "^1.0.0" + "has-flag": "^3.0.0" } } } @@ -11146,12 +9873,12 @@ } }, "postcss-markdown": { - "version": "0.23.7", - "resolved": "https://registry.npmjs.org/postcss-markdown/-/postcss-markdown-0.23.7.tgz", - "integrity": "sha1-fjo5h5QpXEJeUeTwq97m0TrT0TQ=", + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/postcss-markdown/-/postcss-markdown-0.36.0.tgz", + "integrity": "sha512-rl7fs1r/LNSB2bWRhyZ+lM/0bwKv9fhl38/06gF6mKMo/NPnp55+K1dSTosSVjFZc0e1ppBlu+WT91ba0PMBfQ==", "dev": true, "requires": { - "remark": "^9.0.0", + "remark": "^10.0.1", "unist-util-find-all-after": "^1.0.2" } }, @@ -12321,15 +11048,61 @@ } }, "postcss-reporter": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-5.0.0.tgz", - "integrity": "sha512-rBkDbaHAu5uywbCR2XE8a25tats3xSOsGNx6mppK6Q9kSFGKc/FyAzfci+fWM2l+K402p1D0pNcfDGxeje5IKg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-6.0.1.tgz", + "integrity": "sha512-LpmQjfRWyabc+fRygxZjpRxfhRf9u/fdlKf4VHG4TSPbV2XNsuISzYW1KL+1aQzx53CAppa1bKG4APIB/DOXXw==", "dev": true, "requires": { - "chalk": "^2.0.1", - "lodash": "^4.17.4", - "log-symbols": "^2.0.0", - "postcss": "^6.0.8" + "chalk": "^2.4.1", + "lodash": "^4.17.11", + "log-symbols": "^2.2.0", + "postcss": "^7.0.7" + }, + "dependencies": { + "postcss": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz", + "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + } + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "postcss-resolve-nested-selector": { @@ -12339,44 +11112,163 @@ "dev": true }, "postcss-safe-parser": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-3.0.1.tgz", - "integrity": "sha1-t1Pv9sfArqXoN1++TN6L+QY/8UI=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.1.tgz", + "integrity": "sha512-xZsFA3uX8MO3yAda03QrG3/Eg1LN3EPfjjf07vke/46HERLZyHrTsQ9E1r1w1W//fWEhtYNndo2hQplN2cVpCQ==", "dev": true, "requires": { - "postcss": "^6.0.6" + "postcss": "^7.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz", + "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "postcss-sass": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/postcss-sass/-/postcss-sass-0.3.2.tgz", - "integrity": "sha512-0HgxikiZ07VKYr98KT+k7/rAzyMgZlP+3+R8vUti56T2dPdhW0OhPGDQzddxY/N2iDtBVZQqCHRDA09j5I6EWg==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/postcss-sass/-/postcss-sass-0.3.5.tgz", + "integrity": "sha512-B5z2Kob4xBxFjcufFnhQ2HqJQ2y/Zs/ic5EZbCywCkxKd756Q40cIQ/veRDwSrw1BF6+4wUgmpm0sBASqVi65A==", "dev": true, "requires": { - "gonzales-pe": "4.2.3", - "postcss": "6.0.22" + "gonzales-pe": "^4.2.3", + "postcss": "^7.0.1" }, "dependencies": { - "postcss": { - "version": "6.0.22", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", - "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "chalk": "^2.4.1", + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz", + "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", "source-map": "^0.6.1", - "supports-color": "^5.4.0" + "supports-color": "^6.1.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" } } } }, "postcss-scss": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-1.0.6.tgz", - "integrity": "sha512-4EFYGHcEw+H3E06PT/pQQri06u/1VIIPjeJQaM8skB80vZuXMhp4cSNV5azmdNkontnOID/XYWEvEEELLFB1ww==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-2.0.0.tgz", + "integrity": "sha512-um9zdGKaDZirMm+kZFKKVsnKPF7zF7qBAtIfTSnZXD1jZ0JNZIxdB6TxQOjCnlSzLRInVl2v3YdBh/M881C4ug==", "dev": true, "requires": { - "postcss": "^6.0.23" + "postcss": "^7.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz", + "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "postcss-selector-matches": { @@ -12616,9 +11508,9 @@ } }, "postcss-syntax": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/postcss-syntax/-/postcss-syntax-0.9.1.tgz", - "integrity": "sha512-oC78MbSKmT/kPgqRl9sQrBIsbfr4TN+vH0STuUa7gnewVg9cs+wjJ00Lclu1GbKy38vJE7tBrhvjlEWvyxiZcg==", + "version": "0.36.2", + "resolved": "https://registry.npmjs.org/postcss-syntax/-/postcss-syntax-0.36.2.tgz", + "integrity": "sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==", "dev": true }, "postcss-unique-selectors": { @@ -12796,12 +11688,6 @@ "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", "dev": true }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", - "dev": true - }, "pretty-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", @@ -12812,16 +11698,6 @@ "utila": "~0.4" } }, - "pretty-format": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-20.0.3.tgz", - "integrity": "sha1-Ag41ClYKH+GpjcO+tsz/s4beixQ=", - "dev": true, - "requires": { - "ansi-regex": "^2.1.1", - "ansi-styles": "^3.0.0" - } - }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", @@ -12891,12 +11767,6 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, - "psl": { - "version": "1.1.29", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", - "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==", - "dev": true - }, "public-encrypt": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz", @@ -13003,31 +11873,6 @@ "integrity": "sha512-GXpfrYVPwx3K7RQ6aYT8KPS8XViSXUVJT1ONhoKPE9VAleW42YE+U+8VEyGWt41EnEQW7gwecYJriTI0pKoecQ==", "dev": true }, - "randomatic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.0.tgz", - "integrity": "sha512-KnGPVE0lo2WoXxIZ7cPR8YBpiol4gsSuOwDSg410oHh80ZMp5EiypNqL2K4Z77vJn6lB5rap7IkAmcUlalcnBQ==", - "dev": true, - "requires": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, "randombytes": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", @@ -13454,15 +12299,6 @@ "private": "^0.1.6" } }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "dev": true, - "requires": { - "is-equal-shallow": "^0.1.3" - } - }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -13520,20 +12356,20 @@ "dev": true }, "remark": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/remark/-/remark-9.0.0.tgz", - "integrity": "sha512-amw8rGdD5lHbMEakiEsllmkdBP+/KpjW/PRK6NSGPZKCQowh0BT4IWXDAkRMyG3SB9dKPXWMviFjNusXzXNn3A==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/remark/-/remark-10.0.1.tgz", + "integrity": "sha512-E6lMuoLIy2TyiokHprMjcWNJ5UxfGQjaMSMhV+f4idM625UjjK4j798+gPs5mfjzDE6vL0oFKVeZM6gZVSVrzQ==", "dev": true, "requires": { - "remark-parse": "^5.0.0", - "remark-stringify": "^5.0.0", - "unified": "^6.0.0" + "remark-parse": "^6.0.0", + "remark-stringify": "^6.0.0", + "unified": "^7.0.0" } }, "remark-parse": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-5.0.0.tgz", - "integrity": "sha512-b3iXszZLH1TLoyUzrATcTQUZrwNl1rE70rVdSruJFlDaJ9z5aMkhrG43Pp68OgfHndL/ADz6V69Zow8cTQu+JA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-6.0.3.tgz", + "integrity": "sha512-QbDXWN4HfKTUC0hHa4teU463KclLAnwpn/FBn87j9cKYJWWawbiLgMfP2Q4XwhxxuuuOxHlw+pSN0OKuJwyVvg==", "dev": true, "requires": { "collapse-white-space": "^1.0.2", @@ -13554,9 +12390,9 @@ } }, "remark-stringify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-5.0.0.tgz", - "integrity": "sha512-Ws5MdA69ftqQ/yhRF9XhVV29mhxbfGhbz0Rx5bQH+oJcNhhSM6nCu1EpLod+DjrFGrU0BMPs+czVmJZU7xiS7w==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-6.0.4.tgz", + "integrity": "sha512-eRWGdEPMVudijE/psbIDNcnJLRVx3xhfuEsTDGgH4GsFF91dVhw5nhmnBppafJ7+NWINW6C7ZwWbi30ImJzqWg==", "dev": true, "requires": { "ccount": "^1.0.0", @@ -13629,42 +12465,6 @@ "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", "dev": true }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - } - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -13844,47 +12644,6 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "sane": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-1.6.0.tgz", - "integrity": "sha1-lhDEUjB6E10pwf3+JUcDQYDEZ3U=", - "dev": true, - "requires": { - "anymatch": "^1.3.0", - "exec-sh": "^0.2.0", - "fb-watchman": "^1.8.0", - "minimatch": "^3.0.2", - "minimist": "^1.1.1", - "walker": "~1.0.5", - "watch": "~0.10.0" - }, - "dependencies": { - "bser": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bser/-/bser-1.0.2.tgz", - "integrity": "sha1-OBEWlwsqbe6lZG3RXdcnhES1YWk=", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "fb-watchman": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-1.9.2.tgz", - "integrity": "sha1-okz0eCf4LTj7Waaa1wt247auc4M=", - "dev": true, - "requires": { - "bser": "1.0.2" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -14074,12 +12833,6 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, - "shellwords": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true - }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -14453,9 +13206,9 @@ } }, "specificity": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.3.2.tgz", - "integrity": "sha512-Nc/QN/A425Qog7j9aHmwOrlwX2e7pNI47ciwxwy4jOlvbbMHkNNJchit+FX+UjF3IAdiaaV5BKeWuDUnws6G1A==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", + "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", "dev": true }, "split-string": { @@ -14473,23 +13226,6 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "sshpk": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", - "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", - "dev": true, - "requires": { - "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", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, "ssri": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", @@ -14583,15 +13319,6 @@ "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", "dev": true }, - "string-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-1.0.1.tgz", - "integrity": "sha1-VpcPscOFWOnnC3KL894mmsRa36w=", - "dev": true, - "requires": { - "strip-ansi": "^3.0.0" - } - }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -14690,98 +13417,263 @@ "dev": true }, "stylelint": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-9.2.1.tgz", - "integrity": "sha512-zR0rSMITL8VjTVoIEGsUh5m0lMluHaIbDLAJTrFYVLElYhP6d5HcJc5/cexA1mrKzQkKu7gvmbDclNLgAeiabw==", + "version": "9.10.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-9.10.1.tgz", + "integrity": "sha512-9UiHxZhOAHEgeQ7oLGwrwoDR8vclBKlSX7r4fH0iuu0SfPwFaLkb1c7Q2j1cqg9P7IDXeAV2TvQML/fRQzGBBQ==", "dev": true, "requires": { - "autoprefixer": "^8.0.0", + "autoprefixer": "^9.0.0", "balanced-match": "^1.0.0", "chalk": "^2.4.1", "cosmiconfig": "^5.0.0", - "debug": "^3.0.0", + "debug": "^4.0.0", "execall": "^1.0.0", - "file-entry-cache": "^2.0.0", + "file-entry-cache": "^4.0.0", "get-stdin": "^6.0.0", - "globby": "^8.0.0", + "global-modules": "^2.0.0", + "globby": "^9.0.0", "globjoin": "^0.1.4", "html-tags": "^2.0.0", - "ignore": "^3.3.3", + "ignore": "^5.0.4", "import-lazy": "^3.1.0", "imurmurhash": "^0.1.4", - "known-css-properties": "^0.6.0", + "known-css-properties": "^0.11.0", + "leven": "^2.1.0", "lodash": "^4.17.4", "log-symbols": "^2.0.0", "mathml-tag-names": "^2.0.1", "meow": "^5.0.0", - "micromatch": "^2.3.11", + "micromatch": "^3.1.10", "normalize-selector": "^0.2.0", - "pify": "^3.0.0", - "postcss": "^6.0.16", - "postcss-html": "^0.23.6", - "postcss-less": "^1.1.5", - "postcss-markdown": "^0.23.6", + "pify": "^4.0.0", + "postcss": "^7.0.13", + "postcss-html": "^0.36.0", + "postcss-jsx": "^0.36.0", + "postcss-less": "^3.1.0", + "postcss-markdown": "^0.36.0", "postcss-media-query-parser": "^0.2.3", - "postcss-reporter": "^5.0.0", + "postcss-reporter": "^6.0.0", "postcss-resolve-nested-selector": "^0.1.1", - "postcss-safe-parser": "^3.0.1", - "postcss-sass": "^0.3.0", - "postcss-scss": "^1.0.2", + "postcss-safe-parser": "^4.0.0", + "postcss-sass": "^0.3.5", + "postcss-scss": "^2.0.0", "postcss-selector-parser": "^3.1.0", - "postcss-syntax": "^0.9.0", + "postcss-syntax": "^0.36.2", "postcss-value-parser": "^3.3.0", "resolve-from": "^4.0.0", "signal-exit": "^3.0.2", - "specificity": "^0.3.1", - "string-width": "^2.1.0", + "slash": "^2.0.0", + "specificity": "^0.4.1", + "string-width": "^3.0.0", "style-search": "^0.1.0", - "sugarss": "^1.0.0", + "sugarss": "^2.0.0", "svg-tags": "^1.0.0", - "table": "^4.0.1" + "table": "^5.0.0" }, "dependencies": { - "cosmiconfig": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.0.6.tgz", - "integrity": "sha512-6DWfizHriCrFWURP1/qyhsiFvYdlJzbCzmtFWh744+KyWsJo5+kPzUZZaMRSSItoYc0pxFX7gEO7ZC1/gN/7AQ==", + "ajv": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", + "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", "dev": true, "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz", + "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==", + "dev": true + }, + "autoprefixer": { + "version": "9.4.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.8.tgz", + "integrity": "sha512-DIhd0KMi9Nql3oJkJ2HCeOVihrXFPtWXc6ckwaUNwliDOt9OGr0fk8vV8jCLWXnZc1EXvQ2uLUzGpcPxFAQHEQ==", + "dev": true, + "requires": { + "browserslist": "^4.4.1", + "caniuse-lite": "^1.0.30000938", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^7.0.14", + "postcss-value-parser": "^3.3.1" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "browserslist": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.4.1.tgz", + "integrity": "sha512-pEBxEXg7JwaakBXjATYw/D1YZh4QUSCX/Mnd/wnqSRPPSi1U39iDhDoKGoBUcraKdxDlrYqJxSI5nNvD+dWP2A==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30000929", + "electron-to-chromium": "^1.3.103", + "node-releases": "^1.1.3" + } + }, + "caniuse-lite": { + "version": "1.0.30000938", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000938.tgz", + "integrity": "sha512-ekW8NQ3/FvokviDxhdKLZZAx7PptXNwxKgXtnR5y+PR3hckwuP3yJ1Ir+4/c97dsHNqtAyfKUGdw8P4EYzBNgw==", + "dev": true + }, + "cosmiconfig": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.1.0.tgz", + "integrity": "sha512-kCNPvthka8gvLtzAxQXvWo4FxqRB+ftRZyPZNuab5ngvM9Y7yw7hbEysglptLgpkGX9nAOKTBVkHUAe8xtYR6Q==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", "is-directory": "^0.3.1", "js-yaml": "^3.9.0", + "lodash.get": "^4.4.2", "parse-json": "^4.0.0" } }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "electron-to-chromium": { + "version": "1.3.113", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.113.tgz", + "integrity": "sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, - "globby": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.1.tgz", - "integrity": "sha512-oMrYrJERnKBLXNLVTqhm3vPEdJ/b2ZE28xN4YARiix1NOIOBPEpOUnm844K1iu/BkphCaf2WNFwMszv8Soi1pw==", + "file-entry-cache": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-4.0.0.tgz", + "integrity": "sha512-AVSwsnbV8vH/UVbvgEhf3saVQXORNv0ZzSkvkhQIaia5Tia+JhGTaa/ePUSVoPHQyGayQNmYfkzFi3WZV5zcpA==", "dev": true, "requires": { - "array-union": "^1.0.1", - "dir-glob": "^2.0.0", - "fast-glob": "^2.0.2", - "glob": "^7.1.2", - "ignore": "^3.3.5", - "pify": "^3.0.0", - "slash": "^1.0.0" + "flat-cache": "^2.0.1" } }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "globby": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-9.0.0.tgz", + "integrity": "sha512-q0qiO/p1w/yJ0hk8V9x1UXlgsXUxlGd0AHUOXZVXBO6aznDtpx7M8D1kBrCAItoPm+4l8r6ATXV1JpjY2SBQOw==", + "dev": true, + "requires": { + "array-union": "^1.0.2", + "dir-glob": "^2.2.1", + "fast-glob": "^2.2.6", + "glob": "^7.1.3", + "ignore": "^4.0.3", + "pify": "^4.0.1", + "slash": "^2.0.0" + }, + "dependencies": { + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + } + } + }, + "ignore": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.0.5.tgz", + "integrity": "sha512-kOC8IUb8HSDMVcYrDVezCxpJkzSQWTAzf3olpKM6o9rM5zpojx23O0Fl8Wr4+qJ6ZbPEHqf1fdwev/DS7v7pmA==", + "dev": true + }, "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz", + "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==", "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" } }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "postcss": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz", + "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + } + } + }, "postcss-selector-parser": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", @@ -14798,6 +13690,91 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, + "string-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.0.0.tgz", + "integrity": "sha512-rr8CUxBbvOZDUvc5lNIJ+OC1nPVpz+Siw9VBtUjB9b6jZehZLFt0JMCZzShFHIsI8cbhm0EsNIfWJMFV3cu3Ew==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.0.0" + } + }, + "strip-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz", + "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==", + "dev": true, + "requires": { + "ansi-regex": "^4.0.0" + } + }, + "sugarss": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-2.0.0.tgz", + "integrity": "sha512-WfxjozUk0UVA4jm+U1d736AUpzSrNsQcIbyOkoE364GrtWmIrFdk5lksEupgWMD4VaT/0kVx1dobpiDumSgmJQ==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "table": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/table/-/table-5.2.3.tgz", + "integrity": "sha512-N2RsDAMvDLvYwFcwbPyF3VmVSSkuF+G1e+8inhBLtHpvwXGw4QRPEZhihQNeEN0i1up6/f6ObCJXNdlRG3YVyQ==", + "dev": true, + "requires": { + "ajv": "^6.9.1", + "lodash": "^4.17.11", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + } + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } } } }, @@ -15162,12 +14139,6 @@ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" }, - "symbol-tree": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", - "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", - "dev": true - }, "table": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", @@ -15220,30 +14191,12 @@ "integrity": "sha512-dQRhbNQkRnaqauC7WqSJ21EEksgT0fYZX2lqXzGkpo8JNig9zGZTYoMGvyI2nWmXlE2VSVXVDu7wLVGu/mQEsg==", "dev": true }, - "test-exclude": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.2.2.tgz", - "integrity": "sha512-2kTGf+3tykCfrWVREgyTR0bmVO0afE6i7zVXi/m+bZZ8ujV89Aulxdcdv32yH+unVFg3Y5o6GA8IzsHnGQuFgQ==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "minimatch": "^3.0.4", - "read-pkg-up": "^3.0.0", - "require-main-filename": "^1.0.1" - } - }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, - "throat": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-3.2.0.tgz", - "integrity": "sha512-/EY8VpvlqJ+sFtLPeOgc8Pl7kQVOWv0woD87KTXVHPIAE842FGT+rokxIhe8xIUP1cfgrkt0as0vDLjDiMtr8w==", - "dev": true - }, "through": { "version": "2.3.8", "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -15284,12 +14237,6 @@ "os-tmpdir": "~1.0.2" } }, - "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", - "dev": true - }, "to-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", @@ -15350,30 +14297,6 @@ "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=", "dev": true }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "dev": true, - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - } - } - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", - "dev": true - }, "trim": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", @@ -15410,22 +14333,6 @@ "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, - "optional": true - }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -15518,16 +14425,18 @@ } }, "unified": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/unified/-/unified-6.2.0.tgz", - "integrity": "sha512-1k+KPhlVtqmG99RaTbAv/usu85fcSRu3wY8X+vnsEhIxNP5VbVIDiXnLqyKIG+UMdyTg0ZX9EI6k2AfjJkHPtA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-7.1.0.tgz", + "integrity": "sha512-lbk82UOIGuCEsZhPj8rNAkXSDXd6p0QLzIuSsCdxrqnqU56St4eyOB+AlXsVgVeRmetPTYydIuvFfpDIed8mqw==", "dev": true, "requires": { + "@types/unist": "^2.0.0", + "@types/vfile": "^3.0.0", "bail": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^1.1.0", "trough": "^1.0.0", - "vfile": "^2.0.0", + "vfile": "^3.0.0", "x-is-string": "^0.1.0" } }, @@ -15862,39 +14771,36 @@ "integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==", "dev": true }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, "vfile": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-2.3.0.tgz", - "integrity": "sha512-ASt4mBUHcTpMKD/l5Q+WJXNtshlWxOogYyGYYrg4lt/vuRjC1EFQtlAofL5VmtVNIZJzWYFJjzGWZ0Gw8pzW1w==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-3.0.1.tgz", + "integrity": "sha512-y7Y3gH9BsUSdD4KzHsuMaCzRjglXN0W2EcMf0gpvu6+SbsGhMje7xDc8AEoeXy6mIwCKMI6BkjMsRjzQbhMEjQ==", "dev": true, "requires": { - "is-buffer": "^1.1.4", + "is-buffer": "^2.0.0", "replace-ext": "1.0.0", "unist-util-stringify-position": "^1.0.0", "vfile-message": "^1.0.0" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", + "dev": true + } } }, "vfile-location": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.3.tgz", - "integrity": "sha512-zM5/l4lfw1CBoPx3Jimxoc5RNDAHHpk6AM6LM0pTIkm5SUSsx8ZekZ0PVdf0WEZ7kjlhSt7ZlqbRL6Cd6dBs6A==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.4.tgz", + "integrity": "sha512-KRL5uXQPoUKu+NGvQVL4XLORw45W62v4U4gxJ3vRlDfI9QsT4ZN1PNXn/zQpKUulqGDpYuT0XDfp5q9O87/y/w==", "dev": true }, "vfile-message": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.0.1.tgz", - "integrity": "sha512-vSGCkhNvJzO6VcWC6AlJW4NtYOVtS+RgCaqFIYUjoGIlHnFL+i0LbtYvonDWOMcB97uTPT4PRsyYY7REWC9vug==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.1.1.tgz", + "integrity": "sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA==", "dev": true, "requires": { "unist-util-stringify-position": "^1.1.1" @@ -15914,15 +14820,6 @@ "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" }, - "walker": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", - "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", - "dev": true, - "requires": { - "makeerror": "1.0.x" - } - }, "warning": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", @@ -15931,12 +14828,6 @@ "loose-envify": "^1.0.0" } }, - "watch": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/watch/-/watch-0.10.0.tgz", - "integrity": "sha1-d3mLLaD5kQ1ZXxrOWwwiWFIfIdw=", - "dev": true - }, "watchpack": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", @@ -15957,12 +14848,6 @@ "minimalistic-assert": "^1.0.0" } }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true - }, "webpack": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.8.1.tgz", @@ -16601,49 +15486,11 @@ "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", "dev": true }, - "whatwg-encoding": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.4.tgz", - "integrity": "sha512-vM9KWN6MP2mIHZ86ytcyIv7e8Cj3KTfO2nd2c8PFDqcI4bxFmQp83ibq4wadq7rL9l9sZV6o9B0LTt8ygGAAXg==", - "dev": true, - "requires": { - "iconv-lite": "0.4.23" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } - } - }, "whatwg-fetch": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz", "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ=" }, - "whatwg-url": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-4.8.0.tgz", - "integrity": "sha1-0pgaqRSMHgCkHFphMRZqtGg7vMA=", - "dev": true, - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", - "dev": true - } - } - }, "whet.extend": { "version": "0.9.9", "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", @@ -16659,12 +15506,6 @@ "isexe": "^2.0.0" } }, - "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", - "dev": true - }, "window-size": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", @@ -16739,12 +15580,6 @@ "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=", "dev": true }, - "xml-name-validator": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-2.0.1.tgz", - "integrity": "sha1-TYuPHszTQZqjYgYb7O9RXh5VljU=", - "dev": true - }, "xmldoc": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/xmldoc/-/xmldoc-1.1.2.tgz", @@ -16778,173 +15613,19 @@ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true }, - "yargs": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", - "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", - "dev": true, - "requires": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^5.0.0" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - } - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "^0.2.0" - } - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - } - } - }, "yargs-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", - "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", "dev": true, "requires": { - "camelcase": "^3.0.0" + "camelcase": "^4.1.0" }, "dependencies": { "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", "dev": true } } diff --git a/client/package.json b/client/package.json index a5cf9e3d..0ec26a9c 100644 --- a/client/package.json +++ b/client/package.json @@ -16,7 +16,7 @@ "file-saver": "^1.3.8", "i18next": "^12.0.0", "i18next-browser-languagedetector": "^2.2.3", - "lodash": "^4.17.10", + "lodash": "^4.17.11", "nanoid": "^1.2.3", "prop-types": "^15.6.1", "react": "^16.4.0", @@ -40,7 +40,6 @@ "autoprefixer": "^8.6.3", "babel-core": "6.26.0", "babel-eslint": "^8.2.3", - "babel-jest": "20.0.3", "babel-loader": "7.1.2", "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-env": "^1.7.0", @@ -60,7 +59,6 @@ "extract-text-webpack-plugin": "^3.0.2", "file-loader": "1.1.5", "html-webpack-plugin": "^3.2.0", - "jest": "20.0.4", "postcss-flexbugs-fixes": "3.2.0", "postcss-import": "^11.1.0", "postcss-loader": "^2.1.5", @@ -68,7 +66,7 @@ "postcss-preset-env": "^5.1.0", "postcss-svg": "^2.4.0", "style-loader": "^0.21.0", - "stylelint": "9.2.1", + "stylelint": "^9.10.1", "stylelint-webpack-plugin": "0.10.4", "uglifyjs-webpack-plugin": "^1.2.7", "url-loader": "^1.0.1", From 8e7ceec1a133b6318c8bacadfad6cc3bb7c4499a Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Wed, 20 Feb 2019 16:10:32 +0300 Subject: [PATCH 72/88] Remove unused package --- client/package-lock.json | 2 +- client/package.json | 3 +-- client/src/components/Dashboard/index.js | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 5ce9329b..7a93cbc1 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -6160,7 +6160,7 @@ "dependencies": { "minimist": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.1.3.tgz", "integrity": "sha1-O+39kaktOQFvz6ocaB6Pqhoe/ag=", "dev": true } diff --git a/client/package.json b/client/package.json index 0ec26a9c..9b000c61 100644 --- a/client/package.json +++ b/client/package.json @@ -33,8 +33,7 @@ "redux-actions": "^2.4.0", "redux-form": "^7.4.2", "redux-thunk": "^2.3.0", - "svg-url-loader": "^2.3.2", - "whatwg-fetch": "2.0.3" + "svg-url-loader": "^2.3.2" }, "devDependencies": { "autoprefixer": "^8.6.3", diff --git a/client/src/components/Dashboard/index.js b/client/src/components/Dashboard/index.js index 2016b16f..dbd9901c 100644 --- a/client/src/components/Dashboard/index.js +++ b/client/src/components/Dashboard/index.js @@ -1,6 +1,5 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; -import 'whatwg-fetch'; import { Trans, withNamespaces } from 'react-i18next'; import Statistics from './Statistics'; From c53a132072b3601a459878c40a40740fe1e88917 Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Wed, 20 Feb 2019 16:54:14 +0300 Subject: [PATCH 73/88] Added UpdateTopline component --- client/src/__locales/en.json | 3 ++- client/src/components/App/index.js | 9 ++++---- client/src/components/ui/UpdateTopline.js | 27 +++++++++++++++++++++++ client/src/reducers/index.js | 4 ++-- 4 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 client/src/components/ui/UpdateTopline.js diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 3412a12a..4d8ac38c 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -245,5 +245,6 @@ "form_error_port_unsafe": "This is an unsafe port", "form_error_equal": "Shouldn't be equal", "form_error_password": "Password mismatched", - "reset_settings": "Reset settings" + "reset_settings": "Reset settings", + "update_announcement": "AdGuard Home {{version}} is now available! <0>Click here for more info." } \ No newline at end of file diff --git a/client/src/components/App/index.js b/client/src/components/App/index.js index b483f64a..91c5e512 100644 --- a/client/src/components/App/index.js +++ b/client/src/components/App/index.js @@ -17,7 +17,7 @@ import Logs from '../../containers/Logs'; import Footer from '../ui/Footer'; import Toasts from '../Toasts'; import Status from '../ui/Status'; -import Topline from '../ui/Topline'; +import UpdateTopline from '../ui/UpdateTopline'; import EncryptionTopline from '../ui/EncryptionTopline'; import i18n from '../../i18n'; @@ -62,9 +62,10 @@ class App extends Component { {updateAvailable && - - {dashboard.announcement} Click here for more info. - + } {!encryption.processing && diff --git a/client/src/components/ui/UpdateTopline.js b/client/src/components/ui/UpdateTopline.js new file mode 100644 index 00000000..a9124666 --- /dev/null +++ b/client/src/components/ui/UpdateTopline.js @@ -0,0 +1,27 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Trans, withNamespaces } from 'react-i18next'; + +import Topline from './Topline'; + +const UpdateTopline = props => ( + + + Click here + , + ]} + > + update_announcement + + +); + +UpdateTopline.propTypes = { + version: PropTypes.string.isRequired, + url: PropTypes.string.isRequired, +}; + +export default withNamespaces()(UpdateTopline); diff --git a/client/src/reducers/index.js b/client/src/reducers/index.js index 8cd04ac5..e83f48a1 100644 --- a/client/src/reducers/index.js +++ b/client/src/reducers/index.js @@ -120,13 +120,13 @@ const dashboard = handleActions({ if (versionCompare(currentVersion, payload.version) === -1) { const { - announcement, + version, announcement_url: announcementUrl, } = payload; const newState = { ...state, - announcement, + version, announcementUrl, isUpdateAvailable: true, }; From 5ac775aa4a579cdba258b70cf38b0f3a24ab55d8 Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Thu, 21 Feb 2019 15:05:54 +0300 Subject: [PATCH 74/88] Fixed stylelint errors --- client/src/install/Setup/Setup.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/install/Setup/Setup.css b/client/src/install/Setup/Setup.css index c88e8b82..cf58bc3c 100644 --- a/client/src/install/Setup/Setup.css +++ b/client/src/install/Setup/Setup.css @@ -15,7 +15,7 @@ padding: 30px 20px; line-height: 1.6; background-color: #fff; - box-shadow: 0 1px 4px rgba(74, 74, 74, .36); + box-shadow: 0 1px 4px rgba(74, 74, 74, 0.36); border-radius: 3px; } @@ -92,7 +92,7 @@ line-height: 20px; color: #fff; text-align: center; - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); transition: width 0.6s ease; background: linear-gradient(45deg, rgba(99, 125, 120, 1) 0%, rgba(88, 177, 101, 1) 100%); } From 37a1a98c49a91c2a6948474b9d8ed8fbebaf5bab Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Thu, 21 Feb 2019 15:39:15 +0300 Subject: [PATCH 75/88] Fixed EncryptionTopline check --- client/src/components/ui/EncryptionTopline.js | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/client/src/components/ui/EncryptionTopline.js b/client/src/components/ui/EncryptionTopline.js index 21731219..7c84a847 100644 --- a/client/src/components/ui/EncryptionTopline.js +++ b/client/src/components/ui/EncryptionTopline.js @@ -8,27 +8,29 @@ import Topline from './Topline'; import { EMPTY_DATE } from '../../helpers/constants'; const EncryptionTopline = (props) => { - if (props.notAfter !== EMPTY_DATE) { - const isAboutExpire = isAfter(addDays(Date.now(), 30), props.notAfter); - const isExpired = isAfter(Date.now(), props.notAfter); + if (props.notAfter === EMPTY_DATE) { + return false; + } - if (isExpired) { - return ( - - link]}> - topline_expired_certificate - - - ); - } else if (isAboutExpire) { - return ( - - link]}> - topline_expiring_certificate - - - ); - } + const isAboutExpire = isAfter(addDays(Date.now(), 30), props.notAfter); + const isExpired = isAfter(Date.now(), props.notAfter); + + if (isExpired) { + return ( + + link]}> + topline_expired_certificate + + + ); + } else if (isAboutExpire) { + return ( + + link]}> + topline_expiring_certificate + + + ); } return false; From 251beb24d30ee8a12129f26a00086692b5af4f78 Mon Sep 17 00:00:00 2001 From: Andrey Meshkov Date: Thu, 21 Feb 2019 17:33:46 +0300 Subject: [PATCH 76/88] Added openapi description --- app.go | 2 +- config.go | 33 +++++----- control.go | 24 +------ helpers.go | 29 --------- openapi/openapi.yaml | 148 ++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 165 insertions(+), 71 deletions(-) diff --git a/app.go b/app.go index 5485b94a..518bb397 100644 --- a/app.go +++ b/app.go @@ -173,7 +173,7 @@ func run(args options) { for { // this is an endless loop httpsServer.cond.L.Lock() // this mechanism doesn't let us through until all conditions are ment - for config.TLS.Enabled == false || config.TLS.PortHTTPS == 0 || config.TLS.PrivateKey == "" || config.TLS.CertificateChain == "" { // sleep until neccessary data is supplied + for config.TLS.Enabled == false || config.TLS.PortHTTPS == 0 || config.TLS.PrivateKey == "" || config.TLS.CertificateChain == "" { // sleep until necessary data is supplied httpsServer.cond.Wait() } address := net.JoinHostPort(config.BindHost, strconv.Itoa(config.TLS.PortHTTPS)) diff --git a/config.go b/config.go index c1aa2f8d..158381c1 100644 --- a/config.go +++ b/config.go @@ -63,37 +63,34 @@ type dnsConfig struct { var defaultDNS = []string{"tls://1.1.1.1", "tls://1.0.0.1"} type tlsConfigSettings struct { - Enabled bool `yaml:"enabled" json:"enabled"` - ServerName string `yaml:"server_name" json:"server_name,omitempty"` - ForceHTTPS bool `yaml:"force_https" json:"force_https,omitempty"` - PortHTTPS int `yaml:"port_https" json:"port_https,omitempty"` - PortDNSOverTLS int `yaml:"port_dns_over_tls" json:"port_dns_over_tls,omitempty"` + Enabled bool `yaml:"enabled" json:"enabled"` // Enabled is the encryption (DOT/DOH/HTTPS) status + ServerName string `yaml:"server_name" json:"server_name,omitempty"` // ServerName is the hostname of your HTTPS/TLS server + ForceHTTPS bool `yaml:"force_https" json:"force_https,omitempty"` // ForceHTTPS: if true, forces HTTP->HTTPS redirect + PortHTTPS int `yaml:"port_https" json:"port_https,omitempty"` // HTTPS port. If 0, HTTPS will be disabled + PortDNSOverTLS int `yaml:"port_dns_over_tls" json:"port_dns_over_tls,omitempty"` // DNS-over-TLS port. If 0, DOT will be disabled dnsforward.TLSConfig `yaml:",inline" json:",inline"` } // field ordering is not important -- these are for API and are recalculated on each run type tlsConfigStatus struct { - // certificate status - ValidCert bool `yaml:"-" json:"valid_cert"` - ValidChain bool `yaml:"-" json:"valid_chain"` - Subject string `yaml:"-" json:"subject,omitempty"` - Issuer string `yaml:"-" json:"issuer,omitempty"` - NotBefore time.Time `yaml:"-" json:"not_before,omitempty"` - NotAfter time.Time `yaml:"-" json:"not_after,omitempty"` - DNSNames []string `yaml:"-" json:"dns_names"` - StatusCertificate string `yaml:"-" json:"status_cert,omitempty"` + ValidCert bool `yaml:"-" json:"valid_cert"` // ValidCert is true if the specified certificates chain is a valid chain of X509 certificates + ValidChain bool `yaml:"-" json:"valid_chain"` // ValidChain is true if the specified certificates chain is verified and issued by a known CA + Subject string `yaml:"-" json:"subject,omitempty"` // Subject is the subject of the first certificate in the chain + Issuer string `yaml:"-" json:"issuer,omitempty"` // Issuer is the issuer of the first certificate in the chain + NotBefore time.Time `yaml:"-" json:"not_before,omitempty"` // NotBefore is the NotBefore field of the first certificate in the chain + NotAfter time.Time `yaml:"-" json:"not_after,omitempty"` // NotAfter is the NotAfter field of the first certificate in the chain + DNSNames []string `yaml:"-" json:"dns_names"` // DNSNames is the value of SubjectAltNames field of the first certificate in the chain // key status - ValidKey bool `yaml:"-" json:"valid_key"` - KeyType string `yaml:"-" json:"key_type,omitempty"` + ValidKey bool `yaml:"-" json:"valid_key"` // ValidKey is true if the key is a valid private key + KeyType string `yaml:"-" json:"key_type,omitempty"` // KeyType is one of RSA or ECDSA // is usable? set by validator usable bool // warnings - Warning string `yaml:"-" json:"warning,omitempty"` - WarningValidation string `yaml:"-" json:"warning_validation,omitempty"` + WarningValidation string `yaml:"-" json:"warning_validation,omitempty"` // WarningValidation is a validation warning message with the issue description } // field ordering is important -- yaml fields will mirror ordering from here diff --git a/control.go b/control.go index 4d13a9b8..a87bd2cd 100644 --- a/control.go +++ b/control.go @@ -1118,7 +1118,7 @@ func validateCertificates(data tlsConfig) tlsConfig { // clear out status for certificates data.tlsConfigStatus = tlsConfigStatus{} - // check only public certificate separetely from the key + // check only public certificate separately from the key if data.CertificateChain != "" { log.Tracef("got certificate: %s", data.CertificateChain) @@ -1194,24 +1194,6 @@ func validateCertificates(data tlsConfig) tlsConfig { data.NotAfter = notAfter data.NotBefore = mainCert.NotBefore data.DNSNames = mainCert.DNSNames - - data.StatusCertificate = fmt.Sprintf("Certificate expires on %s", notAfter) //, valid for hostname %s", mainCert.NotAfter, mainCert.Subject.CommonName) - if len(mainCert.DNSNames) == 1 { - data.StatusCertificate += fmt.Sprintf(", valid for hostname %s", mainCert.DNSNames[0]) - } else if len(mainCert.DNSNames) > 1 { - data.StatusCertificate += ", valid for hostnames " + strings.Join(mainCert.DNSNames, ", ") - } - - // issue a warning if certificate is about to expire - now := time.Now() - if now.AddDate(0, 0, 30).After(notAfter) { - timeLeft := notAfter.Sub(now) - if timeLeft > 0 { - data.Warning = fmt.Sprintf("Your certificate expires in %.0f days, we recommend you update it soon", timeLeft.Hours()/24) - } else { - data.Warning = fmt.Sprintf("Your certificate has expired on %s, we recommend you update it immediatedly", mainCert.NotAfter) - } - } } } @@ -1322,11 +1304,11 @@ func marshalTLS(w http.ResponseWriter, data tlsConfig) { w.Header().Set("Content-Type", "application/json") if data.CertificateChain != "" { encoded := base64.StdEncoding.EncodeToString([]byte(data.CertificateChain)) - data.CertificateChain = string(encoded) + data.CertificateChain = encoded } if data.PrivateKey != "" { encoded := base64.StdEncoding.EncodeToString([]byte(data.PrivateKey)) - data.PrivateKey = string(encoded) + data.PrivateKey = encoded } err := json.NewEncoder(w).Encode(data) if err != nil { diff --git a/helpers.go b/helpers.go index cf2d3598..822c77df 100644 --- a/helpers.go +++ b/helpers.go @@ -6,7 +6,6 @@ import ( "fmt" "io" "io/ioutil" - "math/rand" "net" "net/http" "net/url" @@ -257,34 +256,6 @@ func checkPacketPortAvailable(host string, port int) error { return err } -// ------------------------ -// random string generation -// ------------------------ -const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" -const ( - letterIdxBits = 6 // 6 bits to represent a letter index - letterIdxMask = 1<= 0; { - if remain == 0 { - cache, remain = rand.Int63(), letterIdxMax - } - if idx := int(cache & letterIdxMask); idx < len(letterBytes) { - b[i] = letterBytes[idx] - i-- - } - cache >>= letterIdxBits - remain-- - } - - return string(b) -} - // --------------------- // debug logging helpers // --------------------- diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index c9801b4b..452e5036 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -2,7 +2,7 @@ swagger: '2.0' info: title: 'AdGuard Home' description: 'AdGuard Home REST API. Admin web interface is built on top of this REST API.' - version: 0.92.0 + version: 0.93.0 schemes: - http basePath: /control @@ -12,6 +12,9 @@ tags: - name: global description: 'AdGuard Home server general settings and controls' + - + name: tls + description: 'AdGuard Home HTTPS/DOH/DOT settings' - name: log description: 'AdGuard Home query log' @@ -267,6 +270,70 @@ paths: 200: description: OK + # -------------------------------------------------- + # TLS server methods + # -------------------------------------------------- + + /tls/status: + get: + tags: + - tls + operationId: tlsStatus + summary: "Returns TLS configuration and its status" + responses: + 200: + description: OK + schema: + $ref: "#/definitions/TlsConfig" + + /tls/configure: + post: + tags: + - tls + operationId: tlsConfigure + summary: "Updates current TLS configuration" + consumes: + - application/json + parameters: + - in: "body" + name: "body" + description: "TLS configuration JSON" + required: true + schema: + $ref: "#/definitions/TlsConfig" + responses: + 200: + description: "TLS configuration and its status" + schema: + $ref: "#/definitions/TlsConfig" + 400: + description: "Invalid configuration or unavailable port" + 500: + description: "Error occurred while applying configuration" + + /tls/validate: + post: + tags: + - tls + operationId: tlsValidate + summary: "Checks if the current TLS configuration is valid" + consumes: + - application/json + parameters: + - in: "body" + name: "body" + description: "TLS configuration JSON" + required: true + schema: + $ref: "#/definitions/TlsConfig" + responses: + 200: + description: "TLS configuration and its status" + schema: + $ref: "#/definitions/TlsConfig" + 400: + description: "Invalid configuration or unavailable port" + # -------------------------------------------------- # DHCP server methods # -------------------------------------------------- @@ -1063,4 +1130,81 @@ definitions: type: "array" description: "Query log" items: - $ref: "#/definitions/QueryLogItem" \ No newline at end of file + $ref: "#/definitions/QueryLogItem" + TlsConfig: + type: "object" + description: "TLS configuration settings and status" + properties: + # TLS configuration + enabled: + type: "boolean" + example: "true" + description: "enabled is the encryption (DOT/DOH/HTTPS) status" + server_name: + type: "string" + example: "example.org" + description: "server_name is the hostname of your HTTPS/TLS server" + force_https: + type: "boolean" + example: "true" + description: "if true, forces HTTP->HTTPS redirect" + port_https: + type: "integer" + format: "int32" + example: 443 + description: "HTTPS port. If 0, HTTPS will be disabled." + port_dns_over_tls: + type: "integer" + format: "int32" + example: 853 + description: "DNS-over-TLS port. If 0, DOT will be disabled." + certificate_chain: + type: "string" + description: "Base64 string with PEM-encoded certificates chain" + private_key: + type: "string" + description: "Base64 string with PEM-encoded private key" + # Below goes validation fields + valid_cert: + type: "boolean" + example: "true" + description: "valid_cert is true if the specified certificates chain is a valid chain of X509 certificates" + valid_chain: + type: "boolean" + example: "true" + description: "valid_chain is true if the specified certificates chain is verified and issued by a known CA" + subject: + type: "string" + example: "CN=example.org" + description: "subject is the subject of the first certificate in the chain" + issuer: + type: "string" + example: "CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US" + description: "issuer is the issuer of the first certificate in the chain" + not_before: + type: "string" + example: "2019-01-31T10:47:32Z" + description: "not_before is the NotBefore field of the first certificate in the chain" + not_after: + type: "string" + example: "2019-05-01T10:47:32Z" + description: "not_after is the NotAfter field of the first certificate in the chain" + dns_names: + type: "array" + items: + type: "string" + description: "dns_names is the value of SubjectAltNames field of the first certificate in the chain" + example: + - "*.example.org" + valid_key: + type: "boolean" + example: "true" + description: "valid_key is true if the key is a valid private key" + key_type: + type: "string" + example: "RSA" + description: "key_type is either RSA or ECDSA" + warning_validation: + type: "string" + example: "You have specified an empty certificate" + description: "warning_validation is a validation warning message with the issue description" \ No newline at end of file From 37431735fda57f9042612b8318b9ad096908e929 Mon Sep 17 00:00:00 2001 From: Andrey Meshkov Date: Thu, 21 Feb 2019 17:48:18 +0300 Subject: [PATCH 77/88] Added new config fields to readme --- README.md | 8 ++++++++ dnsforward/dnsforward.go | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 52b678db..0fd70c65 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,14 @@ Settings are stored in [YAML format](https://en.wikipedia.org/wiki/YAML), possib * `range_start` - start IP address of the controlled range. * `range_end` - end IP address of the controlled range. * `lease_duration` - lease duration in seconds. If 0, using default duration (2 hours). + * `tls` - HTTPS/DOH/DOT settings. + * `enabled` - encryption (DOT/DOH/HTTPS) status. + * `server_name` - the hostname of your HTTPS/TLS server. + * `force_https` - if true, forces HTTP->HTTPS redirect. + * `port_https` - HTTPS port. If 0, HTTPS will be disabled. + * `port_dns_over_tls` - DNS-over-TLS port. If 0, DOT will be disabled. + * `certificate_chain` - PEM-encoded certificates chain. + * `private_key` - PEM-encoded private key. * `user_rules` — User-specified filtering rules. * `log_file` — Path to the log file. If empty, writes to stdout, if `syslog` -- system log (or eventlog on Windows). * `verbose` — Enable our disables debug verbose output. diff --git a/dnsforward/dnsforward.go b/dnsforward/dnsforward.go index 54695c6f..785f32b7 100644 --- a/dnsforward/dnsforward.go +++ b/dnsforward/dnsforward.go @@ -72,8 +72,8 @@ type FilteringConfig struct { type TLSConfig struct { TLSListenAddr *net.TCPAddr `yaml:"-" json:"-"` - CertificateChain string `yaml:"certificate_chain" json:"certificate_chain"` - PrivateKey string `yaml:"private_key" json:"private_key"` + CertificateChain string `yaml:"certificate_chain" json:"certificate_chain"` // PEM-encoded certificates chain + PrivateKey string `yaml:"private_key" json:"private_key"` // PEM-encoded private key } // ServerConfig represents server configuration. From 2814c393ad7128f6db90a8d0fdfa63e7a11722a6 Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Thu, 21 Feb 2019 18:28:23 +0300 Subject: [PATCH 78/88] Fixed checkRedirect helper --- .../components/Settings/Encryption/Form.js | 4 +- client/src/helpers/helpers.js | 61 ++++++++++++------- 2 files changed, 40 insertions(+), 25 deletions(-) diff --git a/client/src/components/Settings/Encryption/Form.js b/client/src/components/Settings/Encryption/Form.js index b85bcfeb..f4b01560 100644 --- a/client/src/components/Settings/Encryption/Form.js +++ b/client/src/components/Settings/Encryption/Form.js @@ -27,8 +27,8 @@ const clearFields = (change, setTlsConfig, t) => { const fields = { private_key: '', certificate_chain: '', - port_https: '', - port_dns_over_tls: '', + port_https: 443, + port_dns_over_tls: 853, server_name: '', force_https: false, enabled: false, diff --git a/client/src/helpers/helpers.js b/client/src/helpers/helpers.js index 3c0544e6..2ac0afe9 100644 --- a/client/src/helpers/helpers.js +++ b/client/src/helpers/helpers.js @@ -11,7 +11,6 @@ import { STANDARD_WEB_PORT, STANDARD_HTTPS_PORT, CHECK_TIMEOUT, - STOP_TIMEOUT, } from './constants'; export const formatTime = (time) => { @@ -149,26 +148,42 @@ export const getWebAddress = (ip, port = '') => { return address; }; -export const redirectCheck = (url) => { - const redirectCheck = setInterval(() => { - axios.get(url) - .then((response) => { - if (response) { - clearInterval(redirectCheck); - window.location.replace(url); - } - }) - .catch((error) => { - if (error.response) { - clearInterval(redirectCheck); - window.location.replace(url); - } - }); - }, CHECK_TIMEOUT); - setTimeout(() => { - clearInterval(redirectCheck); - console.error('Redirect check stopped'); - }, STOP_TIMEOUT); +export const checkRedirect = (url, attempts) => { + let count = attempts || 1; + + if (count > 10) { + window.location.replace(url); + return false; + } + + const rmTimeout = t => t && clearTimeout(t); + const setRecursiveTimeout = (time, ...args) => setTimeout( + checkRedirect, + time, + ...args, + ); + + let timeout; + + axios.get(url) + .then((response) => { + rmTimeout(timeout); + if (response) { + window.location.replace(url); + return; + } + timeout = setRecursiveTimeout(CHECK_TIMEOUT, url, count += 1); + }) + .catch((error) => { + rmTimeout(timeout); + if (error.response) { + window.location.replace(url); + return; + } + timeout = setRecursiveTimeout(CHECK_TIMEOUT, url, count += 1); + }); + + return false; }; export const redirectToCurrentProtocol = (values, httpPort = 80) => { @@ -179,9 +194,9 @@ export const redirectToCurrentProtocol = (values, httpPort = 80) => { const httpsPort = port_https !== STANDARD_HTTPS_PORT ? `:${port_https}` : ''; if (protocol !== 'https:' && enabled && port_https) { - redirectCheck(`https://${hostname}${httpsPort}/${hash}`); + checkRedirect(`https://${hostname}${httpsPort}/${hash}`); } else if (protocol === 'https:' && enabled && port_https && port_https !== port) { - redirectCheck(`https://${hostname}${httpsPort}/${hash}`); + checkRedirect(`https://${hostname}${httpsPort}/${hash}`); } else if (protocol === 'https:' && (!enabled || !port_https)) { window.location.replace(`http://${hostname}:${httpPort}/${hash}`); } From 12f85902289f5daf57982efaed5d047dcdacf251 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Thu, 21 Feb 2019 19:01:20 +0300 Subject: [PATCH 79/88] /tls/configure -- don't close https connection mid-request when configuration removes ports and certificates --- control.go | 1 + 1 file changed, 1 insertion(+) diff --git a/control.go b/control.go index a87bd2cd..8c2fa789 100644 --- a/control.go +++ b/control.go @@ -1104,6 +1104,7 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { // until all requests are finished, and _we_ are inside a request right now, so it will block indefinitely if restartHTTPS { go func() { + time.Sleep(time.Second) // TODO: could not find a way to reliably know that data was fully sent to client by https server, so we wait a bit to let response through before closing the server httpsServer.cond.Broadcast() if httpsServer.server != nil { httpsServer.server.Shutdown(context.TODO()) From 8e993cd788d7e5ec4e085e58799b76aa45a393b5 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Thu, 21 Feb 2019 19:07:12 +0300 Subject: [PATCH 80/88] Fix data races found by race detector. --- app.go | 2 ++ control.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/app.go b/app.go index 518bb397..1abad8c5 100644 --- a/app.go +++ b/app.go @@ -183,7 +183,9 @@ func run(args options) { log.Fatal(data.WarningValidation) os.Exit(1) } + config.Lock() config.TLS = data // update warnings + config.Unlock() // prepare certs for HTTPS server // important -- they have to be copies, otherwise changing the contents in config.TLS will break encryption for in-flight requests diff --git a/control.go b/control.go index 8c2fa789..edc2647a 100644 --- a/control.go +++ b/control.go @@ -1105,10 +1105,12 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { if restartHTTPS { go func() { time.Sleep(time.Second) // TODO: could not find a way to reliably know that data was fully sent to client by https server, so we wait a bit to let response through before closing the server + httpsServer.cond.L.Lock() httpsServer.cond.Broadcast() if httpsServer.server != nil { httpsServer.server.Shutdown(context.TODO()) } + httpsServer.cond.L.Unlock() }() } } From 158f2f6100085a1c9001f06383c02d06721a256f Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Thu, 21 Feb 2019 19:16:09 +0300 Subject: [PATCH 81/88] Fixed port validation --- client/src/helpers/form.js | 2 +- client/src/helpers/helpers.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/helpers/form.js b/client/src/helpers/form.js index 58d11ba8..1f0a339a 100644 --- a/client/src/helpers/form.js +++ b/client/src/helpers/form.js @@ -63,7 +63,7 @@ export const isPositive = (value) => { }; export const port = (value) => { - if (value && (value < 80 || value > 65535)) { + if ((value || value === 0) && (value < 80 || value > 65535)) { return form_error_port_range; } return false; diff --git a/client/src/helpers/helpers.js b/client/src/helpers/helpers.js index 2ac0afe9..eb7c7db2 100644 --- a/client/src/helpers/helpers.js +++ b/client/src/helpers/helpers.js @@ -195,7 +195,7 @@ export const redirectToCurrentProtocol = (values, httpPort = 80) => { if (protocol !== 'https:' && enabled && port_https) { checkRedirect(`https://${hostname}${httpsPort}/${hash}`); - } else if (protocol === 'https:' && enabled && port_https && port_https !== port) { + } else if (protocol === 'https:' && enabled && port_https && port_https !== parseInt(port, 10)) { checkRedirect(`https://${hostname}${httpsPort}/${hash}`); } else if (protocol === 'https:' && (!enabled || !port_https)) { window.location.replace(`http://${hostname}:${httpPort}/${hash}`); From 71df659dc9479a5a615274bd42adc1cdb2a7a259 Mon Sep 17 00:00:00 2001 From: Andrey Meshkov Date: Fri, 22 Feb 2019 15:23:39 +0300 Subject: [PATCH 82/88] Added DNS-over-TLS unit-test and a test looking for race-conditions --- dnsforward/dnsforward.go | 1 + dnsforward/dnsforward_test.go | 209 +++++++++++++++++++++++++++++++--- 2 files changed, 197 insertions(+), 13 deletions(-) diff --git a/dnsforward/dnsforward.go b/dnsforward/dnsforward.go index 785f32b7..e035f8ed 100644 --- a/dnsforward/dnsforward.go +++ b/dnsforward/dnsforward.go @@ -70,6 +70,7 @@ type FilteringConfig struct { dnsfilter.Config `yaml:",inline"` } +// TLSConfig is the TLS configuration for HTTPS, DNS-over-HTTPS, and DNS-over-TLS type TLSConfig struct { TLSListenAddr *net.TCPAddr `yaml:"-" json:"-"` CertificateChain string `yaml:"certificate_chain" json:"certificate_chain"` // PEM-encoded certificates chain diff --git a/dnsforward/dnsforward_test.go b/dnsforward/dnsforward_test.go index 9553b9ed..28c115d1 100644 --- a/dnsforward/dnsforward_test.go +++ b/dnsforward/dnsforward_test.go @@ -1,17 +1,34 @@ package dnsforward import ( + "crypto/ecdsa" + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" "net" "os" + "sync" "testing" "time" + "github.com/AdguardTeam/dnsproxy/proxy" + "github.com/stretchr/testify/assert" "github.com/AdguardTeam/AdGuardHome/dnsfilter" "github.com/miekg/dns" ) +const ( + tlsServerName = "testdns.adguard.com" + dataDir = "testData" + testMessagesCount = 10 +) + func TestServer(t *testing.T) { s := createTestServer(t) defer removeDataDir(t) @@ -22,7 +39,7 @@ func TestServer(t *testing.T) { // message over UDP req := createTestMessage() - addr := s.dnsProxy.Addr("udp") + addr := s.dnsProxy.Addr(proxy.ProtoUDP) client := dns.Client{Net: "udp"} reply, _, err := client.Exchange(req, addr.String()) if err != nil { @@ -63,6 +80,69 @@ func TestServer(t *testing.T) { } } +func TestDotServer(t *testing.T) { + // Prepare the proxy server + _, certPem, keyPem := createServerTLSConfig(t) + s := createTestServer(t) + defer removeDataDir(t) + + s.TLSConfig = TLSConfig{ + TLSListenAddr: &net.TCPAddr{Port: 0}, + CertificateChain: string(certPem), + PrivateKey: string(keyPem), + } + + // Starting the server + err := s.Start(nil) + if err != nil { + t.Fatalf("Failed to start server: %s", err) + } + + // Add our self-signed generated config to roots + roots := x509.NewCertPool() + roots.AppendCertsFromPEM(certPem) + tlsConfig := &tls.Config{ServerName: tlsServerName, RootCAs: roots} + + // Create a DNS-over-TLS client connection + addr := s.dnsProxy.Addr(proxy.ProtoTLS) + conn, err := dns.DialWithTLS("tcp-tls", addr.String(), tlsConfig) + if err != nil { + t.Fatalf("cannot connect to the proxy: %s", err) + } + + sendTestMessages(t, conn) + + // Stop the proxy + err = s.Stop() + if err != nil { + t.Fatalf("DNS server failed to stop: %s", err) + } +} + +func TestServerRace(t *testing.T) { + s := createTestServer(t) + defer removeDataDir(t) + err := s.Start(nil) + if err != nil { + t.Fatalf("Failed to start server: %s", err) + } + + // message over UDP + addr := s.dnsProxy.Addr(proxy.ProtoUDP) + conn, err := dns.Dial("udp", addr.String()) + if err != nil { + t.Fatalf("cannot connect to the proxy: %s", err) + } + + sendTestMessagesAsync(t, conn) + + // Stop the proxy + err = s.Stop() + if err != nil { + t.Fatalf("DNS server failed to stop: %s", err) + } +} + func TestInvalidRequest(t *testing.T) { s := createTestServer(t) defer removeDataDir(t) @@ -72,7 +152,7 @@ func TestInvalidRequest(t *testing.T) { } // server is running, send a message - addr := s.dnsProxy.Addr("udp") + addr := s.dnsProxy.Addr(proxy.ProtoUDP) req := dns.Msg{} req.Id = dns.Id() req.RecursionDesired = true @@ -106,7 +186,7 @@ func TestBlockedRequest(t *testing.T) { if err != nil { t.Fatalf("Failed to start server: %s", err) } - addr := s.dnsProxy.Addr("udp") + addr := s.dnsProxy.Addr(proxy.ProtoUDP) // // NXDomain blocking @@ -147,7 +227,7 @@ func TestBlockedByHosts(t *testing.T) { if err != nil { t.Fatalf("Failed to start server: %s", err) } - addr := s.dnsProxy.Addr("udp") + addr := s.dnsProxy.Addr(proxy.ProtoUDP) // // Hosts blocking @@ -195,7 +275,7 @@ func TestBlockedBySafeBrowsing(t *testing.T) { if err != nil { t.Fatalf("Failed to start server: %s", err) } - addr := s.dnsProxy.Addr("udp") + addr := s.dnsProxy.Addr(proxy.ProtoUDP) // // Safebrowsing blocking @@ -251,6 +331,7 @@ func createTestServer(t *testing.T) *Server { s := NewServer(createDataDir(t)) s.UDPListenAddr = &net.UDPAddr{Port: 0} s.TCPListenAddr = &net.TCPAddr{Port: 0} + s.QueryLogEnabled = true s.FilteringConfig.FilteringEnabled = true s.FilteringConfig.ProtectionEnabled = true @@ -266,20 +347,111 @@ func createTestServer(t *testing.T) *Server { return s } -func createDataDir(t *testing.T) string { - dir := "testData" - err := os.MkdirAll(dir, 0755) +func createServerTLSConfig(t *testing.T) (*tls.Config, []byte, []byte) { + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { - t.Fatalf("Cannot create %s: %s", dir, err) + t.Fatalf("cannot generate RSA key: %s", err) } - return dir + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + t.Fatalf("failed to generate serial number: %s", err) + } + + notBefore := time.Now() + notAfter := notBefore.Add(5 * 365 * time.Hour * 24) + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"AdGuard Tests"}, + }, + NotBefore: notBefore, + NotAfter: notAfter, + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + IsCA: true, + } + template.DNSNames = append(template.DNSNames, tlsServerName) + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(privateKey), privateKey) + if err != nil { + t.Fatalf("failed to create certificate: %s", err) + } + + certPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + keyPem := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)}) + + cert, err := tls.X509KeyPair(certPem, keyPem) + if err != nil { + t.Fatalf("failed to create certificate: %s", err) + } + + return &tls.Config{Certificates: []tls.Certificate{cert}, ServerName: tlsServerName}, certPem, keyPem +} + +func createDataDir(t *testing.T) string { + err := os.MkdirAll(dataDir, 0755) + if err != nil { + t.Fatalf("Cannot create %s: %s", dataDir, err) + } + return dataDir } func removeDataDir(t *testing.T) { - dir := "testData" - err := os.RemoveAll(dir) + err := os.RemoveAll(dataDir) if err != nil { - t.Fatalf("Cannot remove %s: %s", dir, err) + t.Fatalf("Cannot remove %s: %s", dataDir, err) + } +} + +func sendTestMessageAsync(t *testing.T, conn *dns.Conn, g *sync.WaitGroup) { + defer func() { + g.Done() + }() + + req := createTestMessage() + err := conn.WriteMsg(req) + if err != nil { + t.Fatalf("cannot write message: %s", err) + } + + res, err := conn.ReadMsg() + if err != nil { + t.Fatalf("cannot read response to message: %s", err) + } + assertResponse(t, res) +} + +// sendTestMessagesAsync sends messages in parallel +// so that we could find race issues +func sendTestMessagesAsync(t *testing.T, conn *dns.Conn) { + g := &sync.WaitGroup{} + g.Add(testMessagesCount) + + for i := 0; i < testMessagesCount; i++ { + go sendTestMessageAsync(t, conn, g) + } + + g.Wait() +} + +func sendTestMessages(t *testing.T, conn *dns.Conn) { + for i := 0; i < 10; i++ { + req := createTestMessage() + err := conn.WriteMsg(req) + if err != nil { + t.Fatalf("cannot write message #%d: %s", i, err) + } + + res, err := conn.ReadMsg() + if err != nil { + t.Fatalf("cannot read response to message #%d: %s", i, err) + } + assertResponse(t, res) } } @@ -305,3 +477,14 @@ func assertResponse(t *testing.T, reply *dns.Msg) { t.Fatalf("DNS server returned wrong answer type instead of A: %v", reply.Answer[0]) } } + +func publicKey(priv interface{}) interface{} { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &k.PublicKey + case *ecdsa.PrivateKey: + return &k.PublicKey + default: + return nil + } +} From e8898811fee5f94a537d64538f65166ffa84143e Mon Sep 17 00:00:00 2001 From: Andrey Meshkov Date: Fri, 22 Feb 2019 15:52:12 +0300 Subject: [PATCH 83/88] Added DOH url --- control.go | 19 +++++++++++++++++++ dnsforward/dnsforward.go | 19 ++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/control.go b/control.go index edc2647a..e6431941 100644 --- a/control.go +++ b/control.go @@ -1320,6 +1320,23 @@ func marshalTLS(w http.ResponseWriter, data tlsConfig) { } } +// -------------- +// DNS-over-HTTPS +// -------------- +func handleDOH(w http.ResponseWriter, r *http.Request) { + if r.TLS == nil { + httpError(w, http.StatusNotFound, "Not Found") + return + } + + if !isRunning() { + httpError(w, http.StatusInternalServerError, "DNS server is not running") + return + } + + dnsServer.ServeHTTP(w, r) +} + // ------------------------ // registration of handlers // ------------------------ @@ -1370,4 +1387,6 @@ func registerControlHandlers() { http.HandleFunc("/control/tls/status", postInstall(optionalAuth(ensureGET(handleTLSStatus)))) http.HandleFunc("/control/tls/configure", postInstall(optionalAuth(ensurePOST(handleTLSConfigure)))) http.HandleFunc("/control/tls/validate", postInstall(optionalAuth(ensurePOST(handleTLSValidate)))) + + http.HandleFunc("/dns-query", postInstall(handleDOH)) } diff --git a/dnsforward/dnsforward.go b/dnsforward/dnsforward.go index e035f8ed..331f0b9c 100644 --- a/dnsforward/dnsforward.go +++ b/dnsforward/dnsforward.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "net" + "net/http" "strings" "sync" "time" @@ -259,24 +260,38 @@ func (s *Server) Reconfigure(config *ServerConfig) error { return nil } +// ServeHTTP is a HTTP handler method we use to provide DNS-over-HTTPS +func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + s.RLock() + s.dnsProxy.ServeHTTP(w, r) + s.RUnlock() +} + // GetQueryLog returns a map with the current query log ready to be converted to a JSON func (s *Server) GetQueryLog() []map[string]interface{} { + s.RLock() + defer s.RUnlock() return s.queryLog.getQueryLog() } // GetStatsTop returns the current stop stats func (s *Server) GetStatsTop() *StatsTop { + s.RLock() + defer s.RUnlock() return s.queryLog.runningTop.getStatsTop() } // PurgeStats purges current server stats func (s *Server) PurgeStats() { - // TODO: Locks? + s.Lock() + defer s.Unlock() s.stats.purgeStats() } // GetAggregatedStats returns aggregated stats data for the 24 hours func (s *Server) GetAggregatedStats() map[string]interface{} { + s.RLock() + defer s.RUnlock() return s.stats.getAggregatedStats() } @@ -286,6 +301,8 @@ func (s *Server) GetAggregatedStats() map[string]interface{} { // end is end of the time range // returns nil if time unit is not supported func (s *Server) GetStatsHistory(timeUnit time.Duration, startTime time.Time, endTime time.Time) (map[string]interface{}, error) { + s.RLock() + defer s.RUnlock() return s.stats.getStatsHistory(timeUnit, startTime, endTime) } From 4e1c1618cb62153510fb0c538438e9e39c979923 Mon Sep 17 00:00:00 2001 From: Andrey Meshkov Date: Fri, 22 Feb 2019 17:59:42 +0300 Subject: [PATCH 84/88] Added install methods to openapi.yaml Print all net interfaces when bind_host is 0.0.0.0 --- app.go | 33 +++++++++++-- config.go | 10 ++-- control.go | 53 ++++----------------- helpers.go | 52 +++++++++++++++++++++ helpers_test.go | 25 ++++++++++ openapi/openapi.yaml | 107 ++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 224 insertions(+), 56 deletions(-) create mode 100644 helpers_test.go diff --git a/app.go b/app.go index 1abad8c5..50d378d1 100644 --- a/app.go +++ b/app.go @@ -208,8 +208,7 @@ func run(args options) { }, } - URL := fmt.Sprintf("https://%s", address) - log.Println("Go to " + URL) + printHTTPAddresses("https") err = httpsServer.server.ListenAndServeTLS("", "") if err != http.ErrServerClosed { log.Fatal(err) @@ -220,10 +219,10 @@ func run(args options) { // this loop is used as an ability to change listening host and/or port for { - address := net.JoinHostPort(config.BindHost, strconv.Itoa(config.BindPort)) - URL := fmt.Sprintf("http://%s", address) - log.Println("Go to " + URL) + printHTTPAddresses("http") + // we need to have new instance, because after Shutdown() the Server is not usable + address := net.JoinHostPort(config.BindHost, strconv.Itoa(config.BindPort)) httpServer = &http.Server{ Addr: address, } @@ -395,3 +394,27 @@ func loadOptions() options { return o } + +// prints IP addresses which user can use to open the admin interface +// proto is either "http" or "https" +func printHTTPAddresses(proto string) { + var address string + if config.BindHost == "0.0.0.0" { + log.Println("AdGuard Home is available on the following addresses:") + ifaces, err := getValidNetInterfacesForWeb() + if err != nil { + // That's weird, but we'll ignore it + address = net.JoinHostPort(config.BindHost, strconv.Itoa(config.BindPort)) + log.Printf("Go to %s://%s", proto, address) + return + } + + for _, iface := range ifaces { + address = net.JoinHostPort(iface.Addresses[0], strconv.Itoa(config.BindPort)) + log.Printf("Go to %s://%s", proto, address) + } + } else { + address = net.JoinHostPort(config.BindHost, strconv.Itoa(config.BindPort)) + log.Printf("Go to %s://%s", proto, address) + } +} diff --git a/config.go b/config.go index 158381c1..87e5c6a8 100644 --- a/config.go +++ b/config.go @@ -32,11 +32,11 @@ type configuration struct { ourWorkingDir string // Location of our directory, used to protect against CWD being somewhere else firstRun bool // if set to true, don't run any services except HTTP web inteface, and serve only first-run html - BindHost string `yaml:"bind_host"` - BindPort int `yaml:"bind_port"` - AuthName string `yaml:"auth_name"` - AuthPass string `yaml:"auth_pass"` - Language string `yaml:"language"` // two-letter ISO 639-1 language code + BindHost string `yaml:"bind_host"` // BindHost is the IP address of the HTTP server to bind to + BindPort int `yaml:"bind_port"` // BindPort is the port the HTTP server + AuthName string `yaml:"auth_name"` // AuthName is the basic auth username + AuthPass string `yaml:"auth_pass"` // AuthPass is the basic auth password + Language string `yaml:"language"` // two-letter ISO 639-1 language code DNS dnsConfig `yaml:"dns"` TLS tlsConfig `yaml:"tls"` Filters []filter `yaml:"filters"` diff --git a/control.go b/control.go index e6431941..596e054d 100644 --- a/control.go +++ b/control.go @@ -909,17 +909,6 @@ type firstRunData struct { func handleInstallGetAddresses(w http.ResponseWriter, r *http.Request) { data := firstRunData{} - ifaces, err := getValidNetInterfaces() - if err != nil { - httpError(w, http.StatusInternalServerError, "Couldn't get interfaces: %s", err) - return - } - if len(ifaces) == 0 { - httpError(w, http.StatusServiceUnavailable, "Couldn't find any legible interface, plase try again later") - return - } - - // fill out the fields // find out if port 80 is available -- if not, fall back to 3000 if checkPortAvailable("", 80) == nil { @@ -934,41 +923,15 @@ func handleInstallGetAddresses(w http.ResponseWriter, r *http.Request) { data.DNS.Warning = "Port 53 is not available for binding -- this will make DNS clients unable to contact AdGuard Home." } + ifaces, err := getValidNetInterfacesForWeb() + if err != nil { + httpError(w, http.StatusInternalServerError, "Couldn't get interfaces: %s", err) + return + } + data.Interfaces = make(map[string]interface{}) for _, iface := range ifaces { - addrs, e := iface.Addrs() - if e != nil { - httpError(w, http.StatusInternalServerError, "Failed to get addresses for interface %s: %s", iface.Name, err) - return - } - - jsonIface := netInterface{ - Name: iface.Name, - MTU: iface.MTU, - HardwareAddr: iface.HardwareAddr.String(), - } - - if iface.Flags != 0 { - jsonIface.Flags = iface.Flags.String() - } - - // we don't want link-local addresses in json, so skip them - for _, addr := range addrs { - ipnet, ok := addr.(*net.IPNet) - if !ok { - // not an IPNet, should not happen - httpError(w, http.StatusInternalServerError, "SHOULD NOT HAPPEN: got iface.Addrs() element %s that is not net.IPNet, it is %T", addr, addr) - return - } - // ignore link-local - if ipnet.IP.IsLinkLocalUnicast() { - continue - } - jsonIface.Addresses = append(jsonIface.Addresses, ipnet.IP.String()) - } - if len(jsonIface.Addresses) != 0 { - data.Interfaces[iface.Name] = jsonIface - } + data.Interfaces[iface.Name] = iface } w.Header().Set("Content-Type", "application/json") @@ -983,7 +946,7 @@ func handleInstallConfigure(w http.ResponseWriter, r *http.Request) { newSettings := firstRunData{} err := json.NewDecoder(r.Body).Decode(&newSettings) if err != nil { - httpError(w, http.StatusBadRequest, "Failed to parse new DHCP config json: %s", err) + httpError(w, http.StatusBadRequest, "Failed to parse new config json: %s", err) return } diff --git a/helpers.go b/helpers.go index 822c77df..8e884d36 100644 --- a/helpers.go +++ b/helpers.go @@ -15,6 +15,8 @@ import ( "runtime" "strconv" "strings" + + "github.com/joomcode/errorx" ) // ---------------------------------- @@ -237,6 +239,56 @@ func getValidNetInterfaces() ([]net.Interface, error) { return netIfaces, nil } +// getValidNetInterfacesMap returns interfaces that are eligible for DNS and WEB only +// we do not return link-local addresses here +func getValidNetInterfacesForWeb() ([]netInterface, error) { + ifaces, err := getValidNetInterfaces() + if err != nil { + return nil, errorx.Decorate(err, "Couldn't get interfaces") + } + if len(ifaces) == 0 { + return nil, errors.New("couldn't find any legible interface") + } + + var netInterfaces []netInterface + + for _, iface := range ifaces { + addrs, e := iface.Addrs() + if e != nil { + return nil, errorx.Decorate(e, "Failed to get addresses for interface %s", iface.Name) + } + + netIface := netInterface{ + Name: iface.Name, + MTU: iface.MTU, + HardwareAddr: iface.HardwareAddr.String(), + } + + if iface.Flags != 0 { + netIface.Flags = iface.Flags.String() + } + + // we don't want link-local addresses in json, so skip them + for _, addr := range addrs { + ipnet, ok := addr.(*net.IPNet) + if !ok { + // not an IPNet, should not happen + return nil, fmt.Errorf("SHOULD NOT HAPPEN: got iface.Addrs() element %s that is not net.IPNet, it is %T", addr, addr) + } + // ignore link-local + if ipnet.IP.IsLinkLocalUnicast() { + continue + } + netIface.Addresses = append(netIface.Addresses, ipnet.IP.String()) + } + if len(netIface.Addresses) != 0 { + netInterfaces = append(netInterfaces, netIface) + } + } + + return netInterfaces, nil +} + // checkPortAvailable is not a cheap test to see if the port is bindable, because it's actually doing the bind momentarily func checkPortAvailable(host string, port int) error { ln, err := net.Listen("tcp", net.JoinHostPort(host, strconv.Itoa(port))) diff --git a/helpers_test.go b/helpers_test.go new file mode 100644 index 00000000..66fc3d27 --- /dev/null +++ b/helpers_test.go @@ -0,0 +1,25 @@ +package main + +import ( + "testing" + + "github.com/hmage/golibs/log" +) + +func TestGetValidNetInterfacesForWeb(t *testing.T) { + ifaces, err := getValidNetInterfacesForWeb() + if err != nil { + t.Fatalf("Cannot get net interfaces: %s", err) + } + if len(ifaces) == 0 { + t.Fatalf("No net interfaces found") + } + + for _, iface := range ifaces { + if len(iface.Addresses) == 0 { + t.Fatalf("No addresses found for %s", iface.Name) + } + + log.Printf("%v", iface) + } +} diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 452e5036..f1e23f86 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -39,6 +39,9 @@ tags: - name: dhcp description: 'Built-in DHCP server controls' + - + name: install + description: 'First-time install configuration handlers' paths: # API TO-DO LIST @@ -713,6 +716,42 @@ paths: text/plain: en + # -------------------------------------------------- + # First-time install configuration methods + # -------------------------------------------------- + + /install/get_addresses: + get: + tags: + - install + operationId: installGetAddresses + summary: "Gets the network interfaces information." + responses: + 200: + description: OK + schema: + $ref: "#/definitions/AddressesInfo" + /install/configure: + post: + tags: + - install + operationId: installConfigure + summary: "Applies the initial configuration." + parameters: + - in: "body" + name: "body" + description: "Initial configuration JSON" + required: true + schema: + $ref: "#/definitions/InitialConfiguration" + responses: + 200: + description: OK + 400: + description: "Failed to parse initial configuration or cannot listen to the specified addresses" + 500: + description: "Cannot start the DNS server" + definitions: ServerStatus: type: "object" @@ -1207,4 +1246,70 @@ definitions: warning_validation: type: "string" example: "You have specified an empty certificate" - description: "warning_validation is a validation warning message with the issue description" \ No newline at end of file + description: "warning_validation is a validation warning message with the issue description" + NetInterface: + type: "object" + description: "Network interface info" + properties: + flags: + type: "string" + example: "up|broadcast|multicast" + hardware_address: + type: "string" + example: "52:54:00:11:09:ba" + mtu: + type: "integer" + format: "int32" + example: 1500 + name: + type: "string" + example: "eth0" + ip_addresses: + type: "array" + items: + type: "string" + example: + - "127.0.0.1" + AddressInfo: + type: "object" + description: "Port information" + properties: + ip: + type: "string" + example: "127.0.01" + port: + type: "integer" + format: "int32" + example: 53 + warning: + type: "string" + example: "Cannot bind to this port" + AddressesInfo: + type: "object" + description: "AdGuard Home addresses configuration" + properties: + dns: + $ref: "#/definitions/AddressInfo" + web: + $ref: "#/definitions/AddressInfo" + interfaces: + type: "object" + description: "Network interfaces dictionary (key is the interface name)" + additionalProperties: + $ref: "#/definitions/NetInterface" + InitialConfiguration: + type: "object" + description: "AdGuard Home initial configuration (for the first-install wizard)" + properties: + dns: + $ref: "#/definitions/AddressInfo" + web: + $ref: "#/definitions/AddressInfo" + username: + type: "string" + description: "Basic auth username" + example: "admin" + password: + type: "string" + description: "Basic auth password" + example: "password" \ No newline at end of file From ad4b58472f538c525b2604e1c83ac873397e8b78 Mon Sep 17 00:00:00 2001 From: Andrey Meshkov Date: Fri, 22 Feb 2019 18:16:47 +0300 Subject: [PATCH 85/88] Update dnsproxy to 0.11.0 --- control.go | 4 ++-- dns.go | 6 +++++- dnsforward/dnsforward.go | 2 +- go.mod | 2 +- go.sum | 8 ++++---- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/control.go b/control.go index 596e054d..fd1759e3 100644 --- a/control.go +++ b/control.go @@ -86,7 +86,7 @@ func httpUpdateConfigReloadDNSReturnOK(w http.ResponseWriter, r *http.Request) { func handleStatus(w http.ResponseWriter, r *http.Request) { data := map[string]interface{}{ - "dns_address": config.BindHost, + "dns_address": config.DNS.BindHost, "http_port": config.BindPort, "dns_port": config.DNS.Port, "protection_enabled": config.DNS.ProtectionEnabled, @@ -409,7 +409,7 @@ func handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) { func checkDNS(input string) error { log.Printf("Checking if DNS %s works...", input) - u, err := upstream.AddressToUpstream(input, "", dnsforward.DefaultTimeout) + u, err := upstream.AddressToUpstream(input, upstream.Options{Timeout: dnsforward.DefaultTimeout}) if err != nil { return fmt.Errorf("failed to choose upstream for %s: %s", input, err) } diff --git a/dns.go b/dns.go index adb0d896..b7f0d130 100644 --- a/dns.go +++ b/dns.go @@ -59,7 +59,11 @@ func generateServerConfig() dnsforward.ServerConfig { } for _, u := range config.DNS.UpstreamDNS { - dnsUpstream, err := upstream.AddressToUpstream(u, config.DNS.BootstrapDNS, dnsforward.DefaultTimeout) + opts := upstream.Options{ + Timeout: dnsforward.DefaultTimeout, + Bootstrap: []string{config.DNS.BootstrapDNS}, + } + dnsUpstream, err := upstream.AddressToUpstream(u, opts) if err != nil { log.Printf("Couldn't get upstream: %s", err) // continue, just ignore the upstream diff --git a/dnsforward/dnsforward.go b/dnsforward/dnsforward.go index 331f0b9c..7585f116 100644 --- a/dnsforward/dnsforward.go +++ b/dnsforward/dnsforward.go @@ -102,7 +102,7 @@ func init() { defaultUpstreams := make([]upstream.Upstream, 0) for _, addr := range defaultDNS { - u, err := upstream.AddressToUpstream(addr, "", DefaultTimeout) + u, err := upstream.AddressToUpstream(addr, upstream.Options{Timeout: DefaultTimeout}) if err == nil { defaultUpstreams = append(defaultUpstreams, u) } diff --git a/go.mod b/go.mod index bef5f146..312acb6c 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/AdguardTeam/AdGuardHome require ( - github.com/AdguardTeam/dnsproxy v0.9.11 + github.com/AdguardTeam/dnsproxy v0.11.0 github.com/StackExchange/wmi v0.0.0-20180725035823-b12b22c5341f // indirect github.com/bluele/gcache v0.0.0-20171010155617-472614239ac7 github.com/go-ole/go-ole v1.2.1 // indirect diff --git a/go.sum b/go.sum index c2a71787..6a3b7c18 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/AdguardTeam/dnsproxy v0.9.11 h1:1rsXJTHUqwxTLaGMUD2NrzHw2j88Nk9EhTX6EgmdVlM= -github.com/AdguardTeam/dnsproxy v0.9.11/go.mod h1:JAmwpTphfUBBw6TIOFGxL49/oSl+J97kaRW8viqXNe8= +github.com/AdguardTeam/dnsproxy v0.11.0 h1:NrP2QZ+Htcp1UKTM8LKmZu7DMkj3CFJYc3pNh3J9218= +github.com/AdguardTeam/dnsproxy v0.11.0/go.mod h1:lEi2srAWwfSQWoy8GeZR6lwS+FSMoiZid8bQPreOLb0= github.com/StackExchange/wmi v0.0.0-20180725035823-b12b22c5341f h1:5ZfJxyXo8KyX8DgGXC5B7ILL8y51fci/qYz2B4j8iLY= github.com/StackExchange/wmi v0.0.0-20180725035823-b12b22c5341f/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= @@ -31,8 +31,6 @@ github.com/hmage/golibs v0.0.0-20190121112702-20153bd03c24/go.mod h1:H6Ev6svFxUV github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jmcvetta/randutil v0.0.0-20150817122601-2bb1b664bcff h1:6NvhExg4omUC9NfA+l4Oq3ibNNeJUdiAF3iBVB0PlDk= -github.com/jmcvetta/randutil v0.0.0-20150817122601-2bb1b664bcff/go.mod h1:ddfPX8Z28YMjiqoaJhNBzWHapTHXejnB5cDCUWDwriw= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/joomcode/errorx v0.1.0 h1:QmJMiI1DE1UFje2aI1ZWO/VMT5a32qBoXUclGOt8vsc= @@ -80,6 +78,8 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FY golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190122071731-054c452bb702 h1:Lk4tbZFnlyPgV+sLgTw5yGfzrlOn9kx4vSombi2FFlY= golang.org/x/sys v0.0.0-20190122071731-054c452bb702/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= gopkg.in/asaskevich/govalidator.v4 v4.0.0-20160518190739-766470278477 h1:5xUJw+lg4zao9W4HIDzlFbMYgSgtvNVHh00MEHvbGpQ= gopkg.in/asaskevich/govalidator.v4 v4.0.0-20160518190739-766470278477/go.mod h1:QDV1vrFSrowdoOba0UM8VJPUZONT7dnfdLsM+GG53Z8= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= From 1da954fa9738600a540a724d24bfc24f2dc38d4e Mon Sep 17 00:00:00 2001 From: Andrey Meshkov Date: Fri, 22 Feb 2019 18:41:59 +0300 Subject: [PATCH 86/88] Fix tests --- dnsforward/dnsforward.go | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/dnsforward/dnsforward.go b/dnsforward/dnsforward.go index 7585f116..99f09e6d 100644 --- a/dnsforward/dnsforward.go +++ b/dnsforward/dnsforward.go @@ -386,9 +386,9 @@ func (s *Server) genDNSFilterMessage(d *proxy.DNSContext, result *dnsfilter.Resu switch result.Reason { case dnsfilter.FilteredSafeBrowsing: - return s.genBlockedHost(m, safeBrowsingBlockHost, d.Upstream) + return s.genBlockedHost(m, safeBrowsingBlockHost, d) case dnsfilter.FilteredParental: - return s.genBlockedHost(m, parentalBlockHost, d.Upstream) + return s.genBlockedHost(m, parentalBlockHost, d) default: if result.IP != nil { return s.genARecord(m, result.IP) @@ -417,22 +417,30 @@ func (s *Server) genARecord(request *dns.Msg, ip net.IP) *dns.Msg { return &resp } -func (s *Server) genBlockedHost(request *dns.Msg, newAddr string, upstream upstream.Upstream) *dns.Msg { +func (s *Server) genBlockedHost(request *dns.Msg, newAddr string, d *proxy.DNSContext) *dns.Msg { // look up the hostname, TODO: cache replReq := dns.Msg{} replReq.SetQuestion(dns.Fqdn(newAddr), request.Question[0].Qtype) replReq.RecursionDesired = true - reply, err := upstream.Exchange(&replReq) + + newContext := &proxy.DNSContext{ + Proto: d.Proto, + Addr: d.Addr, + StartTime: time.Now(), + Req: &replReq, + } + + err := s.dnsProxy.Resolve(newContext) if err != nil { - log.Printf("Couldn't look up replacement host '%s' on upstream %s: %s", newAddr, upstream.Address(), err) + log.Printf("Couldn't look up replacement host '%s': %s", newAddr, err) return s.genServerFailure(request) } resp := dns.Msg{} resp.SetReply(request) resp.Authoritative, resp.RecursionAvailable = true, true - if reply != nil { - for _, answer := range reply.Answer { + if newContext.Res != nil { + for _, answer := range newContext.Res.Answer { answer.Header().Name = request.Question[0].Name resp.Answer = append(resp.Answer, answer) } From a04923a4f39ff2d08124f6326b25f161d8c117ad Mon Sep 17 00:00:00 2001 From: Andrey Meshkov Date: Fri, 22 Feb 2019 18:47:54 +0300 Subject: [PATCH 87/88] Fix printing HTTPS address --- app.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app.go b/app.go index 50d378d1..0b55b6fa 100644 --- a/app.go +++ b/app.go @@ -399,7 +399,14 @@ func loadOptions() options { // proto is either "http" or "https" func printHTTPAddresses(proto string) { var address string - if config.BindHost == "0.0.0.0" { + + if proto == "https" && config.TLS.ServerName != "" { + if config.TLS.PortHTTPS == 443 { + log.Printf("Go to https://%s", config.TLS.ServerName) + } else { + log.Printf("Go to https://%s:%d", config.TLS.ServerName, config.TLS.PortHTTPS) + } + } else if config.BindHost == "0.0.0.0" { log.Println("AdGuard Home is available on the following addresses:") ifaces, err := getValidNetInterfacesForWeb() if err != nil { From 84604e292b7c80980dc51a4692715350e93590c9 Mon Sep 17 00:00:00 2001 From: Andrey Meshkov Date: Fri, 22 Feb 2019 19:45:43 +0300 Subject: [PATCH 88/88] Update dnsproxy to v0.11.1 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 312acb6c..cba63387 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/AdguardTeam/AdGuardHome require ( - github.com/AdguardTeam/dnsproxy v0.11.0 + github.com/AdguardTeam/dnsproxy v0.11.1 github.com/StackExchange/wmi v0.0.0-20180725035823-b12b22c5341f // indirect github.com/bluele/gcache v0.0.0-20171010155617-472614239ac7 github.com/go-ole/go-ole v1.2.1 // indirect diff --git a/go.sum b/go.sum index 6a3b7c18..5ba6426d 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/AdguardTeam/dnsproxy v0.11.0 h1:NrP2QZ+Htcp1UKTM8LKmZu7DMkj3CFJYc3pNh3J9218= -github.com/AdguardTeam/dnsproxy v0.11.0/go.mod h1:lEi2srAWwfSQWoy8GeZR6lwS+FSMoiZid8bQPreOLb0= +github.com/AdguardTeam/dnsproxy v0.11.1 h1:qO5VH0GYF9vdksQRG8frEfJ+CJjsPBwuct8FH6Mij7o= +github.com/AdguardTeam/dnsproxy v0.11.1/go.mod h1:lEi2srAWwfSQWoy8GeZR6lwS+FSMoiZid8bQPreOLb0= github.com/StackExchange/wmi v0.0.0-20180725035823-b12b22c5341f h1:5ZfJxyXo8KyX8DgGXC5B7ILL8y51fci/qYz2B4j8iLY= github.com/StackExchange/wmi v0.0.0-20180725035823-b12b22c5341f/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=