A lot of small UI changes, trying to make the report look more polished.

This commit is contained in:
Shay Nehmad 2019-08-27 11:58:15 +03:00
parent 6cd5cff818
commit 07eb9ec32f
7 changed files with 75 additions and 35 deletions

View File

@ -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

View File

@ -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>;
} }

View File

@ -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>
); );
} }

View File

@ -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>

View File

@ -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};

View File

@ -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,
} }
] ]
} }

View File

@ -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>
); );
} }
} }