forked from p15670423/monkey
A lot of small UI changes, trying to make the report look more polished.
This commit is contained in:
parent
6cd5cff818
commit
07eb9ec32f
monkey/monkey_island/cc/ui/src/components
|
@ -8,7 +8,6 @@ import SinglePillarDirectivesStatus from "../report-components/zerotrust/SingleP
|
||||||
import MonkeysStillAliveWarning from "../report-components/common/MonkeysStillAliveWarning";
|
import MonkeysStillAliveWarning from "../report-components/common/MonkeysStillAliveWarning";
|
||||||
import ReportLoader from "../report-components/common/ReportLoader";
|
import ReportLoader from "../report-components/common/ReportLoader";
|
||||||
import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning";
|
import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning";
|
||||||
import SecurityIssuesGlance from "../report-components/common/SecurityIssuesGlance";
|
|
||||||
import StatusesToPillarsSummary from "../report-components/zerotrust/StatusesToPillarsSummary";
|
import StatusesToPillarsSummary from "../report-components/zerotrust/StatusesToPillarsSummary";
|
||||||
import PrintReportButton from "../report-components/common/PrintReportButton";
|
import PrintReportButton from "../report-components/common/PrintReportButton";
|
||||||
import {extractExecutionStatusFromServerResponse} from "../report-components/common/ExecutionStatus";
|
import {extractExecutionStatusFromServerResponse} from "../report-components/common/ExecutionStatus";
|
||||||
|
@ -94,6 +93,11 @@ class ZeroTrustReportPageComponent extends AuthComponent {
|
||||||
generateFindingsSection() {
|
generateFindingsSection() {
|
||||||
return (<div id="findings-overview">
|
return (<div id="findings-overview">
|
||||||
<h2>Findings</h2>
|
<h2>Findings</h2>
|
||||||
|
<p>
|
||||||
|
Deep-dive into the details of each test, and see the explicit events and exact timestamps in which things
|
||||||
|
happened in your network. This will enable you to match up with your SOC logs and alerts and to gain deeper
|
||||||
|
insight as to what exactly happened during this test.
|
||||||
|
</p>
|
||||||
<FindingsTable pillarsToStatuses={this.state.pillars.pillarsToStatuses} findings={this.state.findings}/>
|
<FindingsTable pillarsToStatuses={this.state.pillars.pillarsToStatuses} findings={this.state.findings}/>
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
@ -101,6 +105,10 @@ class ZeroTrustReportPageComponent extends AuthComponent {
|
||||||
generateDirectivesSection() {
|
generateDirectivesSection() {
|
||||||
return (<div id="directives-overview">
|
return (<div id="directives-overview">
|
||||||
<h2>Directives</h2>
|
<h2>Directives</h2>
|
||||||
|
<p>
|
||||||
|
Analyze each zero trust recommendation by pillar, and see if you've followed through with it. See test results
|
||||||
|
to understand how the monkey tested your adherence to that recommendation.
|
||||||
|
</p>
|
||||||
{
|
{
|
||||||
Object.keys(this.state.directives).map((pillar) =>
|
Object.keys(this.state.directives).map((pillar) =>
|
||||||
<SinglePillarDirectivesStatus
|
<SinglePillarDirectivesStatus
|
||||||
|
|
|
@ -8,15 +8,15 @@ import * as PropTypes from "prop-types";
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
Header: 'Directives status',
|
|
||||||
columns: [
|
columns: [
|
||||||
{ Header: 'Directive', accessor: 'directive',
|
|
||||||
style: {'whiteSpace': 'unset'} // This enables word wrap
|
|
||||||
},
|
|
||||||
{ Header: 'Status', id: 'status',
|
{ Header: 'Status', id: 'status',
|
||||||
accessor: x => {
|
accessor: x => {
|
||||||
return <StatusLabel status={x.status} size="fa-3x" showText={false} />;
|
return <StatusLabel status={x.status} size="fa-3x" showText={false} />;
|
||||||
}
|
},
|
||||||
|
maxWidth: 80
|
||||||
|
},
|
||||||
|
{ Header: 'Directive', accessor: 'directive',
|
||||||
|
style: {'whiteSpace': 'unset'} // This enables word wrap
|
||||||
},
|
},
|
||||||
{ Header: 'Tests', id: 'tests',
|
{ Header: 'Tests', id: 'tests',
|
||||||
style: {'whiteSpace': 'unset'}, // This enables word wrap
|
style: {'whiteSpace': 'unset'}, // This enables word wrap
|
||||||
|
@ -56,7 +56,7 @@ class TestsStatus extends AuthComponent {
|
||||||
return (<li key={test.test}>{test.test}</li>)
|
return (<li key={test.test}>{test.test}</li>)
|
||||||
});
|
});
|
||||||
return <Fragment>
|
return <Fragment>
|
||||||
<StatusLabel status={statusToFilter} showText={true}/>
|
<StatusLabel status={statusToFilter} showText={false}/>
|
||||||
<ul>{listItems}</ul>
|
<ul>{listItems}</ul>
|
||||||
</Fragment>;
|
</Fragment>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import EventsModal from "./EventsModal";
|
||||||
import {Button} from "react-bootstrap";
|
import {Button} from "react-bootstrap";
|
||||||
import FileSaver from "file-saver";
|
import FileSaver from "file-saver";
|
||||||
import * as PropTypes from "prop-types";
|
import * as PropTypes from "prop-types";
|
||||||
|
import ExportEventsButton from "./ExportEventsButton";
|
||||||
|
|
||||||
export default class EventsAndButtonComponent extends Component {
|
export default class EventsAndButtonComponent extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -23,22 +24,17 @@ export default class EventsAndButtonComponent extends Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<EventsModal events={this.props.events} showEvents={this.state.isShow} hideCallback={this.hide}/>
|
<EventsModal events={this.props.events} showEvents={this.state.isShow} hideCallback={this.hide} exportFilename={this.props.exportFilename} />
|
||||||
<p style={{margin: '1px'}}>
|
<div className="text-center" style={{"display": "grid"}}>
|
||||||
<Button className="btn btn-info btn-lg center-block"
|
<Button className="btn btn-info btn-lg" onClick={this.show}>
|
||||||
onClick={this.show}>
|
|
||||||
Show Events
|
Show Events
|
||||||
</Button>
|
</Button>
|
||||||
<Button className="btn btn-primary btn-lg center-block"
|
<ExportEventsButton onClick={() => {
|
||||||
onClick={() => {
|
const content = JSON.stringify(this.props.events, null, 2);
|
||||||
const content = JSON.stringify(this.props.events, null, 2);
|
const blob = new Blob([content], {type: "text/plain;charset=utf-8"});
|
||||||
const blob = new Blob([content], {type: "text/plain;charset=utf-8"});
|
FileSaver.saveAs(blob, this.props.exportFilename + ".json");
|
||||||
FileSaver.saveAs(blob, this.props.exportFilename + ".json");
|
}}/>
|
||||||
}}
|
</div>
|
||||||
>
|
|
||||||
Export Events
|
|
||||||
</Button>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ import React, {Component} from "react";
|
||||||
import {Modal} from "react-bootstrap";
|
import {Modal} from "react-bootstrap";
|
||||||
import EventsTimeline from "./EventsTimeline";
|
import EventsTimeline from "./EventsTimeline";
|
||||||
import * as PropTypes from "prop-types";
|
import * as PropTypes from "prop-types";
|
||||||
|
import FileSaver from "file-saver";
|
||||||
|
import ExportEventsButton from "./ExportEventsButton";
|
||||||
|
|
||||||
export default class EventsModal extends Component {
|
export default class EventsModal extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -24,6 +26,11 @@ export default class EventsModal extends Component {
|
||||||
onClick={() => this.props.hideCallback()}>
|
onClick={() => this.props.hideCallback()}>
|
||||||
Close
|
Close
|
||||||
</button>
|
</button>
|
||||||
|
<ExportEventsButton onClick={() => {
|
||||||
|
const content = JSON.stringify(this.props.events, null, 2);
|
||||||
|
const blob = new Blob([content], {type: "text/plain;charset=utf-8"});
|
||||||
|
FileSaver.saveAs(blob, this.props.exportFilename + ".json");
|
||||||
|
}}/>
|
||||||
</div>
|
</div>
|
||||||
</Modal.Body>
|
</Modal.Body>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import React, {Component} from "react";
|
||||||
|
import {Button} from "react-bootstrap";
|
||||||
|
import * as PropTypes from "prop-types";
|
||||||
|
|
||||||
|
export default class ExportEventsButton extends Component {
|
||||||
|
render() {
|
||||||
|
return <Button className="btn btn-primary btn-lg"
|
||||||
|
onClick={this.props.onClick}
|
||||||
|
>
|
||||||
|
Export Events
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExportEventsButton.propTypes = {onClick: PropTypes.func};
|
|
@ -1,4 +1,4 @@
|
||||||
import React, {Component} from "react";
|
import React, {Component, Fragment} from "react";
|
||||||
import PillarLabel from "./PillarLabel";
|
import PillarLabel from "./PillarLabel";
|
||||||
import PaginatedTable from "../common/PaginatedTable";
|
import PaginatedTable from "../common/PaginatedTable";
|
||||||
import EventsAndButtonComponent from "./EventsAndButtonComponent";
|
import EventsAndButtonComponent from "./EventsAndButtonComponent";
|
||||||
|
@ -6,24 +6,27 @@ import EventsAndButtonComponent from "./EventsAndButtonComponent";
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
Header: 'Findings',
|
|
||||||
columns: [
|
columns: [
|
||||||
{ Header: 'Finding', accessor: 'test',
|
|
||||||
style: {'whiteSpace': 'unset'} // This enables word wrap
|
|
||||||
},
|
|
||||||
{ Header: 'Pillars', id: "pillars",
|
{ Header: 'Pillars', id: "pillars",
|
||||||
accessor: x => {
|
accessor: x => {
|
||||||
const pillars = x.pillars;
|
const pillars = x.pillars;
|
||||||
const listItems = pillars.map((pillar) =>
|
const pillarLabels = pillars.map((pillar) =>
|
||||||
<li key={pillar.name}><PillarLabel pillar={pillar.name} status={pillar.status}/></li>
|
<PillarLabel key={pillar.name} pillar={pillar.name} status={pillar.status}/>
|
||||||
);
|
);
|
||||||
return <ul>{listItems}</ul>;
|
return <Fragment>{pillarLabels}</Fragment>;
|
||||||
}
|
},
|
||||||
|
maxWidth: 200,
|
||||||
|
style: {'whiteSpace': 'unset'}
|
||||||
},
|
},
|
||||||
|
{ Header: 'Finding', accessor: 'test',
|
||||||
|
style: {'whiteSpace': 'unset'} // This enables word wrap
|
||||||
|
},
|
||||||
|
|
||||||
{ Header: 'Events', id:"events",
|
{ Header: 'Events', id:"events",
|
||||||
accessor: x => {
|
accessor: x => {
|
||||||
return <EventsAndButtonComponent events={x.events} exportFilename={"Events_" + x.test_key}/>;
|
return <EventsAndButtonComponent events={x.events} exportFilename={"Events_" + x.test_key}/>;
|
||||||
}
|
},
|
||||||
|
maxWidth: 160,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import PillarLabel from "./PillarLabel";
|
||||||
import DirectivesStatusTable from "./DirectivesStatusTable";
|
import DirectivesStatusTable from "./DirectivesStatusTable";
|
||||||
import React, {Fragment} from "react";
|
import React, {Fragment} from "react";
|
||||||
import * as PropTypes from "prop-types";
|
import * as PropTypes from "prop-types";
|
||||||
|
import {Panel} from "react-bootstrap";
|
||||||
|
|
||||||
export default class SinglePillarDirectivesStatus extends AuthComponent {
|
export default class SinglePillarDirectivesStatus extends AuthComponent {
|
||||||
render() {
|
render() {
|
||||||
|
@ -11,10 +12,20 @@ export default class SinglePillarDirectivesStatus extends AuthComponent {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Panel>
|
||||||
<h3><PillarLabel pillar={this.props.pillar} status={this.props.pillarsToStatuses[this.props.pillar]} /></h3>
|
<Panel.Heading>
|
||||||
<DirectivesStatusTable directivesStatus={this.props.directivesStatus}/>
|
<Panel.Title toggle>
|
||||||
</Fragment>
|
<h3 style={{"text-align": "center"}}>
|
||||||
|
🔽 <PillarLabel pillar={this.props.pillar} status={this.props.pillarsToStatuses[this.props.pillar]} />
|
||||||
|
</h3>
|
||||||
|
</Panel.Title>
|
||||||
|
</Panel.Heading>
|
||||||
|
<Panel.Collapse>
|
||||||
|
<Panel.Body>
|
||||||
|
<DirectivesStatusTable directivesStatus={this.props.directivesStatus}/>
|
||||||
|
</Panel.Body>
|
||||||
|
</Panel.Collapse>
|
||||||
|
</Panel>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue