diff --git a/monkey/common/data/validation_formats.py b/monkey/common/data/validation_formats.py new file mode 100644 index 000000000..2f04dbe21 --- /dev/null +++ b/monkey/common/data/validation_formats.py @@ -0,0 +1,3 @@ +# Defined in UI on ValidationFormats.js +IP_RANGE = "ip-range" +IP = "ip" diff --git a/monkey/monkey_island/cc/services/config_schema/basic_network.py b/monkey/monkey_island/cc/services/config_schema/basic_network.py index 3674c0f71..60b510e6c 100644 --- a/monkey/monkey_island/cc/services/config_schema/basic_network.py +++ b/monkey/monkey_island/cc/services/config_schema/basic_network.py @@ -1,3 +1,4 @@ +from common.data.validation_formats import IP, IP_RANGE from monkey_island.cc.services.utils.typographic_symbols import WARNING_SIGN BASIC_NETWORK = { @@ -13,7 +14,8 @@ BASIC_NETWORK = { "type": "array", "uniqueItems": True, "items": { - "type": "string" + "type": "string", + "format": IP, }, "default": [ ], @@ -28,6 +30,7 @@ BASIC_NETWORK = { "depth": { "title": "Distance from island", "type": "integer", + "minimum": 1, "default": 2, "description": "Amount of hops allowed for the monkey to spread from the island. " @@ -39,7 +42,8 @@ BASIC_NETWORK = { "type": "array", "uniqueItems": True, "items": { - "type": "string" + "type": "string", + "format": IP_RANGE }, "default": [ ], @@ -59,7 +63,8 @@ BASIC_NETWORK = { "type": "array", "uniqueItems": True, "items": { - "type": "string" + "type": "string", + "format": IP_RANGE }, "default": [ ], diff --git a/monkey/monkey_island/cc/services/config_schema/monkey.py b/monkey/monkey_island/cc/services/config_schema/monkey.py index 0f5db452f..8e0ae9439 100644 --- a/monkey/monkey_island/cc/services/config_schema/monkey.py +++ b/monkey/monkey_island/cc/services/config_schema/monkey.py @@ -1,4 +1,3 @@ -from monkey_island.cc.services.utils.typographic_symbols import WARNING_SIGN from common.data.system_info_collectors_names import (AWS_COLLECTOR, ENVIRONMENT_COLLECTOR, HOSTNAME_COLLECTOR, @@ -101,6 +100,7 @@ MONKEY = { "title": "Max iterations", "type": "integer", "default": 1, + "minimum": 1, "description": "Determines how many iterations of the monkey's full lifecycle should occur " "(how many times to do the scan)" }, @@ -108,6 +108,7 @@ MONKEY = { "title": "Wait time between iterations", "type": "integer", "default": 100, + "minimum": 0, "description": "Determines for how long (in seconds) should the monkey wait before starting another scan" }, diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js b/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js index 745b6c49e..c11b5d4da 100644 --- a/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js @@ -13,7 +13,14 @@ export default function UiSchema(props) { } } }, - basic_network: {}, + basic_network: { + 'ui:order': ['scope', 'network_analysis'], + scope: { + subnet_scan_list:{ + format: 'ip-list', + } + } + }, monkey: { post_breach: { post_breach_actions: { diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/ValidationFormats.js b/monkey/monkey_island/cc/ui/src/components/configuration-components/ValidationFormats.js new file mode 100644 index 000000000..ce605be39 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/ValidationFormats.js @@ -0,0 +1,19 @@ +const ipRegex = '((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' +const cidrNotationRegex = '([0-9]|1[0-9]|2[0-9]|3[0-2])' + +export const formValidationFormats = { + 'ip-range': buildIpRangeRegex(), + 'ip': buildIpRegex(), +}; + +function buildIpRangeRegex(){ + return new RegExp([ + '^'+ipRegex+'$|', // Single IP + '^'+ipRegex+'-'+ipRegex+'$|', // IP range IP-IP + '^'+ipRegex+'/'+cidrNotationRegex+'$' // IP range with cidr notation: IP/cidr + ].join('')) +} + +function buildIpRegex(){ + return new RegExp('^'+ipRegex+'$') +} diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js b/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js index 3db5826e1..7bc2a919e 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js @@ -9,6 +9,7 @@ import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle'; import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck'; import {faExclamationCircle} from '@fortawesome/free-solid-svg-icons/faExclamationCircle'; +import {formValidationFormats} from "../configuration-components/ValidationFormats"; const ATTACK_URL = '/api/attack'; const CONFIG_URL = '/api/configuration/island'; @@ -342,8 +343,9 @@ class ConfigurePageComponent extends AuthComponent { })} formData={this.state.configuration[this.state.selectedSection]} onChange={this.onChange} - noValidate={true} - className={'config-form'}> + customFormats={formValidationFormats} + className={'config-form'} + liveValidate> ) diff --git a/monkey/monkey_island/cc/ui/src/styles/pages/ConfigurationPage.scss b/monkey/monkey_island/cc/ui/src/styles/pages/ConfigurationPage.scss index 48b168476..0947a1540 100644 --- a/monkey/monkey_island/cc/ui/src/styles/pages/ConfigurationPage.scss +++ b/monkey/monkey_island/cc/ui/src/styles/pages/ConfigurationPage.scss @@ -31,6 +31,10 @@ margin-left: 2em; } +.config-form div.card.errors { + display: none; +} + .config-template-no-header > p { display: none; }