@@ -459,10 +351,6 @@ class RunMonkeyPageComponent extends AuthComponent {
}
-
-
- Go ahead and monitor the ongoing infection in the Infection Map view.
-
);
}
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunMonkeyPage2.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunMonkeyPage2.js
new file mode 100644
index 00000000000..8edd0e44e80
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunMonkeyPage2.js
@@ -0,0 +1,472 @@
+import React from 'react';
+import {css} from '@emotion/core';
+import {Button, Col, Card, Nav, Collapse, Row} from 'react-bootstrap';
+import CopyToClipboard from 'react-copy-to-clipboard';
+import GridLoader from 'react-spinners/GridLoader';
+
+import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
+import {faClipboard} from '@fortawesome/free-solid-svg-icons/faClipboard';
+import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck';
+import {faSync} from '@fortawesome/free-solid-svg-icons/faSync';
+import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle';
+import {faExclamationTriangle} from '@fortawesome/free-solid-svg-icons/faExclamationTriangle';
+
+import {Link} from 'react-router-dom';
+import AuthComponent from '../../AuthComponent';
+import AwsRunTable from '../../run-monkey/AwsRunTable';
+
+import MissingBinariesModal from '../../ui-components/MissingBinariesModal';
+import ManualRunOptions from './ManualRunOptions';
+import Emoji from '../../ui-components/Emoji';
+
+const loading_css_override = css`
+ display: block;
+ margin-right: auto;
+ margin-left: auto;
+`;
+
+class RunMonkeyPageComponent2 extends AuthComponent {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ ips: [],
+ runningOnIslandState: 'not_running',
+ runningOnClientState: 'not_running',
+ awsClicked: false,
+ selectedIp: '0.0.0.0',
+ selectedOs: 'windows-32',
+ showManual: false,
+ showAws: false,
+ isOnAws: false,
+ awsUpdateClicked: false,
+ awsUpdateFailed: false,
+ awsMachines: [],
+ isLoadingAws: true,
+ isErrorWhileCollectingAwsMachines: false,
+ awsMachineCollectionErrorMsg: '',
+ showModal: false,
+ errorDetails: ''
+ };
+
+ this.closeModal = this.closeModal.bind(this);
+ }
+
+ componentDidMount() {
+ this.authFetch('/api')
+ .then(res => res.json())
+ .then(res => this.setState({
+ ips: res['ip_addresses'],
+ selectedIp: res['ip_addresses'][0]
+ }));
+
+ this.authFetch('/api/local-monkey')
+ .then(res => res.json())
+ .then(res => {
+ if (res['is_running']) {
+ this.setState({runningOnIslandState: 'running'});
+ } else {
+ this.setState({runningOnIslandState: 'not_running'});
+ }
+ });
+
+ this.fetchAwsInfo();
+ this.fetchConfig();
+
+ this.authFetch('/api/client-monkey')
+ .then(res => res.json())
+ .then(res => {
+ if (res['is_running']) {
+ this.setState({runningOnClientState: 'running'});
+ } else {
+ this.setState({runningOnClientState: 'not_running'});
+ }
+ });
+
+ this.props.onStatusChange();
+ }
+
+ fetchAwsInfo() {
+ return this.authFetch('/api/remote-monkey?action=list_aws')
+ .then(res => res.json())
+ .then(res => {
+ let is_aws = res['is_aws'];
+ if (is_aws) {
+ // On AWS!
+ // Checks if there was an error while collecting the aws machines.
+ let is_error_while_collecting_aws_machines = (res['error'] != null);
+ if (is_error_while_collecting_aws_machines) {
+ // There was an error. Finish loading, and display error message.
+ this.setState({
+ isOnAws: true,
+ isErrorWhileCollectingAwsMachines: true,
+ awsMachineCollectionErrorMsg: res['error'],
+ isLoadingAws: false
+ });
+ } else {
+ // No error! Finish loading and display machines for user
+ this.setState({isOnAws: true, awsMachines: res['instances'], isLoadingAws: false});
+ }
+ } else {
+ // Not on AWS. Finish loading and don't display the AWS div.
+ this.setState({isOnAws: false, isLoadingAws: false});
+ }
+ });
+ }
+
+ static generateLinuxCmd(ip, is32Bit) {
+ let bitText = is32Bit ? '32' : '64';
+ return `wget --no-check-certificate https://${ip}:5000/api/monkey/download/monkey-linux-${bitText}; chmod +x monkey-linux-${bitText}; ./monkey-linux-${bitText} m0nk3y -s ${ip}:5000`
+ }
+
+ static generateWindowsCmd(ip, is32Bit) {
+ let bitText = is32Bit ? '32' : '64';
+ return `powershell [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}; (New-Object System.Net.WebClient).DownloadFile('https://${ip}:5000/api/monkey/download/monkey-windows-${bitText}.exe','.\\monkey.exe'); ;Start-Process -FilePath '.\\monkey.exe' -ArgumentList 'm0nk3y -s ${ip}:5000';`;
+ }
+
+ runLocalMonkey = () => {
+ this.authFetch('/api/local-monkey',
+ {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({action: 'run'})
+ })
+ .then(res => res.json())
+ .then(res => {
+ if (res['is_running']) {
+ this.setState({
+ runningOnIslandState: 'installing'
+ });
+ } else {
+ /* If Monkey binaries are missing, change the state accordingly */
+ if (res['error_text'].startsWith('Copy file failed')) {
+ this.setState({
+ showModal: true,
+ errorDetails: res['error_text']
+ }
+ );
+ }
+ this.setState({
+ runningOnIslandState: 'not_running'
+ });
+ }
+
+ this.props.onStatusChange();
+ });
+ };
+
+ generateCmdDiv() {
+ let isLinux = (this.state.selectedOs.split('-')[0] === 'linux');
+ let is32Bit = (this.state.selectedOs.split('-')[1] === '32');
+ let cmdText = '';
+ if (isLinux) {
+ cmdText = RunMonkeyPageComponent2.generateLinuxCmd(this.state.selectedIp, is32Bit);
+ } else {
+ cmdText = RunMonkeyPageComponent2.generateWindowsCmd(this.state.selectedIp, is32Bit);
+ }
+ return (
+
+
+
+
+
+ {cmdText}
+
+
+ )
+ }
+
+ setSelectedOs = (key) => {
+ this.setState({
+ selectedOs: key
+ });
+ };
+
+ setSelectedIp = (key) => {
+ this.setState({
+ selectedIp: key
+ });
+ };
+
+ static renderIconByState(state) {
+ if (state === 'running') {
+ return (
)
+ } else if (state === 'installing') {
+ return (
)
+ } else {
+ return '';
+ }
+ }
+
+ toggleManual = () => {
+ this.setState({
+ showManual: !this.state.showManual
+ });
+ };
+
+ toggleAws = () => {
+ this.setState({
+ showAws: !this.state.showAws
+ });
+ };
+
+ runOnAws = () => {
+ this.setState({
+ awsClicked: true
+ });
+
+ let instances = this.awsTable.state.selection.map(x => this.instanceIdToInstance(x));
+
+ this.authFetch('/api/remote-monkey',
+ {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({type: 'aws', instances: instances, island_ip: this.state.selectedIp})
+ }).then(res => res.json())
+ .then(res => {
+ let result = res['result'];
+
+ // update existing state, not run-over
+ let prevRes = this.awsTable.state.result;
+ for (let key in result) {
+ if (result.hasOwnProperty(key)) {
+ prevRes[key] = result[key];
+ }
+ }
+ this.awsTable.setState({
+ result: prevRes,
+ selection: [],
+ selectAll: false
+ });
+
+ this.setState({
+ awsClicked: false
+ });
+ });
+ };
+
+ fetchConfig() {
+ return this.authFetch('/api/configuration/island')
+ .then(res => res.json())
+ .then(res => {
+ return res.configuration;
+ })
+ }
+
+ instanceIdToInstance = (instance_id) => {
+ let instance = this.state.awsMachines.find(
+ function (inst) {
+ return inst['instance_id'] === instance_id;
+ });
+ return {'instance_id': instance_id, 'os': instance['os']}
+
+ };
+
+ renderAwsMachinesDiv() {
+ return (
+
+
+ {
+ this.state.ips.length > 1 ?
+
+ :
+ }
+
+
(this.awsTable = r)}
+ />
+
+
+
+
+ )
+ }
+
+ closeModal = () => {
+ this.setState({
+ showModal: false
+ })
+ };
+
+ render() {
+ return (
+
+
1. Run Monkey
+
+ Go ahead and run the monkey!
+ (Or configure the monkey to fine tune its behavior)
+
+
+
+
+
+
+ OR
+
+
+
+
+
+
+
+
+ Choose the operating system where you want to run the monkey:
+
+
+
+
+
+
+
+ {this.state.ips.length > 1 ?
+
+
+
+
+ Choose the interface to communicate with:
+
+
+
+
+
+
+
+
+
+ :
+ }
+
+ Copy the following command to your machine and run it with Administrator or root privileges.
+
+ {this.generateCmdDiv()}
+
+
+ {
+ this.state.isLoadingAws ?
+
+ : null
+ }
+ {
+ this.state.isOnAws ?
+
+ OR
+
+ :
+ null
+ }
+ {
+ this.state.isOnAws ?
+
+
+
+ :
+ null
+ }
+
+ {
+ this.state.isErrorWhileCollectingAwsMachines ?
+
+
+
+ Error while collecting AWS machine data. Error
+ message: {this.state.awsMachineCollectionErrorMsg}
+ Are you sure you've set the correct role on your Island AWS machine?
+ Not sure what this is? Read
+ the documentation!
+
+
+ :
+ this.renderAwsMachinesDiv()
+ }
+
+
+
+
+ Go ahead and monitor the ongoing infection in the Infection Map view.
+
+
+ );
+ }
+}
+
+export default RunMonkeyPageComponent2;
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/CommandTypes.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOptions.js
similarity index 100%
rename from monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/CommandTypes.js
rename to monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOptions.js
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/commands/local_linux_curl.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/commands/local_linux_curl.js
new file mode 100644
index 00000000000..fb0171bfd97
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/commands/local_linux_curl.js
@@ -0,0 +1,13 @@
+import {OS_TYPES} from '../OsTypes';
+
+
+export default function generateLocalLinuxCurl(ip, osType) {
+ let bitText = osType === OS_TYPES.LINUX_32 ? '32' : '64';
+ return `curl https://${ip}:5000/api/monkey/download/monkey-linux-${bitText} -k
+ -o monkey-linux-${bitText};
+ chmod +x monkey-linux-${bitText};
+ ./monkey-linux-${bitText} m0nk3y -s ${ip}:5000\`;`;
+ }
+
+
+
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/commands/local_linux_wget.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/commands/local_linux_wget.js
new file mode 100644
index 00000000000..766822ee1f0
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/commands/local_linux_wget.js
@@ -0,0 +1,10 @@
+import {OS_TYPES} from '../OsTypes';
+
+
+export default function generateLocalLinuxWget(ip, osType) {
+ let bitText = osType === OS_TYPES.LINUX_32 ? '32' : '64';
+ return `wget --no-check-certificate https://${ip}:5000/api/monkey/download/
+ monkey-linux-${bitText};
+ chmod +x monkey-linux-${bitText};
+ ./monkey-linux-${bitText} m0nk3y -s ${ip}:5000`;
+ }
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/commands/local_windows_cmd.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/commands/local_windows_cmd.js
new file mode 100644
index 00000000000..74afbe512b5
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/commands/local_windows_cmd.js
@@ -0,0 +1,10 @@
+import {OS_TYPES} from '../OsTypes';
+
+
+export default function generateLocalWindowsCmd(ip, osType) {
+ let bitText = osType === OS_TYPES.WINDOWS_32 ? '32' : '64';
+ return `powershell [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true};
+ (New-Object System.Net.WebClient).DownloadFile('https://${ip}:5000/api/monkey/download/
+ monkey-windows-${bitText}.exe','.\\monkey.exe');
+ ;Start-Process -FilePath '.\\monkey.exe' -ArgumentList 'm0nk3y -s ${ip}:5000';`;
+}
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/commands/local_windows_powershell.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/commands/local_windows_powershell.js
new file mode 100644
index 00000000000..1ebd1f4ac8e
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/commands/local_windows_powershell.js
@@ -0,0 +1,10 @@
+import {OS_TYPES} from '../OsTypes';
+
+
+export default function generateLocalWindowsPowershell(ip, osType) {
+ let bitText = osType === OS_TYPES.WINDOWS_32 ? '32' : '64';
+ return `[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true};
+ (New-Object System.Net.WebClient).DownloadFile('https://${ip}:5000/api/monkey/download/
+ monkey-windows-${bitText}.exe','.\\monkey.exe');
+ ;Start-Process -FilePath '.\\monkey.exe' -ArgumentList 'm0nk3y -s ${ip}:5000';`;
+}
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
new file mode 100644
index 00000000000..d57f14fe448
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/ui-components/DropdownSelect.js
@@ -0,0 +1,62 @@
+import React, {useState} from 'react';
+import {Dropdown} from 'react-bootstrap';
+import PropTypes from 'prop-types';
+
+export default function DropdownSelect(props) {
+ const [selectedOption, setSelectedOption] = useState(props.defaultKey);
+
+ function generateDropdownItems(data) {
+ if (Array.isArray(data)) {
+ return generateDropdownItemsFromArray(data);
+ } else if (typeof data === 'object') {
+ return generateDropdownItemsFromObject(data);
+ } else {
+ throw "Component can only generate dropdown intems from lists and objects."
+ }
+ }
+
+ function generateDropdownItemsFromArray(data) {
+ const dropdownItems = [];
+ for (let i = 0; i < data.length; i++) {
+ dropdownItems.push(generateDropdownItem(i, data[i]));
+ }
+ return dropdownItems;
+ }
+
+ function generateDropdownItemsFromObject(data) {
+ const dropdownItems = [];
+ for (let [key, value] of Object.entries(data)) {
+ dropdownItems.push(generateDropdownItem(key, value));
+ }
+ return dropdownItems;
+ }
+
+ function generateDropdownItem(key, value) {
+ return (
+
{ setSelectedOption(key);
+ props.onClick(key)}}
+ active={(key === selectedOption)}>
+ {value}
+ );
+ }
+
+ return (
+ <>
+
+
+ {props.options[selectedOption]}
+
+
+
+ {generateDropdownItems(props.options)}
+
+
+ >
+ )
+}
+
+DropdownSelect.propTypes = {
+ options: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
+ defaultKey: PropTypes.oneOfType([PropTypes.string,PropTypes.number]),
+ onClick: PropTypes.func
+}
diff --git a/monkey/monkey_island/cc/ui/src/components/ui-components/Emoji.js b/monkey/monkey_island/cc/ui/src/components/ui-components/Emoji.js
new file mode 100644
index 00000000000..1773efbd26a
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/ui-components/Emoji.js
@@ -0,0 +1,12 @@
+import React from 'react';
+const Emoji = props => (
+
+ {props.symbol}
+
+);
+export default Emoji;
diff --git a/monkey/monkey_island/cc/ui/src/components/ui-components/inline-selection/InlineSelection.js b/monkey/monkey_island/cc/ui/src/components/ui-components/inline-selection/InlineSelection.js
index 97bb82d99a9..2a6dbc897ab 100644
--- a/monkey/monkey_island/cc/ui/src/components/ui-components/inline-selection/InlineSelection.js
+++ b/monkey/monkey_island/cc/ui/src/components/ui-components/inline-selection/InlineSelection.js
@@ -18,7 +18,7 @@ function setPreviousComponent(props, previousComponent) {
if(previousComponent === ManualRunOptions){
return props.setComponent()
} else {
- return props.setComponent(previousComponent)
+ return props.setComponent(previousComponent, props)
}
}
From 0d047b28e39eece43138f71501d88626207ebde4 Mon Sep 17 00:00:00 2001
From: VakarisZ
Date: Tue, 25 Aug 2020 11:30:12 +0300
Subject: [PATCH 03/17] More work and styling of monkey run page components
---
.../pages/RunMonkeyPage/CommandDisplay.js | 39 ++++++++++++-------
.../RunMonkeyPage/LocalManualRunOptions.js | 8 ++--
.../pages/RunMonkeyPage/ManualRunOptions.js | 28 ++++++++-----
.../ui-components/DropdownSelect.js | 5 ++-
.../inline-selection/BackButton.js | 15 +++++--
.../inline-selection/InlineSelection.js | 5 ++-
.../inline-selection/NextSelectionButton.js | 23 ++++++++---
.../monkey_island/cc/ui/src/styles/Main.scss | 4 +-
.../styles/components/InlineSelection.scss | 8 ----
.../inline-selection/BackButton.scss | 20 ++++++++++
.../inline-selection/InlineSelection.scss | 12 ++++++
.../inline-selection/NextSelectionButton.scss | 34 ++++++++++++++++
12 files changed, 152 insertions(+), 49 deletions(-)
delete mode 100644 monkey/monkey_island/cc/ui/src/styles/components/InlineSelection.scss
create mode 100644 monkey/monkey_island/cc/ui/src/styles/components/inline-selection/BackButton.scss
create mode 100644 monkey/monkey_island/cc/ui/src/styles/components/inline-selection/InlineSelection.scss
create mode 100644 monkey/monkey_island/cc/ui/src/styles/components/inline-selection/NextSelectionButton.scss
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 bf0852c5920..b237e25d2c2 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
@@ -2,42 +2,53 @@ import {Button, Card, Nav} from 'react-bootstrap';
import CopyToClipboard from 'react-copy-to-clipboard';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faClipboard} from '@fortawesome/free-solid-svg-icons/faClipboard';
-import React, {useState} from 'react';
+import React, {useEffect, useState} from 'react';
import PropTypes from 'prop-types';
export default function commandDisplay(props) {
- const [selectedVariant, setSelectedVariant] = useState(props.commands[0].name);
+ const [selectedCommand, setSelectedCommand] = useState(props.commands[0]);
+
+ function setSelectedCommandByName(type){
+ setSelectedCommand(getCommandByName(props.commands, type));
+ }
+
+ function getCommandByName(commands, type){
+ return commands.find((command) => {return command.type === type});
+ }
+
+ useEffect(() => {
+ let sameTypeCommand = getCommandByName(props.commands, selectedCommand.type);
+ if( sameTypeCommand !== undefined){
+ setSelectedCommand(sameTypeCommand);
+ } else {
+ setSelectedCommand(props.commands[0]);
+ }
+ }, [props.commands]);
function renderNav() {
return (
-