11package postee.trivyoperator.slack
22
3- title:= sprintf (" Trivy Operator %s Report for - %s" , [input .kind, input .metadata.name])
4-
5- result:= res {
6- res:= [
7- { " type" :" section" ,
8- " text" : {" type" :" mrkdwn" ," text" : sprintf (" *CRITICAL:* %d" , [input .report.summary.criticalCount])}},
9- { " type" :" section" ,
10- " text" : {" type" :" mrkdwn" ," text" : sprintf (" *HIGH:* %d" , [input .report.summary.highCount])}},
11- { " type" :" section" ,
12- " text" : {" type" :" mrkdwn" ," text" : sprintf (" *MEDIUM:* %d" , [input .report.summary.mediumCount])}},
13- { " type" :" section" ,
14- " text" : {" type" :" mrkdwn" ," text" : sprintf (" *LOW:* %d" , [input .report.summary.lowCount])}},
15- { " type" :" section" ,
16- " text" : {" type" :" mrkdwn" ," text" : sprintf (" *UNKNOWN:* %d" , [input .report.summary.unknownCount])}},
17- { " type" :" section" ,
18- " text" : {" type" :" mrkdwn" ," text" : sprintf (" *NONE:* %d" , [input .report.summary.noneCount])}},
19- ]
20- }
3+ import data .postee.flat_array # converts [[{...},{...}], [{...},{...}]] to [{...},{...},{...},{...}]
4+ import data .postee.with_default
5+
6+ # ############################################ Common functions ############################################
7+
8+ # render_sections split collection of cells provided to chunks of 5 rows each and wraps every chunk with section element
9+ render_sections (rows, caption, headers) = result {
10+ count (rows) > 0 # only if some vulnerabilities are found
11+ rows_and_header := array.concat (headers, rows)
12+ a := flat_array ([s |
13+ # code below converts 2 dimension array like [[row1, row2, ... row5], ....]
14+ group_size := 10 # it's 5 but every row is represented by 2 items
15+ num_chunks := ceil (count (rows_and_header) / group_size) - 1
16+ indices := {b | b := numbers.range (0 , num_chunks)[_] * group_size}
17+ some k
18+ fields := [array.slice (rows_and_header, i, i + group_size) | i := indices[_]][k]
19+ # builds markdown section based on slice
20+
21+ s := with_caption (fields, caption, k)
22+ ])
23+ result := array.concat (a, [{" type" : " divider" }])
24+ }
25+
26+ render_sections (rows, caption, headers) = [] { # do not render section if provided collection is empty
27+ count (rows) == 0
28+ }
29+
30+ with_caption (fields, caption, position) = s {
31+ position == 0
32+ s := [
33+ {
34+ " type" : " section" ,
35+ " text" : {
36+ " type" : " mrkdwn" ,
37+ " text" : caption,
38+ },
39+ },
40+ {
41+ " type" : " section" ,
42+ " fields" : fields,
43+ },
44+ ]
45+ }
46+ with_caption (fields, caption, position) = s {
47+ position > 0
48+ s := [
49+ {
50+ " type" : " section" ,
51+ " fields" : fields,
52+ },
53+ ]
54+ }
55+
56+
57+ # ##########################################################################################################
58+
59+ vln_list (severity) = l {
60+ # builds list of rows for section for the given severity
61+ some i
62+ vlnrb := [r |
63+ item := input .report.vulnerabilities[i]
64+ vlnname := item.vulnerabilityID
65+
66+ fxvrsn := with_default (item, " fixedVersion" , " none" )
67+ resource_name = with_default (item, " resource" , " none" )
68+ resource_version = with_default (item, " installedVersion" , " none" )
69+ url = with_default (item, " primaryLink" ," " )
70+ item.severity == severity # only items with severity matched
71+
72+ r := [
73+ {" type" : " mrkdwn" , " text" : sprintf (" <%s|%s>" ,[url,vlnname])},
74+ {" type" : " mrkdwn" , " text" : concat (" / " , [resource_name, resource_version, fxvrsn])},
75+ ]
76+ ]
77+
78+ caption := sprintf (" *%s severity vulnerabilities*" , [severity]) # TODO make first char uppercase
79+
80+ headers := [
81+ {" type" : " mrkdwn" , " text" : " *Vulnerability ID*" },
82+ {" type" : " mrkdwn" , " text" : " *Resource / Version / Fixed version*" },
83+ ]
84+
85+ # split rows and wrap slices with markdown section
86+ l := render_sections (flat_array (vlnrb), caption, headers)
87+ }
88+
89+ image_name := sprintf (" %s:%s" , [
90+ with_default (input .report.artifact," repository" ," unknown" ),
91+ with_default (input .report.artifact," tag" ," unknown" )
92+ ])
93+ # ##########################################################################################################
94+ postee := with_default (input , " postee" , {})
95+
96+ title = sprintf (" Vulnerability scan report %s" , [image_name]) # title is
97+
98+ result = res {
99+
100+ header := [
101+ {
102+ " type" : " header" ,
103+ " text" : {
104+ " type" : " plain_text" ,
105+ " text" : sprintf (" Vulnerability issue with image:%s in namespace %s" ,[image_name, with_default (input .metadata," namespace" ," unknown" )]),
106+ },
107+ }
108+ ]
109+
110+ summary := [
111+ {
112+ " type" : " divider"
113+ },
114+ {
115+ " type" : " context" ,
116+ " elements" : [
117+ {" type" : " mrkdwn" , " text" : " *Summary totals:*" },
118+ ],
119+ },
120+ {
121+ " type" : " context" ,
122+ " elements" : [
123+ {" type" : " mrkdwn" , " text" : sprintf (" Critical: *%d*" , [input .report.summary.criticalCount])},
124+ {" type" : " mrkdwn" , " text" : sprintf (" High: *%d*" , [input .report.summary.highCount])},
125+ {" type" : " mrkdwn" , " text" : sprintf (" Medium: *%d*" , [input .report.summary.mediumCount])},
126+ {" type" : " mrkdwn" , " text" : sprintf (" Low: *%d*" , [input .report.summary.lowCount])},
127+ {" type" : " mrkdwn" , " text" : sprintf (" Unknown: *%d*" , [input .report.summary.unknownCount])},
128+ ],
129+ },
130+ {
131+ " type" : " divider"
132+ }
133+ ]
134+
135+ res := flat_array ([
136+ header,
137+ summary,
138+ vln_list (" CRITICAL" ),
139+ vln_list (" HIGH" ),
140+ vln_list (" MEDIUM" ),
141+ vln_list (" LOW" ),
142+ vln_list (" UNKNOWN" )
143+ ])
144+ }
0 commit comments