11package com .faction ;
22
33import java .util .ArrayList ;
4+
45import java .util .Arrays ;
6+ import java .util .Collections ;
57import java .util .HashMap ;
68import java .util .LinkedHashMap ;
9+ import java .util .LinkedList ;
710import java .util .List ;
811import java .util .Map ;
912import java .util .stream .Collectors ;
1619import org .jfree .chart .ChartFactory ;
1720import org .jfree .chart .ChartUtils ;
1821import org .jfree .chart .JFreeChart ;
22+ import org .jfree .chart .axis .CategoryAxis ;
1923import org .jfree .chart .axis .NumberAxis ;
2024import org .jfree .chart .axis .NumberTickUnit ;
25+ import org .jfree .chart .axis .ValueAxis ;
26+ import org .jfree .chart .labels .ItemLabelAnchor ;
27+ import org .jfree .chart .labels .ItemLabelPosition ;
28+ import org .jfree .chart .labels .StandardCategoryItemLabelGenerator ;
2129import org .jfree .chart .plot .CategoryPlot ;
2230import org .jfree .chart .plot .PlotOrientation ;
2331import org .jfree .chart .renderer .category .BarRenderer ;
2432import org .jfree .chart .renderer .category .StandardBarPainter ;
33+ import org .jfree .chart .ui .TextAnchor ;
2534import org .jfree .chart .renderer .category .CategoryItemRenderer ;
35+ import org .jfree .chart .renderer .category .CategoryItemRendererState ;
2636import org .jfree .data .category .CategoryDataset ;
2737import org .jfree .data .category .DefaultCategoryDataset ;
2838import org .sonatype .plexus .components .cipher .Base64 ;
2939
40+ import java .awt .BasicStroke ;
3041import java .awt .Color ;
3142import java .awt .Font ;
43+ import java .awt .FontMetrics ;
44+ import java .awt .Graphics2D ;
3245import java .awt .Paint ;
46+ import java .awt .geom .Rectangle2D ;
3347import java .io .ByteArrayOutputStream ;
3448import java .io .IOException ;
3549
3650public class VulnerabilityBarChart extends BaseExtension implements com .faction .extender .ReportManager {
3751
3852 private int totalLevels = 6 ;
3953 private LinkedHashMap <String ,Color > severities = new LinkedHashMap <>();
54+ private HashMap <Long , SevObj > sevMap = new HashMap <>();
55+ private Boolean showNumbers = false ;
56+ private Boolean showBorders = false ;
57+ private String yAxisName ="" ;
58+
59+
60+ private class SevObj {
61+ public Color color ;
62+ public Color border ;
63+ public String name ;
64+ public SevObj (String name , Color color , Color border ) {
65+ this .color = color ;
66+ this .border = border ;
67+ this .name = name ;
68+ }
69+ }
4070
4171
4272 @ Override
@@ -55,31 +85,70 @@ public String reportCreate(Assessment asmt, List<Vulnerability> vulns, String re
5585 * This section represents the getters from the user supplied configs
5686 *
5787 */
88+ private Color getColorOrDefault (String value ) {
89+ if (value == null ) {
90+ return new Color (0x000000 );
91+ }else {
92+ try {
93+ return new Color (Integer .parseInt (value .trim (), 16 ));
94+ }catch (Exception e ) {
95+ return new Color (0x000000 );
96+ }
97+ }
98+ }
5899 private void setUpSeverities () {
59100 String colorsCSV = this .getConfigs ().get ("Severity Colors" );
101+ String sevMapCSV = this .getConfigs ().get ("Severity Mapping" );
102+ String bordersCSV = this .getConfigs ().get ("Border Colors" );
103+ showNumbers = this .getConfigs ().get ("Show Numbers" ) == null ? false : Boolean .parseBoolean (this .getConfigs ().get ("Show Numbers" ).trim ());
104+ showBorders = this .getConfigs ().get ("Show Borders" ) == null ? false : Boolean .parseBoolean (this .getConfigs ().get ("Show Borders" ).trim ());
105+ yAxisName = this .getConfigs ().get ("Y Axis Label" ) == null ? "" : this .getConfigs ().get ("Y Axis Label" );
60106 String w = this .getConfigs ().get ("Width" );
61107 if (colorsCSV == null || colorsCSV .trim ().equals ("" )) {
62- severities . put ("Critical" , new Color (0xdd4b39 ));
63- severities . put ("High" , new Color (0xf39c12 ));
64- severities . put ("Medium" , new Color (0x00c0ef ));
65- severities . put ("Low" , new Color (0x39cccc ));
66- severities . put ("Recommended" , new Color (0x00a65a ));
67- severities . put ("Informational" , new Color (0x95a5a6 ));
108+ this . sevMap . put (5l , new SevObj ( "Critical" , new Color (0xdd4b39 ), new Color ( 0xdd4b39 ) ));
109+ this . sevMap . put (4l , new SevObj ( "High" , new Color (0xf39c12 ), new Color ( 0xf39c12 ) ));
110+ this . sevMap . put (3l , new SevObj ( "Medium" , new Color (0x00c0ef ), new Color ( 0x00c0ef ) ));
111+ this . sevMap . put (2l , new SevObj ( "Low" , new Color (0x39cccc ), new Color ( 0x39cccc ) ));
112+ this . sevMap . put (1l , new SevObj ( "Recommended" , new Color (0x00a65a ), new Color ( 0x00a65a ) ));
113+ this . sevMap . put (0l , new SevObj ( "Informational" , new Color (0x95a5a6 ), new Color ( 0x95a5a6 ) ));
68114 }else {
69115 String [] colors = colorsCSV .split ("," );
116+ String [] sevIds = sevMapCSV .split ("," );
117+ String [] borderColors = bordersCSV .split ("," );
70118 this .severities = Arrays
71119 .stream (colors )
72120 .map ( color -> color .split (":" ))
73121 .collect (Collectors .toMap (
74122 keyValue -> keyValue [0 ],
75- keyValue -> new Color (Integer .parseInt (keyValue [1 ].trim (), 16 )),
123+ keyValue -> this .getColorOrDefault (keyValue [1 ]),
124+ (v1 ,v2 ) -> v1 ,
125+ LinkedHashMap <String , Color >::new
126+ ));
127+ LinkedHashMap <String ,Color > borders = Arrays
128+ .stream (borderColors )
129+ .map ( color -> color .split (":" ))
130+ .collect (Collectors .toMap (
131+ keyValue -> keyValue [0 ],
132+ keyValue -> this .getColorOrDefault (keyValue [1 ]),
76133 (v1 ,v2 ) -> v1 ,
77134 LinkedHashMap <String , Color >::new
78135 ));
79- this .totalLevels = colors .length ;
80136
81- String [] sevs = this .severities .keySet ().toArray (new String [0 ]);
137+ this .sevMap = Arrays
138+ .stream (sevIds )
139+ .map ( sevs -> sevs .split (":" ))
140+ .collect (Collectors .toMap (
141+ keyValue -> Long .parseLong (keyValue [1 ].trim ()),
142+ keyValue -> new SevObj (
143+ keyValue [0 ].trim (),
144+ this .severities .get (keyValue [0 ].trim ()),
145+ borders .get (keyValue [0 ].trim ())
146+ ),
147+ (v1 ,v2 ) -> v1 ,
148+ HashMap <Long , SevObj >::new
149+ ));
82150
151+ this .totalLevels = colors .length ;
83152 }
84153
85154 }
@@ -106,6 +175,14 @@ private Paint[] getColors() {
106175 return new Paint [] {new Color (Integer .parseInt (defaultColor .trim (), 16 )) };
107176 }
108177 }
178+ private Paint getDefaultLabelColor () {
179+ String defaultColor = this .getConfigs ().get ("Default Label Color" );
180+ if (defaultColor == null || defaultColor .trim ().equals ("" )) {
181+ return new Color (0xFFFFFF ) ; // Default Color
182+ }else {
183+ return new Color (Integer .parseInt (defaultColor .trim (), 16 ));
184+ }
185+ }
109186
110187 private int [] getDimensions () {
111188 int [] dimensions = {900 , 600 };
@@ -136,7 +213,7 @@ private String vulnSeverityChartImage(List<Vulnerability> vulns, String reportTe
136213 chart .getPlot ().setBackgroundPaint ( new Color (0 ,0 ,0 ,0 ));
137214 chart .getPlot ().setBackgroundImageAlpha (0 );
138215 // set bar colors
139- final CategoryItemRenderer renderer = new CustomRenderer (this .getColors () , this .getDefaultColor ());
216+ final CategoryItemRenderer renderer = new CustomRenderer (this .sevMap , this . showBorders , this . showNumbers , this . getDefaultColor ()[ 0 ] , this .getDefaultLabelColor ());
140217 final CategoryPlot plot = chart .getCategoryPlot ();
141218 //Match Font
142219 Font font = new Font ("Arial" , Font .PLAIN , 25 );
@@ -145,6 +222,14 @@ private String vulnSeverityChartImage(List<Vulnerability> vulns, String reportTe
145222 // remove bar gradient and shaddow
146223 ((BarRenderer ) renderer ).setBarPainter (new StandardBarPainter ());
147224 ((BarRenderer ) renderer ).setShadowVisible (false );
225+ ((BarRenderer ) renderer ).setDefaultItemLabelGenerator (new StandardCategoryItemLabelGenerator ());
226+ if (this .showNumbers ) {
227+ ((BarRenderer ) renderer ).setDefaultItemLabelsVisible (true );
228+ ItemLabelPosition position = new ItemLabelPosition (ItemLabelAnchor .CENTER ,
229+ TextAnchor .CENTER , TextAnchor .CENTER , 0.0 );
230+ renderer .setDefaultPositiveItemLabelPosition (position );
231+ renderer .setDefaultItemLabelPaint (this .getDefaultLabelColor ());
232+ }
148233 plot .setRenderer (renderer );
149234 // Show mininal gridlines
150235 plot .setRangeGridlinesVisible (false );
@@ -164,42 +249,133 @@ private String vulnSeverityChartImage(List<Vulnerability> vulns, String reportTe
164249 return image ;
165250 }
166251
252+ private List <String >getSevNames () {
253+ return this .sevMap .keySet ().stream ()
254+ .map ( id -> this .sevMap .get (id ).name )
255+ .collect (Collectors .toList ());
256+ }
257+ static Long [] getSevIds (HashMap <Long ,SevObj > sevMap ) {
258+ Long [] ids = (Long []) sevMap .keySet ().toArray (new Long [0 ]);
259+ Arrays .sort (ids , Collections .reverseOrder ());
260+ return ids ;
261+ }
262+
167263
168264 private CategoryDataset createDataset (List <Vulnerability > vulns ) {
169265
170266 DefaultCategoryDataset dataset = new DefaultCategoryDataset ();
171- String [] severityNames = (String []) this .severities .keySet ().toArray (new String [0 ]);
172- for (int level =totalLevels -1 ; level >=0 ; level -- ) {
173- final int index = level ;
174- long count = vulns .stream ().filter (v -> v .getOverall ().equals (new Long (index ))).count ();
175- dataset .setValue (count , "Severity" , severityNames [this .totalLevels - index -1 ]);
176- }
267+ Arrays .asList (getSevIds (this .sevMap )).forEach (id -> {
268+ long count = vulns .stream ().filter (v -> v .getOverall ().equals (id )).count ();
269+ dataset .setValue (count , this .yAxisName , this .sevMap .get (id ).name );
270+ });
271+
177272 return dataset ;
178273 }
179-
180274 private JFreeChart createChart (CategoryDataset dataset ) {
181-
182- JFreeChart barChart = ChartFactory .createBarChart ("" , "" , "Severity" , dataset , PlotOrientation .VERTICAL , false ,
275+ String yAxisName = this . yAxisName ;
276+ JFreeChart barChart = ChartFactory .createBarChart ("" , "" , yAxisName , dataset , PlotOrientation .VERTICAL , false ,
183277 true , false );
184278 return barChart ;
185279 }
186280
187281 class CustomRenderer extends BarRenderer {
188282
189- private Paint [] colors ;
190- private Paint [] defaultColor ;
283+ private Paint defaultBarColor ;
284+ private Paint labelColor ;
285+ private HashMap <Long , SevObj > colorMap ;
286+ private Boolean showBorders ;
287+ private Boolean showNumbers ;
191288
192- public CustomRenderer (final Paint [] colors , final Paint [] defaultColor ) {
193- this .colors = colors ;
289+ public CustomRenderer (final HashMap <Long ,SevObj > colorMap , Boolean showBorders , Boolean showNumbers , Paint defaultBarColor , Paint labelColor ) {
290+ this .colorMap = colorMap ;
291+ this .showBorders = showBorders ;
292+ this .defaultBarColor = defaultBarColor ;
293+ this .labelColor = labelColor ;
294+ this .showNumbers = showNumbers ;
194295 }
195-
296+ @ Override
196297 public Paint getItemPaint (final int row , final int column ) {
197- if (column >= this .colors . length ) {
198- Paint [] defaultPaint = this .defaultColor ;
199- return defaultPaint [ 0 ] ;
298+ if (column >= this .colorMap . keySet (). size () ) {
299+ Paint defaultPaint = this .defaultBarColor ;
300+ return defaultPaint ;
200301 }
201- return this .colors [column % this .colors .length ];
302+ Long [] sevIds = VulnerabilityBarChart .getSevIds (colorMap );
303+ Long id = sevIds [column ];
304+
305+
306+ return sevMap .get (id ).color ;
202307 }
308+ @ Override
309+ public void drawItem (Graphics2D g2 ,
310+ CategoryItemRendererState state ,
311+ Rectangle2D dataArea ,
312+ CategoryPlot plot ,
313+ CategoryAxis domainAxis ,
314+ ValueAxis rangeAxis ,
315+ CategoryDataset dataset ,
316+ int row ,
317+ int column ,
318+ int pass ) {
319+
320+ // Only draw bars in pass 1
321+ if (pass != 0 ) {
322+ return ;
323+ }
324+
325+ // Determine the bar's position and dimensions
326+ double value = dataset .getValue (row , column ).doubleValue ();
327+ double barStart = rangeAxis .valueToJava2D (
328+ rangeAxis .getLowerBound (), dataArea , plot .getRangeAxisEdge ());
329+ double barEnd = rangeAxis .valueToJava2D (value , dataArea , plot .getRangeAxisEdge ());
330+ double barWidth = state .getBarWidth ();
331+
332+ // Calculate the bar's rectangle
333+ double x = domainAxis .getCategoryStart (column , getColumnCount (), dataArea , plot .getDomainAxisEdge ())
334+ + state .getBarWidth () * row ;
335+ double y = Math .min (barStart , barEnd );
336+ double height = Math .abs (barEnd - barStart );
337+
338+ Rectangle2D bar = new Rectangle2D .Double (x , y , barWidth , height );
339+
340+ // Draw the bar (default rendering)
341+ g2 .setPaint (getItemPaint (row , column ));
342+ g2 .fill (bar );
343+
344+ // Draw the border
345+ if (this .showBorders ) {
346+ Long [] sevIds = VulnerabilityBarChart .getSevIds (colorMap );
347+ Long id = sevIds [column ];
348+ Color c = sevMap .get (id ).border ;
349+ System .out .println (c );
350+ g2 .setPaint (c );
351+ g2 .setStroke (new BasicStroke (1.0f )); // Adjust stroke as needed
352+ }else {
353+ g2 .setPaint (Color .BLACK ); // Default border color
354+ g2 .setStroke (new BasicStroke (0f )); // Adjust stroke as needed
355+ }
356+ g2 .draw (bar );
357+
358+ if (this .showNumbers ) {
359+ // Draw the item label (bar value) in the center of the bar
360+ int fontSize = 12 ; // Set your desired font size
361+ Font originalFont = g2 .getFont (); // Save the original font
362+ Font newFont = originalFont .deriveFont ((float ) fontSize );
363+ g2 .setFont (newFont );
364+ String label = String .valueOf ( (int ) value ); // Convert the value to a string
365+ FontMetrics metrics = g2 .getFontMetrics ();
366+ double labelWidth = metrics .stringWidth (label );
367+ double labelHeight = metrics .getHeight ();
368+
369+ // Calculate label position (center of the bar)
370+ double labelX = bar .getCenterX () - (labelWidth / 2 );
371+ double labelY = bar .getCenterY () + (labelHeight / 4 ); // Adjust for vertical alignment
372+
373+ g2 .setPaint (this .labelColor ); // Set label color
374+ g2 .drawString (label , (float ) labelX , (float ) labelY );
375+ g2 .setFont (originalFont );
376+ }
377+ }
378+
203379 }
204380
205381
0 commit comments