diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 02929a8e..461e0a19 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -140,7 +140,7 @@ "add_allowlist": "Add allowlist", "cancel_btn": "Cancel", "enter_name_hint": "Enter name", - "enter_url_hint": "Enter URL", + "enter_url_or_path_hint": "Enter URL or absolute path of the list", "check_updates_btn": "Check for updates", "new_blocklist": "New blocklist", "new_allowlist": "New allowlist", @@ -149,6 +149,7 @@ "enter_valid_blocklist": "Enter a valid URL to the blocklist.", "enter_valid_allowlist": "Enter a valid URL to the allowlist.", "form_error_url_format": "Invalid url format", + "form_error_url_or_path_format": "Invalid url or absolute path of the list", "custom_filter_rules": "Custom filtering rules", "custom_filter_rules_hint": "Enter one rule on a line. You can use either adblock rules or hosts files syntax.", "examples_title": "Examples", diff --git a/client/src/components/Filters/Form.js b/client/src/components/Filters/Form.js index 22993a1f..4056802a 100644 --- a/client/src/components/Filters/Form.js +++ b/client/src/components/Filters/Form.js @@ -4,7 +4,7 @@ import { Field, reduxForm } from 'redux-form'; import { Trans, withNamespaces } from 'react-i18next'; import flow from 'lodash/flow'; -import { renderInputField, required, isValidUrl } from '../../helpers/form'; +import { renderInputField, required, isValidPath } from '../../helpers/form'; const Form = (props) => { const { @@ -37,8 +37,8 @@ const Form = (props) => { type="text" component={renderInputField} className="form-control" - placeholder={t('enter_url_hint')} - validate={[required, isValidUrl]} + placeholder={t('enter_url_or_path_hint')} + validate={[required, isValidPath]} />
diff --git a/client/src/components/Filters/Table.js b/client/src/components/Filters/Table.js index 4df02e92..5d866150 100644 --- a/client/src/components/Filters/Table.js +++ b/client/src/components/Filters/Table.js @@ -2,11 +2,10 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import ReactTable from 'react-table'; import { withNamespaces, Trans } from 'react-i18next'; - import CellWrap from '../ui/CellWrap'; - import { MODAL_TYPE } from '../../helpers/constants'; import { formatDetailedDateTime } from '../../helpers/helpers'; +import { isValidAbsolutePath } from '../../helpers/form'; class Table extends Component { getDateCell = row => CellWrap(row, formatDetailedDateTime); @@ -50,14 +49,15 @@ class Table extends Component { minWidth: 200, Cell: ({ value }) => (
- - {value} - + {isValidAbsolutePath(value) ? value : + + {value} + }
), }, diff --git a/client/src/helpers/constants.js b/client/src/helpers/constants.js index ae214526..69faa01b 100644 --- a/client/src/helpers/constants.js +++ b/client/src/helpers/constants.js @@ -6,6 +6,8 @@ export const R_CIDR = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}( export const R_MAC = /^((([a-fA-F0-9][a-fA-F0-9]+[-]){5}|([a-fA-F0-9][a-fA-F0-9]+[:]){5})([a-fA-F0-9][a-fA-F0-9])$)|(^([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]+[.]){2}([a-fA-F0-9][a-fA-F0-9][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_PATH_LAST_PART = /\/[^/]*$/; +export const R_UNIX_ABSOLUTE_PATH = /^\/([A-z0-9-_+]+\/)*([A-z0-9]+\.(txt))$/; +export const R_WIN_ABSOLUTE_PATH = /^[a-zA-Z]:\\[\\\S|*\S]?.*\.(txt)$/; export const STATS_NAMES = { avg_processing_time: 'average_processing_time', diff --git a/client/src/helpers/form.js b/client/src/helpers/form.js index 840b3f83..d075feee 100644 --- a/client/src/helpers/form.js +++ b/client/src/helpers/form.js @@ -1,7 +1,10 @@ import React, { Fragment } from 'react'; import { Trans } from 'react-i18next'; import PropTypes from 'prop-types'; -import { R_IPV4, R_MAC, R_HOST, R_IPV6, R_CIDR, R_CIDR_IPV6, UNSAFE_PORTS, R_URL_REQUIRES_PROTOCOL } from '../helpers/constants'; +import { + R_IPV4, R_MAC, R_HOST, R_IPV6, R_CIDR, R_CIDR_IPV6, + UNSAFE_PORTS, R_URL_REQUIRES_PROTOCOL, R_WIN_ABSOLUTE_PATH, R_UNIX_ABSOLUTE_PATH, +} from '../helpers/constants'; import { createOnBlurHandler } from './helpers'; export const renderField = (props, elementType) => { @@ -289,4 +292,14 @@ export const isValidUrl = (value) => { return undefined; }; +export const isValidAbsolutePath = value => R_WIN_ABSOLUTE_PATH.test(value) + || R_UNIX_ABSOLUTE_PATH.test(value); + +export const isValidPath = (value) => { + if (value && !isValidAbsolutePath(value) && !R_URL_REQUIRES_PROTOCOL.test(value)) { + return form_error_url_or_path_format; + } + return undefined; +}; + export const toNumber = value => value && parseInt(value, 10);