forked from p15670423/monkey
Merge pull request #1326 from guardicore/ransomware_landing_page
Ransomware landing page
This commit is contained in:
commit
5a2bb51789
File diff suppressed because it is too large
Load Diff
|
@ -73,6 +73,7 @@
|
|||
"@fortawesome/free-solid-svg-icons": "^5.15.1",
|
||||
"@fortawesome/react-fontawesome": "^0.1.12",
|
||||
"@kunukn/react-collapse": "^1.2.7",
|
||||
"@types/react-router-dom": "^5.1.8",
|
||||
"bootstrap": "^4.5.3",
|
||||
"classnames": "^2.2.6",
|
||||
"core-js": "^3.7.0",
|
||||
|
|
|
@ -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();
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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>
|
||||
)}/>
|
||||
)
|
|
@ -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;
|
|
@ -38,11 +38,11 @@ class StartOverPageComponent extends AuthComponent {
|
|||
<Col sm={{offset: 3, span: 9}} md={{offset: 3, span: 9}}
|
||||
lg={{offset: 3, span: 9}} xl={{offset: 2, span: 7}}
|
||||
className={'main'}>
|
||||
<StartOverModal cleaned = {this.state.cleaned}
|
||||
showCleanDialog = {this.state.showCleanDialog}
|
||||
allMonkeysAreDead = {this.state.allMonkeysAreDead}
|
||||
onVerify = {this.cleanup}
|
||||
onClose = {this.closeModal}/>
|
||||
<StartOverModal cleaned={this.state.cleaned}
|
||||
showCleanDialog={this.state.showCleanDialog}
|
||||
allMonkeysAreDead={this.state.allMonkeysAreDead}
|
||||
onVerify={this.cleanup}
|
||||
onClose={this.closeModal}/>
|
||||
<h1 className="page-title">Start Over</h1>
|
||||
<div style={{'fontSize': '1.2em'}}>
|
||||
<p>
|
||||
|
@ -88,7 +88,10 @@ class StartOverPageComponent extends AuthComponent {
|
|||
cleaned: true
|
||||
});
|
||||
}
|
||||
}).then(this.updateMonkeysRunning());
|
||||
}).then(() => {
|
||||
this.updateMonkeysRunning();
|
||||
this.props.onStatusChange();
|
||||
});
|
||||
};
|
||||
|
||||
closeModal = () => {
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"sourceMap": true,
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
|
|
Loading…
Reference in New Issue