CN-1391 fix: Detection列表按新版设计更新接口和样式

This commit is contained in:
刘洪洪
2023-10-20 15:45:11 +08:00
parent 48b3e2aebd
commit 0f52bd5362
11 changed files with 814 additions and 364 deletions

View File

@@ -60,12 +60,13 @@
font-weight: 400;
}
.row__content {
.row__content, .row__content1 {
display: flex;
//color: #3976CB;
color: #046ECA;
font-weight: 500;
font-size: 14px;
align-items: center;
&.row__content--link {
font-style: italic;
@@ -87,6 +88,17 @@
font-style: italic;
color: #1890FF;
}
.row__content__icon {
font-size: 14px;
margin-right: 5px;
color: #1890FF;
}
.row__content__svg {
font-size: 14px;
margin-right: 7px;
}
}
}
}
@@ -215,13 +227,20 @@
}
}
}
.row__tag {
.row__tag, .row__tag__level {
display: flex;
justify-content: center;
align-items: center;
padding: 0 4px;
//color: white;
}
.row__tag__level {
height: 16px;
font-size: 12px;
font-weight: 400;
color: #fff;
border-radius: 2px;
}
.performance-event-remark {
font-family: NotoSansSChineseRegular;

View File

@@ -1234,7 +1234,8 @@ export default class Parser {
// 不区分大小写用this.columnList里的label
arr.forEach(item => {
if (str.toLowerCase().indexOf(item.toLowerCase()) > -1) {
str = str.replace(new RegExp(item, 'gi'), item)
// todo 记录一下此处取消了转小写转换,后续搜索验证
// str = str.replace(new RegExp(item, 'gi'), item)
if (!operatorList.some(ite => str.includes(ite)) && str.toLowerCase() !== item.toLowerCase()) {
str = this.checkFormatByStr(str)
}

353
src/mock/detectionList.js Normal file
View File

@@ -0,0 +1,353 @@
import Mock from 'mockjs'
const urlAndVersion = BASE_CONFIG.baseUrl + BASE_CONFIG.apiVersion
const openMock = true
if (openMock) {
Mock.mock(new RegExp(urlAndVersion + '/detection/security/list.*'), 'get', function (requestObj) {
const result = []
for (let i = 0; i < 20; i++) {
const obj = {
eventId: 1212,
eventType: 'Anonymity',
eventName: 'Tor',
eventKey: '2,1.1.1,12.2.2.2',
ruleId: 2,
ruleType: 'indicator_match',
isBuiltin: 1,
severity: 'critical',
offenderIp: '1.1.1.1',
victimIp: '2.2.2.2',
domain: '*.vioee.com',
app: 'vio',
startTime: 1697092617,
endTime: 1697092777,
durationS: 30000,
matchTimes: 1,
status: 1,
eventInfo: "{\'knowledge_id\': 123, \'name\': \'example_ioc_malware\', \'ioc_type\': \'domain\', \'ioc_value\': \'iocentity.com\'}",
eventInfoObj: {
knowledge_id: 123,
name: 'example_ioc_malware',
ioc_type: 'domain',
ioc_value: 'iocentity.com'
}
}
if (i % 2 === 0) {
obj.status = 0
}
result.push(obj)
}
const data = {
resultType: 'table',
result: result
}
return {
msg: 'OK',
code: 200,
data: data
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/severity/timedistribution.*'), 'get', function (requestObj) {
const result = [
{
statTime: 1697523900,
severity: 'critical',
count: 25
},
{
statTime: 1697524200,
severity: 'high',
count: 31
},
{
statTime: 1697524500,
severity: 'info',
count: 21
},
{
statTime: 1697524800,
severity: 'low',
count: 25
},
{
statTime: 1697525100,
severity: 'medium',
count: 32
},
{
statTime: 1697525400,
severity: 'critical',
count: 22
},
{
statTime: 1697525700,
severity: 'critical',
count: 23
},
{
statTime: 1697526000,
severity: 'critical',
count: 25
},
{
statTime: 1697526300,
severity: 'critical',
count: 21
}
]
const data = {
resultType: 'table',
result: result
}
return {
msg: 'OK',
code: 200,
data: data
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/severity/statistics.*'), 'get', function (requestObj) {
const result = [
{
severity: 'critical',
count: 25
},
{
severity: 'high',
count: 31
},
{
severity: 'info',
count: 21
},
{
severity: 'low',
count: 25
},
{
severity: 'medium',
count: 32
}
]
const data = {
resultType: 'table',
result: result
}
return {
msg: 'OK',
code: 200,
data: data
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/event-type/statistics.*'), 'get', function (requestObj) {
const result = [
{
eventType: 'Anonymity',
count: 25
},
{
eventType: 'Command and Control',
count: 31
}
]
const data = {
resultType: 'table',
result: result
}
return {
msg: 'OK',
code: 200,
data: data
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/offender-ip/statistics.*'), 'get', function (requestObj) {
const result = [
{
offenderIp: '221.7.1.20',
count: 25
},
{
offenderIp: '58.247.118.253',
count: 31
}
]
const data = {
resultType: 'table',
result: result
}
return {
msg: 'OK',
code: 200,
data: data
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/victim-ip/statistics.*'), 'get', function (requestObj) {
const result = [
{
victimIp: '21.77.1.201',
count: 25
},
{
victimIp: '58.47.118.153',
count: 31
},
{
victimIp: '22.47.223.57',
count: 43
}
]
const data = {
resultType: 'table',
result: result
}
return {
msg: 'OK',
code: 200,
data: data
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/status/statistics.*'), 'get', function (requestObj) {
const result = [
{
status: 1,
count: 25
},
{
status: 0,
count: 31
}
]
const data = {
resultType: 'table',
result: result
}
return {
msg: 'OK',
code: 200,
data: data
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/ip/relation/event.*'), 'get', function (requestObj) {
const result = [
{
eventId: 10010,
severity: 'high',
eventType: 'Command and Control',
offenderIp: '1.1.1.1',
victimIp: '2.2.2.2',
startTime: 1697092617
}
]
const data = {
resultType: 'table',
result: result
}
return {
msg: 'OK',
code: 200,
data: data
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/count.*'), 'get', function (requestObj) {
return {
msg: 'OK',
code: 200,
data: {
resultType: 'single',
result: 123
}
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/entity/detail/ip.*'), 'get', function (requestObj) {
return {
msg: 'OK',
code: 200,
data: {
asn: {
id: 2,
asn: '14061',
organization: 'DIGITALOCEAN-ASN - DigitalOcean, LLC, US'
},
malware: {
threatType: 'command and control',
malwareName: 'IcedID',
malwareAlias: 'BokBot,IceID',
mitreAttackDescription: '[IcedID](https://attack.mitre.org/software/S0483) is a modular banking malware designed to steal financial information that has been observed in the wild since at least 2017. [IcedID](https://attack.mitre.org/software/S0483) has been downloaded by [Emotet](https://attack.mitre.org/software/S0367) in multiple campaigns.(Citation: IBM IcedID November 2017)(Citation: Juniper IcedID June 2020)',
mitreAttackPlatforms: 'Windows',
mitreAttackTechniques: '[""Asymmetric Cryptography(T1573.002)"",""Asynchronous Procedure Call(T1055.004)"",""Browser Session Hijacking(T1185)"",""Domain Account(T1087.002)"",""Ingress Tool Transfer(T1105)"",""Malicious File(T1204.002)"",""Msiexec(T1218.007)"",""Native API(T1106)"",""Obfuscated Files or Information(T1027)"",""Permission Groups Discovery(T1069)"",""Registry Run Keys / Startup Folder(T1547.001)"",""Scheduled Task(T1053.005)"",""Software Packing(T1027.002)"",""Spearphishing Attachment(T1566.001)"",""Steganography(T1027.003)"",""System Information Discovery(T1082)"",""Visual Basic(T1059.005)"",""Web Protocols(T1071.001)"",""Windows Management Instrumentation(T1047)""]',
mitreAttackGroups: '[""TA551(G0127)""]',
confidenceLevel: 'high',
reference: ''
},
location: {
continent: 'North America',
country: 'United States',
province: 'New York',
city: '',
lngwgs: '-74.006',
latwgs: '40.713',
isp: 'dba Omsoft',
owner: 'tie net'
}
}
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/entity/detail/domain.*'), 'get', function (requestObj) {
return {
msg: 'OK',
code: 200,
data: {
malware: {
threatType: 'command and control',
malwareName: 'IcedID',
malwareAlias: 'BokBot,IceID',
mitreAttackDescription: '[IcedID](https://attack.mitre.org/software/S0483) is a modular banking malware designed to steal financial information that has been observed in the wild since at least 2017. [IcedID](https://attack.mitre.org/software/S0483) has been downloaded by [Emotet](https://attack.mitre.org/software/S0367) in multiple campaigns.(Citation: IBM IcedID November 2017)(Citation: Juniper IcedID June 2020)',
mitreAttackPlatforms: 'Windows',
mitreAttackTechniques: '[""Asymmetric Cryptography(T1573.002)"",""Asynchronous Procedure Call(T1055.004)"",""Browser Session Hijacking(T1185)"",""Domain Account(T1087.002)"",""Ingress Tool Transfer(T1105)"",""Malicious File(T1204.002)"",""Msiexec(T1218.007)"",""Native API(T1106)"",""Obfuscated Files or Information(T1027)"",""Permission Groups Discovery(T1069)"",""Registry Run Keys / Startup Folder(T1547.001)"",""Scheduled Task(T1053.005)"",""Software Packing(T1027.002)"",""Spearphishing Attachment(T1566.001)"",""Steganography(T1027.003)"",""System Information Discovery(T1082)"",""Visual Basic(T1059.005)"",""Web Protocols(T1071.001)"",""Windows Management Instrumentation(T1047)""]',
mitreAttackGroups: '[""TA551(G0127)""]',
confidenceLevel: 'high',
reference: ''
},
category: {
categoryName: '门户网站',
categoryGroup: '互联网',
reputationLevel: 'high'
}
}
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/entity/detail/app.*'), 'get', function (requestObj) {
return {
msg: 'OK',
code: 200,
data: {
category: {
appName: 'QQ',
appId: 333,
appCategory: '娱乐',
appSubcategory: '聊天',
appRisk: 'low',
appDescription: '聊天社交软件',
appLongname: 'Tencent qq',
appTechnology: 'socket',
appCompany: 'tencent',
appCompanyCategory: '互联网'
}
}
}
})
}

View File

@@ -3,3 +3,4 @@ import './linkMonitor'
import './dns'
import './entity'
import './detection'
import './detectionList'

View File

@@ -123,7 +123,20 @@ export const api = {
listBasic: '/interface/detection/security/list/basic',
listCount: '/interface/detection/security/list/count',
overviewBasic: '/interface/detection/security/detail/overview/basic',
overviewEvent: '/interface/detection/security/detail/overview/event'
overviewEvent: '/interface/detection/security/detail/overview/event',
securityList: apiVersion + '/detection/security/list', // 安全事件列表
timeDistribution: apiVersion + '/detection/security/severity/timedistribution', // 事件严重等级分布(顶部柱状图)
severityStatistics: apiVersion + '/detection/security/severity/statistics', // 事件严重等级统计(左侧filter事件严重等级和饼图)
statusStatistics: apiVersion + '/detection/security/status/statistics', // 事件状态统计
eventTypeStatistics: apiVersion + '/detection/security/event-type/statistics', // 事件类型统计
offenderIpStatistics: apiVersion + '/detection/security/offender-ip/statistics', // 攻击者IP统计
victimIpStatistics: apiVersion + '/detection/security/victim-ip/statistics', // 受害者IP统计
relationEvent: apiVersion + '/detection/security/ip/relation/event', // IP相关近期事件
securityCount: apiVersion + '/detection/security/count', // 安全事件总数
detail: apiVersion + '/detection/security/entity/detail', // 安全事件实体详情,后面得加上实体类型
ipDetail: apiVersion + '/detection/security/entity/detail/ip', // 安全事件实体详情ip响应
domainDetail: apiVersion + '/detection/security/entity/detail/domain', // 安全事件实体详情domain响应
appDetail: apiVersion + '/detection/security/entity/detail/app' // 安全事件实体详情app响应
},
performanceEvent: {
eventSeverityTrend: '/interface/detection/performance/filter/severityTrend',

View File

@@ -334,11 +334,91 @@ export const columnList1 = [
}
}
]
const securityEvent = [
{
name: 'event_type',
type: 'string',
label: 'event_type',
doc: {
constraints: {
operator_functions: '=,in,like'
}
}
},
{
name: 'event_name',
type: 'string',
label: 'event_name',
doc: {
constraints: {
operator_functions: '=,in,like'
}
}
},
{
name: 'severity',
type: 'string',
label: 'severity',
doc: {
constraints: {
operator_functions: '=,in,like'
}
}
},
{
name: 'offender_ip',
type: 'string',
label: 'offender Ip',
doc: {
constraints: {
operator_functions: '=,in,like'
}
}
},
{
name: 'victim_ip',
type: 'string',
label: 'victim Ip',
doc: {
constraints: {
operator_functions: '=,in,like'
}
}
},
{
name: 'domain',
type: 'string',
label: 'domain',
doc: {
constraints: {
operator_functions: '=,in,like'
}
}
},
{
name: 'app',
type: 'string',
label: 'app',
doc: {
constraints: {
operator_functions: '=,in,like'
}
}
}
]
let schemaEntityExplore = localStorage.getItem(storageKey.schemaEntityExplore)
schemaEntityExplore = schemaEntityExplore ? JSON.parse(schemaEntityExplore).entityMetadata.searchColumns : columnList1
const schema = localStorage.getItem(storageKey.schemaEntityExplore)
const schemaEntityExplore = schema ? JSON.parse(schema).entityMetadata.searchColumns : columnList1
export const columnList = schemaEntityExplore
let securityEventMetadata = securityEvent
if (schema) {
if (JSON.parse(schema).securityEventMetadata) {
securityEventMetadata = JSON.parse(schema).securityEventMetadata.searchColumns
}
}
export const schemaDetectionSecurity = securityEventMetadata
export const operatorList = ['=', '!=', /* '>', '<', '>=', '<=', */'IN', 'NOT IN', 'LIKE', 'NOT LIKE']
export const connectionList = [
{

View File

@@ -3,12 +3,12 @@
<loading :loading="loading"></loading>
<div class="detection-list__content">
<div class="detection-list--list">
<div class="no-data" v-if="noData">{{ $t('npm.noData') }}</div>
<div class="no-data" v-if="myListData.length===0">{{ $t('npm.noData') }}</div>
<div v-if="!isCollapse" @click="collapse" class="cn-detection__shadow new-cn-detection__shadow"></div>
<detection-row
style="margin-bottom: 10px"
class="detection-border"
v-for="(data, index) in listData"
v-for="(data, index) in myListData"
:detection="data"
:page-type="pageType"
:timeFilter="timeFilter"
@@ -26,6 +26,8 @@
<script>
import DetectionRow from '@/views/detections/DetectionRow'
import Loading from '@/components/common/Loading'
import axios from 'axios'
import { api } from '@/utils/api'
export default {
name: 'DetectionList',
components: {
@@ -49,7 +51,8 @@ export default {
collapseIndex: 0,
tableId: 'detectionList',
listDataCopy: [],
noData: false
noData: true,
myListData: [] // listData的克隆避免因为修改listData里的malWareName而触发watch监听
}
},
mounted () {
@@ -60,6 +63,23 @@ export default {
window.removeEventListener('mousewheel', this.handleScroll)
},
methods: {
initData () {
this.myListData = []
this.listData.forEach((item, i) => {
this.myListData.push(this.$_.cloneDeep(item))
if (item.eventInfoObj) {
axios.get(`${api.detection.securityEvent.detail}/${item.eventInfoObj.ioc_type}/resource=${item.eventInfoObj.ioc_value}`).then(res => {
if (res.status === 200) {
this.myListData[i].malwareName = res.data.data.malware.malwareName
} else {
this.myListData[i].malwareName = '-'
}
}).catch(e => {
this.myListData[i].malwareName = '-'
})
}
})
},
switchCollapse (isCollapse, index) {
this.isCollapse = isCollapse
this.collapseIndex = index
@@ -84,6 +104,7 @@ export default {
},
watch: {
listData: {
immediate: true,
deep: true,
handler (n) {
if (!n || n.length === 0) {
@@ -93,6 +114,7 @@ export default {
} else {
clearTimeout(this.timeout)
this.noData = false
this.initData()
}
}
}

View File

@@ -9,14 +9,15 @@
</div>
</div>
<div class="cn-detection__case">
<div class="cn-detection__icon" :style="`background-color: ${eventSeverityColor[detection.eventSecurity]}`"></div>
<div class="cn-detection__icon" :style="`background-color: ${eventSeverityColor[detection.severity]}`"></div>
<div class="cn-detection__row">
<div class="cn-detection__header" v-if="pageType === detectionPageType.securityEvent">
<span
class="detection-event-severity-color-block"
:style="`background-color: ${eventSeverityColor[detection.eventSeverity]}`">
</span>
<span class="detection-event-severity-block">{{ detection.securityType || '-' }}</span>
<!-- <span class="detection-event-severity-block">{{ detection.securityType || '-' }}</span>-->
<span class="detection-event-severity-block">{{ detection.eventType || '-' }}</span>
<i class="cn-icon cn-icon-attacker" ></i>{{detection.offenderIp || '-'}}
<div v-if="detection.domain" class="domain">{{detection.domain}}</div>
<span class="line">-------</span>
@@ -30,31 +31,31 @@
<div class="cn-detection__body">
<div class="body__basic-info">
<div class="basic-info">
<div class="basic-info__item" v-if="detection.eventSecurity">
<div class="basic-info__item" v-if="detection.severity">
<i class="cn-icon cn-icon-severity-level"></i>
<span>{{$t('detection.list.security')}}&nbsp;:&nbsp;&nbsp;</span>
<span>{{detection.eventSecurity || '-'}}</span>
<span>{{detection.severity || '-'}}</span>
</div>
<div class="basic-info__item" v-if="detection.eventSeverity">
<i class="cn-icon cn-icon-severity-level"></i>
<span>{{$t('detections.severity')}}&nbsp;:&nbsp;&nbsp;</span>
<span>{{detection.eventSeverity || '-'}}</span>
</div>
<div class="basic-info__item" v-if="detection.eventType">
<i class="cn-icon cn-icon-event-type"></i>
<span>{{$t('detections.eventType')}}&nbsp;:&nbsp;&nbsp;</span>
<span>{{detection.eventType || '-'}}</span>
</div>
<div class="basic-info__item" v-if="detection.malwareName">
<!-- <div class="basic-info__item" v-if="detection.eventType">-->
<!-- <i class="cn-icon cn-icon-event-type"></i>-->
<!-- <span>{{$t('detections.eventType')}}&nbsp;:&nbsp;&nbsp;</span>-->
<!-- <span>{{detection.eventType || '-'}}</span>-->
<!-- </div>-->
<div class="basic-info__item" v-if="detection.eventName">
<i class="cn-icon cn-icon-trojan"></i>
<span>{{$t('detection.list.malwareName')}}&nbsp;:&nbsp;&nbsp;</span>
<span>{{detection.malwareName || '-'}}</span>
</div>
<div class="basic-info__item" v-if="detection.cryptominingPool">
<i class="cn-icon cn-icon-mining-pool"></i>
<span>{{$t('detection.list.cryptominingPool')}}&nbsp;:&nbsp;&nbsp;</span>
<span>{{detection.cryptominingPool || '-'}}</span>
<span>{{ $_.get(detection, 'malwareName', '-') || '-' }}</span>
</div>
<!-- <div class="basic-info__item" v-if="detection.cryptominingPool">-->
<!-- <i class="cn-icon cn-icon-mining-pool"></i>-->
<!-- <span>{{$t('detection.list.cryptominingPool')}}&nbsp;:&nbsp;&nbsp;</span>-->
<!-- <span>{{detection.cryptominingPool || '-'}}</span>-->
<!-- </div>-->
<div class="basic-info__item">
<i class="cn-icon cn-icon-time2"></i>
<span>{{$t('detection.list.startTime')}}&nbsp;:&nbsp;&nbsp;</span>
@@ -63,9 +64,11 @@
<div class="basic-info__item">
<i class="cn-icon cn-icon-duration"></i>
<span>{{$t('overall.duration')}}&nbsp;:&nbsp;&nbsp;</span>
<span style="display: inline-block;height: 6px;width: 6px;border-radius: 50%;margin-right: 8px;"
:style="pointColor(detection)"></span>
<span>{{unitConvert(detection.durationMs, 'time', null, null, 0).join(' ') || '-'}}</span>
<span>
{{ detection.matchTimes || '-'}} {{ $t('detection.list.times') }} /
{{unitConvert(detection.durationS, 'time', 's', null, 0).join(' ') || '-'}}
</span>
<div v-if="parseInt(detection.status)===0" class="margin-l-10 detection-row-active">Active</div>
</div>
</div>
</div>
@@ -108,7 +111,7 @@
<script>
import { eventSeverityColor, detectionPageType, entityType } from '@/utils/constants'
import { getMillisecond } from '@/utils/date-util'
import { getMillisecond, dateFormatByAppearance } from '@/utils/date-util'
import unitConvert from '@/utils/unit-convert'
import DetectionSecurityEventOverview from '@/views/detections/overview/DetectionSecurityEventOverview'
import DetectionPerformanceEventIpOverview from '@/views/detections/overview/DetectionPerformanceEventIpOverview'
@@ -177,6 +180,7 @@ export default {
methods: {
unitConvert,
getMillisecond,
dateFormatByAppearance,
/* 切换折叠状态 */
switchCollapse () {
this.isCollapse = !this.isCollapse
@@ -235,3 +239,17 @@ export default {
}
}
</script>
<style>
.detection-row-active {
height: 20px;
line-height: 20px;
padding: 0 7px;
background: #E9EFE1;
border-radius: 2px;
font-family: NotoSansHans-Medium;
font-size: 12px;
color: #7E9F54;
font-weight: 500;
}
</style>

View File

@@ -5,16 +5,16 @@
<advanced-search
ref="search"
:column-list="columnList[pageType]"
:operator-list="operatorList"
:connection-list="connectionList"
:full-text="false"
class="advanced-search--show-list"
:full-text="true"
:show-list="showList"
@search="search"
></advanced-search>
</div>
<div class="search-symbol-inline">
<i class="cn-icon cn-icon-help"></i>
</div>
<!-- <div class="search-symbol-inline">-->
<!-- <i class="cn-icon cn-icon-help"></i>-->
<!-- </div>-->
</div>
</div>
</template>
@@ -22,6 +22,7 @@
<script>
import AdvancedSearch from '@/components/advancedSearch/Index'
import { humpToLine } from '@/utils/tools'
import { schemaDetectionSecurity } from '@/utils/static-data'
export default {
name: 'DetectionSearch',
props: {
@@ -33,74 +34,7 @@ export default {
data () {
return {
columnList: {
securityEvent: [
{
name: 'event_severity',
type: 'string',
// label: 'Event severity',
label: 'event_severity',
doc: {
constraints: {
operator_functions: '=,in'
}
}
},
{
name: 'security_type',
type: 'string',
// label: 'Security type',
label: 'security_type',
doc: {
constraints: {
operator_functions: '=,in'
}
}
},
{
name: 'victim_ip',
type: 'string',
// label: 'Victim IP'
label: 'victim_ip',
doc: {
constraints: {
operator_functions: '=,in'
}
}
},
{
name: 'victim_location_country',
type: 'string',
// label: 'Victim location'
label: 'victim_location_country',
doc: {
constraints: {
operator_functions: '=,in'
}
}
},
{
name: 'offender_ip',
type: 'string',
// label: 'Offender IP'
label: 'offender_ip',
doc: {
constraints: {
operator_functions: '=,in'
}
}
},
{
name: 'offender_location_country',
type: 'string',
// label: 'Offender location'
label: 'offender_location_country',
doc: {
constraints: {
operator_functions: '=,in'
}
}
}
],
securityEvent: schemaDetectionSecurity,
performanceEvent: [
{
name: 'event_severity',
@@ -113,17 +47,6 @@ export default {
}
}
},
{
name: 'event_type',
type: 'string',
// label: 'Event type'
label: 'event_type',
doc: {
constraints: {
operator_functions: '=,in'
}
}
},
{
name: 'app_name',
type: 'string',
@@ -169,7 +92,8 @@ export default {
value: 'OR',
label: 'OR'
}
]
],
showList: true
}
},
emits: ['search'],

View File

@@ -54,39 +54,36 @@
<div class="detection__list-statistics detection-border">
<div class="statistics__severity">
<div class="chart-header">
<div class="chart-header__title">{{$t('detection.severity')}}</div>
<div class="chart-header__title">{{$t('detections.severity')}}</div>
</div>
<template v-if="isStatisticsSeverityNoData">
<div class="no-data chart-content" >{{ $t('npm.noData') }}</div>
</template>
<template v-else>
<div class="chart-content" :id="`eventSeverityPie${pageType}`">
</div>
<div class="chart-content" :id="`eventSeverityPie${pageType}`"></div>
</template>
</div>
<div class="statistics__category">
<div class="chart-header">
<div class="chart-header__title">{{$t('detection.categoryProportion')}}</div>
<div class="chart-header__title">{{$t('detections.eventType')}}</div>
</div>
<template v-if="isStatisticsCategoryNoData">
<div class="no-data chart-content" >{{ $t('npm.noData') }}</div>
</template>
<template v-else>
<div class="chart-content" :id="`detectionCategoryPer${pageType}`">
</div>
<div class="chart-content" :id="`detectionCategoryPer${pageType}`"></div>
</template>
</div>
<div class="statistics__active-attack">
<div class="chart-header">
<div class="chart-header__title">{{pageType === detectionPageType.securityEvent ? $t('detection.activeAttacker') : $t('detections.activeEntity')}}</div>
<div class="chart-header__title">{{pageType === detectionPageType.securityEvent ? $t('detection.activeOffender') : $t('detections.activeEntity')}}</div>
</div>
<template v-if="isStatisticsActiveAttackNoData">
<div class="no-data chart-content" >{{ $t('npm.noData') }}</div>
</template>
<template v-else>
<div class="chart-content" style="padding-left: 5px;" :id="`detectionActiveAttacker${pageType}`">
</div>
<div class="chart-content" style="padding-left: 5px;" :id="`detectionActiveAttacker${pageType}`"></div>
</template>
</div>
</div>
@@ -136,7 +133,7 @@ import {
} from '@/views/detections/options/detectionOptions'
import { api, getData } from '@/utils/api'
import axios from 'axios'
import { extensionEchartY, reverseSortBy } from '@/utils/tools'
import { extensionEchartY, reverseSortBy, switchStatus } from '@/utils/tools'
import { useRoute } from 'vue-router'
import Loading from '@/components/common/Loading'
import ChartTabs from '@/components/common/ChartTabs'
@@ -160,18 +157,18 @@ export default {
i18n: 'entities.securityEvents',
path: '/detection/securityEvent',
icon: 'cn-icon cn-icon-a-SecurityEvent'
},
}
// {
// i18n: 'entities.regulatoryRiskEvents',
// path: '/detection/securityEvent',
// icon: 'cn-icon cn-icon-a-RegulatoryRiskEvent',
// disable: true
// },
{
i18n: 'overall.performanceEvents',
path: '/detection/performanceEvent',
icon: 'cn-icon cn-icon-a-PerformanceEvent'
}
// {
// i18n: 'overall.performanceEvents',
// path: '/detection/performanceEvent',
// icon: 'cn-icon cn-icon-a-PerformanceEvent'
// }
],
chartInit: [],
pageObj: {
@@ -185,61 +182,73 @@ export default {
filterData: {
securityEvent: [
{
title: this.$t('detections.eventSeverity'),
column: 'eventSeverity',
title: this.$t('overall.status'),
column: '',
topColumn: '', // todo schema暂无标识
collapse: false,
value: [], // value之间是or的关系
data: [] // 从接口动态获取,本项在获得数据后需要特殊处理左边框颜色
},
{
title: this.$t('detections.securityType'),
column: 'securityType',
title: this.$t('detections.severity'),
column: 'severity',
topColumn: 'severity',
collapse: false,
value: [], // value之间是or的关系
data: [] // 从接口动态获取,本项在获得数据后需要特殊处理左边框颜色
},
{
title: this.$t('detections.eventType'),
column: 'event_type',
topColumn: 'event_type',
collapse: false,
value: [],
data: [] // 从接口动态获取
},
{
title: this.$t('detections.victimIp'),
column: 'victimIp',
column: 'victim_ip',
topColumn: 'victim_ip',
collapse: false,
value: [],
showMore: true,
showIndex: 9,
data: [] // 从接口动态获取
},
{
title: this.$t('detections.victimLocation'),
column: 'victimLocationCountry',
collapse: false,
value: [],
showMore: false,
showIndex: 9,
data: [
{
label: 'China',
value: 'china',
count: 50
}
] // 从接口动态获取
},
// {
// title: this.$t('detections.victimLocation'),
// column: 'victimLocationCountry',
// collapse: false,
// value: [],
// showMore: false,
// showIndex: 9,
// data: [
// {
// label: 'China',
// value: 'china',
// count: 50
// }
// ] // 从接口动态获取
// },
{
title: this.$t('detections.offenderIp'),
column: 'offenderIp',
collapse: false,
value: [],
showMore: false,
showIndex: 9,
data: [] // 从接口动态获取
},
{
title: this.$t('detections.offenderLocation'),
column: 'offenderLocationCountry',
column: 'offender_ip',
topColumn: 'offender_ip',
collapse: false,
value: [],
showMore: false,
showIndex: 9,
data: [] // 从接口动态获取
}
// {
// title: this.$t('detections.offenderLocation'),
// column: 'offenderLocationCountry',
// collapse: false,
// value: [],
// showMore: false,
// showIndex: 9,
// data: [] // 从接口动态获取
// }
],
performanceEvent: [
{
@@ -275,24 +284,31 @@ export default {
isStatisticsCategoryNoData: false,
isStatisticsActiveAttackNoData: false,
loading: false,
oldActiveEntitySearchValue: ''
oldActiveEntitySearchValue: '',
initFlag: true // 初始化标识初始化时保证mounted执行
}
},
methods: {
// 初始化顶部大柱状图
initStatusData (params) {
getData(api.detection[this.pageType].statusStatistics, params).then(data => {
this.filterData[this.pageType][0].data = data.map(r => ({ label: this.$t(switchStatus(r.status)), value: r.status, count: r.count }))
})
},
/** 初始化顶部大柱状图 */
initEventSeverityTrendData (params) {
this.loading = true
/* getData(api.detection[this.pageType].eventSeverityTrend, params).then(data => {
// getData(api.detection[this.pageType].eventSeverityTrend, params).then(data => {
getData(api.detection[this.pageType].timeDistribution, params).then(data => {
this.eventSeverityData = data
if (!this.$_.isEmpty(data)) {
const dataMap = new Map()
data.forEach(item => {
if (item.eventSeverity) {
if (!dataMap.has(item.eventSeverity)) {
if (item.severity) {
if (!dataMap.has(item.severity)) {
const count = [[toTime(item.statTime), item.count]]
dataMap.set(item.eventSeverity, count)
dataMap.set(item.severity, count)
} else {
dataMap.get(item.eventSeverity).push([toTime(item.statTime), item.count])
dataMap.get(item.severity).push([toTime(item.statTime), item.count])
}
}
})
@@ -354,26 +370,18 @@ export default {
this.$nextTick(() => {
this.loading = false
})
}) */
const timer = setTimeout(() => {
this.loading = false
this.isEventSeverityNoData = true
this.isStatisticsCategoryNoData = true
this.isStatisticsSeverityNoData = true
this.isStatisticsActiveAttackNoData = true
clearTimeout(timer)
}, 150)
})
},
// 初始化左侧事件严重等级和小饼图
/** 初始化左侧事件严重等级和第一个小饼图 */
initEventSeverityData (params) {
getData(api.detection[this.pageType].eventSeverity, params).then(data => {
// getData(api.detection[this.pageType].eventSeverity, params).then(data => {
getData(api.detection[this.pageType].severityStatistics, params).then(data => {
this.statisticsSeverityData = data
if (!this.$_.isEmpty(data)) {
this.filterData[this.pageType][0].data = data.map(r => ({ label: r.eventSeverity, value: r.eventSeverity, count: r.count }))
this.filterData[this.pageType][1].data = data.map(r => ({ label: r.severity, value: r.severity, count: r.count }))
const eventSeverityOption = this.$_.cloneDeep(pieForSeverity)
eventSeverityOption.series[0].data = data.map(d => {
return { value: d.count, name: d.eventSeverity, itemStyle: { color: getSeverityColor(d.eventSeverity) } }
return { value: d.count, name: d.severity, itemStyle: { color: getSeverityColor(d.severity) } }
})
const chartDom = document.getElementById(`eventSeverityPie${this.pageType}`)
let detectionChart = echarts.getInstanceByDom(chartDom)
@@ -401,7 +409,7 @@ export default {
getData(api.detection[this.pageType].eventType, params).then(data => {
this.statisticsCategoryData = data
if (!this.$_.isEmpty(data)) {
this.filterData[this.pageType][1].data = data.map(r => ({
this.filterData[this.pageType][2].data = data.map(r => ({
label: r.eventType,
value: r.eventType,
count: r.count
@@ -429,13 +437,15 @@ export default {
console.log(error)
})
},
/** 第二个饼图和左侧filter的eventType */
initSecurityTypeData (params) {
getData(api.detection[this.pageType].securityType, params).then(data => {
// getData(api.detection[this.pageType].securityType, params).then(data => {
getData(api.detection[this.pageType].eventTypeStatistics, params).then(data => {
this.statisticsCategoryData = data
if (!this.$_.isEmpty(data)) {
this.filterData[this.pageType][1].data = data.map(r => ({
label: r.securityType,
value: r.securityType,
this.filterData[this.pageType][2].data = data.map(r => ({
label: r.eventType,
value: r.eventType,
count: r.count
}))
const chartDom = document.getElementById(`detectionCategoryPer${this.pageType}`)
@@ -447,7 +457,7 @@ export default {
this.chartInit.push(shallowRef(detectionChart))
const securityTypeOption = this.$_.cloneDeep(pieForSeverity)
securityTypeOption.series[0].data = data.map(d => {
return { value: d.count, name: d.securityType, itemStyle: { color: getAttackColor(d.securityType) } }
return { value: d.count, name: d.eventType, itemStyle: { color: getAttackColor(d.eventType) } }
})
detectionChart.setOption(securityTypeOption)
@@ -461,8 +471,10 @@ export default {
console.log(error)
})
},
/** 横向柱状图和左侧filter的offenderIp */
initOffenderIpData (params) {
getData(api.detection[this.pageType].offenderIp, params).then(data => {
// getData(api.detection[this.pageType].offenderIp, params).then(data => {
getData(api.detection[this.pageType].offenderIpStatistics, params).then(data => {
this.statisticsActiveAttackData = data
if (!this.$_.isEmpty(data)) {
this.filterData[this.pageType][4].data = data.map(r => ({
@@ -501,11 +513,12 @@ export default {
},
initVictimIpData (params) {
getData(api.detection[this.pageType].victimIp, params).then(data => {
this.filterData[this.pageType][2].data = data.map(r => ({ label: r.victimIp, value: r.victimIp, count: r.count }))
const { showMore, showIndex } = this.computeFilterPage(this.filterData[this.pageType][2].data)
this.filterData[this.pageType][2].showMore = showMore
this.filterData[this.pageType][2].showIndex = showIndex
// getData(api.detection[this.pageType].victimIp, params).then(data => {
getData(api.detection[this.pageType].victimIpStatistics, params).then(data => {
this.filterData[this.pageType][3].data = data.map(r => ({ label: r.victimIp, value: r.victimIp, count: r.count }))
const { showMore, showIndex } = this.computeFilterPage(this.filterData[this.pageType][3].data)
this.filterData[this.pageType][3].showMore = showMore
this.filterData[this.pageType][3].showIndex = showIndex
}).catch(error => {
console.log(error)
})
@@ -610,15 +623,15 @@ export default {
showIndex: 9
}
},
queryList () {
queryList (q) {
const params = {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime),
q: this.q,
resource: q,
pageSize: this.pageObj.pageSize,
pageNo: this.pageObj.pageNo
}
/* axios.get(api.detection[this.pageType].listBasic, { params }).then(response => {
axios.get(api.detection[this.pageType].securityList, { params }).then(response => {
if (response.status === 200) {
this.listData = response.data.data.result
} else {
@@ -627,11 +640,12 @@ export default {
this.$message.error(response.data.message)
}
})
getData(api.detection[this.pageType].listCount, params).then(data => {
// getData(api.detection[this.pageType].listCount, params).then(data => {
getData(api.detection[this.pageType].securityCount, params).then(data => {
this.pageObj.total = data
}).catch(error => {
console.log(error)
}) */
})
},
timeRefreshChange () {
this.initNoData()
@@ -674,8 +688,15 @@ export default {
} else {
this.pageObj.resetPageNo = true
}
this.queryFilter()
this.queryList()
// 参数q避免切换页码时地址栏参数q为空
let urlQ = ''
if (param.str) {
urlQ = encodeURI(param.str)
} else if (this.q) {
urlQ = encodeURI(this.q)
}
this.queryFilter(urlQ)
this.queryList(urlQ)
},
resetFilterData () {
this.filterData.securityEvent.forEach(d => {
@@ -685,22 +706,23 @@ export default {
d.data = []
})
},
queryFilter () {
queryFilter (q) {
this.resetFilterData()
const params = {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime),
q: this.q
resource: q
}
this.listData = []
this.initStatusData(params)
this.initEventSeverityTrendData(params)
// this.initEventSeverityData(params)
this.initEventSeverityData(params)
if (this.pageType === detectionPageType.securityEvent) {
// this.initOffenderIpData(params)
this.initOffenderIpData(params)
// this.initOffenderLocationData(params)
// this.initVictimIpData(params)
this.initVictimIpData(params)
// this.initVictimLocationData(params)
// this.initSecurityTypeData(params)
this.initSecurityTypeData(params)
} else if (this.pageType === detectionPageType.performanceEvent) {
// this.initActiveEntity(params)
// this.initEventTypeData(params)
@@ -713,7 +735,11 @@ export default {
pageNo (val) {
this.pageObj.pageNo = val || 1
this.pageObj.resetPageNo = false
this.search(this.metaList, this.q)
// 初始化时mounted和pageNo都会调用列表接口且pageNo先执行
// 初始化保证mounted执行后续pageNo变动则不影响接口调用
if (!this.initFlag) {
this.search(this.metaList, this.q)
}
},
// 点击上一页箭头
prev () {
@@ -748,6 +774,7 @@ export default {
},
mounted () {
this.queryFilter()
this.initFlag = false
this.queryList()
this.debounceFunc = this.$_.debounce(this.resize, 300)
window.addEventListener('resize', this.debounceFunc)

View File

@@ -3,162 +3,148 @@
<div class="overview__left">
<div class="overview__title">{{ $t('overall.remark') }}</div>
<div class="overview__row">
<div class="row__content">
<template v-if="detection.securityType === 'command and control'">
<div class="row__content1" v-if="basicInfo.malwareInfo">
<template v-if="basicInfo.malwareInfo.threatType === 'command and control'">
<span
class="row__content--link"
@click="goDetail('ip', detection.victimIp)"
>{{ detection.victimIp }}</span>&nbsp;
<span>
{{$t('detection.commandAndControl')}}
@click="goDetail('ip', detection.victimIp)">
{{ detection.victimIp }}
</span>&nbsp;
<span>{{$t('detection.commandAndControl')}}</span>
<span class="row__content--link" @click="goDetail('ip', detection.eventInfoObj.ioc_value)">
{{ detection.eventInfoObj.ioc_value || '-' }}
</span>
<span class="row__content--link" @click="goDetail('ip', basicInfo.iocValue)">{{basicInfo.iocValue || '-'}}</span></template
>
</template>
<template v-else>
<span
class="row__content--link"
@click="goDetail('ip', detection.victimIp)"
>{{ detection.victimIp }}</span
>&nbsp;
<span>
{{$t('detection.payloadAndDelivery')}}
class="row__content--link"
@click="goDetail('ip', detection.victimIp)">
{{ detection.victimIp }}
</span>&nbsp;
<span>{{$t('detection.payloadAndDelivery')}}</span>
<span
class="row__content--link"
@click="goDetail('ip', detection.eventInfoObj.ioc_value)">
{{ detection.eventInfoObj.ioc_value }}
</span>
<span class="row__content--link"
@click="goDetail('ip', basicInfo.iocValue)"
>{{
basicInfo.iocValue
}}</span></template>
</template>
</div>
</div>
<div class="overview__title">Fields</div>
<div class="overview__row">
<div class="row__label">{{ $t('detection.list.startTime') }}</div>
<div class="row__content">
{{
basicInfo.startTime
? dateFormatByAppearance(basicInfo.startTime)
: '-'
}}
<i class="cn-icon cn-icon-time2 row__content__icon"></i>
{{ detection.startTime ? dateFormatByAppearance(detection.startTime) : '-' }}
</div>
</div>
<div class="overview__row">
<div class="row__label">{{ $t('detections.victimIp') }}</div>
<div class="row__content">{{ basicInfo.victimIp || '-' }}</div>
<div class="row__content">{{ detection.victimIp || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label">{{ $t('detections.victimLocation') }}</div>
<div class="row__content">
{{ basicInfo.victimLocationCountry || '-' }}
</div>
<div class="row__content">{{ $_.get(basicInfo, 'victimInfo.location.country', '-') || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label">{{ $t('detections.victimAsn') }}</div>
<div class="row__content">{{ basicInfo.victimAsn || '-' }}</div>
<div class="row__content">{{ $_.get(basicInfo, 'victimInfo.asn.asn', '-') || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label">{{ $t('detections.offenderIp') }}</div>
<div class="row__content">{{ basicInfo.offenderIp || '-' }}</div>
<div class="row__content">{{ detection.offenderIp || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label">{{ $t('detections.offenderLocation') }}</div>
<div class="row__content">
{{ basicInfo.offenderLocationCountry || '-' }}
</div>
<div class="row__content">{{ $_.get(basicInfo, 'offenderInfo.location.country', '-') || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label">{{ $t('detections.offenderAsn') }}</div>
<div class="row__content">{{ basicInfo.offenderAsn || '-' }}</div>
<div class="row__content">{{ $_.get(basicInfo, 'offenderInfo.asn.asn', '-') || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label">{{ $t('overall.domain') }}</div>
<div class="row__content">{{ basicInfo.domain || '-' }}</div>
<div class="row__content">{{ detection.domain || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label">{{ $t('entities.domainCategory') }}</div>
<div class="row__content">
{{ basicInfo.domainCategoryName || '-' }}
</div>
<div class="row__content">{{ $_.get(basicInfo, 'domainInfo.category.categoryName', '-') || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label">
{{ $t('entities.domainDetail.categoryGroup') }}
</div>
<div class="row__content">
{{ basicInfo.domainCategoryGroup || '-' }}
</div>
<div class="row__label">{{ $t('entities.domainDetail.categoryGroup') }}</div>
<div class="row__content">{{ $_.get(basicInfo, 'domainInfo.category.categoryGroup', '-') || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label">{{ $t('entities.reputationLevel') }}</div>
<div class="row__content">
<div class="row__content" v-if="$_.get(basicInfo, 'domainInfo.category.reputationLevel')">
<div
class="row__tag"
:style="`background-color:${eventSeverityColor[basicInfo.domainReputationLevel]}`"
>
{{ basicInfo.domainReputationLevel || '-' }}
class="row__tag row__tag__level"
:style="`background-color:${eventSeverityColor[basicInfo.domainInfo.category.reputationLevel]}`">
{{ basicInfo.domainInfo.category.reputationLevel }}
</div>
</div>
<div class="row__content" v-else>-</div>
</div>
<div class="overview__row">
<div class="row__label">APP</div>
<div class="row__content">{{ basicInfo.appName || '-' }}</div>
<div class="row__content">{{ $_.get(basicInfo, 'appInfo.category.appName', '-') || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label">APP {{ $t('entities.category') }}</div>
<div class="row__content">{{ basicInfo.appCategory || '-' }}</div>
<div class="row__content">{{ $_.get(basicInfo, 'appInfo.category.appCategory', '-') || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label">APP {{ $t('entities.subcategory') }}</div>
<div class="row__content">{{ basicInfo.appSubcategory || '-' }}</div>
<div class="row__content">{{ $_.get(basicInfo, 'appInfo.category.appSubcategory', '-') || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label">{{ $t('overall.appRisk') }}</div>
<div class="row__content">
<div class="row__content" v-if="$_.get(basicInfo, 'appInfo.category.appRisk')">
<div
class="row__tag"
:style="`background-color:${eventSeverityColor[basicInfo.appRisk]}`"
>
{{ basicInfo.appRisk || '-' }}
class="row__tag row__tag__level"
:style="`background-color:${eventSeverityColor[basicInfo.appInfo.category.appRisk]}`">
{{ basicInfo.appInfo.category.appRisk }}
</div>
</div>
<div class="row__content" v-else>-</div>
</div>
<div class="overview__row">
<div class="row__label">{{ $t('detections.malware') }}</div>
<div class="row__content">{{ basicInfo.malwareName || '-' }}</div>
<div class="row__content">{{ $_.get(basicInfo, 'malwareInfo.malwareName', '-') || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label">{{ $t('detections.malwareAlias') }}</div>
<div class="row__content">{{ basicInfo.malwareAlias || '-' }}</div>
<div class="row__content">{{ $_.get(basicInfo, 'malwareInfo.malwareAlias', '-') || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label">{{ $t('detections.malwareDescription') }}</div>
<div class="row__content">
{{ basicInfo.malwareDescription || '-' }}
</div>
<div class="row__content">{{ $_.get(basicInfo, 'malwareInfo.mitreAttackDescription', '-') || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label">{{ $t('detections.malwarePlatforms') }}</div>
<div class="row__content">{{ basicInfo.malwarePlatforms || '-' }}</div>
<div class="row__content" v-if="$_.get(basicInfo, 'malwareInfo.mitreAttackPlatforms')">
<svg class="icon item-popover-up row__content__svg" aria-hidden="true">
<use xlink:href="#cn-icon-windows"></use>
</svg>
{{ basicInfo.malwareInfo.mitreAttackPlatforms }}
</div>
<div class="row__content" v-else>-</div>
</div>
<div class="overview__row">
<div class="row__label">{{ $t('detections.malwareTechniques') }}</div>
<div class="row__content">
{{ basicInfo.malwareTechniques || '-' }}
</div>
<div class="row__content">{{ $_.get(basicInfo, 'malwareInfo.mitreAttackTechniques', '-') || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label">{{ $t('detections.malwareGroups') }}</div>
<div class="row__content">
{{ basicInfo.malwareGroups || '-' }}
</div>
<div class="row__content">{{ $_.get(basicInfo, 'malwareInfo.mitreAttackGroups', '-') || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label">{{ $t('detections.reference') }}</div>
<div class="row__content row__content--link">
{{ reference || '-' }}
<div class="row__content row__content--link" v-if="$_.get(basicInfo, 'malwareInfo.reference')">
{{ basicInfo.malwareInfo.reference }}
</div>
<div class="row__content">-</div>
</div>
<!-- <template v-if="detection.securityType === 'command and control' || detection.securityType === 'payload_delivery'">
</template>-->
</div>
<div class="overview__right">
<div class="overview__title">{{ $t('detections.goToVictim') }}</div>
@@ -167,7 +153,7 @@
<span class="row__content--span">{{ $t('detections.viewDetailOf') }}</span> &nbsp;
<span
class="row__content--link"
@click="goDetail('ip', basicInfo.victimIp)">{{ basicInfo.victimIp }}</span>
@click="goDetail('ip', detection.victimIp)">{{ detection.victimIp }}</span>
</div>
</div>
<div class="overview__title">{{ $t('detections.goToOffender') }}</div>
@@ -176,22 +162,22 @@
<span class="row__content--span">{{ $t('detections.viewDetailOf') }}</span> &nbsp;
<span
class="row__content--link"
@click="goDetail('ip', basicInfo.offenderIp)"
>{{ basicInfo.offenderIp }}</span
@click="goDetail('ip', detection.offenderIp)"
>{{ detection.offenderIp }}</span
>&nbsp;&nbsp;
<span
class="row__content--link"
@click="goDetail('domain', basicInfo.domain)"
>{{ basicInfo.domain }}</span
@click="goDetail('domain', detection.domain)"
>{{ detection.domain }}</span
>
</div>
</div>
<div class="overview__title">{{ $t('detections.goToHunt') }}</div>
<div class="overview__row">
<div class="row__content row__content--link">
{{ $t('detections.viewAllRelated') }}
</div>
</div>
<!-- <div class="overview__title">{{ $t('detections.goToHunt') }}</div>-->
<!-- <div class="overview__row">-->
<!-- <div class="row__content row__content&#45;&#45;link">-->
<!-- {{ $t('detections.viewAllRelated') }}-->
<!-- </div>-->
<!-- </div>-->
<div class="overview__title">
{{ $t('detections.relatedDetections') }}
</div>
@@ -221,18 +207,12 @@
<div class="timeline__severity timeline__severity--high">
<i
class="cn-icon cn-icon-alert-level"
:style="`color:${eventSeverityColor[event.eventSeverity]}`"
:style="`color:${eventSeverityColor[event.severity]}`"
></i>
<span>{{ event.eventSeverity }}</span>
</div>
<div class="timeline__security-type">
{{ event.securityType }}
</div>
<div class="timeline__start-time">
{{
dateFormatByAppearance(event.startTime)
}}
<span>{{ event.severity }}</span>
</div>
<div class="timeline__security-type">{{ event.eventType }}</div>
<div class="timeline__start-time">{{ dateFormatByAppearance(event.startTime) }}</div>
</div>
</div>
<div class="row-timeline__foot">
@@ -240,7 +220,7 @@
class="detection-ip"
:class="{
'detection-ip__current':
[basicInfo.offenderIp, basicInfo.victimIp].indexOf(
[detection.offenderIp, detection.victimIp].indexOf(
event.offenderIp,
) > -1,
}"
@@ -252,7 +232,7 @@
class="detection-ip"
:class="{
'detection-ip__current':
[basicInfo.offenderIp, basicInfo.victimIp].indexOf(
[detection.offenderIp, detection.victimIp].indexOf(
event.victimIp,
) > -1,
}"
@@ -270,7 +250,7 @@
<script>
import axios from 'axios'
import { api } from '@/utils/api'
import { getMillisecond } from '@/utils/date-util'
import { getMillisecond, dateFormatByAppearance } from '@/utils/date-util'
import { eventSeverityColor, unitTypes } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
import _ from 'lodash'
@@ -291,25 +271,15 @@ export default {
formatT0 () {
const vm = this
return function (event) {
const diffSeconds = event.diffSeconds
const diffSeconds = this.detection.startTime - event.startTime
if (diffSeconds === 0) {
return 'T0'
}
const eventStartTime = event.startTime
const entityStartTime = vm.detection.startTime
if (
_.isNumber(diffSeconds) &&
_.isNumber(eventStartTime) &&
_.isNumber(entityStartTime)
) {
const suffix = unitConvert(
diffSeconds,
unitTypes.time,
's',
null,
0
).join('')
if (_.isNumber(diffSeconds) && _.isNumber(eventStartTime) && _.isNumber(entityStartTime)) {
const suffix = unitConvert(diffSeconds, unitTypes.time, 's', null, 0).join('')
if (eventStartTime > entityStartTime) {
return `T0+${suffix}`
} else if (eventStartTime < entityStartTime) {
@@ -322,58 +292,70 @@ export default {
},
methods: {
getMillisecond,
query () {
Promise.all([this.queryBasic(), this.queryEvent()]).then((responses) => {
responses[0].malwareTechniques = responses[0].malwareTechniques.length > 2 ? responses[0].malwareTechniques.replace('[', '').replace(']', '').split(',', 5).join(', ') : ''
responses[0].malwareGroups = responses[0].malwareGroups.length > 2 ? responses[0].malwareGroups.replace('[', '').replace(']', '').split(',', 5).join(', ') : ''
responses[0].malwarePlatforms = responses[0].malwarePlatforms.length > 1 ? responses[0].malwarePlatforms : ''
responses[0].malwareDescription = responses[0].malwareDescription.length > 1 ? responses[0].malwareDescription : ''
responses[0] && (this.basicInfo = responses[0])
responses[1] &&
(this.events = responses[1].sort(
(e1, e2) => e1.startTime - e2.startTime
))
})
},
queryBasic () {
return new Promise((resolve, reject) => {
try {
axios.get(api.detection.securityEvent.overviewBasic, {
params: {
eventId: this.detection.eventId,
startTime: this.detection.startTime,
endTime: this.detection.endTime
dateFormatByAppearance,
/** 初始化实体详情 */
initEntityDetail () {
// 为完整填充IP信息攻击者ip、受害者ip都进行调用
// 根据detection的eventInfo对象的ioc_type进行判断若为domainmalware信息从domain详情中获取并填充domain信息
// 若ioc_type为ip则调用ip接口填充malware信息
// 最后调用app填充app信息。经上获取完整实体详情则最少需要调用4次接口
const offenderIPDetail = axios.get(`${api.detection.securityEvent.ipDetail}/resource=${this.detection.offenderIp}`)
const victimIPDetail = axios.get(`${api.detection.securityEvent.ipDetail}/resource=${this.detection.victimIp}`)
let ipDetail
let domainDetail
let promiseList = []
const appDetail = axios.get(`${api.detection.securityEvent.appDetail}/resource=${this.detection.app}`)
if (this.detection.eventInfoObj.ioc_type.toLowerCase() === 'domain') {
domainDetail = axios.get(`${api.detection.securityEvent.domainDetail}/resource=${this.detection.eventInfoObj.ioc_value}`)
promiseList = [offenderIPDetail, victimIPDetail, domainDetail, appDetail]
}
if (this.detection.eventInfoObj.ioc_type.toLowerCase() === 'ip') {
ipDetail = axios.get(`${api.detection.securityEvent.ipDetail}/resource=${this.detection.eventInfoObj.ioc_value}`)
domainDetail = axios.get(`${api.detection.securityEvent.domainDetail}/resource=${this.detection.domain}`)
promiseList = [offenderIPDetail, victimIPDetail, domainDetail, appDetail, ipDetail]
}
Promise.all(promiseList).then((responses) => {
responses.forEach((res, i) => {
if (res.status === 200) {
if (i === 0) {
this.basicInfo.offenderInfo = res.data.data
}
}).then((response) => {
if (response.status === 200) {
resolve(response.data.data.result[0])
} else {
reject(response.data)
if (i === 1) {
this.basicInfo.victimInfo = res.data.data
}
})
} catch (e) {
reject(e)
if (i === 2) {
this.basicInfo.domainInfo = res.data.data
}
if (i === 3) {
this.basicInfo.appInfo = res.data.data
}
if (i === 4) {
this.basicInfo.malwareInfo = res.data.data.malwareInfo
}
} else {
console.error(res)
}
})
if (this.detection.eventInfoObj.ioc_type.toLowerCase() === 'domain') {
this.basicInfo.malwareInfo = this.basicInfo.domainInfo.malware || {}
}
})
},
queryEvent () {
return new Promise((resolve, reject) => {
try {
axios.get(api.detection.securityEvent.overviewEvent, {
params: {
startTime: this.detection.startTime,
offenderIp: this.detection.offenderIp,
victimIp: this.detection.victimIp
}
}).then((response) => {
if (response.status === 200) {
resolve(response.data.data.result)
} else {
reject(response.data)
}
})
} catch (e) {
reject(e)
axios.get(api.detection.securityEvent.relationEvent, {
params: {
// startTime: this.detection.startTime,
unbiasedTime: this.detection.startTime,
offenderIp: this.detection.offenderIp,
victimIp: this.detection.victimIp,
biasSecond: 3600
}
}).then((response) => {
if (response.status === 200) {
this.events = response.data.data.result.sort((e1, e2) => e1.startTime - e2.startTime)
} else {
this.events = []
}
})
},
@@ -391,7 +373,17 @@ export default {
}
},
mounted () {
this.query()
this.initEntityDetail()
this.queryEvent()
}
}
</script>
<style scoped>
.row__label {
width: 176px;
}
.row__content {
width: calc(100% - 176px);
}
</style>