diff --git a/client/package-lock.json b/client/package-lock.json index f1172e00..56bf51c4 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -3066,12 +3066,6 @@ "pkg-up": "^2.0.0" } }, - "caniuse-lite": { - "version": "1.0.30001062", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001062.tgz", - "integrity": "sha512-ei9ZqeOnN7edDrb24QfJ0OZicpEbsWxv7WusOiQGz/f2SfvBgHHbOEwBJ8HKGVSyx8Z6ndPjxzR6m0NQq+0bfw==", - "dev": true - }, "postcss": { "version": "7.0.30", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.30.tgz", @@ -3928,9 +3922,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001059", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001059.tgz", - "integrity": "sha512-oOrc+jPJWooKIA0IrNZ5sYlsXc7NP7KLhNWrSGEJhnfSzDvDJ0zd3i6HXsslExY9bbu+x0FQ5C61LcqmPt7bOQ==", + "version": "1.0.30001165", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001165.tgz", + "integrity": "sha512-8cEsSMwXfx7lWSUMA2s08z9dIgsnR5NAqjXP23stdsU3AUWkCr/rr4s4OFtHXn5XXr6+7kam3QFVoYyXNPdJPA==", "dev": true }, "capture-exit": { diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 388691b0..ad70bbe4 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -32,6 +32,7 @@ "form_error_ip_format": "Invalid IP format", "form_error_mac_format": "Invalid MAC format", "form_error_client_id_format": "Invalid client ID format", + "form_error_server_name": "Invalid server name or wildcard certificate", "form_error_positive": "Must be greater than 0", "form_error_negative": "Must be equal to 0 or greater", "range_end_error": "Must be greater than range start", @@ -331,7 +332,7 @@ "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 to enter the server name that matches your SSL certificate.", + "encryption_server_desc": "In order to use HTTPS, you need to enter the server name that matches your SSL certificate or wildcard certificate. If the field is not set, it will accept TLS connections for any domain.", "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", @@ -387,7 +388,7 @@ "client_edit": "Edit Client", "client_identifier": "Identifier", "ip_address": "IP address", - "client_identifier_desc": "Clients can be identified by the IP address, CIDR, MAC address. Please note that using MAC as identifier is possible only if AdGuard Home is also a <0>DHCP server", + "client_identifier_desc": "Clients can be identified by the IP address, CIDR, MAC address or domain. Please note that using MAC as identifier is possible only if AdGuard Home is also a <0>DHCP server", "form_enter_ip": "Enter IP", "form_enter_mac": "Enter MAC", "form_enter_id": "Enter identifier", diff --git a/client/src/components/Settings/Encryption/CertificateStatus.js b/client/src/components/Settings/Encryption/CertificateStatus.js index a93389f5..911d1d36 100644 --- a/client/src/components/Settings/Encryption/CertificateStatus.js +++ b/client/src/components/Settings/Encryption/CertificateStatus.js @@ -50,7 +50,7 @@ const CertificateStatus = ({ {dnsNames && (
  • encryption_hostnames:  - {dnsNames} + {dnsNames.join(', ')}
  • )} @@ -65,7 +65,7 @@ CertificateStatus.propTypes = { subject: PropTypes.string, issuer: PropTypes.string, notAfter: PropTypes.string, - dnsNames: PropTypes.string, + dnsNames: PropTypes.arrayOf(PropTypes.string), }; export default withTranslation()(CertificateStatus); diff --git a/client/src/components/Settings/Encryption/Form.js b/client/src/components/Settings/Encryption/Form.js index 1619ea3e..58cd181e 100644 --- a/client/src/components/Settings/Encryption/Form.js +++ b/client/src/components/Settings/Encryption/Form.js @@ -12,7 +12,7 @@ import { toNumber, } from '../../../helpers/form'; import { - validateIsSafePort, validatePort, validatePortQuic, validatePortTLS, + validateServerName, validateIsSafePort, validatePort, validatePortQuic, validatePortTLS, } from '../../../helpers/validators'; import i18n from '../../../i18n'; import KeyStatus from './KeyStatus'; @@ -127,6 +127,7 @@ let Form = (props) => { placeholder={t('encryption_server_enter')} onChange={handleChange} disabled={!isEnabled} + validate={validateServerName} />
    encryption_server_desc @@ -413,7 +414,7 @@ Form.propTypes = { valid_key: PropTypes.bool, valid_cert: PropTypes.bool, valid_pair: PropTypes.bool, - dns_names: PropTypes.string, + dns_names: PropTypes.arrayOf(PropTypes.string), key_type: PropTypes.string, issuer: PropTypes.string, subject: PropTypes.string, diff --git a/client/src/components/ui/Guide.js b/client/src/components/ui/Guide.js index a06af83b..1ae03b43 100644 --- a/client/src/components/ui/Guide.js +++ b/client/src/components/ui/Guide.js @@ -11,15 +11,17 @@ const MOBILE_CONFIG_LINKS = { DOT: '/apple/dot.mobileconfig', DOH: '/apple/doh.mobileconfig', }; + +/* FIXME: find out `client_id` */ const renderMobileconfigInfo = ({ label, components, server_name }) =>
  • {label} diff --git a/client/src/helpers/constants.js b/client/src/helpers/constants.js index af10524f..1564a5da 100644 --- a/client/src/helpers/constants.js +++ b/client/src/helpers/constants.js @@ -13,6 +13,10 @@ export const R_MAC = /^((([a-fA-F0-9][a-fA-F0-9]+[-]){5}|([a-fA-F0-9][a-fA-F0-9] export const R_CIDR_IPV6 = /^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/(12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))$/; +export const R_DOMAIN = /^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}$/; + +export const R_SERVER_NAME = /^(\*\.)?[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}$/; + export const R_PATH_LAST_PART = /\/[^/]*$/; // eslint-disable-next-line no-control-regex diff --git a/client/src/helpers/validators.js b/client/src/helpers/validators.js index 9b853120..e85ebd4c 100644 --- a/client/src/helpers/validators.js +++ b/client/src/helpers/validators.js @@ -9,6 +9,8 @@ import { R_URL_REQUIRES_PROTOCOL, STANDARD_WEB_PORT, UNSAFE_PORTS, + R_DOMAIN, + R_SERVER_NAME, } from './constants'; import { getLastIpv4Octet, isValidAbsolutePath } from './form'; @@ -71,12 +73,28 @@ export const validateClientId = (value) => { || R_MAC.test(formattedValue) || R_CIDR.test(formattedValue) || R_CIDR_IPV6.test(formattedValue) + || R_DOMAIN.test(formattedValue) )) { return 'form_error_client_id_format'; } return undefined; }; +/** + * @param value {string} + * @returns {undefined|string} + */ +export const validateServerName = (value) => { + if (!value) { + return undefined; + } + const formattedValue = value ? value.trim() : value; + if (formattedValue && !R_SERVER_NAME.test(formattedValue)) { + return 'form_error_server_name'; + } + return undefined; +}; + /** * @param value {string} * @returns {undefined|string}