ATT&CK matrix formed in front-end

This commit is contained in:
VakarisZ 2019-03-19 15:48:58 +02:00
parent d2670be767
commit 17a51cd92e
6 changed files with 277 additions and 8 deletions

View File

@ -8,6 +8,5 @@ from cc.services.attck.attck import AttckService
class AttckConfiguration(flask_restful.Resource):
@jwt_required()
def get(self):
return jsonify(schema=AttckService.get_config_schema(),
configuration=AttckService.get_config())
return jsonify(configuration=AttckService.get_config()['properties'])

View File

@ -13,6 +13,13 @@ SCHEMA = {
"description": "Exploitation of a software vulnerability occurs when an adversary "
"takes advantage of a programming error in a program, service, or within the "
"operating system software or kernel itself to execute adversary-controlled code."
},
"T1075": {
"title": "T1075 Pass the hash",
"type": "bool",
"default": True,
"description": "Pass the hash (PtH) is a method of authenticating as a user without "
"having access to the user's cleartext password."
}
}
},

View File

@ -0,0 +1,79 @@
import React from 'react';
import Form from 'react-jsonschema-form';
import {Col, Nav, NavItem} from 'react-bootstrap';
import CheckBox from '../ui-components/checkBox'
import AuthComponent from '../AuthComponent';
import 'filepond/dist/filepond.min.css';
import ReactTable from "react-table";
let renderTechnique = function (technique) {
console.log(technique);
if (technique == null){
return (<div></div>)
} else {
return (<div>{technique.title}</div>)
}
};
// Finds which attack type has most techniques and returns that number
let findMaxTechniques = function (data){
let maxLen = 0;
data.forEach(function(techType) {
if (Object.keys(techType.properties).length > maxLen){
maxLen = Object.keys(techType.properties).length
}
});
return maxLen
};
let parseTechniques = function (data, maxLen) {
let techniques = [];
// Create rows with attack techniques
for (let i = 0; i < maxLen; i++) {
let row = {};
data.forEach(function(techType){
let rowColumn = {};
rowColumn.techName = techType.title;
if (i <= Object.keys(techType.properties).length) {
rowColumn.technique = Object.values(techType.properties)[i];
} else {
rowColumn.technique = null
}
row[rowColumn.techName] = rowColumn
});
techniques.push(row)
}
return techniques;
};
class MatrixComponent extends AuthComponent {
constructor(props) {
super(props);
this.maxTechniques = findMaxTechniques(Object.values(this.props.configuration));
this.data = parseTechniques(Object.values(this.props.configuration), this.maxTechniques);
}
getColumns() {
return Object.keys(this.data[0]).map((key)=>{
return {
Header: key,
id: key,
accessor: x => renderTechnique(x[key].technique)
};
});
}
render() {
console.log(this.data);
let columns = this.getColumns();
return (<ReactTable
columns={columns}
data={this.data}
showPagination={false}
defaultPageSize={this.maxTechniques}
/>);
}
}
export default MatrixComponent;

View File

@ -1,8 +1,7 @@
import React from 'react';
import Form from 'react-jsonschema-form';
import {Col, Nav, NavItem} from 'react-bootstrap';
import AuthComponent from '../AuthComponent';
import 'filepond/dist/filepond.min.css';
import MatrixComponent from '../attck/MatrixComponent'
class AttckComponent extends AuthComponent {
constructor(props) {
@ -12,7 +11,6 @@ class AttckComponent extends AuthComponent {
this.sectionsOrder = ['ATT&CK matrix'];
// set schema from server
this.state = {
schema: {},
configuration: {},
lastAction: 'none',
sections: [],
@ -29,16 +27,21 @@ class AttckComponent extends AuthComponent {
sections.push({key: sectionKey, title: res.configuration.title});
}
this.setState({
schema: res.schema,
configuration: res.configuration,
sections: sections,
selectedSection: 'ATT&CK matrix'
})
});
});
}
render() {
return (<Col xs={12} lg={8}> Vakaris </Col>);
let content;
if (Object.keys(this.state.configuration).length === 0) {
content = (<h1>Fetching configuration...</h1>);
} else {
content = (<MatrixComponent configuration={this.state.configuration} />);
}
return <div>{content}</div>;
}
}

View File

@ -0,0 +1,78 @@
import '../../styles/CheckBox.scss'
import React from 'react';
import MatrixComponent from "../attck/MatrixComponent";
class Checkbox extends React.PureComponent {
constructor() {
super();
this.state = {
checked: false,
isAnimating: false,
};
this.toggleChecked = this.toggleChecked.bind(this);
this.ping = this.ping.bind(this);
this.composeStateClasses = this.composeStateClasses.bind(this);
}
//
toggleChecked() {
if (this.state.isAnimating) return false;
this.setState({
checked: !this.state.checked,
isAnimating: true,
});
}
//
ping() {
this.setState({ isAnimating: false })
}
//
composeStateClasses(core) {
let result = core;
if (this.state.checked) { result += ' is-checked'; }
else { result += ' is-unchecked' }
if (this.state.isAnimating) { result += ' do-ping'; }
return result;
}
//
render() {
const cl = this.composeStateClasses('ui-checkbox-btn');
return (
<div
className={ cl }
onClick={ this.toggleChecked }>
<input className="ui ui-checkbox" type="checkbox" checked={this.state.checked} />
{
this.state.checked &&
<i className="icon">
<svg width="24" height="24" viewBox="0 0 24 24">
<path d="M21 5q0.43 0 0.715 0.285t0.285 0.715q0 0.422-0.289 0.711l-12 12q-0.289 0.289-0.711 0.289t-0.711-0.289l-6-6q-0.289-0.289-0.289-0.711 0-0.43 0.285-0.715t0.715-0.285q0.422 0 0.711 0.289l5.289 5.297 11.289-11.297q0.289-0.289 0.711-0.289z"></path>
</svg>
</i>
}
{
!this.state.checked &&
<i className="icon">
<svg width="24" height="24" viewBox="0 0 24 24">
<path d="M19 4q0.43 0 0.715 0.285t0.285 0.715q0 0.422-0.289 0.711l-6.297 6.289 6.297 6.289q0.289 0.289 0.289 0.711 0 0.43-0.285 0.715t-0.715 0.285q-0.422 0-0.711-0.289l-6.289-6.297-6.289 6.297q-0.289 0.289-0.711 0.289-0.43 0-0.715-0.285t-0.285-0.715q0-0.422 0.289-0.711l6.297-6.289-6.297-6.289q-0.289-0.289-0.289-0.711 0-0.43 0.285-0.715t0.715-0.285q0.422 0 0.711 0.289l6.289 6.297 6.289-6.297q0.289-0.289 0.711-0.289z"></path>
</svg>
</i>
}
<label className="text">{ this.props.children }</label>
<div className="ui-btn-ping" onTransitionEnd={this.ping}></div>
</div>
)
}
}
export default Checkbox;

View File

@ -0,0 +1,103 @@
// readable
$desired-line-height: 24px;
$desired-height: 36px;
$text-offset: 2px;
// usable
$dlh: $desired-line-height;
$dh: $desired-height;
$to: $text-offset;
// coooolors
$light-grey: #EAF4F4;
$medium-grey: #7B9EA8;
$dark-grey: #7B9EA8;
$green: #44CF6C;
.ui-checkbox-btn {
position: relative;
display: inline-block;
padding: (($dh - $dlh) / 2) ($dlh / 2);
border-radius: $dh / 2; // overcompensate
background-color: rgba(red, .6);
input { display: none; } // turn off, but not forgotten
.icon,
.text {
display: inline-block;
vertical-align: top;
color: inherit;
}
.text {
font-size: 14px;
line-height: $dlh - $to;
padding-top: $to;
padding-left: 4px;
}
// color states
&.is-unchecked {
border: 1px solid $medium-grey;
background-color: transparent;
color: $dark-grey;
fill: $dark-grey;
}
&.is-checked {
border: 1px solid $green;
background-color: $green;
color: white;
fill: white;
}
}
.icon {
position: relative;
display: inline-block;
width: $dlh - 4;
height: $dlh;
svg {
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
margin: auto;
width: 16px;
height: auto;
fill: inherit;
}
.is-checked & {
color: white;
fill: white;
}
}
// ping animation magic
.ui-btn-ping {
position: absolute;
top: 50%;
left: 50%;
width: 50%;
transform: translate3d(-50%, -50%, 0); // center center by default
// set the square
&:before {
content: '';
transform: scale(0, 0); // center center by default
transition-property: background-color transform;
transition-timing-function: cubic-bezier(0.0, 0.0, 0.2, 1);
display: block;
padding-bottom: 100%;
border-radius: 50%;
background-color: rgba(white, .84);;
}
.do-ping &:before {
transform: scale(2.5, 2.5);
transition-duration: .35s;
background-color: rgba(white, .08);
}
}