add new ui files

This commit is contained in:
Barak Argaman 2017-08-29 18:43:25 +03:00
parent 61fa2540d8
commit 8f13092e56
34 changed files with 10527 additions and 410 deletions

View File

@ -1,56 +0,0 @@
.arrow {
float: right;
line-height: 1.42857;
}
.glyphicon.arrow:before {
content: "\e079";
}
.active > a > .glyphicon.arrow:before {
content: "\e114";
}
/*
* Require Font-Awesome
* http://fortawesome.github.io/Font-Awesome/
*/
.fa.arrow:before {
content: "\f104";
}
.active > a > .fa.arrow:before {
content: "\f107";
}
.plus-times {
float: right;
}
.fa.plus-times:before {
content: "\f067";
}
.active > a > .fa.plus-times {
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
}
.plus-minus {
float: right;
}
.fa.plus-minus:before {
content: "\f067";
}
.active > a > .fa.plus-minus:before {
content: "\f068";
}

View File

@ -1,354 +0,0 @@
/*!
* Start Bootstrap - SB Admin 2 Bootstrap Admin Theme (http://startbootstrap.com)
* Code licensed under the Apache License v2.0.
* For details, see http://www.apache.org/licenses/LICENSE-2.0.
*/
body {
background-color: #f8f8f8;
}
#wrapper {
width: 100%;
}
#page-wrapper {
padding: 0 15px;
min-height: 568px;
background-color: #fff;
}
@media(min-width:768px) {
#page-wrapper {
position: inherit;
margin: 0 0 0 250px;
padding: 0 30px;
border-left: 1px solid #e7e7e7;
}
}
.navbar-top-links {
margin-right: 0;
}
.navbar-top-links li {
display: inline-block;
}
.navbar-top-links li:last-child {
margin-right: 15px;
}
.navbar-top-links li a {
padding: 15px;
min-height: 50px;
}
.navbar-top-links .dropdown-menu li {
display: block;
}
.navbar-top-links .dropdown-menu li:last-child {
margin-right: 0;
}
.navbar-top-links .dropdown-menu li a {
padding: 3px 20px;
min-height: 0;
}
.navbar-top-links .dropdown-menu li a div {
white-space: normal;
}
.navbar-top-links .dropdown-messages,
.navbar-top-links .dropdown-tasks,
.navbar-top-links .dropdown-alerts {
width: 310px;
min-width: 0;
}
.navbar-top-links .dropdown-messages {
margin-left: 5px;
}
.navbar-top-links .dropdown-tasks {
margin-left: -59px;
}
.navbar-top-links .dropdown-alerts {
margin-left: -123px;
}
.navbar-top-links .dropdown-user {
right: 0;
left: auto;
}
.sidebar .sidebar-nav.navbar-collapse {
padding-right: 0;
padding-left: 0;
}
.sidebar .sidebar-search {
padding: 15px;
}
.sidebar ul li {
border-bottom: 1px solid #e7e7e7;
}
.sidebar ul li a.active {
background-color: #eee;
}
.sidebar .arrow {
float: right;
}
.sidebar .fa.arrow:before {
content: "\f104";
}
.sidebar .active>a>.fa.arrow:before {
content: "\f107";
}
.sidebar .nav-second-level li,
.sidebar .nav-third-level li {
border-bottom: 0!important;
}
.sidebar .nav-second-level li a {
padding-left: 37px;
}
.sidebar .nav-third-level li a {
padding-left: 52px;
}
@media(min-width:768px) {
.sidebar {
z-index: 1;
position: absolute;
width: 250px;
margin-top: 51px;
}
.navbar-top-links .dropdown-messages,
.navbar-top-links .dropdown-tasks,
.navbar-top-links .dropdown-alerts {
margin-left: auto;
}
}
.btn-outline {
color: inherit;
background-color: transparent;
transition: all .5s;
}
.btn-primary.btn-outline {
color: #428bca;
}
.btn-success.btn-outline {
color: #5cb85c;
}
.btn-info.btn-outline {
color: #5bc0de;
}
.btn-warning.btn-outline {
color: #f0ad4e;
}
.btn-danger.btn-outline {
color: #d9534f;
}
.btn-primary.btn-outline:hover,
.btn-success.btn-outline:hover,
.btn-info.btn-outline:hover,
.btn-warning.btn-outline:hover,
.btn-danger.btn-outline:hover {
color: #fff;
}
.chat {
margin: 0;
padding: 0;
list-style: none;
}
.chat li {
margin-bottom: 10px;
padding-bottom: 5px;
border-bottom: 1px dotted #999;
}
.chat li.left .chat-body {
margin-left: 60px;
}
.chat li.right .chat-body {
margin-right: 60px;
}
.chat li .chat-body p {
margin: 0;
}
.panel .slidedown .glyphicon,
.chat .glyphicon {
margin-right: 5px;
}
.chat-panel .panel-body {
height: 350px;
overflow-y: scroll;
}
.login-panel {
margin-top: 25%;
}
.flot-chart {
display: block;
height: 400px;
}
.flot-chart-content {
width: 100%;
height: 100%;
}
.dataTables_wrapper {
position: relative;
clear: both;
}
table.dataTable thead .sorting,
table.dataTable thead .sorting_asc,
table.dataTable thead .sorting_desc,
table.dataTable thead .sorting_asc_disabled,
table.dataTable thead .sorting_desc_disabled {
background: 0 0;
}
table.dataTable thead .sorting_asc:after {
content: "\f0de";
float: right;
font-family: fontawesome;
}
table.dataTable thead .sorting_desc:after {
content: "\f0dd";
float: right;
font-family: fontawesome;
}
table.dataTable thead .sorting:after {
content: "\f0dc";
float: right;
font-family: fontawesome;
color: rgba(50,50,50,.5);
}
.btn-circle {
width: 30px;
height: 30px;
padding: 6px 0;
border-radius: 15px;
text-align: center;
font-size: 12px;
line-height: 1.428571429;
}
.btn-circle.btn-lg {
width: 50px;
height: 50px;
padding: 10px 16px;
border-radius: 25px;
font-size: 18px;
line-height: 1.33;
}
.btn-circle.btn-xl {
width: 70px;
height: 70px;
padding: 10px 16px;
border-radius: 35px;
font-size: 24px;
line-height: 1.33;
}
.show-grid [class^=col-] {
padding-top: 10px;
padding-bottom: 10px;
border: 1px solid #ddd;
background-color: #eee!important;
}
.show-grid {
margin: 15px 0;
}
.huge {
font-size: 40px;
}
.panel-green {
border-color: #5cb85c;
}
.panel-green .panel-heading {
border-color: #5cb85c;
color: #fff;
background-color: #5cb85c;
}
.panel-green a {
color: #5cb85c;
}
.panel-green a:hover {
color: #3d8b3d;
}
.panel-red {
border-color: #d9534f;
}
.panel-red .panel-heading {
border-color: #d9534f;
color: #fff;
background-color: #d9534f;
}
.panel-red a {
color: #d9534f;
}
.panel-red a:hover {
color: #b52b27;
}
.panel-yellow {
border-color: #f0ad4e;
}
.panel-yellow .panel-heading {
border-color: #f0ad4e;
color: #fff;
background-color: #f0ad4e;
}
.panel-yellow a {
color: #f0ad4e;
}
.panel-yellow a:hover {
color: #df8a13;
}

View File

@ -0,0 +1,7 @@
{
"presets": [
"es2015",
"stage-0",
"react"
]
}

View File

@ -0,0 +1,12 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

View File

@ -0,0 +1,35 @@
{
"parser": "babel-eslint",
"plugins": [
"react"
],
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"env": {
"browser": true,
"amd": true,
"es6": true,
"node": true,
"mocha": true
},
"rules": {
"comma-dangle": 1,
"quotes": [ 1, "single" ],
"no-undef": 1,
"global-strict": 0,
"no-extra-semi": 1,
"no-underscore-dangle": 0,
"no-console": 1,
"no-unused-vars": 1,
"no-trailing-spaces": [1, { "skipBlankLines": true }],
"no-unreachable": 1,
"no-alert": 0,
"react/jsx-uses-react": 1,
"react/jsx-uses-vars": 1
}
}

33
monkey_island/cc/ui/.gitignore vendored Normal file
View File

@ -0,0 +1,33 @@
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
# Bower
bower_components/
# IDE/Editor data
.idea

View File

@ -0,0 +1,3 @@
{
"generator-react-webpack": {}
}

View File

@ -0,0 +1,43 @@
'use strict';
let path = require('path');
let defaultSettings = require('./defaults');
// Additional npm or bower modules to include in builds
// Add all foreign plugins you may need into this array
// @example:
// let npmBase = path.join(__dirname, '../node_modules');
// let additionalPaths = [ path.join(npmBase, 'react-bootstrap') ];
let additionalPaths = [];
module.exports = {
additionalPaths: additionalPaths,
port: defaultSettings.port,
debug: true,
devtool: 'eval',
output: {
path: path.join(__dirname, '/../dist/assets'),
filename: 'app.js',
publicPath: defaultSettings.publicPath
},
devServer: {
contentBase: './src/',
historyApiFallback: true,
hot: true,
port: defaultSettings.port,
publicPath: defaultSettings.publicPath,
noInfo: false
},
resolve: {
extensions: ['', '.js', '.jsx'],
alias: {
actions: `${defaultSettings.srcPath}/actions/`,
components: `${defaultSettings.srcPath}/components/`,
sources: `${defaultSettings.srcPath}/sources/`,
stores: `${defaultSettings.srcPath}/stores/`,
styles: `${defaultSettings.srcPath}/styles/`,
config: `${defaultSettings.srcPath}/config/` + process.env.REACT_WEBPACK_ENV,
'react/lib/ReactMount': 'react-dom/lib/ReactMount'
}
},
module: {}
};

View File

@ -0,0 +1,68 @@
/**
* Function that returns default values.
* Used because Object.assign does a shallow instead of a deep copy.
* Using [].push will add to the base array, so a require will alter
* the base array output.
*/
'use strict';
const path = require('path');
const srcPath = path.join(__dirname, '/../src');
const dfltPort = 8000;
/**
* Get the default modules object for webpack
* @return {Object}
*/
function getDefaultModules() {
return {
preLoaders: [
{
test: /\.(js|jsx)$/,
include: srcPath,
loader: 'eslint-loader'
}
],
loaders: [
{
test: /\.css$/,
loader: 'style-loader!css-loader'
},
{
test: /\.sass/,
loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded&indentedSyntax'
},
{
test: /\.scss/,
loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded'
},
{
test: /\.less/,
loader: 'style-loader!css-loader!less-loader'
},
{
test: /\.styl/,
loader: 'style-loader!css-loader!stylus-loader'
},
{
test: /\.(png|jpg|gif)$/,
loader: 'url-loader?limit=8192'
},
{
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'url-loader?limit=10000&mimetype=application/font-woff'
},
{
test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'file-loader'
}
]
};
}
module.exports = {
srcPath: srcPath,
publicPath: '/assets/',
port: dfltPort,
getDefaultModules: getDefaultModules
};

View File

@ -0,0 +1,47 @@
'use strict';
let path = require('path');
let webpack = require('webpack');
let baseConfig = require('./base');
let defaultSettings = require('./defaults');
// Add needed plugins here
let BowerWebpackPlugin = require('bower-webpack-plugin');
let config = Object.assign({}, baseConfig, {
entry: [
'webpack-dev-server/client?http://127.0.0.1:' + defaultSettings.port,
'webpack/hot/only-dev-server',
'./src/index'
],
cache: true,
devtool: 'eval-source-map',
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
new BowerWebpackPlugin({
searchResolveModulesDirectories: false
})
],
module: defaultSettings.getDefaultModules()
});
// Add needed loaders to the defaults here
config.module.loaders.push({
test: /\.(js|jsx)$/,
loader: 'react-hot!babel-loader',
include: [].concat(
config.additionalPaths,
[ path.join(__dirname, '/../src') ]
)
});
// proxy to backend server
config.devServer.proxy = {
'/api': {
target: 'https://localhost:5000',
secure: false
}
};
module.exports = config;

View File

@ -0,0 +1,42 @@
'use strict';
let path = require('path');
let webpack = require('webpack');
let baseConfig = require('./base');
let defaultSettings = require('./defaults');
// Add needed plugins here
let BowerWebpackPlugin = require('bower-webpack-plugin');
let config = Object.assign({}, baseConfig, {
entry: path.join(__dirname, '../src/index'),
cache: false,
devtool: 'sourcemap',
plugins: [
new webpack.optimize.DedupePlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
new BowerWebpackPlugin({
searchResolveModulesDirectories: false
}),
new webpack.optimize.UglifyJsPlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.AggressiveMergingPlugin(),
new webpack.NoErrorsPlugin()
],
module: defaultSettings.getDefaultModules()
});
// Add needed loaders to the defaults here
config.module.loaders.push({
test: /\.(js|jsx)$/,
loader: 'babel',
include: [].concat(
config.additionalPaths,
[ path.join(__dirname, '/../src') ]
)
});
module.exports = config;

View File

@ -0,0 +1,58 @@
'use strict';
let path = require('path');
let srcPath = path.join(__dirname, '/../src/');
let baseConfig = require('./base');
// Add needed plugins here
let BowerWebpackPlugin = require('bower-webpack-plugin');
module.exports = {
devtool: 'eval',
module: {
preLoaders: [
{
test: /\.(js|jsx)$/,
loader: 'isparta-instrumenter-loader',
include: [
path.join(__dirname, '/../src')
]
}
],
loaders: [
{
test: /\.(png|jpg|gif|woff|woff2|css|sass|scss|less|styl)$/,
loader: 'null-loader'
},
{
test: /\.(js|jsx)$/,
loader: 'babel-loader',
include: [].concat(
baseConfig.additionalPaths,
[
path.join(__dirname, '/../src'),
path.join(__dirname, '/../test')
]
)
}
]
},
resolve: {
extensions: [ '', '.js', '.jsx' ],
alias: {
actions: srcPath + 'actions/',
helpers: path.join(__dirname, '/../test/helpers'),
components: srcPath + 'components/',
sources: srcPath + 'sources/',
stores: srcPath + 'stores/',
styles: srcPath + 'styles/',
config: srcPath + 'config/' + process.env.REACT_WEBPACK_ENV
}
},
plugins: [
new BowerWebpackPlugin({
searchResolveModulesDirectories: false
})
]
};

View File

@ -0,0 +1,36 @@
var webpackCfg = require('./webpack.config');
// Set node environment to testing
process.env.NODE_ENV = 'test';
module.exports = function(config) {
config.set({
basePath: '',
browsers: [ 'PhantomJS' ],
files: [
'test/loadtests.js'
],
port: 8000,
captureTimeout: 60000,
frameworks: [ 'mocha', 'chai' ],
client: {
mocha: {}
},
singleRun: true,
reporters: [ 'mocha', 'coverage' ],
preprocessors: {
'test/loadtests.js': [ 'webpack', 'sourcemap' ]
},
webpack: webpackCfg,
webpackServer: {
noInfo: true
},
coverageReporter: {
dir: 'coverage/',
reporters: [
{ type: 'html' },
{ type: 'text' }
]
}
});
};

9166
monkey_island/cc/ui/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,82 @@
{
"private": true,
"version": "0.0.1",
"description": "YOUR DESCRIPTION - Generated by generator-react-webpack",
"main": "",
"scripts": {
"clean": "rimraf dist/*",
"copy": "copyfiles -f ./src/index.html ./src/favicon.ico ./dist",
"dist": "npm run copy & webpack --env=dist",
"lint": "eslint ./src",
"posttest": "npm run lint",
"release:major": "npm version major && npm publish && git push --follow-tags",
"release:minor": "npm version minor && npm publish && git push --follow-tags",
"release:patch": "npm version patch && npm publish && git push --follow-tags",
"serve": "node server.js --env=dev",
"serve:dist": "node server.js --env=dist",
"start": "node server.js --env=dev",
"test": "karma start",
"test:watch": "karma start --autoWatch=true --singleRun=false"
},
"repository": "",
"keywords": [],
"author": "Your name here",
"devDependencies": {
"babel-core": "^6.25.0",
"babel-eslint": "^6.0.0",
"babel-loader": "^6.4.1",
"babel-polyfill": "^6.3.14",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.5.0",
"bower-webpack-plugin": "^0.1.9",
"chai": "^3.2.0",
"copyfiles": "^1.0.0",
"css-loader": "^0.23.1",
"eslint": "^3.0.0",
"eslint-loader": "^1.0.0",
"eslint-plugin-react": "^6.0.0",
"file-loader": "^0.9.0",
"glob": "^7.0.0",
"isparta-instrumenter-loader": "^1.0.0",
"karma": "^1.0.0",
"karma-chai": "^0.1.0",
"karma-coverage": "^1.0.0",
"karma-mocha": "^1.0.0",
"karma-mocha-reporter": "^2.0.0",
"karma-phantomjs-launcher": "^1.0.0",
"karma-sourcemap-loader": "^0.3.5",
"karma-webpack": "^1.7.0",
"minimist": "^1.2.0",
"mocha": "^3.0.0",
"null-loader": "^0.1.1",
"open": "0.0.5",
"phantomjs-prebuilt": "^2.0.0",
"react-addons-test-utils": "^15.0.0",
"react-hot-loader": "^1.2.9",
"rimraf": "^2.4.3",
"style-loader": "^0.13.2",
"url-loader": "^0.5.9",
"webpack": "^1.15.0",
"webpack-dev-server": "^1.12.0"
},
"dependencies": {
"bootstrap": "^3.3.7",
"core-js": "^2.0.0",
"fetch": "^1.1.0",
"normalize.css": "^4.0.0",
"prop-types": "^15.5.10",
"react": "^15.6.1",
"react-bootstrap": "^0.31.2",
"react-copy-to-clipboard": "^5.0.0",
"react-data-grid": "^2.0.58",
"react-data-grid-addons": "^2.0.58",
"react-dom": "^15.6.1",
"react-fa": "^4.2.0",
"react-graph-vis": "^0.1.3",
"react-json-tree": "^0.10.9",
"react-json-view": "^1.12.0",
"react-jsonschema-form": "^0.49.0",
"react-router-dom": "^4.1.2"
}
}

View File

@ -0,0 +1,36 @@
/*eslint no-console:0 */
'use strict';
require('core-js/fn/object/assign');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const config = require('./webpack.config');
const open = require('open');
/**
* Flag indicating whether webpack compiled for the first time.
* @type {boolean}
*/
let isInitialCompilation = true;
const compiler = webpack(config);
new WebpackDevServer(compiler, config.devServer)
.listen(config.port, 'localhost', (err) => {
if (err) {
console.log(err);
}
console.log('Listening at localhost:' + config.port);
});
compiler.plugin('done', () => {
if (isInitialCompilation) {
// Ensures that we log after webpack printed its stats (is there a better way?)
setTimeout(() => {
console.log('\n✓ The bundle is now ready for serving!\n');
console.log(' Open in iframe mode:\t\x1b[33m%s\x1b[0m', 'http://localhost:' + config.port + '/webpack-dev-server/');
console.log(' Open in inline mode:\t\x1b[33m%s\x1b[0m', 'http://localhost:' + config.port + '/\n');
console.log(' \x1b[33mHMR is active\x1b[0m. The bundle will automatically rebuild and live-update on changes.')
}, 350);
}
isInitialCompilation = false;
});

View File

@ -0,0 +1,90 @@
import React from 'react';
import {NavLink, Route, BrowserRouter as Router} from 'react-router-dom';
import {Col, Grid, Row} from 'react-bootstrap';
import {Icon} from 'react-fa';
import RunServerPage from 'components/pages/RunServerPage';
import ConfigurePage from 'components/pages/ConfigurePage';
import RunMonkeyPage from 'components/pages/RunMonkeyPage';
import MapPage from 'components/pages/MapPage';
import FullLogsPage from 'components/pages/FullLogsPage';
require('normalize.css/normalize.css');
require('styles/App.css');
let logoImage = require('../images/monkey-logo.png');
class AppComponent extends React.Component {
render() {
return (
<Router>
<Grid fluid={true}>
<Row>
<Col sm={3} md={2} className="sidebar">
<div className="header">
<img src={logoImage} alt="Infection Monkey"/>
by GuardiCore
</div>
<ul className="navigation">
<li>
<NavLink to="/" exact={true}>
<span className="number">1.</span>
Run Server
<Icon name="check" className="pull-right checkmark text-success"/>
</NavLink>
</li>
<li>
<NavLink to="/configure">
<span className="number">2.</span>
Configure
</NavLink>
</li>
<li>
<NavLink to="/run-monkey">
<span className="number">3.</span>
Run Monkey
</NavLink>
</li>
<li>
<a className="disabled">
<span className="number">4.</span>
Infection
</a>
<ul>
<li><NavLink to="/infection/map">Map</NavLink></li>
<li><NavLink to="/infection/logs">Full Logs</NavLink></li>
</ul>
</li>
<li>
<NavLink to="/report">
<span className="number">5.</span>
Pen. Test Report
</NavLink>
</li>
</ul>
<hr/>
<ul>
<li><a>Clear DB</a></li>
<li><a>Kill All Monkeys</a></li>
</ul>
</Col>
<Col sm={9} md={10} smOffset={3} mdOffset={2} className="main">
<Route exact path="/" component={RunServerPage}/>
<Route path="/configure" component={ConfigurePage}/>
<Route path="/run-monkey" component={RunMonkeyPage}/>
<Route path="/infection/map" component={MapPage}/>
<Route path="/infection/logs" component={FullLogsPage}/>
{/*<Route path="/report" component={ReportPage}/>*/}
</Col>
</Row>
</Grid>
</Router>
);
}
}
AppComponent.defaultProps = {};
export default AppComponent;

View File

@ -0,0 +1,62 @@
import React from 'react';
import Form from 'react-jsonschema-form';
import {Col} from 'react-bootstrap';
class ConfigurePageComponent extends React.Component {
constructor(props) {
super(props);
// set schema from server
this.state = {
schema: {},
configuration: {},
saved: false
};
}
componentDidMount() {
fetch('/api/configuration')
.then(res => res.json())
.then(res => this.setState({
schema: res.schema,
configuration: res.configuration
}));
}
onSubmit = ({formData}) => {
fetch('/api/configuration',
{
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(formData)
})
.then(res => res.json())
.then(res => {
this.setState({
saved: true,
schema: res.schema,
configuration: res.configuration
});
});
};
render() {
return (
<Col xs={8}>
<h1 className="page-title">Monkey Configuration</h1>
<div className="alert alert-info">
<i className="glyphicon glyphicon-info-sign" style={{'marginRight': '5px'}}/>
This configuration will only apply on new infections.
</div>
<Form schema={this.state.schema}
formData={this.state.configuration}
onSubmit={this.onSubmit}/>
{ this.state.saved ?
<p>Configuration saved successfully.</p>
: ''}
</Col>
);
}
}
export default ConfigurePageComponent;

View File

@ -0,0 +1,147 @@
import React from 'react';
import {Col} from 'react-bootstrap';
import ReactJson from 'react-json-view'
import JSONTree from 'react-json-tree'
import ReactDataGrid, {Row} from 'react-data-grid';
import {Icon} from "react-fa";
const { Toolbar, Data: { Selectors } } = require('react-data-grid-addons');
// Custom Formatter component
const JsonCellFormatter = React.createClass({
render() {
return (
<ReactJson src={this.props.value} collapsed={true} />
);
}
});
const RowRenderer = React.createClass({
render() {
return (
<Row ref={ node => this.row = node } {...this.props}/>
);
}
// height: '50px',
//
// onClick() {
// this.height = '200px';
// },
//
// render() {
// return (
// <div style={{height: this.height}} onClick={this.onClick()}>
// <Icon name="expand" className="pull-right"/>
// <Row style={{minHeight: '100px'}} ref={ node => this.row = node } {...this.props}/>
// </div>
// );
// }
});
class FullLogsPageComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
...this.getInitialState()
};
}
getInitialState() {
this._columns = [
{
key: 'telem_type',
name: 'Type',
width: 200,
filterable: true,
sortable: true
},
{
key: 'monkey_guid',
name: 'Monkey ID',
filterable: true,
sortable: true
},
{
key: 'timestamp',
name: 'Time',
filterable: true,
sortable: true
},
{
key: 'data',
name: 'More Info',
formatter: JsonCellFormatter
}
];
return { rows: [], filters: {}, sortColumn: null, sortDirection: null };
}
getRandomDate(start, end) {
return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())).toLocaleDateString();
}
getRows() {
return Selectors.getRows(this.state);
}
getSize() {
return this.getRows().length;
}
rowGetter = (rowIdx) => {
const rows = this.getRows();
return rows[rowIdx];
};
handleGridSort = (sortColumn, sortDirection) => {
this.setState({ sortColumn: sortColumn, sortDirection: sortDirection });
};
handleFilterChange = (filter) => {
let newFilters = Object.assign({}, this.state.filters);
if (filter.filterTerm) {
newFilters[filter.column.key] = filter;
} else {
delete newFilters[filter.column.key];
}
this.setState({ filters: newFilters });
};
onClearFilters = () => {
this.setState({ filters: {} });
};
componentDidMount = () => {
this.dataGrid.setState({canFilter: true}, () => this.hideFilterButton());
fetch('/api/telemetry')
.then(res => res.json())
.then(res => this.setState({rows: res.objects}));
};
hideFilterButton = () => document.getElementsByClassName('react-grid-Toolbar')[0].style.display = 'none';
render() {
return (
<Col xs={12}>
<h1 className="page-title">Full Logs</h1>
<div>
<ReactDataGrid
ref={(grid) => { this.dataGrid = grid; }}
rowRenderer={RowRenderer}
rowHeight={50}
minHeight={500}
columns={this._columns}
toolbar={<Toolbar enableFilter={true}/>}
rowGetter={this.rowGetter}
rowsCount={this.getSize()}
onGridSort={this.handleGridSort}
onAddFilter={this.handleFilterChange}
onClearFilters={this.onClearFilters} />
</div>
</Col>
);
}
}
export default FullLogsPageComponent;

View File

@ -0,0 +1,125 @@
import React from 'react';
import {Col} from 'react-bootstrap';
import Graph from 'react-graph-vis';
import {Icon} from 'react-fa'
let options = {
layout: {
improvedLayout: false
},
groups: {
manuallyInfected: {
shape: 'icon',
icon: {
face: 'FontAwesome',
code: '\uf120',
size: 50,
color: '#8f5a0b'
}
},
infected: {
shape: 'icon',
icon: {
face: 'FontAwesome',
code: '\uf06d',
size: 50,
color: '#d30d09'
}
},
clean: {
shape: 'icon',
icon: {
face: 'FontAwesome',
code: '\uf108',
size: 50,
color: '#999'
}
}
}
};
class MapPageComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
graph: {nodes: [], edges: []}
};
}
events = {
select: event => this.selectionChanged(event)
};
componentDidMount() {
fetch('/api/netmap')
.then(res => res.json())
.then(res => this.setState({graph: res}));
}
selectionChanged(event) {
if (event.nodes.length === 1) {
console.log('selected node:', event.nodes[0]);
}
else if (event.edges.length === 1) {
console.log('selected edge:', event.edges[0]);
}
else {
console.log('no preview.');
}
}
render() {
return (
<div>
<Col xs={12}>
<h1 className="page-title">Infection Map</h1>
</Col>
<Col xs={8}>
<div className="pull-left">
<input placeholder="Search" />
</div>
<Graph graph={this.state.graph} options={options} events={this.events}/>
</Col>
<Col xs={4}>
<div className="panel panel-default preview">
<div className="panel-heading">
<h3>
<Icon name="fire"/>
vm4
<small>Infected Asset</small>
</h3>
</div>
<div className="panel-body">
<h4>Machine Info</h4>
<p>...</p>
<h4 style={{'marginTop': '2em'}}>Exploit Method</h4>
<p>...</p>
<h4 style={{'marginTop': '2em'}}>Timeline</h4>
<ul className="timeline">
<li>
<div className="bullet"></div>
failed attempt1
</li>
<li>
<div className="bullet"></div>
failed attempt2
</li>
<li>
<div className="bullet bad"></div>
Infection!
</li>
</ul>
</div>
</div>
</Col>
</div>
);
}
}
export default MapPageComponent;

View File

@ -0,0 +1,71 @@
import React from 'react';
import {Button, Col, Well} from 'react-bootstrap';
import CopyToClipboard from 'react-copy-to-clipboard';
import {Icon} from 'react-fa';
class RunMonkeyPageComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
ip: '0.0.0.0',
cmd: '-',
isRunning: true
};
}
componentDidMount() {
fetch('/api')
.then(res => res.json())
.then(res => this.setState({
ip: res.ip,
cmd: this.generateCmd(res.ip)
}));
fetch('/api/local-monkey')
.then(res => res.json())
.then(res => this.setState({
isRunning: res['is_running']
}));
}
generateCmd(ip) {
return `curl http://${ip}:5000/get-monkey | sh`;
}
runLocalMonkey() {
fetch('/api/local-monkey/run', {method: 'POST'})
.then(res => res.json())
.then(res => {
this.setState({
isRunning: res['is_running']
});
});
}
render() {
return (
<Col xs={8}>
<h1 className="page-title">Run the Monkey</h1>
<p>Run this snippet on a host for manually infecting it with a Monkey:</p>
<Well>
<CopyToClipboard text={this.state.cmd} className="pull-right">
<Button style={{margin: '-0.5em'}} title="Copy to Clipboard">
<Icon name="clipboard"/>
</Button>
</CopyToClipboard>
<code>{this.state.cmd}</code>
</Well>
<p>
Or simply click here to <a onClick={this.runLocalMonkey()}
className="btn btn-default btn-sm"
style={{'marginLeft': '0.2em'}}>Run on <b>{this.state.ip}</b></a>
{ this.state.isRunning ?
<i className="text-success" style={{'marginLeft': '5px'}}>Running...</i>
: ''}
</p>
</Col>
);
}
}
export default RunMonkeyPageComponent;

View File

@ -0,0 +1,32 @@
import React from 'react';
import {Col} from 'react-bootstrap';
import {Link} from 'react-router-dom';
class RunServerPageComponent extends React.Component {
constructor(props) {
super(props);
this.state = {ip: '0.0.0.0'};
}
componentDidMount() {
fetch('/api')
.then(res => res.json())
.then(res => this.setState({ip: res.ips.join(', ')}));
}
render() {
return (
<Col xs={8}>
<h1 className="page-title">Monkey Island C&C Server</h1>
<div style={{'fontSize': '1.5em'}}>
<p>Your Monkey Island server is up and running on <b>{this.state.ip}</b> &#x1F44F; &#x1F44F;</p>
<p>
Now <Link to="/configure">configure the monkey</Link> (or just stick with the default configuration) and <Link to="/run-monkey">run the monkey</Link>.
</p>
</div>
</Col>
);
}
}
export default RunServerPageComponent;

View File

@ -0,0 +1,35 @@
# About this folder
This folder holds configuration files for different environments.
You can use it to provide your app with different settings based on the
current environment, e.g. to configure different API base urls depending on
whether your setup runs in dev mode or is built for distribution.
You can include the configuration into your code like this:
**ES2015 Modules**
```js
import config from 'config';
```
**Common JS**
Due to Babel6 we need to append `.default`.
```js
let config = require('config').default;
```
**Example**
```javascript
import React from 'react';
import config from 'config';
class MyComponent extends React.Component {
constructor(props, ctx) {
super(props, ctx);
let currentAppEnv = config.appEnv;
}
}
```

View File

@ -0,0 +1,5 @@
'use strict';
// Settings configured here will be merged into the final config object.
export default {
}

View File

@ -0,0 +1,9 @@
'use strict';
import baseConfig from './base';
let config = {
appEnv: 'dev' // feel free to remove the appEnv property here
};
export default Object.freeze(Object.assign({}, baseConfig, config));

View File

@ -0,0 +1,9 @@
'use strict';
import baseConfig from './base';
let config = {
appEnv: 'dist' // feel free to remove the appEnv property here
};
export default Object.freeze(Object.assign({}, baseConfig, config));

View File

@ -0,0 +1,9 @@
'use strict';
import baseConfig from './base';
let config = {
appEnv: 'test' // don't remove the appEnv property here
};
export default Object.freeze(Object.assign(baseConfig, config));

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View File

@ -0,0 +1,16 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Monkey Island C&C Server</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
</head>
<body>
<div id="app">Loading...</div>
<script>__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__</script>
<script type="text/javascript" src="/assets/app.js"></script>
</body>
</html>

View File

@ -0,0 +1,8 @@
import 'core-js/fn/object/assign';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/Main';
import Bootstrap from 'bootstrap/dist/css/bootstrap.css'; // eslint-disable-line no-unused-vars
// Render the main component into the dom
ReactDOM.render(<App />, document.getElementById('app'));

View File

@ -0,0 +1,209 @@
@import url('https://fonts.googleapis.com/css?family=Open+Sans');
@import url('https://fonts.googleapis.com/css?family=Alegreya');
/* Base Application Styles */
body {
color: #fff;
background: #222;
}
#app {
font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
/*
* Sidebar
*/
@media (min-width: 768px) {
.sidebar {
position: fixed !important;
top: 0;
bottom: 0;
left: 0;
z-index: 1000;
display: block;
padding: 1em;
overflow-x: hidden;
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
background-color: #f5f5f5;
border-right: 1px solid #e8e8e8;
}
.header {
text-align: center;
font-weight: bold;
font-size: 1.2em;
}
.header img {
width: 100%;
}
.navigation {
margin-top: 1em;
}
ul {
list-style: none;
padding-left: 0;
}
li {
overflow: auto;
}
li .number {
color: #666;
display: inline-block;
width: 1.1em;
}
li a {
/*color: #d30d09;*/
color: #666;
display: block;
padding: 0.5em 1em;
margin: 0.1em 0;
}
li a:hover {
color: #000;
background: #e9e9e9;
text-decoration: none;
}
li a.active {
background: #fff;
text-decoration: none;
color: #d30d09;
}
li a.active:hover {
color: #d30d09;
}
li a.disabled {
color: #666;
cursor: auto;
pointer-events: none;
}
li ul {
margin-left: 1.5em;
}
li .checkmark {
font-size: 1.3em;
}
}
/*
* Main content
*/
.main {
padding: 2em 1em;
}
.page-title {
margin-top: 0;
margin-bottom: 1em;
padding-bottom: 0.5em;
border-bottom: 2px dotted #d30d09;
font-size: 2.5em;
color: #d30d09;
font-family: 'Alegreya', serif;
}
@media (min-width: 768px) {
.main {
padding-right: 40px;
padding-left: 40px;
}
}
.main .page-header {
margin-top: 0;
}
.index img {
margin: 40px auto;
border-radius: 4px;
background: #fff;
display: block;
}
.index .notice {
margin: 1em auto;
padding: 15px 0;
text-align: center;
border: 1px solid #000;
border-width: 1px 0;
background: #666;
}
/*
* Map Preview Pane
*/
.preview.well {
padding: 1em;
}
.preview h3 {
margin: 0;
}
.preview h3 small {
margin-top: 0.5em;
display: block;
}
.preview h3 .fa {
margin-right: 5px;
}
.preview h4 {
text-transform: uppercase;
color: #999;
font-size: 1em;
margin-top: 0;
}
.preview p, .preview .timeline {
margin-left: 1em;
}
.timeline {
position: relative;
padding: 0.5em 0;
}
.timeline:before {
content: '';
position: absolute;
top: 0;
left: 5px;
width: 2px;
height: 100%;
background: #ccc;
z-index: 1
}
.timeline li {
margin-left: 1.5em;
position: relative;
overflow: visible;
margin-bottom: 1em;
}
.timeline .bullet {
width: 16px;
background: #ccc;
position: absolute;
right: 100%;
top: 0;
bottom: 0;
margin: 2px 0.5em;
z-index: 2;
border-radius: 10px;
}
.timeline .bullet.bad {
background: #d30d09;
}

View File

@ -0,0 +1,32 @@
'use strict';
const path = require('path');
const args = require('minimist')(process.argv.slice(2));
// List of allowed environments
const allowedEnvs = ['dev', 'dist', 'test'];
// Set the correct environment
let env;
if (args._.length > 0 && args._.indexOf('start') !== -1) {
env = 'test';
} else if (args.env) {
env = args.env;
} else {
env = 'dev';
}
process.env.REACT_WEBPACK_ENV = env;
/**
* Build the webpack configuration
* @param {String} wantedEnv The wanted environment
* @return {Object} Webpack config
*/
function buildConfig(wantedEnv) {
let isValid = wantedEnv && wantedEnv.length > 0 && allowedEnvs.indexOf(wantedEnv) !== -1;
let validEnv = isValid ? wantedEnv : 'dev';
let config = require(path.join(__dirname, 'cfg/' + validEnv));
return config;
}
module.exports = buildConfig(env);