From 6e10dd20d11ff640520b869f03703a3e0dc9f281 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 23 Sep 2020 14:47:05 +0300 Subject: [PATCH 1/3] Run monkey page: fixed a bunch of bugs, CR comments --- .travis.yml | 2 +- .../pages/RunMonkeyPage/CommandDisplay.js | 2 +- .../pages/RunMonkeyPage/InterfaceSelection.js | 2 +- .../RunMonkeyPage/LocalManualRunOptions.js | 2 +- .../pages/RunMonkeyPage/RunOnIslandButton.js | 42 ++++++++++++------- .../ui-components/DropdownSelect.js | 12 +----- 6 files changed, 32 insertions(+), 30 deletions(-) diff --git a/.travis.yml b/.travis.yml index 092090d23..cdf2049b6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -89,7 +89,7 @@ script: - cd monkey_island/cc/ui - npm ci # See https://docs.npmjs.com/cli/ci.html - eslint ./src --quiet # Test for errors -- JS_WARNINGS_AMOUNT_UPPER_LIMIT=8 +- JS_WARNINGS_AMOUNT_UPPER_LIMIT=6 - eslint ./src --max-warnings $JS_WARNINGS_AMOUNT_UPPER_LIMIT # Test for max warnings # Build documentation diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/CommandDisplay.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/CommandDisplay.js index ca312722a..ff2f877dd 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/CommandDisplay.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/CommandDisplay.js @@ -43,7 +43,7 @@ export default function commandDisplay(props) { {renderNav()}
- + diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/InterfaceSelection.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/InterfaceSelection.js index 6e1e9ca02..6e74fb4a0 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/InterfaceSelection.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/InterfaceSelection.js @@ -8,7 +8,7 @@ function InterfaceSelection(props) { const getContents = (props) => { const ips = props.ips.map((ip) => -
{ip}
+
{ip}
); return (
{ips}
); } diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/LocalManualRunOptions.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/LocalManualRunOptions.js index 3bd8accae..b28285a18 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/LocalManualRunOptions.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/LocalManualRunOptions.js @@ -49,7 +49,7 @@ const getContents = (props) => { return ( <> - + diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOnIslandButton.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOnIslandButton.js index 23de0cd21..b7bae48f1 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOnIslandButton.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOnIslandButton.js @@ -6,16 +6,24 @@ import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck'; import {faSync} from '@fortawesome/free-solid-svg-icons/faSync'; import AuthComponent from '../../AuthComponent'; -import MissingBinariesModal from '../../ui-components/MissingBinariesModal'; +import IslandMonkeyRunErrorModal from '../../ui-components/IslandMonkeyRunErrorModal'; import '../../../styles/components/RunOnIslandButton.scss'; +import {faTimes} from '@fortawesome/free-solid-svg-icons'; +const MONKEY_STATES = { + RUNNING: 'running', + NOT_RUNNING: 'not_running', + STARTING: 'starting', + FAILED: 'failed' +} + class RunOnIslandButton extends AuthComponent { constructor(props) { super(props); this.state = { - runningOnIslandState: 'not_running', + runningOnIslandState: MONKEY_STATES.NOT_RUNNING, showModal: false, errorDetails: '' }; @@ -28,19 +36,19 @@ class RunOnIslandButton extends AuthComponent { .then(res => res.json()) .then(res => { if (res['is_running']) { - this.setState({runningOnIslandState: 'running'}); + this.setState({runningOnIslandState: MONKEY_STATES.RUNNING}); } else { - this.setState({runningOnIslandState: 'not_running'}); + this.setState({runningOnIslandState: MONKEY_STATES.NOT_RUNNING}); } }); } runIslandMonkey = () => { - this.setState({runningOnIslandState: 'installing'}, this.sendRunMonkeyRequest) + this.setState({runningOnIslandState: MONKEY_STATES.STARTING}, this.sendRunMonkeyRequest) }; - sendRunMonkeyRequest = () => { + sendRunMonkeyRequest() { this.authFetch('/api/local-monkey', { method: 'POST', @@ -48,23 +56,22 @@ class RunOnIslandButton extends AuthComponent { body: JSON.stringify({action: 'run'}) }) .then(res => res.json()) - .then(res => { + .then(async res => { if (res['is_running']) { + await new Promise(r => setTimeout(r, 1000)); this.setState({ - runningOnIslandState: 'running' + runningOnIslandState: MONKEY_STATES.RUNNING }); } else { /* If Monkey binaries are missing, change the state accordingly */ - if (res['error_text'].startsWith('Copy file failed')) { + if (res['error_text'] !== '') { this.setState({ showModal: true, - errorDetails: res['error_text'] + errorDetails: res['error_text'], + runningOnIslandState: MONKEY_STATES.FAILED } ); } - this.setState({ - runningOnIslandState: 'not_running' - }); } }); } @@ -76,12 +83,15 @@ class RunOnIslandButton extends AuthComponent { }; getMonkeyRunStateIcon = () => { - if (this.state.runningOnIslandState === 'running') { + if (this.state.runningOnIslandState === MONKEY_STATES.RUNNING) { return () - } else if (this.state.runningOnIslandState === 'installing') { + } else if (this.state.runningOnIslandState === MONKEY_STATES.STARTING) { return () + } else if (this.state.runningOnIslandState === MONKEY_STATES.FAILED) { + return () } else { return ''; } @@ -93,7 +103,7 @@ class RunOnIslandButton extends AuthComponent { return ( - diff --git a/monkey/monkey_island/cc/ui/src/components/ui-components/DropdownSelect.js b/monkey/monkey_island/cc/ui/src/components/ui-components/DropdownSelect.js index 5585bbc1b..8628c0b60 100644 --- a/monkey/monkey_island/cc/ui/src/components/ui-components/DropdownSelect.js +++ b/monkey/monkey_island/cc/ui/src/components/ui-components/DropdownSelect.js @@ -16,19 +16,11 @@ export default function DropdownSelect(props) { } function generateDropdownItemsFromArray(data) { - const dropdownItems = []; - for (let i = 0; i < data.length; i++) { - dropdownItems.push(generateDropdownItem(i, data[i])); - } - return dropdownItems; + return data.map((x, i) => generateDropdownItem(i, x)); } function generateDropdownItemsFromObject(data) { - const dropdownItems = []; - for (let [key, value] of Object.entries(data)) { - dropdownItems.push(generateDropdownItem(key, value)); - } - return dropdownItems; + return Object.entries(data).map(([key, value]) => generateDropdownItem(key, value)); } function generateDropdownItem(key, value) { From c5e4493db5092fa29080d73b5d3bc080efe2cc36 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 23 Sep 2020 14:47:34 +0300 Subject: [PATCH 2/3] Improved run monkey error modal to display any kind of error. --- .../monkey_island/cc/resources/local_run.py | 2 +- .../IslandMonkeyRunErrorModal.js | 100 ++++++++++++++++++ .../ui-components/MissingBinariesModal.js | 62 ----------- 3 files changed, 101 insertions(+), 63 deletions(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/ui-components/IslandMonkeyRunErrorModal.js delete mode 100644 monkey/monkey_island/cc/ui/src/components/ui-components/MissingBinariesModal.js diff --git a/monkey/monkey_island/cc/resources/local_run.py b/monkey/monkey_island/cc/resources/local_run.py index d743fc835..045729787 100644 --- a/monkey/monkey_island/cc/resources/local_run.py +++ b/monkey/monkey_island/cc/resources/local_run.py @@ -51,7 +51,7 @@ def run_local_monkey(): logger.error('popen failed', exc_info=True) return False, "popen failed: %s" % exc - return True, "pis: %s" % pid + return True, "" class LocalRun(flask_restful.Resource): diff --git a/monkey/monkey_island/cc/ui/src/components/ui-components/IslandMonkeyRunErrorModal.js b/monkey/monkey_island/cc/ui/src/components/ui-components/IslandMonkeyRunErrorModal.js new file mode 100644 index 000000000..8b2a8857d --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/ui-components/IslandMonkeyRunErrorModal.js @@ -0,0 +1,100 @@ +import {Modal} from 'react-bootstrap'; +import React from 'react'; +import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; +import {faExclamationTriangle} from '@fortawesome/free-solid-svg-icons'; + + +class IslandMonkeyRunErrorModal extends React.PureComponent { + + constructor(props) { + super(props); + + this.state = { + showModal: this.props.showModal, + errorDetails: this.props.errorDetails + }; + } + + componentDidUpdate(prevProps) { + if (this.props !== prevProps) { + this.setState({ + showModal: this.props.showModal, + errorDetails: this.props.errorDetails + }) + } + } + + getMissingBinariesContent() { + return ( +
+ Some Monkey binaries are not found where they should be...
+ You can download the files from here, + at the bottommost section titled "Assets", and place them under the + directory monkey/monkey_island/cc/binaries. +
+ ) + } + + getMonkeyAlreadyRunningContent() { + return ( +
+ Most likely, monkey is already running on the Island. Wait until it finishes or kill the process to run again. +
+ ) + } + + getUndefinedErrorContent() { + return ( +
+ You encountered an undefined error. Please report it to support@infectionmonkey.com or our slack channel. +
+ ) + } + + getDisplayContentByError(errorMsg) { + if (errorMsg.includes('Permission denied:')) { + return this.getMonkeyAlreadyRunningContent() + } else if (errorMsg.startsWith('Copy file failed')) { + return this.getMissingBinariesContent() + } else { + return this.getUndefinedErrorContent() + } + } + + render = () => { + return ( + this.props.onClose()}> + +

+
Uh oh...
+

+
+

+ + {this.getDisplayContentByError(this.state.errorDetails)} +

+
+
+

+ Error Details +

+
+
+              {this.state.errorDetails}
+            
+
+
+ +
+
+
+ ) + }; + +} + +export default IslandMonkeyRunErrorModal; diff --git a/monkey/monkey_island/cc/ui/src/components/ui-components/MissingBinariesModal.js b/monkey/monkey_island/cc/ui/src/components/ui-components/MissingBinariesModal.js deleted file mode 100644 index ae7f6ac4e..000000000 --- a/monkey/monkey_island/cc/ui/src/components/ui-components/MissingBinariesModal.js +++ /dev/null @@ -1,62 +0,0 @@ -import {Modal} from 'react-bootstrap'; -import React from 'react'; - - -class MissingBinariesModal extends React.PureComponent { - - constructor(props) { - super(props); - - this.state = { - showModal: this.props.showModal, - errorDetails: this.props.errorDetails - }; - } - - componentDidUpdate(prevProps) { - if (this.props !== prevProps) { - this.setState({ - showModal: this.props.showModal, - errorDetails: this.props.errorDetails - }) - } - } - - render = () => { - return ( - this.props.onClose()}> - -

-
Uh oh...
-

-
-

- - Some Monkey binaries are not found where they should be...
- You can download the files from here, - at the bottommost section titled "Assets", and place them under the directory monkey/monkey_island/cc/binaries. -

-
-
-

- Error Details -

-
-
-              {this.state.errorDetails}
-            
-
-
- -
-
-
- ) - }; - -} - -export default MissingBinariesModal; From 12e7c40db919c8736ecf39d6df3b9d1ba57472ae Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 23 Sep 2020 14:54:30 +0300 Subject: [PATCH 3/3] Increased js warning limit to 7, because I used ` for classnames. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index cdf2049b6..887b7cc67 100644 --- a/.travis.yml +++ b/.travis.yml @@ -89,7 +89,7 @@ script: - cd monkey_island/cc/ui - npm ci # See https://docs.npmjs.com/cli/ci.html - eslint ./src --quiet # Test for errors -- JS_WARNINGS_AMOUNT_UPPER_LIMIT=6 +- JS_WARNINGS_AMOUNT_UPPER_LIMIT=7 - eslint ./src --max-warnings $JS_WARNINGS_AMOUNT_UPPER_LIMIT # Test for max warnings # Build documentation