forked from p15670423/monkey
Moved attack matrix to attack report page
This commit is contained in:
parent
222644a3fb
commit
cc1f46af32
|
@ -1,7 +1,8 @@
|
|||
import flask_restful
|
||||
from flask import jsonify
|
||||
from monkey_island.cc.auth import jwt_required
|
||||
from monkey_island.cc.services.attack.attack_report import AttackReportService
|
||||
from monkey_island.cc.services.attack.attack_schema import SCHEMA
|
||||
from flask import json, current_app
|
||||
|
||||
__author__ = "VakarisZ"
|
||||
|
||||
|
@ -10,4 +11,9 @@ class AttackReport(flask_restful.Resource):
|
|||
|
||||
@jwt_required()
|
||||
def get(self):
|
||||
return jsonify(AttackReportService.get_latest_report()['techniques'])
|
||||
response_content = {'techniques': AttackReportService.get_latest_report()['techniques'], 'schema': SCHEMA}
|
||||
return current_app.response_class(json.dumps(response_content,
|
||||
indent=None,
|
||||
separators=(",", ":"),
|
||||
sort_keys=False) + "\n",
|
||||
mimetype=current_app.config['JSONIFY_MIMETYPE'])
|
||||
|
|
|
@ -173,3 +173,15 @@ class AttackConfig(object):
|
|||
for key, technique in list(attack_type['properties'].items()):
|
||||
techniques[key] = technique['value']
|
||||
return techniques
|
||||
|
||||
@staticmethod
|
||||
def get_techniques_for_report():
|
||||
"""
|
||||
:return: Format: {"T1110": {"selected": True, "type": "Credential Access", "T1075": ...}
|
||||
"""
|
||||
attack_config = AttackConfig.get_config()
|
||||
techniques = {}
|
||||
for type_name, attack_type in list(attack_config.items()):
|
||||
for key, technique in list(attack_type['properties'].items()):
|
||||
techniques[key] = {'selected': technique['value'], 'type': SCHEMA['properties'][type_name]['title']}
|
||||
return techniques
|
||||
|
|
|
@ -58,10 +58,11 @@ class AttackReportService:
|
|||
'name': REPORT_NAME
|
||||
}
|
||||
|
||||
for tech_id, value in list(AttackConfig.get_technique_values().items()):
|
||||
if value:
|
||||
for tech_id, tech_info in list(AttackConfig.get_techniques_for_report().items()):
|
||||
try:
|
||||
report['techniques'].update({tech_id: TECHNIQUES[tech_id].get_report_data()})
|
||||
technique_report_data = TECHNIQUES[tech_id].get_report_data()
|
||||
technique_report_data.update(tech_info)
|
||||
report['techniques'].update({tech_id: technique_report_data})
|
||||
except KeyError as e:
|
||||
LOG.error("Attack technique does not have it's report component added "
|
||||
"to attack report service. %s" % e)
|
||||
|
|
|
@ -6,6 +6,7 @@ import '../../styles/Collapse.scss';
|
|||
import AuthComponent from '../AuthComponent';
|
||||
import {ScanStatus} from '../attack/techniques/Helpers';
|
||||
import Collapse from '@kunukn/react-collapse';
|
||||
import Matrix from './attack/Matrix';
|
||||
|
||||
import T1210 from '../attack/techniques/T1210';
|
||||
import T1197 from '../attack/techniques/T1197';
|
||||
|
@ -66,7 +67,8 @@ class AttackReportPageComponent extends AuthComponent {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
report: this.props.report,
|
||||
techniques: this.props.report['techniques'],
|
||||
schema: this.props.report['schema'],
|
||||
collapseOpen: ''
|
||||
};
|
||||
}
|
||||
|
@ -81,7 +83,7 @@ class AttackReportPageComponent extends AuthComponent {
|
|||
this.setState(state => ({collapseOpen: state.collapseOpen === technique ? null : technique}));
|
||||
|
||||
getComponentClass(tech_id) {
|
||||
switch (this.state.report[tech_id].status) {
|
||||
switch (this.state.techniques[tech_id].status) {
|
||||
case ScanStatus.SCANNED:
|
||||
return 'collapse-info';
|
||||
case ScanStatus.USED:
|
||||
|
@ -95,7 +97,7 @@ class AttackReportPageComponent extends AuthComponent {
|
|||
return (
|
||||
<div key={tech_id} className={classNames('collapse-item', {'item--active': this.state.collapseOpen === tech_id})}>
|
||||
<button className={classNames('btn-collapse', this.getComponentClass(tech_id))} onClick={() => this.onToggle(tech_id)}>
|
||||
<span>{this.state.report[tech_id].title}</span>
|
||||
<span>{this.state.techniques[tech_id].title}</span>
|
||||
<span>
|
||||
<i className={classNames('fa', this.state.collapseOpen === tech_id ? 'fa-chevron-down' : 'fa-chevron-up')}></i>
|
||||
</span>
|
||||
|
@ -118,22 +120,26 @@ class AttackReportPageComponent extends AuthComponent {
|
|||
const TechniqueComponent = tech_components[technique];
|
||||
return (
|
||||
<div className={`content ${collapseState}`}>
|
||||
<TechniqueComponent data={this.state.report[technique]} reportData={this.props.reportData}/>
|
||||
<TechniqueComponent data={this.state.techniques[technique]}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderLegend() {
|
||||
return (<div id="header" className="row justify-content-between attack-legend">
|
||||
<Col xs={4}>
|
||||
<Col xs={3}>
|
||||
<i className="fa fa-circle-thin icon-unchecked"></i>
|
||||
<span> - Dissabled</span>
|
||||
</Col>
|
||||
<Col xs={3}>
|
||||
<i className="fa fa-circle icon-default"></i>
|
||||
<span> - Unscanned</span>
|
||||
</Col>
|
||||
<Col xs={4}>
|
||||
<Col xs={3}>
|
||||
<i className="fa fa-circle icon-info"></i>
|
||||
<span> - Scanned</span>
|
||||
</Col>
|
||||
<Col xs={4}>
|
||||
<Col xs={3}>
|
||||
<i className="fa fa-circle icon-danger"></i>
|
||||
<span> - Used</span>
|
||||
</Col>
|
||||
|
@ -142,7 +148,7 @@ class AttackReportPageComponent extends AuthComponent {
|
|||
|
||||
generateReportContent() {
|
||||
let content = [];
|
||||
Object.keys(this.state.report).forEach((tech_id) => {
|
||||
Object.keys(this.state.techniques).forEach((tech_id) => {
|
||||
content.push(this.getTechniqueCollapse(tech_id))
|
||||
});
|
||||
return (
|
||||
|
@ -153,8 +159,9 @@ class AttackReportPageComponent extends AuthComponent {
|
|||
<p>
|
||||
This report shows information about ATT&CK techniques used by Infection Monkey.
|
||||
</p>
|
||||
<div>
|
||||
{this.renderLegend()}
|
||||
<Matrix techniques={this.state.techniques} schema={this.state.schema}/>
|
||||
<div>
|
||||
<section className="attack-report">{content}</section>
|
||||
</div>
|
||||
<br/>
|
||||
|
@ -163,7 +170,7 @@ class AttackReportPageComponent extends AuthComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
if (Object.keys(this.state.report).length === 0 && this.state.runStarted) {
|
||||
if (Object.keys(this.state.techniques).length === 0 && this.state.runStarted) {
|
||||
return (<h1>No techniques were scanned</h1>);
|
||||
} else {
|
||||
return (<div> {this.generateReportContent()}</div>);
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
import React from 'react';
|
||||
import Checkbox from '../../ui-components/Checkbox';
|
||||
import ReactTable from 'react-table';
|
||||
import 'filepond/dist/filepond.min.css';
|
||||
|
||||
class MatrixComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {techniques: this.props.techniques,
|
||||
schema: this.props.schema}
|
||||
}
|
||||
|
||||
getColumns() {
|
||||
let columns = [];
|
||||
for(let type_key in this.state.schema.properties){
|
||||
if (! this.state.schema.properties.hasOwnProperty(type_key)){
|
||||
continue;
|
||||
}
|
||||
let tech_type = this.state.schema.properties[type_key];
|
||||
columns.push({
|
||||
Header: tech_type.title,
|
||||
id: type_key,
|
||||
accessor: x => MatrixComponent.renderTechnique(x[tech_type.title]),
|
||||
style: {'whiteSpace': 'unset'}
|
||||
});
|
||||
}
|
||||
return columns;
|
||||
}
|
||||
|
||||
getTableRows() {
|
||||
let rows = [];
|
||||
for (let tech_id in this.state.techniques) {
|
||||
if (this.state.techniques.hasOwnProperty(tech_id)){
|
||||
let technique_added = false;
|
||||
let technique = this.state.techniques[tech_id];
|
||||
for(let row of rows){
|
||||
if (! row.hasOwnProperty(technique.type)){
|
||||
row[technique.type] = technique;
|
||||
technique_added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! technique_added){
|
||||
let newRow = {};
|
||||
newRow[technique.type] = technique;
|
||||
rows.push(newRow)
|
||||
}
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
static renderTechnique(technique) {
|
||||
if (technique == null || typeof technique === undefined) {
|
||||
return (<div/>)
|
||||
} else {
|
||||
return (
|
||||
<Checkbox checked={technique.selected}
|
||||
necessary={true}
|
||||
name={technique.title}
|
||||
changeHandler={function(){}}
|
||||
status={technique.status}>
|
||||
{technique.title}
|
||||
</Checkbox>)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let tableRows = this.getTableRows();
|
||||
return (
|
||||
<div>
|
||||
<div className={'attack-matrix'}>
|
||||
<ReactTable columns={this.getColumns()}
|
||||
data={tableRows}
|
||||
showPagination={false}
|
||||
defaultPageSize={tableRows.length}/>
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
|
||||
export default MatrixComponent;
|
|
@ -14,10 +14,18 @@ class CheckboxComponent extends React.PureComponent {
|
|||
changeHandler(name, checked) function will be called with these parameters:
|
||||
this.props.name (the name of this component) and
|
||||
this.state.checked (boolean indicating if this component is checked or not)
|
||||
this.props.status (int) adds a class "status-x" to this checkbox. Used for styling.
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
if (this.props.hasOwnProperty("status")){
|
||||
this.status = this.props.status;
|
||||
} else {
|
||||
this.status = false
|
||||
}
|
||||
console.log(this.props);
|
||||
this.state = {
|
||||
status: this.status,
|
||||
checked: this.props.checked,
|
||||
necessary: this.props.necessary,
|
||||
isAnimating: false
|
||||
|
@ -48,6 +56,9 @@ class CheckboxComponent extends React.PureComponent {
|
|||
// Creates class string for component
|
||||
composeStateClasses(core) {
|
||||
let result = core;
|
||||
if (this.state.status) {
|
||||
result += ' status-'+this.state.status;
|
||||
}
|
||||
if (this.state.necessary) {
|
||||
return result + ' blocked'
|
||||
}
|
||||
|
@ -56,7 +67,6 @@ class CheckboxComponent extends React.PureComponent {
|
|||
} else {
|
||||
result += ' is-unchecked'
|
||||
}
|
||||
|
||||
if (this.state.isAnimating) {
|
||||
result += ' do-ping';
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue