forked from p15670423/monkey
Merge branch 'run_page_ui_improvements' into 519/scoutsuite-integration
This commit is contained in:
commit
a7fc5d1191
|
@ -90,7 +90,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=7
|
||||
- eslint ./src --max-warnings $JS_WARNINGS_AMOUNT_UPPER_LIMIT # Test for max warnings
|
||||
|
||||
# Build documentation
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -43,7 +43,7 @@ export default function commandDisplay(props) {
|
|||
{renderNav()}
|
||||
<Card>
|
||||
<div style={{'overflow': 'auto', 'padding': '0.5em'}}>
|
||||
<CopyToClipboard text={selectedCommand.type} className="pull-right btn-sm">
|
||||
<CopyToClipboard text={selectedCommand.command} className="pull-right btn-sm">
|
||||
<Button style={{margin: '-0.5em'}} title="Copy to Clipboard">
|
||||
<FontAwesomeIcon icon={faClipboard}/>
|
||||
</Button>
|
||||
|
|
|
@ -8,7 +8,7 @@ function InterfaceSelection(props) {
|
|||
|
||||
const getContents = (props) => {
|
||||
const ips = props.ips.map((ip) =>
|
||||
<div>{ip}</div>
|
||||
<div key={ip}>{ip}</div>
|
||||
);
|
||||
return (<div>{ips}</div>);
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ const getContents = (props) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<DropdownSelect defaultKey={'win64'} options={osTypes} onClick={setOsType} variant={'outline-monkey'}/>
|
||||
<DropdownSelect defaultKey={OS_TYPES.WINDOWS_64} options={osTypes} onClick={setOsType} variant={'outline-monkey'}/>
|
||||
<DropdownSelect defaultKey={0} options={props.ips} onClick={setIp} variant={'outline-monkey'}/>
|
||||
<CommandDisplay commands={commands}/>
|
||||
</>
|
||||
|
|
|
@ -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 (<FontAwesomeIcon icon={faCheck}
|
||||
className={`monkey-on-island-run-state-icon text-success`}/>)
|
||||
} else if (this.state.runningOnIslandState === 'installing') {
|
||||
} else if (this.state.runningOnIslandState === MONKEY_STATES.STARTING) {
|
||||
return (<FontAwesomeIcon icon={faSync}
|
||||
className={`monkey-on-island-run-state-icon text-success spinning-icon`}/>)
|
||||
} else if (this.state.runningOnIslandState === MONKEY_STATES.FAILED) {
|
||||
return (<FontAwesomeIcon icon={faTimes}
|
||||
className={`monkey-on-island-run-state-icon text-danger`}/>)
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
@ -93,7 +103,7 @@ class RunOnIslandButton extends AuthComponent {
|
|||
return (
|
||||
<Row>
|
||||
<Col>
|
||||
<MissingBinariesModal
|
||||
<IslandMonkeyRunErrorModal
|
||||
showModal={this.state.showModal}
|
||||
onClose={this.closeModal}
|
||||
errorDetails={this.state.errorDetails}/>
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 (
|
||||
<div>
|
||||
Some Monkey binaries are not found where they should be...<br/>
|
||||
You can download the files from <a href="https://github.com/guardicore/monkey/releases/latest"
|
||||
target="blank">here</a>,
|
||||
at the bottommost section titled "Assets", and place them under the
|
||||
directory <code>monkey/monkey_island/cc/binaries</code>.
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
getMonkeyAlreadyRunningContent() {
|
||||
return (
|
||||
<div>
|
||||
Most likely, monkey is already running on the Island. Wait until it finishes or kill the process to run again.
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
getUndefinedErrorContent() {
|
||||
return (
|
||||
<div>
|
||||
You encountered an undefined error. Please report it to support@infectionmonkey.com or our slack channel.
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
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 (
|
||||
<Modal show={this.state.showModal} onHide={() => this.props.onClose()}>
|
||||
<Modal.Body>
|
||||
<h3>
|
||||
<div className='text-center'>Uh oh...</div>
|
||||
</h3>
|
||||
<div style={{'marginTop': '1em', 'marginBottom': '1em'}}>
|
||||
<p className="alert alert-warning">
|
||||
<FontAwesomeIcon icon={faExclamationTriangle} style={{'marginRight': '5px'}}/>
|
||||
{this.getDisplayContentByError(this.state.errorDetails)}
|
||||
</p>
|
||||
</div>
|
||||
<hr/>
|
||||
<h4>
|
||||
Error Details
|
||||
</h4>
|
||||
<div style={{'marginTop': '1em', 'marginBottom': '1em'}}>
|
||||
<pre>
|
||||
{this.state.errorDetails}
|
||||
</pre>
|
||||
</div>
|
||||
<div className='text-center'>
|
||||
<button type='button' className='btn btn-success btn-lg' style={{margin: '5px'}}
|
||||
onClick={() => this.props.onClose()}>
|
||||
Dismiss
|
||||
</button>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
)
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export default IslandMonkeyRunErrorModal;
|
|
@ -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 (
|
||||
<Modal show={this.state.showModal} onHide={() => this.props.onClose()}>
|
||||
<Modal.Body>
|
||||
<h3>
|
||||
<div className='text-center'>Uh oh...</div>
|
||||
</h3>
|
||||
<div style={{'marginTop': '1em', 'marginBottom': '1em'}}>
|
||||
<p className="alert alert-warning">
|
||||
<i className="glyphicon glyphicon-info-sign" style={{'marginRight': '5px'}}/>
|
||||
Some Monkey binaries are not found where they should be...<br/>
|
||||
You can download the files from <a href="https://github.com/guardicore/monkey/releases/latest" target="blank">here</a>,
|
||||
at the bottommost section titled "Assets", and place them under the directory <code>monkey/monkey_island/cc/binaries</code>.
|
||||
</p>
|
||||
</div>
|
||||
<hr/>
|
||||
<h4>
|
||||
Error Details
|
||||
</h4>
|
||||
<div style={{'marginTop': '1em', 'marginBottom': '1em'}}>
|
||||
<pre>
|
||||
{this.state.errorDetails}
|
||||
</pre>
|
||||
</div>
|
||||
<div className='text-center'>
|
||||
<button type='button' className='btn btn-success btn-lg' style={{margin: '5px'}}
|
||||
onClick={() => this.props.onClose()}>
|
||||
Dismiss
|
||||
</button>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
)
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export default MissingBinariesModal;
|
Loading…
Reference in New Issue