diff --git a/src/assets/css/components/views/detections/detections.scss b/src/assets/css/components/views/detections/detections.scss index e536d61a..bcf81f8a 100644 --- a/src/assets/css/components/views/detections/detections.scss +++ b/src/assets/css/components/views/detections/detections.scss @@ -15,5 +15,44 @@ margin-bottom: 10px; width: 100%; background-color: white; + + .chart-header { + display: flex; + justify-content:space-between; + align-items:center; + padding: 10px 20px 10px 0px; + height: 40px; + + font-size: 14px; + color: $--color-text-primary; + transition: all 0.2s; + border-bottom: 1px solid #E7EAED; + + .chart-header__title { + max-width: calc(100% - 100px); + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + } + + .chart-content { + height:calc(100% - 40px); + } + .statistics__severity { + width: 33%; + margin-left:15px; + margin-right:15px; + } + .statistics__category { + width: 34%; + margin-left:15px; + margin-right:15px; + } + .statistics__active-attack { + width: 33%; + margin-left:15px; + margin-right:15px; + } } } diff --git a/src/utils/api.js b/src/utils/api.js index 75160ba7..15e2e317 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -72,7 +72,12 @@ export const api = { entityIpDetailAlert: '/interface/entity/detail/overview/ip/alert', entityIpDetailSecurity: '/interface/entity/detail/overview/ip/security', entityIpRelatedServerDomain: '/interface/entity/detail/overview/ip/relatedDomain', - entityIpRelatedServerApp: '/interface/entity/detail/overview/ip/relatedApp' + entityIpRelatedServerApp: '/interface/entity/detail/overview/ip/relatedApp', + //detection + detectionEventSeverity:'/interface/detection/filter/severity', + detectionAttackType:'/interface/detection/filter/attackType', + detectionOffenderIp:'/interface/detection/filter/offenderIp', + detectionSeverity:'/interface/detection/filter/severity', } /* panel */ diff --git a/src/views/detections/Index.vue b/src/views/detections/Index.vue index c0554727..a1822e71 100644 --- a/src/views/detections/Index.vue +++ b/src/views/detections/Index.vue @@ -14,8 +14,8 @@ >
-
-
+
+
-
+
+
+
+
{{$t('detection.severity')}}
+
+
+
+
+
+
+
{{$t('detection.categoryProportion')}}
+
+
+
+
+
+
+
{{$t('detection.activeAttacker')}}
+
+
+
+
+
{ + response = { + "code": 200, + "success": true, + "msg": "OK", + "data": { + "resultType": "table", + "result": [ + { + "eventSeverity": "Critical", + "count": 1048 + },{ + "eventSeverity": "High", + "count": 735 + },{ + "eventSeverity": "Medium", + "count": 580 + },{ + "eventSeverity": "Low", + "count": 484 + },{ + "eventSeverity": "Info", + "count": 300 + } + ] + } + } + if (response.code === 200) { + this.eventSeverityData = [ + ['time', '2021/11/12 12:00', '2021/11/13 12:00', '2021/11/14 12:00', '2021/11/15 12:00','2021/11/16 12:00','2021/11/17 12:00','2021/11/18 12:00','2021/11/19 12:00','2021/11/20 12:00','2021/11/21 12:00'], + ['Critical', 5, 0, 2, 0,0,3,3,0,4,2], + ['High', 5, 7, 3,0, 0,3,3,20,2,2], + ['Medium', 10, 8, 20, 8,0,5,5,5,5,20], + ['Low', 18, 6, 5, 0,0,2,20,2,2,2], + ['Info', 16, 10, 4, 0,7,0,8,5,15,1] + ] + } + }).finally(() => { + this.$nextTick(() => { + this.eventSeverityOption.dataset.source = this.eventSeverityData + this.eventSeverityOption && detectionChart.setOption(this.eventSeverityOption); + }) + }) + }, + initSeverityPerData(){ + const chartDom = document.getElementById('detectionSeverityPer'); + const detectionChart = echarts.init(chartDom); + this.severityPerOption = this.$_.cloneDeep(pieForSeverity) + + const queryParams = { + ...this.timeFilter, + q:this.q, + } + get(api.detectionSeverity, queryParams).then(response => { + response = { + "code": 200, + "success": true, + "msg": "OK", + "data": { + "resultType": "table", + "result": [ + { + "eventSeverity": "Critical", + "count": 1048 + },{ + "eventSeverity": "High", + "count": 735 + },{ + "eventSeverity": "Medium", + "count": 580 + },{ + "eventSeverity": "Low", + "count": 484 + },{ + "eventSeverity": "Info", + "count": 300 + } + ] + } + } + if (response.code === 200) { + this.severityPerData = response.data.result.map(d => { + return { value: d.count, name: d.eventSeverity,itemStyle:{color:getSeverityColor(d.eventSeverity)}} + }) + } + }).finally(() => { + this.$nextTick(() => { + this.severityPerOption.series[0].data = this.severityPerData + this.severityPerOption && detectionChart.setOption(this.severityPerOption); + }) + }) + }, + initCategoryPerData(){ + const chartDom = document.getElementById('detectionCategoryPer'); + const detectionChart = echarts.init(chartDom); + this.categoryPerOption = this.$_.cloneDeep(pieForSeverity) + + const queryParams = { + ...this.timeFilter, + q:this.q, + } + get(api.detectionAttackType, queryParams).then(response => { + response = { + "code": 200, + "success": true, + "msg": "OK", + "data": { + "resultType": "table", + "result": [ + { + "attackType": "Command and control", + "count": 1048 + },{ + "attackType": "Payload_delivery", + "count": 735 + },{ + "attackType": "Cryptomining", + "count": 580 + },{ + "attackType": "phishing", + "count": 484 + },{ + "attackType": "dga", + "count": 300 + },{ + "attackType": "ddos", + "count": 50 + }, + ] + } + } + if (response.code === 200) { + this.categoryPerData = response.data.result.map(d => { + return { value: d.count, name: d.attackType,itemStyle:{color:getAttackColor(d.attackType)}} + }) + } + }).finally(() => { + this.$nextTick(() => { + this.categoryPerOption.series[0].data = this.categoryPerData + this.categoryPerOption && detectionChart.setOption(this.categoryPerOption); + }) + }) + + }, + initActiveAttackData(){ + const chartDom = document.getElementById('detectionActiveAttacker'); + const detectionChart = echarts.init(chartDom); + this.activeAttackOption = this.$_.cloneDeep(activeAttackBar) + + const queryParams = { + ...this.timeFilter, + q:this.q, + } + get(api.detectionOffenderIp, queryParams).then(response => { + response = { + "code": 200, + "success": true, + "msg": "OK", + "data": { + "resultType": "table", + "result": [ + { + "offenderIp": "192.168.12.21", + "count": 99999 + },{ + "offenderIp": "192.168.22.21", + "count": 88888 + },{ + "offenderIp": "192.168.32.21", + "count": 77777 + },{ + "offenderIp": "192.168.42.21", + "count": 66666 + },{ + "offenderIp": "192.168.52.21", + "count": 55555 + } + ] + } + } + if (response.code === 200) { + this.activeAttackData = response.data.result.map(d => { + return [d.count,d.offenderIp] + }) + } + }).finally(() => { + this.$nextTick(() => { + this.activeAttackOption.series[0].data = this.activeAttackData.reverse() + this.activeAttackOption && detectionChart.setOption(this.activeAttackOption); + }) + }) + }, timeRefreshChange () { if (!this.$refs.dateTimeRange.isCustom) { const value = this.timeFilter.dateRangeValue @@ -226,6 +464,12 @@ export default { }) } }, + mounted () { + this.initEventSeverityData() + this.initSeverityPerData() + this.initCategoryPerData() + this.initActiveAttackData() + }, watch: { timeFilter (n) { this.search(this.metaList, this.q) @@ -236,7 +480,23 @@ export default { const { startTime, endTime } = getNowTime(dateRangeValue) const timeFilter = ref({ startTime, endTime, dateRangeValue }) return { - timeFilter + timeFilter, + severityPerChart: { + params: { + url: '/interface/entity/detail/ip/trafficMap?startTime={{startTime}}&endTime={{endTime}}&country={{country}}&ip={{ip}}', + unitType: 'number' + }, + id: 'detectionSeverityPer', + type: 32 + }, + categoryPerChart: { + params: { + url: '/interface/entity/detail/ip/trafficMap?startTime={{startTime}}&endTime={{endTime}}&country={{country}}&ip={{ip}}', + unitType: 'number' + }, + id: 'detectionCategoryPer', + type: 32 + } } } } diff --git a/src/views/detections/options/detectionOptions.js b/src/views/detections/options/detectionOptions.js new file mode 100644 index 00000000..cb18368e --- /dev/null +++ b/src/views/detections/options/detectionOptions.js @@ -0,0 +1,249 @@ +import { + chartColor, + tooLongFormatter +} from '@/views/charts/charts/tools' +import unitConvert from '@/utils/unit-convert' +import _ from 'lodash' + +const activeAttackColorMappings = [ + { value: 'Command and control', color: '#51a9ee' }, + { value: 'Payload_delivery', color: '#49bcf2' }, + { value: 'Cryptomining', color: '#4ad7eb' }, + { value: 'phishing', color: '#4cd4c8' }, + { value: 'dga', color: '#7acc7e' }, + { value: 'ddos', color: '#a7db69' } +] + +export function getAttackColor (type) { + const mapping = activeAttackColorMappings.find(m => m.value === type) + return mapping && mapping.color ? _.cloneDeep(mapping.color) : null +} + +const severityColorMappings = [ + { value: 'Critical', color: '#d84c4c' }, + { value: 'High', color: '#ff9a79' }, + { value: 'Medium', color: '#ffb65a' }, + { value: 'Low', color: '#ffdd4a' }, + { value: 'Info', color: '#d7c668' } +] + +export function getSeverityColor (type) { + const mapping = severityColorMappings.find(m => m.value === type) + return mapping && mapping.color ? _.cloneDeep(mapping.color) : null +} + +export const multipleBarOption = { + legend: { + icon: 'circle', + top: 10, + right:10, + itemWidth: 10, // 设置宽度 + itemHeight: 10, // 设置高度 + }, + tooltip: {}, + dataset: { + source: [ + ] + }, + xAxis: { + type: 'category', + axisTick: { show: false }, + axisLine: { + show: true , + lineStyle :{ + color:'#eaedef' + } + }, + axisLabel:{ + color:'#737373', + interval: 0 + }, + splitLine:{ + show:false + }, + }, + yAxis: { + axisTick: { show: false }, + axisLine: { + show: true, + lineStyle :{ + color:'#eaedef' + } + }, + axisLabel:{ + color:'#737373' + }, + splitLine:{ + show:false + }, + interval:10 + }, + grid: { + top: 40, + left: 25, + right: 25, + bottom: 20, + containLabel: true + }, + series: [ + { + name: 'Critical', + type: 'bar', + barWidth: 15, + seriesLayoutBy: 'row', + itemStyle: { + color: '#d84c4c' + } + }, + { + name: 'High', + type: 'bar', + barWidth: 15, + seriesLayoutBy: 'row', + itemStyle: { + color: '#ff9a79' + } + }, + { + name: 'Medium', + type: 'bar', + barWidth: 15, + seriesLayoutBy: 'row', + itemStyle: { + color: '#ffb65a' + } + }, + { + name: 'Low', + type: 'bar', + barWidth: 15, + seriesLayoutBy: 'row', + itemStyle: { + color: '#ffdd4a' + } + }, + { + name: 'Info', + type: 'bar', + barWidth: 15, + seriesLayoutBy: 'row', + itemStyle: { + color: '#d7c668' + } + }, + ] +}; +export const pieForSeverity = { + tooltip: { + appendToBody: true + }, + color: chartColor, + animation: false, + legend: { + orient: 'vertical', + type: 'plain', + left: '60%', + top: 'middle', + icon: 'circle', + itemWidth: 10, // 设置宽度 + itemHeight: 10, // 设置高度 + itemGap: 10, + formatter: tooLongFormatter, + tooltip: { + show: true + } + }, + series: [ + { + type: 'pie', + selectedMode: 'single', + radius: ['42%', '65%'], + center: ['30%', '50%'], + data: [], + label: { + show:false + }, + labelLine:{ + show:false + }, + tooltip: { + formatter: function (param, index, callback) { + return `${param.name}: ${param.value}` + } + }, + emphasis: { + itemStyle: { + shadowBlur: 10, + shadowOffsetX: 0, + shadowColor: 'rgba(0, 0, 0, 0.5)' + } + } + } + ] +} +export const activeAttackBar = { + tooltip: { + appendToBody: true, + trigger: 'item', + textStyle: { + width: '20px', + overflow: 'truncate' + }, + formatter: function (param, index, callback) { + return `${param.name}: ${unitConvert(param.value[0], 'number').join(' ')}` + }, + show: true, + className: 'nz-chart-tooltip', + extraCssText: 'box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);max-width: 300px !important' + }, + xAxis: { + axisTick: { show: false }, + axisLine: { show: false }, + axisLabel:{ + show:false + }, + splitLine:{ + show:false + }, + }, + grid: { + top: 20, + left: 10, + right: 25, + bottom: 20, + containLabel: true + }, + yAxis: { + type: 'category', + axisTick: { show: false }, + axisLine: { show: false }, + axisLabel:{ + show:true + }, + splitLine:{ + show:false + }, + }, + series: [{ + barWidth: 5, + itemStyle: { + normal: { + color: function(params) { + const colorList = ['#d1bd50','#c9d150','#fddd52','#ffb65a','#fe845d']; + return colorList[params.dataIndex] + } + } + }, + data: [], + type: 'bar', + label: { + show: true, + position: 'right', + valueAnimation: true, + formatter: function (param, index, callback) { + return `${unitConvert(param.value[0], 'number').join(' ')}` + }, + }, + barCategoryGap: '10%' + }] +} \ No newline at end of file