forked from p15670423/monkey
Added ScoutSuite UI code
This commit is contained in:
parent
4440027699
commit
c66cb11e79
|
@ -1,9 +1,10 @@
|
|||
from typing import List
|
||||
|
||||
from mongoengine import DateTimeField, Document, StringField, EmbeddedDocumentListField
|
||||
from mongoengine import Document, EmbeddedDocumentListField
|
||||
|
||||
from monkey_island.cc.models.zero_trust.event import Event
|
||||
|
||||
|
||||
class MonkeyFindingDetails(Document):
|
||||
"""
|
||||
This model represents additional information about monkey finding:
|
||||
|
|
|
@ -60,7 +60,7 @@ class ReportPageComponent extends AuthComponent {
|
|||
}
|
||||
|
||||
getZeroTrustReportFromServer = async () => {
|
||||
let ztReport = {findings: {}, principles: {}, pillars: {}};
|
||||
let ztReport = {findings: {}, principles: {}, pillars: {}, scoutsuite_data: {}};
|
||||
await this.authFetch('/api/report/zero_trust/findings')
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
|
@ -76,6 +76,11 @@ class ReportPageComponent extends AuthComponent {
|
|||
.then(res => {
|
||||
ztReport.pillars = res;
|
||||
});
|
||||
await this.authFetch('/api/report/zero_trust/scoutsuite')
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
ztReport.scoutsuite_data = res;
|
||||
});
|
||||
return ztReport
|
||||
};
|
||||
|
||||
|
|
|
@ -29,7 +29,9 @@ class ZeroTrustReportPageComponent extends AuthComponent {
|
|||
<SummarySection allMonkeysAreDead={this.state.allMonkeysAreDead} pillars={this.state.pillars}/>
|
||||
<PrinciplesSection principles={this.state.principles}
|
||||
pillarsToStatuses={this.state.pillars.pillarsToStatuses}/>
|
||||
<FindingsSection pillarsToStatuses={this.state.pillars.pillarsToStatuses} findings={this.state.findings}/>
|
||||
<FindingsSection pillarsToStatuses={this.state.pillars.pillarsToStatuses}
|
||||
findings={this.state.findings}
|
||||
scoutsuite_data={this.state.scoutsuite_data}/>
|
||||
</div>;
|
||||
}
|
||||
|
||||
|
@ -57,7 +59,8 @@ class ZeroTrustReportPageComponent extends AuthComponent {
|
|||
stillLoadingDataFromServer() {
|
||||
return typeof this.state.findings === 'undefined'
|
||||
|| typeof this.state.pillars === 'undefined'
|
||||
|| typeof this.state.principles === 'undefined';
|
||||
|| typeof this.state.principles === 'undefined'
|
||||
|| typeof this.state.scoutsuite_data === 'undefined';
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
const RULE_LEVELS = {
|
||||
LEVEL_WARNING: 'warning',
|
||||
LEVEL_DANGER: 'danger'
|
||||
}
|
||||
|
||||
export default RULE_LEVELS
|
|
@ -0,0 +1,8 @@
|
|||
const STATUSES = {
|
||||
STATUS_UNEXECUTED: 'Unexecuted',
|
||||
STATUS_PASSED: 'Passed',
|
||||
STATUS_VERIFY: 'Verify',
|
||||
STATUS_FAILED: 'Failed'
|
||||
}
|
||||
|
||||
export default STATUSES
|
|
@ -32,9 +32,15 @@ class FindingsSection extends Component {
|
|||
insight as to what exactly happened during this test.
|
||||
</p>
|
||||
|
||||
<FindingsTable data={findingsByStatus[ZeroTrustStatuses.failed]} status={ZeroTrustStatuses.failed}/>
|
||||
<FindingsTable data={findingsByStatus[ZeroTrustStatuses.verify]} status={ZeroTrustStatuses.verify}/>
|
||||
<FindingsTable data={findingsByStatus[ZeroTrustStatuses.passed]} status={ZeroTrustStatuses.passed}/>
|
||||
<FindingsTable data={findingsByStatus[ZeroTrustStatuses.failed]}
|
||||
scoutsuite_data={this.props.scoutsuite_data}
|
||||
status={ZeroTrustStatuses.failed}/>
|
||||
<FindingsTable data={findingsByStatus[ZeroTrustStatuses.verify]}
|
||||
scoutsuite_data={this.props.scoutsuite_data}
|
||||
status={ZeroTrustStatuses.verify}/>
|
||||
<FindingsTable data={findingsByStatus[ZeroTrustStatuses.passed]}
|
||||
scoutsuite_data={this.props.scoutsuite_data}
|
||||
status={ZeroTrustStatuses.passed}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,10 +4,14 @@ import PaginatedTable from '../common/PaginatedTable';
|
|||
import * as PropTypes from 'prop-types';
|
||||
import PillarLabel from './PillarLabel';
|
||||
import EventsButton from './EventsButton';
|
||||
import ScoutSuiteRuleButton from './scoutsuite/ScoutSuiteRuleButton';
|
||||
|
||||
const EVENTS_COLUMN_MAX_WIDTH = 170;
|
||||
const EVENTS_COLUMN_MAX_WIDTH = 250;
|
||||
const PILLARS_COLUMN_MAX_WIDTH = 200;
|
||||
const columns = [
|
||||
|
||||
|
||||
export class FindingsTable extends Component {
|
||||
columns = [
|
||||
{
|
||||
columns: [
|
||||
{
|
||||
|
@ -16,13 +20,18 @@ const columns = [
|
|||
},
|
||||
|
||||
{
|
||||
Header: 'Events', id: 'events',
|
||||
Header: 'Details', id: 'details',
|
||||
accessor: x => {
|
||||
if (x.type === 'scoutsuite_finding') {
|
||||
return <ScoutSuiteRuleButton scoutsuite_rules={x.details.scoutsuite_rules}
|
||||
scoutsuite_data={this.props.scoutsuite_data}/>;
|
||||
} else if (x.type === 'monkey_finding') {
|
||||
return <EventsButton finding_id={x.finding_id}
|
||||
latest_events={x.latest_events}
|
||||
oldest_events={x.oldest_events}
|
||||
event_count={x.event_count}
|
||||
exportFilename={'Events_' + x.test_key} />;
|
||||
latest_events={x.details.latest_events}
|
||||
oldest_events={x.details.oldest_events}
|
||||
event_count={x.details.event_count}
|
||||
exportFilename={'Events_' + x.test_key}/>;
|
||||
}
|
||||
},
|
||||
maxWidth: EVENTS_COLUMN_MAX_WIDTH
|
||||
},
|
||||
|
@ -41,15 +50,13 @@ const columns = [
|
|||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
];
|
||||
|
||||
|
||||
export class FindingsTable extends Component {
|
||||
render() {
|
||||
return <Fragment>
|
||||
<h3>{<span style={{display: 'inline-block'}}><StatusLabel status={this.props.status} showText={true}/>
|
||||
</span>} tests' findings</h3>
|
||||
<PaginatedTable data={this.props.data} pageSize={10} columns={columns}/>
|
||||
<PaginatedTable data={this.props.data} pageSize={10} columns={this.columns}/>
|
||||
</Fragment>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
import React, {useState} from 'react';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import '../../../../styles/components/scoutsuite/RuleDisplay.scss'
|
||||
import classNames from 'classnames';
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import {faChevronDown} from '@fortawesome/free-solid-svg-icons/faChevronDown';
|
||||
import {faChevronUp} from '@fortawesome/free-solid-svg-icons/faChevronUp';
|
||||
import ScoutSuiteDataParser from './ScoutSuiteDataParser';
|
||||
import Collapse from '@kunukn/react-collapse';
|
||||
import {faArrowRight} from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
export default function ResourceDropdown(props) {
|
||||
|
||||
const [isCollapseOpen, setIsCollapseOpen] = useState(false);
|
||||
|
||||
function getResourceDropdown() {
|
||||
return (
|
||||
<div key={props.resource_path} className={classNames('collapse-item',
|
||||
'resource-collapse', {'item--active': isCollapseOpen})}>
|
||||
<button className={'btn-collapse'}
|
||||
onClick={() => setIsCollapseOpen(!isCollapseOpen)}>
|
||||
<span>
|
||||
{props.resource_path}
|
||||
</span>
|
||||
<span>
|
||||
<FontAwesomeIcon icon={isCollapseOpen ? faChevronDown : faChevronUp}/>
|
||||
</span>
|
||||
</button>
|
||||
<Collapse
|
||||
className='collapse-comp'
|
||||
isOpen={isCollapseOpen}
|
||||
render={getResourceDropdownContents}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function replacePathDotsWithArrows(resourcePath) {
|
||||
let path_vars = resourcePath.split('.')
|
||||
let display_path = []
|
||||
for(let i = 0; i < path_vars.length; i++){
|
||||
display_path.push(path_vars[i])
|
||||
if( i !== path_vars.length - 1) {
|
||||
display_path.push(<FontAwesomeIcon icon={faArrowRight} />)
|
||||
}
|
||||
}
|
||||
return display_path;
|
||||
}
|
||||
|
||||
function prettyPrintJson(data) {
|
||||
return JSON.stringify(data, null, 4);
|
||||
}
|
||||
|
||||
function getResourceDropdownContents() {
|
||||
let parser = new ScoutSuiteDataParser(props.scoutsuite_data.data.services);
|
||||
return (
|
||||
<div className={'resource-display'}>
|
||||
<div>
|
||||
<p className={'resource-path-title'}>Path:</p>
|
||||
<p className={'resource-path-contents'}>{replacePathDotsWithArrows(props.resource_path)}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className={'resource-value-title'}>Value:</p>
|
||||
<pre className={'resource-value-json'}>{prettyPrintJson(parser.getValueAt(props.resource_path))}</pre>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return getResourceDropdown();
|
||||
}
|
||||
|
||||
ResourceDropdown.propTypes = {
|
||||
resource_path: PropTypes.object,
|
||||
scoutsuite_data: PropTypes.object
|
||||
};
|
|
@ -0,0 +1,56 @@
|
|||
import React from 'react';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import '../../../../styles/components/scoutsuite/RuleDisplay.scss'
|
||||
import ResourceDropdown from './ResourceDropdown';
|
||||
|
||||
export default function RuleDisplay(props) {
|
||||
|
||||
return (
|
||||
<div className={'scoutsuite-rule-display'}>
|
||||
<div className={'description'}>
|
||||
<h3>{props.rule.description}({props.rule.service})</h3>
|
||||
</div>
|
||||
<div className={'rationale'}>
|
||||
<p>{props.rule.rationale}</p>
|
||||
</div>
|
||||
<div className={'checked-resources'}>
|
||||
<p className={'checked-resources-title'}>Resources checked: </p>
|
||||
<p>{props.rule.checked_items}</p>
|
||||
</div>
|
||||
<div className={'flagged-resources'}>
|
||||
<p className={'checked-resources-title'}>Resources flagged: </p>
|
||||
<p>{props.rule.flagged_items}</p>
|
||||
</div>
|
||||
{props.rule.references.length !== 0 ? getReferences() : ''}
|
||||
{props.rule.items.length !== 0 ? getResources() : ''}
|
||||
</div>);
|
||||
|
||||
function getReferences() {
|
||||
let references = []
|
||||
props.rule.references.forEach(reference => {
|
||||
references.push(<a href={reference} className={'reference-link'} target={'_blank'}>{reference}</a>)
|
||||
})
|
||||
return (
|
||||
<div className={'reference-list'}>
|
||||
<p className={'reference-list-title'}>References:</p>
|
||||
{references}
|
||||
</div>)
|
||||
}
|
||||
|
||||
function getResources() {
|
||||
let resources = []
|
||||
props.rule.items.forEach(item => {
|
||||
resources.push(<ResourceDropdown resource_path={item} scoutsuite_data={props.scoutsuite_data}/>)
|
||||
})
|
||||
return (
|
||||
<div className={'reference-list'}>
|
||||
<p className={'reference-list-title'}>Resources:</p>
|
||||
{resources}
|
||||
</div>)
|
||||
}
|
||||
}
|
||||
|
||||
RuleDisplay.propTypes = {
|
||||
rule: PropTypes.object,
|
||||
scoutsuite_data: PropTypes.object
|
||||
};
|
|
@ -0,0 +1,58 @@
|
|||
export default class ScoutSuiteDataParser {
|
||||
constructor(runResults) {
|
||||
this.runResults = runResults
|
||||
}
|
||||
|
||||
getValueAt(path) {
|
||||
return this.getValueAtRecursive(path, this.runResults)
|
||||
}
|
||||
|
||||
getValueAtRecursive(path, source) {
|
||||
let value = source;
|
||||
let current_path = path;
|
||||
let key;
|
||||
// iterate over each path elements
|
||||
while (current_path) {
|
||||
// check if there are more elements to the path
|
||||
if (current_path.indexOf('.') != -1) {
|
||||
key = current_path.substr(0, current_path.indexOf('.'));
|
||||
}
|
||||
// last element
|
||||
else {
|
||||
key = current_path;
|
||||
}
|
||||
|
||||
try {
|
||||
// path containing an ".id"
|
||||
if (key == 'id') {
|
||||
let v = [];
|
||||
let w;
|
||||
for (let k in value) {
|
||||
// process recursively
|
||||
w = this.getValueAtRecursive(k + current_path.substr(current_path.indexOf('.'), current_path.length), value);
|
||||
v = v.concat(
|
||||
Object.values(w) // get values from array, otherwise it will be an array of key/values
|
||||
);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
// simple path, just return element in value
|
||||
else {
|
||||
value = value[key];
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Error: ' + err)
|
||||
}
|
||||
|
||||
// check if there are more elements to process
|
||||
if (current_path.indexOf('.') != -1) {
|
||||
current_path = current_path.substr(current_path.indexOf('.') + 1, current_path.length);
|
||||
}
|
||||
// otherwise we're done
|
||||
else {
|
||||
current_path = false;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
import React, {Component} from 'react';
|
||||
import {Badge, Button} from 'react-bootstrap';
|
||||
import * as PropTypes from 'prop-types';
|
||||
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import {faList} from '@fortawesome/free-solid-svg-icons/faList';
|
||||
import ScoutSuiteRuleModal from './ScoutSuiteRuleModal';
|
||||
|
||||
export default class ScoutSuiteRuleButton extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isModalOpen: false
|
||||
}
|
||||
}
|
||||
|
||||
toggleModal = () => {
|
||||
this.setState({isModalOpen: !this.state.isModalOpen});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<ScoutSuiteRuleModal scoutsuite_rules={this.props.scoutsuite_rules}
|
||||
scoutsuite_data={this.props.scoutsuite_data}
|
||||
isModalOpen={this.state.isModalOpen}
|
||||
hideCallback={this.toggleModal} />
|
||||
<div className="text-center" style={{'display': 'grid'}}>
|
||||
<Button variant={'monkey-info'} size={'lg'} onClick={this.toggleModal}>
|
||||
<FontAwesomeIcon icon={faList}/> ScoutSuite rules {this.createRuleCountBadge()}
|
||||
</Button>
|
||||
</div>
|
||||
</>);
|
||||
}
|
||||
|
||||
createRuleCountBadge() {
|
||||
const ruleCount = this.props.scoutsuite_rules.length > 9 ? '9+' : this.props.scoutsuite_rules.length;
|
||||
return <Badge variant={'monkey-info-light'}>{ruleCount}</Badge>;
|
||||
}
|
||||
}
|
||||
|
||||
ScoutSuiteRuleButton.propTypes = {
|
||||
scoutsuite_rules: PropTypes.array,
|
||||
scoutsuite_data: PropTypes.object
|
||||
};
|
|
@ -0,0 +1,58 @@
|
|||
import React, {useState} from 'react';
|
||||
import {Modal} from 'react-bootstrap';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import Pluralize from 'pluralize';
|
||||
import ScoutSuiteSingleRuleDropdown from './ScoutSuiteSingleRuleDropdown';
|
||||
import '../../../../styles/components/scoutsuite/RuleModal.scss';
|
||||
|
||||
|
||||
export default function ScoutSuiteRuleModal(props) {
|
||||
const [openRuleId, setOpenRuleId] = useState(null)
|
||||
|
||||
function toggleRuleDropdown(ruleId) {
|
||||
if (openRuleId === ruleId) {
|
||||
setOpenRuleId(null);
|
||||
} else {
|
||||
setOpenRuleId(ruleId);
|
||||
}
|
||||
}
|
||||
|
||||
function renderRuleDropdowns() {
|
||||
let dropdowns = [];
|
||||
props.scoutsuite_rules.forEach(rule => {
|
||||
let dropdown = (<ScoutSuiteSingleRuleDropdown isCollapseOpen={openRuleId === rule.description}
|
||||
toggleCallback={() => toggleRuleDropdown(rule.description)}
|
||||
rule={rule}
|
||||
scoutsuite_data={props.scoutsuite_data}/>)
|
||||
dropdowns.push(dropdown)
|
||||
});
|
||||
return dropdowns;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Modal show={props.isModalOpen} onHide={() => props.hideCallback()} className={'scoutsuite-rule-modal'}>
|
||||
<Modal.Body>
|
||||
<h3>
|
||||
<div className="text-center">ScoutSuite rules</div>
|
||||
</h3>
|
||||
<hr/>
|
||||
<p>
|
||||
There {Pluralize('is', props.scoutsuite_rules.length)} {
|
||||
<div className={'badge badge-primary'}>{props.scoutsuite_rules.length}</div>
|
||||
} ScoutSuite {Pluralize('rule', props.scoutsuite_rules.length)} associated with finding.
|
||||
</p>
|
||||
{renderRuleDropdowns()}
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
ScoutSuiteRuleModal.propTypes = {
|
||||
isModalOpen: PropTypes.bool,
|
||||
scoutsuite_rules: PropTypes.array,
|
||||
scoutsuite_data: PropTypes.object,
|
||||
hideCallback: PropTypes.func
|
||||
};
|
|
@ -0,0 +1,90 @@
|
|||
import React from 'react';
|
||||
import Collapse from '@kunukn/react-collapse';
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'
|
||||
import {faChevronUp} from '@fortawesome/free-solid-svg-icons/faChevronUp'
|
||||
import {faChevronDown} from '@fortawesome/free-solid-svg-icons/faChevronDown'
|
||||
|
||||
import classNames from 'classnames';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import RULE_LEVELS from '../../common/consts/ScoutSuiteConsts/RuleLevels';
|
||||
import STATUSES from '../../common/consts/StatusConsts';
|
||||
import {faCheckCircle, faCircle, faExclamationCircle} from '@fortawesome/free-solid-svg-icons';
|
||||
import RuleDisplay from './RuleDisplay';
|
||||
|
||||
export default function ScoutSuiteSingleRuleDropdown(props) {
|
||||
|
||||
function getRuleCollapse() {
|
||||
return (
|
||||
<div key={props.rule.description} className={classNames('collapse-item',
|
||||
'rule-collapse', {'item--active': props.isCollapseOpen})}>
|
||||
<button className={classNames('btn-collapse', getDropdownClass())}
|
||||
onClick={props.toggleCallback}>
|
||||
<span>
|
||||
<FontAwesomeIcon icon={getRuleIcon()} />
|
||||
{props.rule.description}
|
||||
</span>
|
||||
<span>
|
||||
<FontAwesomeIcon icon={props.isCollapseOpen ? faChevronDown : faChevronUp}/>
|
||||
</span>
|
||||
</button>
|
||||
<Collapse
|
||||
className='collapse-comp'
|
||||
isOpen={props.isCollapseOpen}
|
||||
render={renderRule}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function getRuleIcon() {
|
||||
let ruleStatus = getRuleStatus()
|
||||
switch(ruleStatus) {
|
||||
case STATUSES.STATUS_PASSED:
|
||||
return faCheckCircle;
|
||||
case STATUSES.STATUS_VERIFY:
|
||||
return faExclamationCircle;
|
||||
case STATUSES.STATUS_FAILED:
|
||||
return faExclamationCircle;
|
||||
case STATUSES.STATUS_UNEXECUTED:
|
||||
return faCircle;
|
||||
}
|
||||
}
|
||||
|
||||
function getDropdownClass(){
|
||||
let ruleStatus = getRuleStatus()
|
||||
switch(ruleStatus) {
|
||||
case STATUSES.STATUS_PASSED:
|
||||
return "collapse-success";
|
||||
case STATUSES.STATUS_VERIFY:
|
||||
return "collapse-warning";
|
||||
case STATUSES.STATUS_FAILED:
|
||||
return "collapse-danger";
|
||||
case STATUSES.STATUS_UNEXECUTED:
|
||||
return "collapse-default";
|
||||
}
|
||||
}
|
||||
|
||||
function getRuleStatus(){
|
||||
if(props.rule.checked_items === 0) {
|
||||
return STATUSES.STATUS_UNEXECUTED
|
||||
} else if (props.rule.items.length === 0) {
|
||||
return STATUSES.STATUS_PASSED
|
||||
} else if (props.rule.level === RULE_LEVELS.LEVEL_WARNING) {
|
||||
return STATUSES.STATUS_VERIFY
|
||||
} else {
|
||||
return STATUSES.STATUS_FAILED
|
||||
}
|
||||
}
|
||||
|
||||
function renderRule() {
|
||||
return <RuleDisplay rule={props.rule} scoutsuite_data={props.scoutsuite_data}/>
|
||||
}
|
||||
|
||||
return getRuleCollapse();
|
||||
}
|
||||
|
||||
ScoutSuiteSingleRuleDropdown.propTypes = {
|
||||
isCollapseOpen: PropTypes.bool,
|
||||
rule: PropTypes.object,
|
||||
scoutsuite_data: PropTypes.object,
|
||||
toggleCallback: PropTypes.func
|
||||
};
|
|
@ -12,6 +12,7 @@
|
|||
@import 'components/PreviewPane';
|
||||
@import 'components/AdvancedMultiSelect';
|
||||
@import 'components/particle-component/ParticleBackground';
|
||||
@import 'components/scoutsuite/ResourceDropdown';
|
||||
|
||||
|
||||
// Define custom elements after bootstrap import
|
||||
|
|
|
@ -5,6 +5,7 @@ $disabled-color: #f2f2f2;
|
|||
$info-color: #ade3eb;
|
||||
$default-color: #8c8c8c;
|
||||
$warning-color: #ffe28d;
|
||||
$success-color: #adf6a9;
|
||||
|
||||
.collapse-item button {
|
||||
font-size: inherit;
|
||||
|
@ -39,6 +40,10 @@ $warning-color: #ffe28d;
|
|||
}
|
||||
}
|
||||
|
||||
.collapse-success {
|
||||
background-color: $success-color !important;
|
||||
}
|
||||
|
||||
.collapse-danger {
|
||||
background-color: $danger-color !important;
|
||||
}
|
||||
|
@ -99,3 +104,7 @@ $warning-color: #ffe28d;
|
|||
display: inline-block;
|
||||
min-width: 6em;
|
||||
}
|
||||
|
||||
.rule-collapse svg{
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
.resource-display {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.resource-display .resource-value-json {
|
||||
background-color: $gray-200;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.resource-display .resource-path-contents svg {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.resource-display .resource-value-title,
|
||||
.resource-display .resource-path-title {
|
||||
font-weight: 500;
|
||||
margin-bottom: 0;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
.scoutsuite-rule-display .description h3{
|
||||
font-size: 1.2em;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.scoutsuite-rule-display p{
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.scoutsuite-rule-display .checked-resources-title,
|
||||
.scoutsuite-rule-display .flagged-resources-title,
|
||||
.scoutsuite-rule-display .reference-list-title{
|
||||
font-weight: 500;
|
||||
margin-right: 5px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.scoutsuite-rule-display .reference-list a {
|
||||
display: block;
|
||||
margin-left: 10px;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
.scoutsuite-rule-modal .modal-dialog{
|
||||
max-width: 1000px;
|
||||
top: 0;
|
||||
padding: 30px;
|
||||
}
|
Loading…
Reference in New Issue