Merge pull request #1326 from guardicore/ransomware_landing_page

Ransomware landing page
This commit is contained in:
VakarisZ 2021-07-16 14:58:43 +03:00 committed by GitHub
commit 5a2bb51789
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1275 additions and 558 deletions

File diff suppressed because it is too large Load Diff

View File

@ -73,6 +73,7 @@
"@fortawesome/free-solid-svg-icons": "^5.15.1", "@fortawesome/free-solid-svg-icons": "^5.15.1",
"@fortawesome/react-fontawesome": "^0.1.12", "@fortawesome/react-fontawesome": "^0.1.12",
"@kunukn/react-collapse": "^1.2.7", "@kunukn/react-collapse": "^1.2.7",
"@types/react-router-dom": "^5.1.8",
"bootstrap": "^4.5.3", "bootstrap": "^4.5.3",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"core-js": "^3.7.0", "core-js": "^3.7.0",

View File

@ -0,0 +1,35 @@
import AuthComponent from "./AuthComponent";
import React from "react";
export class Response{
body: any
status: number
constructor(body: any, status: number) {
this.body = body
this.status = status
}
}
class IslandHttpClient extends AuthComponent {
post(endpoint: string, contents: any): Promise<Response>{
let status = null;
return this.authFetch(endpoint,
{
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(contents)
})
.then(res => {status = res.status; return res.json()})
.then(res => new Response(res, status));
}
get(endpoint: string): Promise<Response>{
let status = null;
return this.authFetch(endpoint)
.then(res => {status = res.status; return res.json()})
.then(res => new Response(res, status));
}
}
export default new IslandHttpClient();

View File

@ -1,225 +0,0 @@
import React from 'react';
import {BrowserRouter as Router, Redirect, Route, Switch} from 'react-router-dom';
import {Container} from 'react-bootstrap';
import GettingStartedPage from 'components/pages/GettingStartedPage';
import ConfigurePage from 'components/pages/ConfigurePage';
import RunMonkeyPage from 'components/pages/RunMonkeyPage/RunMonkeyPage';
import MapPage from 'components/pages/MapPage';
import TelemetryPage from 'components/pages/TelemetryPage';
import StartOverPage from 'components/pages/StartOverPage';
import ReportPage from 'components/pages/ReportPage';
import LicensePage from 'components/pages/LicensePage';
import AuthComponent from 'components/AuthComponent';
import LoginPageComponent from 'components/pages/LoginPage';
import RegisterPageComponent from 'components/pages/RegisterPage';
import Notifier from 'react-desktop-notification';
import NotFoundPage from 'components/pages/NotFoundPage';
import 'normalize.css/normalize.css';
import 'react-data-components/css/table-twbs.css';
import 'styles/App.css';
import 'react-toggle/style.css';
import 'react-table/react-table.css';
import notificationIcon from '../images/notification-logo-512x512.png';
import {StandardLayoutComponent} from './layouts/StandardLayoutComponent';
import LoadingScreen from './ui-components/LoadingScreen';
const reportZeroTrustRoute = '/report/zeroTrust';
const islandModeRoute = '/api/island-mode'
class AppComponent extends AuthComponent {
updateStatus = () => {
if (this.state.isLoggedIn === false) {
return
}
this.auth.loggedIn()
.then(res => {
if (this.state.isLoggedIn !== res) {
this.setState({
isLoggedIn: res
});
}
if (!res) {
this.auth.needsRegistration()
.then(result => {
this.setState({
needsRegistration: result
});
})
}
if (res) {
this.authFetch('/api')
.then(res => res.json())
.then(res => {
// This check is used to prevent unnecessary re-rendering
let isChanged = false;
for (let step in this.state.completedSteps) {
if (this.state.completedSteps[step] !== res['completed_steps'][step]) {
isChanged = true;
break;
}
}
if (isChanged) {
this.setState({completedSteps: res['completed_steps']});
this.showInfectionDoneNotification();
}
});
}
});
};
renderRoute = (route_path, page_component, is_exact_path = false) => {
let render_func = () => {
switch (this.state.isLoggedIn) {
case true:
return page_component;
case false:
switch (this.state.needsRegistration) {
case true:
return <Redirect to={{pathname: '/register'}}/>
case false:
return <Redirect to={{pathname: '/login'}}/>;
default:
return <LoadingScreen text={'Loading page...'}/>;
}
default:
return <LoadingScreen text={'Loading page...'}/>;
}
};
if (is_exact_path) {
return <Route exact path={route_path} render={render_func}/>;
} else {
return <Route path={route_path} render={render_func}/>;
}
};
redirectTo = (userPath, targetPath) => {
let pathQuery = new RegExp(userPath + '[/]?$', 'g');
if (window.location.pathname.match(pathQuery)) {
return <Redirect to={{pathname: targetPath}}/>
}
};
constructor(props) {
super(props);
this.state = {
completedSteps: {
run_server: true,
run_monkey: false,
infection_done: false,
report_done: false,
isLoggedIn: undefined,
needsRegistration: undefined,
islandMode: undefined
},
noAuthLoginAttempted: undefined
};
}
updateIslandMode() {
this.authFetch(islandModeRoute)
.then(res => res.json())
.then(res => {
this.setState({islandMode: res.mode})
}
);
}
componentDidMount() {
this.updateStatus();
this.interval = setInterval(this.updateStatus, 10000);
this.updateIslandMode()
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
return (
<Router>
<Container fluid>
<Switch>
<Route path='/login' render={() => (<LoginPageComponent onStatusChange={this.updateStatus}/>)}/>
<Route path='/register' render={() => (<RegisterPageComponent onStatusChange={this.updateStatus}/>)}/>
{this.renderRoute('/',
<StandardLayoutComponent component={GettingStartedPage}
completedSteps={this.state.completedSteps}
onStatusChange={this.updateStatus}
/>,
true)}
{this.renderRoute('/configure',
<StandardLayoutComponent component={ConfigurePage}
islandMode={this.state.islandMode}
onStatusChange={this.updateStatus}
completedSteps={this.state.completedSteps}/>)}
{this.renderRoute('/run-monkey',
<StandardLayoutComponent component={RunMonkeyPage}
islandMode={this.state.islandMode}
onStatusChange={this.updateStatus}
completedSteps={this.state.completedSteps}/>)}
{this.renderRoute('/infection/map',
<StandardLayoutComponent component={MapPage}
onStatusChange={this.updateStatus}
completedSteps={this.state.completedSteps}/>)}
{this.renderRoute('/infection/telemetry',
<StandardLayoutComponent component={TelemetryPage}
onStatusChange={this.updateStatus}
completedSteps={this.state.completedSteps}/>)}
{this.renderRoute('/start-over',
<StandardLayoutComponent component={StartOverPage}
onStatusChange={this.updateStatus}
completedSteps={this.state.completedSteps}/>)}
{this.redirectTo('/report', '/report/security')}
{this.renderRoute('/report/security',
<StandardLayoutComponent component={ReportPage}
completedSteps={this.state.completedSteps}/>)}
{this.renderRoute('/report/attack',
<StandardLayoutComponent component={ReportPage}
completedSteps={this.state.completedSteps}/>)}
{this.renderRoute('/report/zeroTrust',
<StandardLayoutComponent component={ReportPage}
completedSteps={this.state.completedSteps}/>)}
{this.renderRoute('/report/ransomware',
<StandardLayoutComponent component={ReportPage}
completedSteps={this.state.completedSteps}/>)}
{this.renderRoute('/license',
<StandardLayoutComponent component={LicensePage}
onStatusChange={this.updateStatus}
completedSteps={this.state.completedSteps}/>)}
<Route component={NotFoundPage}/>
</Switch>
</Container>
</Router>
);
}
showInfectionDoneNotification() {
if (this.shouldShowNotification()) {
const hostname = window.location.hostname;
const port = window.location.port;
const protocol = window.location.protocol;
const url = `${protocol}//${hostname}:${port}${reportZeroTrustRoute}`;
Notifier.start(
'Monkey Island',
'Infection is done! Click here to go to the report page.',
url,
notificationIcon);
}
}
shouldShowNotification() {
// No need to show the notification to redirect to the report if we're already in the report page
return (this.state.completedSteps.infection_done && !window.location.pathname.startsWith('/report'));
}
}
AppComponent.defaultProps = {};
export default AppComponent;

View File

@ -0,0 +1,266 @@
import React from 'react';
import {BrowserRouter as Router, Redirect, Route, Switch} from 'react-router-dom';
import {Container} from 'react-bootstrap';
import ConfigurePage from './pages/ConfigurePage.js';
import RunMonkeyPage from './pages/RunMonkeyPage/RunMonkeyPage';
import MapPage from './pages/MapPage';
import TelemetryPage from './pages/TelemetryPage';
import StartOverPage from './pages/StartOverPage';
import ReportPage from './pages/ReportPage';
import LicensePage from './pages/LicensePage';
import AuthComponent from './AuthComponent';
import LoginPageComponent from './pages/LoginPage';
import RegisterPageComponent from './pages/RegisterPage';
import LandingPage from "./pages/LandingPage";
import Notifier from 'react-desktop-notification';
import NotFoundPage from './pages/NotFoundPage';
import GettingStartedPage from './pages/GettingStartedPage';
import 'normalize.css/normalize.css';
import 'react-data-components/css/table-twbs.css';
import 'styles/App.css';
import 'react-toggle/style.css';
import 'react-table/react-table.css';
import LoadingScreen from './ui-components/LoadingScreen';
import SidebarLayoutComponent from "./layouts/SidebarLayoutComponent";
import {CompletedSteps} from "./side-menu/CompletedSteps";
import Timeout = NodeJS.Timeout;
import IslandHttpClient from "./IslandHttpClient";
import _ from "lodash";
let notificationIcon = require('../images/notification-logo-512x512.png');
const Routes = {
LandingPage: '/landing-page',
GettingStartedPage: '/',
Report: '/report',
AttackReport: '/report/attack',
ZeroTrustReport: '/report/zeroTrust',
SecurityReport: '/report/security',
RansomwareReport: '/report/ransomware',
LoginPage: '/login',
RegisterPage: '/register',
ConfigurePage: '/configure',
RunMonkeyPage: '/run-monkey',
MapPage: '/infection/map',
TelemetryPage: '/infection/telemetry',
StartOverPage: '/start-over',
LicensePage: '/license'
}
export function isReportRoute(route){
return route.startsWith(Routes.Report);
}
class AppComponent extends AuthComponent {
private interval: Timeout;
constructor(props) {
super(props);
let completedSteps = new CompletedSteps(false);
this.state = {
loading: true,
completedSteps: completedSteps,
islandMode: undefined,
noAuthLoginAttempted: undefined
};
this.interval = undefined;
}
updateStatus = () => {
if (this.state.isLoggedIn === false) {
return
}
this.auth.loggedIn()
.then(res => {
if (this.state.isLoggedIn !== res) {
this.setState({
isLoggedIn: res
});
}
if (!res) {
this.auth.needsRegistration()
.then(result => {
this.setState({
needsRegistration: result
});
})
}
if (res) {
this.setMode()
.then(() => {
if (this.state.islandMode === null) {
return
}
this.authFetch('/api')
.then(res => res.json())
.then(res => {
let completedSteps = CompletedSteps.buildFromResponse(res.completed_steps);
// This check is used to prevent unnecessary re-rendering
if (_.isEqual(this.state.completedSteps, completedSteps)) {
return;
}
this.setState({completedSteps: completedSteps});
this.showInfectionDoneNotification();
});
}
)
}
});
};
setMode = () => {
return IslandHttpClient.get('/api/island-mode')
.then(res => {
this.setState({islandMode: res.body.mode});
});
}
renderRoute = (route_path, page_component, is_exact_path = false) => {
let render_func = () => {
switch (this.state.isLoggedIn) {
case true:
if (this.needsRedirectionToLandingPage(route_path)) {
return <Redirect to={{pathname: Routes.LandingPage}}/>
} else if (this.needsRedirectionToGettingStarted(route_path)) {
return <Redirect to={{pathname: Routes.GettingStartedPage}}/>
}
return page_component;
case false:
switch (this.state.needsRegistration) {
case true:
return <Redirect to={{pathname: Routes.RegisterPage}}/>
case false:
return <Redirect to={{pathname: Routes.LoginPage}}/>;
default:
return <LoadingScreen text={'Loading page...'}/>;
}
default:
return <LoadingScreen text={'Loading page...'}/>;
}
};
if (is_exact_path) {
return <Route exact path={route_path} render={render_func}/>;
} else {
return <Route path={route_path} render={render_func}/>;
}
};
needsRedirectionToLandingPage = (route_path) => {
return (this.state.islandMode === null && route_path !== Routes.LandingPage)
}
needsRedirectionToGettingStarted = (route_path) => {
return route_path === Routes.LandingPage &&
this.state.islandMode !== null && this.state.islandMode !== undefined
}
redirectTo = (userPath, targetPath) => {
let pathQuery = new RegExp(userPath + '[/]?$', 'g');
if (window.location.pathname.match(pathQuery)) {
return <Redirect to={{pathname: targetPath}}/>
}
};
componentDidMount() {
this.updateStatus();
this.interval = setInterval(this.updateStatus, 10000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
return (
<Router>
<Container fluid>
<Switch>
<Route path={Routes.LoginPage} render={() => (<LoginPageComponent onStatusChange={this.updateStatus}/>)}/>
<Route path={Routes.RegisterPage} render={() => (<RegisterPageComponent onStatusChange={this.updateStatus}/>)}/>
{this.renderRoute(Routes.LandingPage,
<SidebarLayoutComponent component={LandingPage}
sideNavDisabled={true}
completedSteps={new CompletedSteps()}
onStatusChange={this.updateStatus}/>)}
{this.renderRoute(Routes.GettingStartedPage,
<SidebarLayoutComponent component={GettingStartedPage}
completedSteps={this.state.completedSteps}
onStatusChange={this.updateStatus}
/>,
true)}
{this.renderRoute(Routes.ConfigurePage,
<SidebarLayoutComponent component={ConfigurePage}
islandMode={this.state.islandMode}
onStatusChange={this.updateStatus}
completedSteps={this.state.completedSteps}/>)}
{this.renderRoute(Routes.RunMonkeyPage,
<SidebarLayoutComponent component={RunMonkeyPage}
islandMode={this.state.islandMode}
onStatusChange={this.updateStatus}
completedSteps={this.state.completedSteps}/>)}
{this.renderRoute(Routes.MapPage,
<SidebarLayoutComponent component={MapPage}
onStatusChange={this.updateStatus}
completedSteps={this.state.completedSteps}/>)}
{this.renderRoute(Routes.TelemetryPage,
<SidebarLayoutComponent component={TelemetryPage}
onStatusChange={this.updateStatus}
completedSteps={this.state.completedSteps}/>)}
{this.renderRoute(Routes.StartOverPage,
<SidebarLayoutComponent component={StartOverPage}
onStatusChange={this.updateStatus}
completedSteps={this.state.completedSteps}/>)}
{this.redirectTo(Routes.Report, Routes.SecurityReport)}
{this.renderRoute(Routes.SecurityReport,
<SidebarLayoutComponent component={ReportPage}
completedSteps={this.state.completedSteps}/>)}
{this.renderRoute(Routes.AttackReport,
<SidebarLayoutComponent component={ReportPage}
completedSteps={this.state.completedSteps}/>)}
{this.renderRoute(Routes.ZeroTrustReport,
<SidebarLayoutComponent component={ReportPage}
completedSteps={this.state.completedSteps}/>)}
{this.renderRoute(Routes.RansomwareReport,
<SidebarLayoutComponent component={ReportPage}
completedSteps={this.state.completedSteps}/>)}
{this.renderRoute(Routes.LicensePage,
<SidebarLayoutComponent component={LicensePage}
onStatusChange={this.updateStatus}
completedSteps={this.state.completedSteps}/>)}
<Route component={NotFoundPage}/>
</Switch>
</Container>
</Router>
);
}
showInfectionDoneNotification() {
if (this.shouldShowNotification()) {
const hostname = window.location.hostname;
const port = window.location.port;
const protocol = window.location.protocol;
const url = `${protocol}//${hostname}:${port}${Routes.ZeroTrustReport}`;
Notifier.start(
'Monkey Island',
'Infection is done! Click here to go to the report page.',
url,
notificationIcon);
}
}
shouldShowNotification() {
// No need to show the notification to redirect to the report if we're already in the report page
return (this.state.completedSteps.infection_done && !window.location.pathname.startsWith(Routes.Report));
}
}
export default AppComponent;

View File

@ -1,92 +0,0 @@
import React from 'react';
import {NavLink} from 'react-router-dom';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck';
import {faUndo} from '@fortawesome/free-solid-svg-icons/faUndo';
import {faExternalLinkAlt} from '@fortawesome/free-solid-svg-icons';
import guardicoreLogoImage from '../images/guardicore-logo.png';
import logoImage from '../images/monkey-icon.svg';
import infectionMonkeyImage from '../images/infection-monkey.svg';
import VersionComponent from './side-menu/VersionComponent';
import '../styles/components/SideNav.scss';
class SideNavComponent extends React.Component {
render() {
return (
<>
<NavLink to={'/'} exact={true}>
<div className='header'>
<img alt='logo' src={logoImage} style={{width: '5vw', margin: '15px'}}/>
<img src={infectionMonkeyImage} style={{width: '15vw'}} alt='Infection Monkey'/>
</div>
</NavLink>
<ul className='navigation'>
<li>
<NavLink to='/run-monkey'>
<span className='number'>1.</span>
Run Monkey
{this.props.completedSteps.run_monkey ?
<FontAwesomeIcon icon={faCheck} className='pull-right checkmark'/>
: ''}
</NavLink>
</li>
<li>
<NavLink to='/infection/map'>
<span className='number'>2.</span>
Infection Map
{this.props.completedSteps.infection_done ?
<FontAwesomeIcon icon={faCheck} className='pull-right checkmark'/>
: ''}
</NavLink>
</li>
<li>
<NavLink to='/report/security'
isActive={(_match, location) => {
return (location.pathname === '/report/attack'
|| location.pathname === '/report/zeroTrust'
|| location.pathname === '/report/security')
}}>
<span className='number'>3.</span>
Security Reports
{this.props.completedSteps.report_done ?
<FontAwesomeIcon icon={faCheck} className='pull-right checkmark'/>
: ''}
</NavLink>
</li>
<li>
<NavLink to='/start-over'>
<span className='number'><FontAwesomeIcon icon={faUndo} style={{'marginLeft': '-1px'}}/></span>
Start Over
</NavLink>
</li>
</ul>
<hr/>
<ul>
<li><NavLink to='/configure'>Configuration</NavLink></li>
<li><NavLink to='/infection/telemetry'>Logs</NavLink></li>
</ul>
<hr/>
<div className='guardicore-link text-center' style={{'marginBottom': '0.5em'}}>
<span>Powered by</span>
<a href='http://www.guardicore.com' rel='noopener noreferrer' target='_blank'>
<img src={guardicoreLogoImage} alt='GuardiCore'/>
</a>
</div>
<div className='license-link text-center'>
<a href='https://www.guardicore.com/infectionmonkey/docs' rel="noopener noreferrer" target="_blank">
<FontAwesomeIcon icon={faExternalLinkAlt} /> Documentation
</a>
<br/>
<NavLink to='/license'>License</NavLink>
</div>
<VersionComponent/>
</>)
}
}
export default SideNavComponent;

View File

@ -0,0 +1,113 @@
import React from 'react';
import {NavLink} from 'react-router-dom';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck';
import {faUndo} from '@fortawesome/free-solid-svg-icons/faUndo';
import {faExternalLinkAlt} from '@fortawesome/free-solid-svg-icons';
import VersionComponent from './side-menu/VersionComponent';
import '../styles/components/SideNav.scss';
import {CompletedSteps} from "./side-menu/CompletedSteps";
import {isReportRoute} from "./Main";
const guardicoreLogoImage = require('../images/guardicore-logo.png');
const logoImage = require('../images/monkey-icon.svg');
const infectionMonkeyImage = require('../images/infection-monkey.svg');
type Props = {
disabled?: boolean,
completedSteps: CompletedSteps
}
const SideNavComponent = ({disabled=false, completedSteps}: Props) => {
return (
<>
<NavLink to={'/'} exact={true}>
<div className='header'>
<img alt='logo' src={logoImage} style={{width: '5vw', margin: '15px'}}/>
<img src={infectionMonkeyImage} style={{width: '15vw'}} alt='Infection Monkey'/>
</div>
</NavLink>
<ul className='navigation'>
<li>
<NavLink to='/run-monkey' className={getNavLinkClass()}>
<span className='number'>1.</span>
Run Monkey
{completedSteps.runMonkey ?
<FontAwesomeIcon icon={faCheck} className='pull-right checkmark'/>
: ''}
</NavLink>
</li>
<li>
<NavLink to='/infection/map' className={getNavLinkClass()}>
<span className='number'>2.</span>
Infection Map
{completedSteps.infectionDone ?
<FontAwesomeIcon icon={faCheck} className='pull-right checkmark'/>
: ''}
</NavLink>
</li>
<li>
<NavLink to='/report/security'
className={getNavLinkClass()}
isActive={(_match, location) => {
return (isReportRoute(location.pathname))
}}>
<span className='number'>3.</span>
Security Reports
{completedSteps.reportDone ?
<FontAwesomeIcon icon={faCheck} className='pull-right checkmark'/>
: ''}
</NavLink>
</li>
<li>
<NavLink to='/start-over' className={getNavLinkClass()}>
<span className='number'><FontAwesomeIcon icon={faUndo} style={{'marginLeft': '-1px'}}/></span>
Start Over
</NavLink>
</li>
</ul>
<hr/>
<ul>
<li><NavLink to='/configure'
className={getNavLinkClass()}>
Configuration
</NavLink></li>
<li><NavLink to='/infection/telemetry'
className={getNavLinkClass()}>
Logs
</NavLink></li>
</ul>
<hr/>
<div className='guardicore-link text-center' style={{'marginBottom': '0.5em'}}>
<span>Powered by</span>
<a href='http://www.guardicore.com' rel='noopener noreferrer' target='_blank'>
<img src={guardicoreLogoImage} alt='GuardiCore'/>
</a>
</div>
<div className='license-link text-center'>
<a href='https://www.guardicore.com/infectionmonkey/docs' rel="noopener noreferrer" target="_blank">
<FontAwesomeIcon icon={faExternalLinkAlt} /> Documentation
</a>
<br/>
<NavLink to='/license'>License</NavLink>
</div>
<VersionComponent/>
</>);
function getNavLinkClass() {
if(disabled){
return `nav-link disabled`
} else {
return ''
}
}
}
export default SideNavComponent;

View File

@ -0,0 +1,22 @@
import React from 'react';
import {Route} from 'react-router-dom';
import SideNavComponent from '../SideNavComponent.tsx';
import {Col, Row} from 'react-bootstrap';
const SidebarLayoutComponent = ({component: Component,
sideNavDisabled = false,
completedSteps = null,
...other
}) => (
<Route {...other} render={() => {
return (
<Row>
<Col sm={3} md={3} lg={3} xl={2} className='sidebar'>
<SideNavComponent disabled={sideNavDisabled} completedSteps={completedSteps}/>
</Col>
<Component {...other} />
</Row>)
}}/>
)
export default SidebarLayoutComponent;

View File

@ -1,15 +0,0 @@
import React from 'react'
import {Route} from 'react-router-dom'
import SideNavComponent from '../SideNavComponent'
import {Col, Row} from 'react-bootstrap';
export const StandardLayoutComponent = ({component: Component, ...rest}) => (
<Route {...rest} render={() => (
<Row>
<Col sm={3} md={3} lg={3} xl={2} className='sidebar'>
<SideNavComponent completedSteps={rest['completedSteps']}/>
</Col>
<Component {...rest} />
</Row>
)}/>
)

View File

@ -0,0 +1,75 @@
import React from 'react';
import {Col, Row} from 'react-bootstrap';
import {Link} from 'react-router-dom';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faFileCode, faLightbulb} from '@fortawesome/free-solid-svg-icons';
import '../../styles/pages/LandingPage.scss';
import IslandHttpClient from "../IslandHttpClient";
const LandingPageComponent = (props) => {
return (
<Col sm={{offset: 3, span: 9}} md={{offset: 3, span: 9}}
lg={{offset: 3, span: 9}} xl={{offset: 2, span: 7}}
className={'landing-page'}>
<h1 className="page-title">Welcome to the Monkey Island Server</h1>
<div style={{'fontSize': '1.2em'}}>
<ScenarioButtons/>
<br/>
</div>
</Col>
);
function ScenarioButtons() {
return (
<section>
<h2 className={'scenario-choice-title'}>Choose a scenario:</h2>
<div className="container">
<Row className="justify-content-center">
<div className="col-lg-6 col-sm-6">
<Link to="/run-monkey"
className="px-4 py-5 bg-white shadow text-center d-block"
onClick={() => {
setScenario('ransomware')
}}>
<h4><FontAwesomeIcon icon={faFileCode}/> Ransomware</h4>
<p>Simulate ransomware infection in the network.</p>
</Link>
</div>
<div className="col-lg-6 col-sm-6">
<Link to="/configure"
className="px-4 py-5 bg-white shadow text-center d-block"
onClick={() => {
setScenario('advanced')
}}>
<h4><FontAwesomeIcon icon={faLightbulb}/> Custom</h4>
<p>Fine tune the simulation to your needs.</p>
</Link>
</div>
</Row>
<MonkeyInfo/>
</div>
</section>
);
}
function setScenario(scenario: string) {
IslandHttpClient.post('/api/island-mode', {'mode': scenario});
props.onStatusChange();
}
}
function MonkeyInfo() {
return (
<>
<h4 className={'monkey-description-title'}>What is Infection Monkey?</h4>
<strong>Infection Monkey</strong> is an open-source security tool for testing a data center's resiliency to
perimeter
breaches and internal server infections. The Monkey uses various methods to propagate across a data center
and reports to this Monkey Island Command and Control server.
</>
);
}
export default LandingPageComponent;

View File

@ -38,11 +38,11 @@ class StartOverPageComponent extends AuthComponent {
<Col sm={{offset: 3, span: 9}} md={{offset: 3, span: 9}} <Col sm={{offset: 3, span: 9}} md={{offset: 3, span: 9}}
lg={{offset: 3, span: 9}} xl={{offset: 2, span: 7}} lg={{offset: 3, span: 9}} xl={{offset: 2, span: 7}}
className={'main'}> className={'main'}>
<StartOverModal cleaned = {this.state.cleaned} <StartOverModal cleaned={this.state.cleaned}
showCleanDialog = {this.state.showCleanDialog} showCleanDialog={this.state.showCleanDialog}
allMonkeysAreDead = {this.state.allMonkeysAreDead} allMonkeysAreDead={this.state.allMonkeysAreDead}
onVerify = {this.cleanup} onVerify={this.cleanup}
onClose = {this.closeModal}/> onClose={this.closeModal}/>
<h1 className="page-title">Start Over</h1> <h1 className="page-title">Start Over</h1>
<div style={{'fontSize': '1.2em'}}> <div style={{'fontSize': '1.2em'}}>
<p> <p>
@ -88,7 +88,10 @@ class StartOverPageComponent extends AuthComponent {
cleaned: true cleaned: true
}); });
} }
}).then(this.updateMonkeysRunning()); }).then(() => {
this.updateMonkeysRunning();
this.props.onStatusChange();
});
}; };
closeModal = () => { closeModal = () => {

View File

@ -0,0 +1,32 @@
export class CompletedSteps {
runServer: boolean
runMonkey: boolean
infectionDone: boolean
reportDone: boolean
isLoggedIn: boolean
needsRegistration: boolean
public constructor(runServer?: boolean,
runMonkey?: boolean,
infectinDone?: boolean,
reportDone?: boolean) {
this.runServer = runServer || false;
this.runMonkey = runMonkey || false;
this.infectionDone = infectinDone || false;
this.reportDone = reportDone || false;
}
static buildFromResponse(response: CompletedStepsRequest) {
return new CompletedSteps(response.run_server,
response.run_monkey,
response.infection_done,
response.report_done);
}
}
type CompletedStepsRequest = {
run_server: boolean,
run_monkey: boolean,
infection_done: boolean,
report_done: boolean
}

View File

@ -0,0 +1,12 @@
.landing-page h1.page-title {
margin-bottom: 20px;
}
.landing-page h2.scenario-choice-title {
margin-bottom: 20px;
margin-left: 12px;
}
.landing-page .monkey-description-title {
margin-top: 30px;
}

View File

@ -1,5 +1,6 @@
{ {
"compilerOptions": { "compilerOptions": {
"allowJs": true,
"sourceMap": true, "sourceMap": true,
"module": "commonjs", "module": "commonjs",
"target": "es6", "target": "es6",