1- import { Component , EventEmitter , Input , OnChanges , OnInit , Output } from '@angular/core' ;
1+ import { Component , Input , OnChanges } from '@angular/core' ;
22import { ECharts } from 'echarts' ;
3+ import { UtmAlertType } from '../../../shared/types/alert/utm-alert.type' ;
4+ import { Side } from '../../../shared/types/event/event' ;
5+ import { EventDataTypeEnum } from '../../alert-management/shared/enums/event-data-type.enum' ;
36import { AdversaryAlerts } from '../models' ;
47
58@Component ( {
@@ -13,14 +16,17 @@ export class AdversaryAlertsGraphComponent implements OnChanges {
1316 baseHeight = 600 ;
1417 nodeGap = 6 ;
1518 option : any ;
19+ viewAlertDetail = false ;
20+ alertDetail : UtmAlertType ;
21+ EventDataTypeEnum = EventDataTypeEnum ;
1622
1723 ngOnChanges ( ) : void {
1824 if ( this . data ) {
1925 this . option = this . buildOption ( this . data ) ;
2026 }
2127 }
2228
23- onChartInit ( chart : ECharts ) {
29+ onChartInit ( chart : any ) {
2430
2531 chart . on ( 'mouseover' , ( params ) => {
2632 if ( params . dataType === 'node' ) {
@@ -35,11 +41,72 @@ export class AdversaryAlertsGraphComponent implements OnChanges {
3541 } ) ;
3642
3743 chart . on ( 'click' , ( params ) => {
38- if ( params . dataType === 'node' ) {
44+ if ( params . componentType === 'series'
45+ && params . seriesType === 'sankey'
46+ && ( params . dataType === 'node' || params . dataType === 'label' || params . dataType === 'edge' ) ) {
47+ this . alertDetail = params . data . meta . alert || null ;
48+ this . viewAlertDetail = ! ! this . alertDetail ;
3949 }
4050 } ) ;
4151 }
4252
53+ private getGraphicElements ( ) {
54+ const sankey = {
55+ left : 10 ,
56+ right : 180 ,
57+ top : 20 ,
58+ bottom : 40
59+ } ;
60+
61+ const chartElement = document . querySelector ( '.chart-container' ) ;
62+ const chartContainerWidth = chartElement ? chartElement . clientWidth : 1000 ;
63+ const columnWidth = chartContainerWidth / 3 ;
64+
65+ const depthPositions = [
66+ { left : sankey . left , width : columnWidth } ,
67+ { left : sankey . left + columnWidth , width : columnWidth } ,
68+ { left : sankey . left + ( columnWidth * 2 ) , width : columnWidth }
69+ ] ;
70+
71+ const graphicElements : any [ ] = [ ] ;
72+ const labels = [ 'Adversary' , 'Alerts' , 'Echoes' ] ;
73+ const colors = [
74+ { fill : 'rgba(31, 119, 180, 0.06)' , text : '#1F77B4' } ,
75+ { fill : 'rgba(255, 127, 14, 0.06)' , text : '#FF7F0E' } ,
76+ { fill : 'rgba(44, 160, 44, 0.06)' , text : '#2CA02C' }
77+ ] ;
78+
79+ depthPositions . forEach ( ( pos , index ) => {
80+ const color = colors [ index ] ;
81+
82+ graphicElements . push ( {
83+ type : 'rect' ,
84+ left : pos . left ,
85+ top : sankey . top ,
86+ shape : { width : pos . width - 10 , height : this . chartHeight - sankey . top - sankey . bottom } ,
87+ style : {
88+ fill : color . fill ,
89+ stroke : 'none'
90+ }
91+ } ) ;
92+
93+ graphicElements . push ( {
94+ type : 'text' ,
95+ left : pos . left + ( pos . width / 2 ) ,
96+ top : 0 ,
97+ style : {
98+ text : labels [ index ] ,
99+ fontSize : 12 ,
100+ fontWeight : 'bold' ,
101+ fill : color . text ,
102+ textAlign : 'center'
103+ }
104+ } ) ;
105+ } ) ;
106+
107+ return graphicElements ;
108+ }
109+
43110 private buildOption ( adversaryAlerts : AdversaryAlerts [ ] ) : any {
44111 const nodes : any [ ] = [ ] ;
45112 const links : any [ ] = [ ] ;
@@ -112,6 +179,10 @@ export class AdversaryAlertsGraphComponent implements OnChanges {
112179 depth : 1 ,
113180 symbolSize : getNodeSize ( childCount ) ,
114181 meta : {
182+ alert : {
183+ ...alert ,
184+ hasChildren : true
185+ } ,
115186 severity : alert . severityLabel ,
116187 timestamp : alert . timestamp ,
117188 dataSource : alert . dataSource
@@ -133,7 +204,7 @@ export class AdversaryAlertsGraphComponent implements OnChanges {
133204
134205 alertWithChildren . children . forEach ( child => {
135206 const childKey = nodeKey ( child . id , child . name ) ;
136- const childLabel = truncate ( child . name ) ;
207+ const childLabel = truncate ( this . getLabel ( child ) ) ;
137208 const childSeverityColor = severityColors [ child . severityLabel . toLowerCase ( ) ] || severityColors . default ;
138209
139210 if ( ! nodeSet . has ( childKey ) ) {
@@ -149,6 +220,7 @@ export class AdversaryAlertsGraphComponent implements OnChanges {
149220 depth : 2 ,
150221 symbolSize : getNodeSize ( 1 ) ,
151222 meta : {
223+ alert : child ,
152224 severity : child . severityLabel ,
153225 timestamp : child . timestamp ,
154226 dataSource : child . dataSource
@@ -165,6 +237,9 @@ export class AdversaryAlertsGraphComponent implements OnChanges {
165237 color : gradientColor ( severityColor , childSeverityColor ) ,
166238 opacity : 0.5 ,
167239 curveness : 0.2
240+ } ,
241+ meta : {
242+ alert : child ,
168243 }
169244 } ) ;
170245 } ) ;
@@ -190,6 +265,7 @@ export class AdversaryAlertsGraphComponent implements OnChanges {
190265 ` ;
191266 }
192267 } ,
268+ graphic : this . getGraphicElements ( ) ,
193269 series : [ {
194270 type : 'sankey' ,
195271 orient : 'horizontal' ,
@@ -214,14 +290,35 @@ export class AdversaryAlertsGraphComponent implements OnChanges {
214290 layoutIterations : 32 ,
215291 left : 20 ,
216292 right : 180 ,
217- top : 20 ,
218- bottom : 40 ,
293+ top : 30 ,
294+ bottom : 50 ,
219295 label : {
296+ show : true ,
220297 position : 'right' ,
221298 fontSize : 10 ,
222299 formatter : ( params : any ) => params . name . split ( '::' ) [ 1 ] || params . name
223300 }
224301 } ]
225302 } ;
226303 }
304+
305+ private getLabel ( alert : UtmAlertType ) {
306+ if ( alert . target ) {
307+ const target : any = alert . target as Side ;
308+ if ( target . ip ) {
309+ return target . ip ;
310+ } else if ( target . host ) {
311+ return target . host ;
312+ } else if ( target . user ) {
313+ return target . user ;
314+ }
315+ } else {
316+ return alert . dataSource ;
317+ }
318+ }
319+
320+ closeDetail ( ) {
321+ this . alertDetail = null ;
322+ this . viewAlertDetail = false ;
323+ }
227324}
0 commit comments